diff --git a/.gitmodules b/.gitmodules index e4ea8a8..efa45ca 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,11 +17,11 @@ [submodule "SDL2"] path = third-party/SDL url = https://github.com/libsdl-org/SDL - branch = main + branch = SDL2 [submodule "libzip"] path = third-party/libzip url = https://github.com/nih-at/libzip - branch = master + branch = main [submodule "efsw"] path = third-party/efsw url = https://github.com/SpartanJ/efsw diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ae29bf..66f0c4a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # MassBuilderSaveTool -# Copyright (C) 2021-2022 Guillaume Jacquemin +# Copyright (C) 2021-2024 Guillaume Jacquemin # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,105 +14,127 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.24) project(MassBuilderSaveTool) set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/modules/" ${CMAKE_MODULE_PATH}) -SET(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) +set(ZLIB_USE_STATIC_LIBS ON CACHE BOOL "" FORCE) # Required on setups where zlib is available as both dynamic and static libs. Which is pretty much everywhere, actually. -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) +option(SAVETOOL_USE_SYSTEM_LIBS "Use system-wide versions of the dependencies instead of the versions provided by submodules." OFF) -set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) -set(DIRECTX OFF CACHE BOOL "" FORCE) # We use OpenGL. -set(SDL_ATOMIC OFF CACHE BOOL "" FORCE) -set(SDL_CPUINFO OFF CACHE BOOL "" FORCE) -set(SDL_EVENTS ON CACHE BOOL "" FORCE) -set(SDL_FILE OFF CACHE BOOL "" FORCE) -set(SDL_FILESYSTEM OFF CACHE BOOL "" FORCE) -set(SDL_HAPTIC OFF CACHE BOOL "" FORCE) -set(SDL_LOCALE OFF CACHE BOOL "" FORCE) -set(SDL_POWER OFF CACHE BOOL "" FORCE) -set(SDL_RENDER OFF CACHE BOOL "" FORCE) -set(SDL_SENSOR OFF CACHE BOOL "" FORCE) -set(SDL_THREADS ON CACHE BOOL "" FORCE) -set(SDL_TIMERS ON CACHE BOOL "" FORCE) -set(SDL_SHARED OFF CACHE BOOL "" FORCE) -add_subdirectory(third-party/SDL EXCLUDE_FROM_ALL) +include(CMakeDependentOption) +cmake_dependent_option(SAVETOOL_USE_SYSTEM_CORRADE_MAGNUM "Use system-wide versions of Corrade and Magnum." ON "SAVETOOL_USE_SYSTEM_LIBS" OFF) +cmake_dependent_option(SAVETOOL_USE_SYSTEM_SDL2 "Use a system-wide version of SDL2." ON "SAVETOOL_USE_SYSTEM_LIBS" OFF) +cmake_dependent_option(SAVETOOL_USE_SYSTEM_LIBZIP "Use a system-wide version of libzip." ON "SAVETOOL_USE_SYSTEM_LIBS" OFF) +cmake_dependent_option(SAVETOOL_USE_SYSTEM_EFSW "Use a system-wide version of EFSW." ON "SAVETOOL_USE_SYSTEM_LIBS" OFF) +cmake_dependent_option(SAVETOOL_USE_SYSTEM_LIBCURL "Use a system-wide version of libcurl." ON "SAVETOOL_USE_SYSTEM_LIBS" OFF) -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) +if(NOT SAVETOOL_USE_SYSTEM_LIBS OR NOT (SAVETOOL_USE_SYSTEM_CORRADE_MAGNUM AND SAVETOOL_USE_SYSTEM_SDL2 AND SAVETOOL_USE_SYSTEM_LIBZIP AND SAVETOOL_USE_SYSTEM_EFSW AND SAVETOOL_USE_SYSTEM_LIBCURL)) + # Generic variables shared by multiple libs that don't provide their own. + set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) + set(BUILD_STATIC_LIBS ON CACHE BOOL "" FORCE) + set(BUILD_TESTING OFF CACHE BOOL "" FORCE) +endif() -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) +if(NOT SAVETOOL_USE_SYSTEM_SDL2) + set(DIRECTX OFF CACHE BOOL "" FORCE) # We use OpenGL. + set(SDL_ATOMIC OFF CACHE BOOL "" FORCE) + set(SDL_CPUINFO OFF CACHE BOOL "" FORCE) + set(SDL_EVENTS ON CACHE BOOL "" FORCE) + set(SDL_FILE OFF CACHE BOOL "" FORCE) + set(SDL_FILESYSTEM OFF CACHE BOOL "" FORCE) + set(SDL_HAPTIC OFF CACHE BOOL "" FORCE) + set(SDL_LOCALE OFF CACHE BOOL "" FORCE) + set(SDL_POWER OFF CACHE BOOL "" FORCE) + set(SDL_RENDER OFF CACHE BOOL "" FORCE) + set(SDL_SENSOR OFF CACHE BOOL "" FORCE) + set(SDL_THREADS ON CACHE BOOL "" FORCE) + set(SDL_TIMERS ON CACHE BOOL "" FORCE) + set(SDL_SHARED OFF CACHE BOOL "" FORCE) + add_subdirectory(third-party/SDL EXCLUDE_FROM_ALL) +endif(NOT SAVETOOL_USE_SYSTEM_SDL2) -set(IMGUI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third-party/imgui) -set(MAGNUM_WITH_IMGUI ON CACHE BOOL "" FORCE) -add_subdirectory(third-party/magnum-integration EXCLUDE_FROM_ALL) +if(NOT SAVETOOL_USE_SYSTEM_CORRADE_MAGNUM) + 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) + add_subdirectory(third-party/corrade EXCLUDE_FROM_ALL) -set(ENABLE_COMMONCRYPTO OFF CACHE BOOL "" FORCE) -set(ENABLE_GNUTLS OFF CACHE BOOL "" FORCE) -set(ENABLE_MBEDTLS OFF CACHE BOOL "" FORCE) -set(ENABLE_OPENSSL OFF CACHE BOOL "" FORCE) -set(ENABLE_WINDOWS_CRYPTO OFF CACHE BOOL "" FORCE) -set(ENABLE_BZIP2 OFF CACHE BOOL "" FORCE) -set(ENABLE_LZMA OFF CACHE BOOL "" FORCE) -set(ENABLE_ZSTD OFF CACHE BOOL "" FORCE) -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) -add_subdirectory(third-party/libzip EXCLUDE_FROM_ALL) + 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(VERBOSE OFF CACHE BOOL "" FORCE) -set(BUILD_TEST_APP OFF CACHE BOOL "" FORCE) -set(EFSW_INSTALL OFF CACHE BOOL "" FORCE) -add_subdirectory(third-party/efsw EXCLUDE_FROM_ALL) + 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(BUILD_TESTING OFF CACHE BOOL "" FORCE) -set(BUILD_CURL_EXE OFF CACHE BOOL "" FORCE) -set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) -set(ENABLE_UNICODE ON CACHE BOOL "" FORCE) -set(ENABLE_INET_PTON OFF CACHE BOOL "" FORCE) -set(ENABLE_DEBUG OFF CACHE BOOL "" FORCE) -set(ENABLE_THREADED_RESOLVER OFF CACHE BOOL "" FORCE) -set(HTTP_ONLY ON CACHE BOOL "" FORCE) -set(USE_LIBIDN2 OFF CACHE BOOL "" FORCE) -set(USE_WIN32_IDN ON CACHE BOOL "" FORCE) -set(CURL_USE_LIBPSL OFF CACHE BOOL "" FORCE) -set(CURL_STATIC_CRT OFF CACHE BOOL "" FORCE) -set(CURL_USE_SCHANNEL ON CACHE BOOL "" FORCE) -set(CURL_USE_LIBSSH2 OFF CACHE BOOL "" FORCE) # For some reason, even when HTTP_ONLY is set to ON, libcurl will try to link to libssh2. -add_subdirectory(third-party/curl EXCLUDE_FROM_ALL) + set(IMGUI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third-party/imgui) + set(MAGNUM_WITH_IMGUI ON CACHE BOOL "" FORCE) + add_subdirectory(third-party/magnum-integration EXCLUDE_FROM_ALL) +endif(NOT SAVETOOL_USE_SYSTEM_CORRADE_MAGNUM) + +if(NOT SAVETOOL_USE_SYSTEM_LIBZIP) + set(ENABLE_COMMONCRYPTO OFF CACHE BOOL "" FORCE) + set(ENABLE_GNUTLS OFF CACHE BOOL "" FORCE) + set(ENABLE_MBEDTLS OFF CACHE BOOL "" FORCE) + set(ENABLE_OPENSSL OFF CACHE BOOL "" FORCE) + set(ENABLE_WINDOWS_CRYPTO OFF CACHE BOOL "" FORCE) + set(ENABLE_BZIP2 OFF CACHE BOOL "" FORCE) + set(ENABLE_LZMA OFF CACHE BOOL "" FORCE) + set(ENABLE_ZSTD OFF CACHE BOOL "" FORCE) + 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) + add_subdirectory(third-party/libzip EXCLUDE_FROM_ALL) +endif(NOT SAVETOOL_USE_SYSTEM_LIBZIP) + +if(NOT SAVETOOL_USE_SYSTEM_EFSW) + set(VERBOSE OFF CACHE BOOL "" FORCE) + set(BUILD_TEST_APP OFF CACHE BOOL "" FORCE) + set(EFSW_INSTALL OFF CACHE BOOL "" FORCE) + add_subdirectory(third-party/efsw EXCLUDE_FROM_ALL) +endif(NOT SAVETOOL_USE_SYSTEM_EFSW) + +if(NOT SAVETOOL_USE_SYSTEM_LIBCURL) + set(BUILD_CURL_EXE OFF CACHE BOOL "" FORCE) + set(ENABLE_UNICODE ON CACHE BOOL "" FORCE) + set(ENABLE_INET_PTON OFF CACHE BOOL "" FORCE) + set(ENABLE_DEBUG OFF CACHE BOOL "" FORCE) + set(ENABLE_THREADED_RESOLVER OFF CACHE BOOL "" FORCE) + set(HTTP_ONLY ON CACHE BOOL "" FORCE) + set(USE_LIBIDN2 OFF CACHE BOOL "" FORCE) + set(USE_WIN32_IDN ON CACHE BOOL "" FORCE) + set(CURL_USE_LIBPSL OFF CACHE BOOL "" FORCE) + set(CURL_STATIC_CRT OFF CACHE BOOL "" FORCE) + set(CURL_USE_SCHANNEL ON CACHE BOOL "" FORCE) + set(CURL_USE_LIBSSH2 OFF CACHE BOOL "" FORCE) # For some reason, even when HTTP_ONLY is set to ON, libcurl will try to link to libssh2. + add_subdirectory(third-party/curl EXCLUDE_FROM_ALL) +endif(NOT SAVETOOL_USE_SYSTEM_LIBCURL) add_subdirectory(src) diff --git a/README.md b/README.md index ef55037..4314677 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ launch `MassBuilderSaveTool-.exe`. 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`. +2. Run `pacman -S git mingw-w64-ucrt-x86_64-toolchain mingw-w64-ucrt-x86_64-cmake mingw-w64-ucrt-x86_64-ninja mingw-w64-ucrt-x86_64-zlib`. 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 ..` diff --git a/modules/FindCorrade.cmake b/modules/FindCorrade.cmake index 780932b..7d2e725 100644 --- a/modules/FindCorrade.cmake +++ b/modules/FindCorrade.cmake @@ -16,6 +16,8 @@ # components, which are: # # Containers - Containers library +# Interconnect - Interconnect library +# Main - Main library # PluginManager - PluginManager library # TestSuite - TestSuite library # Utility - Utility library @@ -68,7 +70,7 @@ # mode for MSVC 2017 # CORRADE_MSVC2015_COMPATIBILITY - Defined if compiled with compatibility # mode for MSVC 2015 -# CORRADE_BUILD_DEPRECATED - Defined if compiled with deprecated APIs +# CORRADE_BUILD_DEPRECATED - Defined if compiled with deprecated features # included # CORRADE_BUILD_STATIC - Defined if compiled as static libraries. # Default are shared libraries. @@ -78,6 +80,9 @@ # CORRADE_BUILD_MULTITHREADED - Defined if compiled in a way that makes it # possible to safely use certain Corrade features simultaneously in multiple # threads +# CORRADE_BUILD_CPU_RUNTIME_DISPATCH - Defined if built with code paths +# optimized for multiple architectres with the best matching variant selected +# at runtime based on detected CPU features # CORRADE_TARGET_UNIX - Defined if compiled for some Unix flavor # (Linux, BSD, macOS) # CORRADE_TARGET_APPLE - Defined if compiled for Apple platforms @@ -98,6 +103,8 @@ # CORRADE_TARGET_MSVC - Defined if compiling with MSVC or Clang with # a MSVC frontend # CORRADE_TARGET_MINGW - Defined if compiling under MinGW +# CORRADE_CPU_USE_IFUNC - Defined if GNU IFUNC is allowed to be used +# for runtime dispatch in the Cpu library # CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT - Defined if PluginManager # doesn't support dynamic plugin loading due to platform limitations # CORRADE_TESTSUITE_TARGET_XCTEST - Defined if TestSuite is targeting Xcode @@ -115,6 +122,7 @@ # automatically) # CORRADE_TESTSUITE_XCTEST_RUNNER - Path to XCTestRunner.mm.in file # CORRADE_TESTSUITE_ADB_RUNNER - Path to AdbRunner.sh file +# CORRADE_UTILITY_JS - Path to CorradeUtility.js file # CORRADE_PEDANTIC_COMPILER_OPTIONS - List of pedantic compiler options used # for targets with :prop_tgt:`CORRADE_USE_PEDANTIC_FLAGS` enabled # CORRADE_PEDANTIC_COMPILER_DEFINITIONS - List of pedantic compiler @@ -208,7 +216,7 @@ # # ...) # -# Unline the above version this puts everything into ```` on +# Unlike the above version this puts everything into ```` on # both DLL and non-DLL platforms. If ```` is set to # :variable:`CMAKE_CURRENT_BINARY_DIR` (e.g. for testing purposes), the files # are copied directly, without the need to perform install step. Note that the @@ -264,7 +272,7 @@ # This file is part of Corrade. # # Copyright © 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, -# 2017, 2018, 2019, 2020, 2021, 2022 +# 2017, 2018, 2019, 2020, 2021, 2022, 2023 # Vladimír Vondruš # # Permission is hereby granted, free of charge, to any person obtaining a @@ -317,6 +325,7 @@ set(_corradeFlags BUILD_STATIC BUILD_STATIC_UNIQUE_GLOBALS BUILD_MULTITHREADED + BUILD_CPU_RUNTIME_DISPATCH TARGET_UNIX TARGET_APPLE TARGET_IOS @@ -325,10 +334,12 @@ set(_corradeFlags TARGET_WINDOWS_RT TARGET_EMSCRIPTEN TARGET_ANDROID - # TARGET_X86 etc and TARGET_LIBCXX are not exposed to CMake as the meaning - # is unclear on platforms with multi-arch binaries or when mixing different - # STL implementations. TARGET_GCC etc are figured out via UseCorrade.cmake, - # as the compiler can be different when compiling the lib & when using it. + # TARGET_X86 etc, TARGET_32BIT, TARGET_BIG_ENDIAN and TARGET_LIBCXX etc. + # are not exposed to CMake as the meaning is unclear on platforms with + # multi-arch binaries or when mixing different STL implementations. + # TARGET_GCC etc are figured out via UseCorrade.cmake, as the compiler can + # be different when compiling the lib & when using it. + CPU_USE_IFUNC PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT TESTSUITE_TARGET_XCTEST UTILITY_USE_ANSI_COLORS) @@ -406,6 +417,8 @@ foreach(_component ${Corrade_FIND_COMPONENTS}) if(TARGET Corrade::${_component}) set(Corrade_${_component}_FOUND TRUE) else() + unset(Corrade_${_component}_FOUND) + # Library (and not header-only) components if(_component IN_LIST _CORRADE_LIBRARY_COMPONENTS AND NOT _component IN_LIST _CORRADE_HEADER_ONLY_COMPONENTS) add_library(Corrade::${_component} UNKNOWN IMPORTED) @@ -461,8 +474,9 @@ foreach(_component ${Corrade_FIND_COMPONENTS}) # Interconnect library if(_component STREQUAL Interconnect) # Disable /OPT:ICF on MSVC, which merges functions with identical - # contents and thus breaks signal comparison - if(CORRADE_TARGET_WINDOWS AND CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # contents and thus breaks signal comparison. Same case is for + # clang-cl which uses the MSVC linker by default. + if(CORRADE_TARGET_WINDOWS AND (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")) if(CMAKE_VERSION VERSION_LESS 3.13) set_property(TARGET Corrade::${_component} PROPERTY INTERFACE_LINK_LIBRARIES "-OPT:NOICF,REF") @@ -496,25 +510,33 @@ foreach(_component ${Corrade_FIND_COMPONENTS}) elseif(_component STREQUAL PluginManager) # -ldl is handled by Utility now - # TestSuite library has some additional files + # TestSuite library has some additional files. If those are not found, + # set the component _FOUND variable to false so it works properly both + # when the component is required and when it's optional. elseif(_component STREQUAL TestSuite) # XCTest runner file if(CORRADE_TESTSUITE_TARGET_XCTEST) find_file(CORRADE_TESTSUITE_XCTEST_RUNNER XCTestRunner.mm.in PATH_SUFFIXES share/corrade/TestSuite) - set(CORRADE_TESTSUITE_XCTEST_RUNNER_NEEDED CORRADE_TESTSUITE_XCTEST_RUNNER) + if(NOT CORRADE_TESTSUITE_XCTEST_RUNNER) + set(Corrade_${_component}_FOUND FALSE) + endif() # ADB runner file elseif(CORRADE_TARGET_ANDROID) find_file(CORRADE_TESTSUITE_ADB_RUNNER AdbRunner.sh PATH_SUFFIXES share/corrade/TestSuite) - set(CORRADE_TESTSUITE_ADB_RUNNER_NEEDED CORRADE_TESTSUITE_ADB_RUNNER) + if(NOT CORRADE_TESTSUITE_ADB_RUNNER) + set(Corrade_${_component}_FOUND FALSE) + endif() # Emscripten runner file elseif(CORRADE_TARGET_EMSCRIPTEN) find_file(CORRADE_TESTSUITE_EMSCRIPTEN_RUNNER EmscriptenRunner.html.in PATH_SUFFIXES share/corrade/TestSuite) - set(CORRADE_TESTSUITE_EMSCRIPTEN_RUNNER_NEEDED CORRADE_TESTSUITE_EMSCRIPTEN_RUNNER) + if(NOT CORRADE_TESTSUITE_EMSCRIPTEN_RUNNER) + set(Corrade_${_component}_FOUND FALSE) + endif() endif() # Utility library (contains all setup that is used by others) @@ -539,6 +561,15 @@ foreach(_component ${Corrade_FIND_COMPONENTS}) set_property(TARGET Corrade::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "log") endif() + # Emscripten has various stuff implemented in JS + if(CORRADE_TARGET_EMSCRIPTEN) + find_file(CORRADE_UTILITY_JS CorradeUtility.js + PATH_SUFFIXES lib) + set_property(TARGET Corrade::${_component} APPEND PROPERTY + # TODO switch to INTERFACE_LINK_OPTIONS and SHELL: once we + # require CMake 3.13 unconditionally + INTERFACE_LINK_LIBRARIES "--js-library ${CORRADE_UTILITY_JS}") + endif() endif() # Find library includes @@ -559,11 +590,14 @@ foreach(_component ${Corrade_FIND_COMPONENTS}) endforeach() endif() - # Decide if the component was found - if((_component IN_LIST _CORRADE_LIBRARY_COMPONENTS AND _CORRADE_${_COMPONENT}_INCLUDE_DIR AND (_component IN_LIST _CORRADE_HEADER_ONLY_COMPONENTS OR CORRADE_${_COMPONENT}_LIBRARY_RELEASE OR CORRADE_${_COMPONENT}_LIBRARY_DEBUG)) OR (_component IN_LIST _CORRADE_EXECUTABLE_COMPONENTS AND CORRADE_${_COMPONENT}_EXECUTABLE)) - set(Corrade_${_component}_FOUND TRUE) - else() - set(Corrade_${_component}_FOUND FALSE) + # Decide if the component was found, unless the _FOUND is already set + # by something above. + if(NOT DEFINED Corrade_${_component}_FOUND) + if((_component IN_LIST _CORRADE_LIBRARY_COMPONENTS AND _CORRADE_${_COMPONENT}_INCLUDE_DIR AND (_component IN_LIST _CORRADE_HEADER_ONLY_COMPONENTS OR CORRADE_${_COMPONENT}_LIBRARY_RELEASE OR CORRADE_${_COMPONENT}_LIBRARY_DEBUG)) OR (_component IN_LIST _CORRADE_EXECUTABLE_COMPONENTS AND CORRADE_${_COMPONENT}_EXECUTABLE)) + set(Corrade_${_component}_FOUND TRUE) + else() + set(Corrade_${_component}_FOUND FALSE) + endif() endif() endif() endforeach() @@ -590,7 +624,7 @@ if(NOT CMAKE_VERSION VERSION_LESS 3.16) # misleading messages. elseif(NOT _component IN_LIST _CORRADE_IMPLICITLY_ENABLED_COMPONENTS) string(TOUPPER ${_component} _COMPONENT) - list(APPEND _CORRADE_REASON_FAILURE_MESSAGE "${_component} is not built by default. Make sure you enabled WITH_${_COMPONENT} when building Corrade.") + list(APPEND _CORRADE_REASON_FAILURE_MESSAGE "${_component} is not built by default. Make sure you enabled CORRADE_WITH_${_COMPONENT} when building Corrade.") # Otherwise we have no idea. Better be silent than to print something # misleading. else() @@ -606,9 +640,6 @@ find_package_handle_standard_args(Corrade REQUIRED_VARS CORRADE_INCLUDE_DIR _CORRADE_MODULE_DIR _CORRADE_CONFIGURE_FILE - ${CORRADE_TESTSUITE_XCTEST_RUNNER_NEEDED} - ${CORRADE_TESTSUITE_ADB_RUNNER_NEEDED} - ${CORRADE_TESTSUITE_EMSCRIPTEN_RUNNER_NEEDED} HANDLE_COMPONENTS ${_CORRADE_REASON_FAILURE_MESSAGE}) diff --git a/modules/FindImGui.cmake b/modules/FindImGui.cmake index 2bd46c5..0219051 100644 --- a/modules/FindImGui.cmake +++ b/modules/FindImGui.cmake @@ -36,7 +36,7 @@ # This file is part of Magnum. # # Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, -# 2020, 2021, 2022 Vladimír Vondruš +# 2020, 2021, 2022, 2023 Vladimír Vondruš # Copyright © 2018 Jonathan Hale # # Permission is hereby granted, free of charge, to any person obtaining a diff --git a/modules/FindMagnum.cmake b/modules/FindMagnum.cmake index caa6999..88959a3 100644 --- a/modules/FindMagnum.cmake +++ b/modules/FindMagnum.cmake @@ -57,6 +57,7 @@ # Audio - Audio library # DebugTools - DebugTools library # GL - GL library +# MaterialTools - MaterialTools library # MeshTools - MeshTools library # Primitives - Primitives library # SceneGraph - SceneGraph library @@ -78,7 +79,6 @@ # WindowlessGlxApplication - Windowless GLX application # WindowlessIosApplication - Windowless iOS application # WindowlessWglApplication - Windowless WGL application -# WindowlessWindowsEglApplication - Windowless Windows/EGL application # CglContext - CGL context # EglContext - EGL context # GlxContext - GLX context @@ -128,19 +128,18 @@ # # Features of found Magnum library are exposed in these variables: # -# MAGNUM_BUILD_DEPRECATED - Defined if compiled with deprecated APIs +# MAGNUM_BUILD_DEPRECATED - Defined if compiled with deprecated features # included # MAGNUM_BUILD_STATIC - Defined if compiled as static libraries # MAGNUM_BUILD_STATIC_UNIQUE_GLOBALS - Defined if static libraries keep the # globals unique even across different shared libraries # MAGNUM_TARGET_GL - Defined if compiled with OpenGL interop # MAGNUM_TARGET_GLES - Defined if compiled for OpenGL ES -# MAGNUM_TARGET_GLES2 - Defined if compiled for OpenGL ES 2.0 -# MAGNUM_TARGET_GLES3 - Defined if compiled for OpenGL ES 3.0 -# MAGNUM_TARGET_DESKTOP_GLES - Defined if compiled with OpenGL ES -# emulation on desktop OpenGL # MAGNUM_TARGET_WEBGL - Defined if compiled for WebGL -# MAGNUM_TARGET_HEADLESS - Defined if compiled for headless machines +# MAGNUM_TARGET_GLES2 - Defined if compiled for OpenGL ES 2.0 / WebGL +# 1 instead of OpenGL ES 3.0+ / WebGL 2 +# MAGNUM_TARGET_EGL - Defined if compiled for EGL instead of a +# platform-specific OpenGL support library like CGL, EAGL, GLX or WGL # MAGNUM_TARGET_VK - Defined if compiled with Vulkan interop # # The following variables are provided for backwards compatibility purposes @@ -149,6 +148,12 @@ # # MAGNUM_BUILD_MULTITHREADED - Alias to CORRADE_BUILD_MULTITHREADED. Use # CORRADE_BUILD_MULTITHREADED instead. +# MAGNUM_TARGET_HEADLESS - Alias to MAGNUM_TARGET_EGL, unless on iOS, +# Android, Emscripten or Windows RT. Use MAGNUM_TARGET_EGL instead. +# MAGNUM_TARGET_DESKTOP_GLES` - Defined if compiled for OpenGL ES but +# GLX / WGL is used instead of EGL. Use MAGNUM_TARGET_EGL instead. +# MAGNUM_TARGET_GLES3 - Defined if compiled for OpenGL ES 3.0+ / +# WebGL 2. Use an inverse of the MAGNUM_TARGET_GLES2 variable instead. # # Additionally these variables are defined for internal usage: # @@ -159,6 +164,7 @@ # MAGNUM_*_LIBRARY - Component libraries (w/o dependencies) # MAGNUM_*_LIBRARY_DEBUG - Debug version of given library, if found # MAGNUM_*_LIBRARY_RELEASE - Release version of given library, if found +# MAGNUM_PLATFORM_JS - Path to MagnumPlatform.js file # MAGNUM_BINARY_INSTALL_DIR - Binary installation directory # MAGNUM_LIBRARY_INSTALL_DIR - Library installation directory # MAGNUM_DATA_INSTALL_DIR - Data installation directory @@ -202,7 +208,7 @@ # This file is part of Magnum. # # Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, -# 2020, 2021, 2022 Vladimír Vondruš +# 2020, 2021, 2022, 2023 Vladimír Vondruš # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), @@ -223,18 +229,37 @@ # DEALINGS IN THE SOFTWARE. # +# CMake policies used by FindMagnum are popped again at the end. +cmake_policy(PUSH) +# Prefer GLVND when finding OpenGL. If this causes problems (known to fail with +# NVidia drivers in Debian Buster, reported on 2019-04-09), users can override +# this by setting OpenGL_GL_PREFERENCE to LEGACY. +if(POLICY CMP0072) + cmake_policy(SET CMP0072 NEW) +endif() + # Corrade library dependencies set(_MAGNUM_CORRADE_DEPENDENCIES ) -foreach(_component ${Magnum_FIND_COMPONENTS}) - string(TOUPPER ${_component} _COMPONENT) +foreach(_magnum_component ${Magnum_FIND_COMPONENTS}) + set(_MAGNUM_${_magnum_component}_CORRADE_DEPENDENCIES ) # Unrolling the transitive dependencies here so this doesn't need to be # after resolving inter-component dependencies. Listing also all plugins. - if(_component MATCHES "^(Audio|DebugTools|MeshTools|Primitives|SceneTools|ShaderTools|Text|TextureTools|Trade|.+Importer|.+ImageConverter|.+Font|.+ShaderConverter)$") - set(_MAGNUM_${_COMPONENT}_CORRADE_DEPENDENCIES PluginManager) + if(_magnum_component MATCHES "^(Audio|DebugTools|MeshTools|Primitives|SceneTools|ShaderTools|Text|TextureTools|Trade|.+Importer|.+ImageConverter|.+Font|.+ShaderConverter)$") + list(APPEND _MAGNUM_${_magnum_component}_CORRADE_DEPENDENCIES PluginManager) + endif() + if(_magnum_component STREQUAL DebugTools) + # DebugTools depends on TestSuite optionally, so if it's not there + # assume it wasn't compiled against it. Also, all variables from the + # FindCorrade module overwrite the local variables here (in particular + # _component, _COMPONENT and such), so we need to prefix extensively. + find_package(Corrade QUIET COMPONENTS TestSuite) + if(Corrade_TestSuite_FOUND) + list(APPEND _MAGNUM_${_magnum_component}_CORRADE_DEPENDENCIES TestSuite) + endif() endif() - list(APPEND _MAGNUM_CORRADE_DEPENDENCIES ${_MAGNUM_${_COMPONENT}_CORRADE_DEPENDENCIES}) + list(APPEND _MAGNUM_CORRADE_DEPENDENCIES ${_MAGNUM_${_magnum_component}_CORRADE_DEPENDENCIES}) endforeach() find_package(Corrade REQUIRED Utility ${_MAGNUM_CORRADE_DEPENDENCIES}) @@ -268,10 +293,8 @@ set(_magnumFlags TARGET_GL TARGET_GLES TARGET_GLES2 - TARGET_GLES3 - TARGET_DESKTOP_GLES TARGET_WEBGL - TARGET_HEADLESS + TARGET_EGL TARGET_VK) foreach(_magnumFlag ${_magnumFlags}) list(FIND _magnumConfigure "#define MAGNUM_${_magnumFlag}" _magnum_${_magnumFlag}) @@ -280,17 +303,23 @@ foreach(_magnumFlag ${_magnumFlags}) endif() endforeach() -# For compatibility only, to be removed at some point -if(MAGNUM_BUILD_DEPRECATED AND CORRADE_BUILD_MULTITHREADED) - set(MAGNUM_BUILD_MULTITHREADED 1) -endif() - -# OpenGL library preference. Prefer to use GLVND, since that's the better -# approach nowadays, but allow the users to override it from outside in case -# it is broken for some reason (Nvidia drivers in Debian's testing (Buster) -- -# reported on 2019-04-09). -if(NOT CMAKE_VERSION VERSION_LESS 3.10 AND NOT OpenGL_GL_PREFERENCE) - set(OpenGL_GL_PREFERENCE GLVND) +# For compatibility only, to be removed at some point. Refer to +# src/Magnum/configure.h.cmake for the decision logic here. +if(MAGNUM_BUILD_DEPRECATED) + if(CORRADE_BUILD_MULTITHREADED) + set(MAGNUM_BUILD_MULTITHREADED 1) + endif() + if(NOT CORRADE_TARGET_IOS AND NOT CORRADE_TARGET_ANDROID AND NOT CORRADE_TARGET_EMSCRIPTEN AND NOT CORRADE_TARGET_WINDOWS_RT) + if(NOT MAGNUM_TARGET_GLES AND MAGNUM_TARGET_EGL) + set(MAGNUM_TARGET_HEADLESS 1) + endif() + if(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_EGL) + set(MAGNUM_TARGET_DESKTOP_GLES 1) + endif() + endif() + if(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_GLES2) + set(MAGNUM_TARGET_GLES3 1) + endif() endif() # Base Magnum library @@ -355,8 +384,8 @@ endif() # Component distinction (listing them explicitly to avoid mistakes with finding # components from other repositories) set(_MAGNUM_LIBRARY_COMPONENTS - Audio DebugTools GL MeshTools Primitives SceneGraph SceneTools Shaders - ShaderTools Text TextureTools Trade + Audio DebugTools GL MaterialTools MeshTools Primitives SceneGraph + SceneTools Shaders ShaderTools Text TextureTools Trade WindowlessEglApplication EglContext OpenGLTester) set(_MAGNUM_PLUGIN_COMPONENTS AnyAudioImporter AnyImageConverter AnyImageImporter AnySceneConverter @@ -395,7 +424,7 @@ if(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE) list(APPEND _MAGNUM_LIBRARY_COMPONENTS GlxApplication XEglApplication WindowlessGlxApplication GlxContext) endif() if(CORRADE_TARGET_WINDOWS) - list(APPEND _MAGNUM_LIBRARY_COMPONENTS WindowlessWglApplication WglContext WindowlessWindowsEglApplication) + list(APPEND _MAGNUM_LIBRARY_COMPONENTS WindowlessWglApplication WglContext) endif() if(CORRADE_TARGET_UNIX OR CORRADE_TARGET_WINDOWS) list(APPEND _MAGNUM_EXECUTABLE_COMPONENTS fontconverter distancefieldconverter) @@ -420,30 +449,24 @@ if(MAGNUM_TARGET_GL) set(_MAGNUM_DebugTools_GL_DEPENDENCY_IS_OPTIONAL ON) endif() +set(_MAGNUM_MaterialTools_DEPENDENCIES Trade) + set(_MAGNUM_MeshTools_DEPENDENCIES Trade) if(MAGNUM_TARGET_GL) list(APPEND _MAGNUM_MeshTools_DEPENDENCIES GL) endif() set(_MAGNUM_OpenGLTester_DEPENDENCIES GL) -if(MAGNUM_TARGET_HEADLESS OR CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID) +if(MAGNUM_TARGET_EGL) list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessEglApplication) elseif(CORRADE_TARGET_IOS) list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessIosApplication) -elseif(CORRADE_TARGET_APPLE AND NOT MAGNUM_TARGET_GLES) +elseif(CORRADE_TARGET_APPLE) list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessCglApplication) elseif(CORRADE_TARGET_UNIX) - if(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES) - list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessEglApplication) - else() - list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessGlxApplication) - endif() + list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessGlxApplication) elseif(CORRADE_TARGET_WINDOWS) - if(NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES) - list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessWglApplication) - else() - list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessWindowsEglApplication) - endif() + list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessWglApplication) endif() set(_MAGNUM_Primitives_DEPENDENCIES MeshTools Trade) @@ -492,7 +515,6 @@ set(_MAGNUM_WindowlessEglApplication_DEPENDENCIES GL) set(_MAGNUM_WindowlessGlxApplication_DEPENDENCIES GL) set(_MAGNUM_WindowlessIosApplication_DEPENDENCIES GL) set(_MAGNUM_WindowlessWglApplication_DEPENDENCIES GL) -set(_MAGNUM_WindowlessWindowsEglApplication_DEPENDENCIES GL) set(_MAGNUM_XEglApplication_DEPENDENCIES GL) set(_MAGNUM_CglContext_DEPENDENCIES GL) set(_MAGNUM_EglContext_DEPENDENCIES GL) @@ -680,7 +702,17 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES android EGL::EGL) - # EmscriptenApplication has no additional dependencies + # Emscripten application dependencies + elseif(_component STREQUAL EmscriptenApplication) + # Emscripten has various stuff implemented in JS + if(CORRADE_TARGET_EMSCRIPTEN) + find_file(MAGNUM_PLATFORM_JS MagnumPlatform.js + PATH_SUFFIXES lib) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + # TODO switch to INTERFACE_LINK_OPTIONS and SHELL: once + # we require CMake 3.13 unconditionally + INTERFACE_LINK_LIBRARIES "--js-library ${MAGNUM_PLATFORM_JS}") + endif() # GLFW application dependencies elseif(_component STREQUAL GlfwApplication) @@ -699,7 +731,7 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS}) endif() - # With GLVND (since CMake 3.11) we need to explicitly link to + # With GLVND (since CMake 3.10) we need to explicitly link to # GLX/EGL because libOpenGL doesn't provide it. For EGL we have # our own EGL find module, which makes things simpler. The # upstream FindOpenGL is anything but simple. Also can't use @@ -708,16 +740,16 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) # OPENGL_opengl_LIBRARY because that's set even if # OpenGL_GL_PREFERENCE is explicitly set to LEGACY. if(MAGNUM_TARGET_GL) - if(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE AND (NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES)) + if(MAGNUM_TARGET_EGL) + find_package(EGL) + set_property(TARGET Magnum::${_component} APPEND + PROPERTY INTERFACE_LINK_LIBRARIES EGL::EGL) + elseif(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE) find_package(OpenGL) if(OPENGL_opengl_LIBRARY AND OpenGL_GL_PREFERENCE STREQUAL GLVND) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES OpenGL::GLX) endif() - elseif(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES AND NOT CORRADE_TARGET_EMSCRIPTEN) - find_package(EGL) - set_property(TARGET Magnum::${_component} APPEND - PROPERTY INTERFACE_LINK_LIBRARIES EGL::EGL) endif() endif() @@ -736,9 +768,17 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) elseif(CORRADE_TARGET_UNIX) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS}) + # Emscripten has various stuff implemented in JS + elseif(CORRADE_TARGET_EMSCRIPTEN) + find_file(MAGNUM_PLATFORM_JS MagnumPlatform.js + PATH_SUFFIXES lib) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + # TODO switch to INTERFACE_LINK_OPTIONS and SHELL: once + # we require CMake 3.13 unconditionally + INTERFACE_LINK_LIBRARIES "--js-library ${MAGNUM_PLATFORM_JS}") endif() - # With GLVND (since CMake 3.11) we need to explicitly link to + # With GLVND (since CMake 3.10) we need to explicitly link to # GLX/EGL because libOpenGL doesn't provide it. For EGL we have # our own EGL find module, which makes things simpler. The # upstream FindOpenGL is anything but simple. Also can't use @@ -747,16 +787,16 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) # OPENGL_opengl_LIBRARY because that's set even if # OpenGL_GL_PREFERENCE is explicitly set to LEGACY. if(MAGNUM_TARGET_GL) - if(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE AND (NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES)) + if(MAGNUM_TARGET_EGL) + find_package(EGL) + set_property(TARGET Magnum::${_component} APPEND + PROPERTY INTERFACE_LINK_LIBRARIES EGL::EGL) + elseif(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE) find_package(OpenGL) if(OPENGL_opengl_LIBRARY AND OpenGL_GL_PREFERENCE STREQUAL GLVND) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES OpenGL::GLX) endif() - elseif(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES AND NOT CORRADE_TARGET_EMSCRIPTEN) - find_package(EGL) - set_property(TARGET Magnum::${_component} APPEND - PROPERTY INTERFACE_LINK_LIBRARIES EGL::EGL) endif() endif() @@ -768,12 +808,19 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${X11_LIBRARIES}) - # With GLVND (since CMake 3.11) we need to explicitly link to + # With GLVND (since CMake 3.10) we need to explicitly link to # GLX because libOpenGL doesn't provide it. Also can't use # OpenGL_OpenGL_FOUND, because that one is set also if GLVND is # *not* found. WTF. Also can't just check for # OPENGL_opengl_LIBRARY because that's set even if # OpenGL_GL_PREFERENCE is explicitly set to LEGACY. + # + # If MAGNUM_TARGET_GLES and MAGNUM_TARGET_EGL is set, these + # applications can be built only if GLVND is available as + # otherwise there would be a conflict between libGL and + # libGLES. Thus, if GLVND is not available, it won't link + # libGLX here, but that shouldn't be a problem since the + # application library won't exist either. find_package(OpenGL) if(OPENGL_opengl_LIBRARY AND OpenGL_GL_PREFERENCE STREQUAL GLVND) set_property(TARGET Magnum::${_component} APPEND PROPERTY @@ -799,12 +846,6 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) # Windowless WGL application has no additional dependencies - # Windowless Windows/EGL application dependencies - elseif(_component STREQUAL WindowlessWindowsEglApplication) - find_package(EGL) - set_property(TARGET Magnum::${_component} APPEND PROPERTY - INTERFACE_LINK_LIBRARIES EGL::EGL) - # X/EGL application dependencies elseif(_component STREQUAL XEglApplication) find_package(EGL) @@ -822,7 +863,7 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) # GLX context dependencies if(_component STREQUAL GlxContext) - # With GLVND (since CMake 3.11) we need to explicitly link to + # With GLVND (since CMake 3.10) we need to explicitly link to # GLX because libOpenGL doesn't provide it. Also can't use # OpenGL_OpenGL_FOUND, because that one is set also if GLVND is # *not* found. If GLVND is not used, link to X11 instead. Also @@ -854,14 +895,14 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) elseif(_component STREQUAL Audio) find_package(OpenAL) set_property(TARGET Magnum::${_component} APPEND PROPERTY - INTERFACE_LINK_LIBRARIES Corrade::PluginManager OpenAL::OpenAL) + INTERFACE_LINK_LIBRARIES OpenAL::OpenAL) # No special setup for DebugTools library # GL library elseif(_component STREQUAL GL) - if(NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES) - # If the GLVND library (CMake 3.11+) was found, link to the + if(NOT MAGNUM_TARGET_GLES OR (MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_EGL AND NOT CORRADE_TARGET_IOS)) + # If the GLVND library (CMake 3.10+) was found, link to the # imported target. Otherwise (and also on all systems except # Linux) link to the classic libGL. Can't use # OpenGL_OpenGL_FOUND, because that one is set also if GLVND is @@ -880,12 +921,16 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) find_package(OpenGLES2 REQUIRED) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES OpenGLES2::OpenGLES2) - elseif(MAGNUM_TARGET_GLES3) + else() find_package(OpenGLES3 REQUIRED) set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES OpenGLES3::OpenGLES3) endif() + # MaterialTools library + elseif(_component STREQUAL MaterialTools) + set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES PhongToPbrMetallicRoughness.h) + # MeshTools library elseif(_component STREQUAL MeshTools) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES CompressIndices.h) @@ -904,26 +949,19 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) # No special setup for SceneGraph library - # ShaderTools library - elseif(_component STREQUAL ShaderTools) - set_property(TARGET Magnum::${_component} APPEND PROPERTY - INTERFACE_LINK_LIBRARIES Corrade::PluginManager) + # SceneTools library + elseif(_component STREQUAL SceneTools) + set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES Hierarchy.h) + # No special setup for ShaderTools library # No special setup for Shaders library - - # Text library - elseif(_component STREQUAL Text) - set_property(TARGET Magnum::${_component} APPEND PROPERTY - INTERFACE_LINK_LIBRARIES Corrade::PluginManager) + # No special setup for Text library # TextureTools library elseif(_component STREQUAL TextureTools) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES Atlas.h) - # Trade library - elseif(_component STREQUAL Trade) - set_property(TARGET Magnum::${_component} APPEND PROPERTY - INTERFACE_LINK_LIBRARIES Corrade::PluginManager) + # No special setup for Trade library # Vk library elseif(_component STREQUAL Vk) @@ -952,8 +990,13 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) endif() # Automatic import of static plugins. Skip in case the include dir was - # not found -- that'll fail later with a proper message. - if(_component IN_LIST _MAGNUM_PLUGIN_COMPONENTS AND _MAGNUM_${_COMPONENT}_INCLUDE_DIR) + # not found -- that'll fail later with a proper message. Skip it also + # if the include dir doesn't contain the generated configure.h, which + # is the case with Magnum as a subproject and given plugin not enabled + # -- there it finds just the sources, where's just configure.h.cmake, + # and that's not useful for anything. The assumption here is that it + # will fail later anyway on the binary not being found. + if(_component IN_LIST _MAGNUM_PLUGIN_COMPONENTS AND _MAGNUM_${_COMPONENT}_INCLUDE_DIR AND EXISTS ${_MAGNUM_${_COMPONENT}_INCLUDE_DIR}/configure.h) # Automatic import of static plugins file(READ ${_MAGNUM_${_COMPONENT}_INCLUDE_DIR}/configure.h _magnum${_component}Configure) string(FIND "${_magnum${_component}Configure}" "#define MAGNUM_${_COMPONENT}_BUILD_STATIC" _magnum${_component}_BUILD_STATIC) @@ -967,9 +1010,13 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) # are optional dependencies, defer adding them to later once we know if # they were found or not. if(_component IN_LIST _MAGNUM_LIBRARY_COMPONENTS OR _component IN_LIST _MAGNUM_PLUGIN_COMPONENTS) + foreach(_dependency ${_MAGNUM_${_component}_CORRADE_DEPENDENCIES}) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES Corrade::${_dependency}) + endforeach() set_property(TARGET Magnum::${_component} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Magnum::Magnum) - set(_MAGNUM_${component}_OPTIONAL_DEPENDENCIES_TO_ADD ) + set(_MAGNUM_${_component}_OPTIONAL_DEPENDENCIES_TO_ADD ) foreach(_dependency ${_MAGNUM_${_component}_DEPENDENCIES}) if(NOT _MAGNUM_${_component}_${_dependency}_DEPENDENCY_IS_OPTIONAL) set_property(TARGET Magnum::${_component} APPEND PROPERTY @@ -1031,20 +1078,6 @@ if(CORRADE_TARGET_EMSCRIPTEN) MAGNUM_EMSCRIPTENAPPLICATION_JS MAGNUM_WINDOWLESSEMSCRIPTENAPPLICATION_JS MAGNUM_WEBAPPLICATION_CSS) - - # If we are on CMake 3.13 and up, `-s USE_WEBGL2=1` linker option is - # propagated from FindOpenGLES3.cmake already. If not (and the GL library - # is used), we need to modify the global CMAKE_EXE_LINKER_FLAGS. Do it here - # instead of in FindOpenGLES3.cmake so it works also for CMake subprojects - # (in which case find_package(OpenGLES3) is called in (and so - # CMAKE_EXE_LINKER_FLAGS would be modified in) Magnum's root CMakeLists.txt - # and thus can't affect the variable in the outer project). CMake supports - # IN_LIST as an operator since 3.1 (Emscripten needs at least 3.7), but - # it's behind a policy, so enable that one as well. - cmake_policy(SET CMP0057 NEW) - if(CMAKE_VERSION VERSION_LESS 3.13 AND GL IN_LIST Magnum_FIND_COMPONENTS AND NOT MAGNUM_TARGET_GLES2 AND NOT CMAKE_EXE_LINKER_FLAGS MATCHES "-s USE_WEBGL2=1") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s USE_WEBGL2=1") - endif() endif() # For CMake 3.16+ with REASON_FAILURE_MESSAGE, provide additional potentially @@ -1069,7 +1102,7 @@ if(NOT CMAKE_VERSION VERSION_LESS 3.16) # misleading messages. elseif(NOT _component IN_LIST _MAGNUM_IMPLICITLY_ENABLED_COMPONENTS) string(TOUPPER ${_component} _COMPONENT) - list(APPEND _MAGNUM_REASON_FAILURE_MESSAGE "${_component} is not built by default. Make sure you enabled WITH_${_COMPONENT} when building Magnum.") + list(APPEND _MAGNUM_REASON_FAILURE_MESSAGE "${_component} is not built by default. Make sure you enabled MAGNUM_WITH_${_COMPONENT} when building Magnum.") # Otherwise we have no idea. Better be silent than to print something # misleading. else() @@ -1271,3 +1304,6 @@ if(MAGNUM_PLUGINS_RELEASE_DIR) set(MAGNUM_PLUGINS_SCENECONVERTER_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/sceneconverters) set(MAGNUM_PLUGINS_AUDIOIMPORTER_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/audioimporters) endif() + +# Resets CMake policies set at the top of the file to not affect other code. +cmake_policy(POP) diff --git a/modules/FindMagnumIntegration.cmake b/modules/FindMagnumIntegration.cmake index f0d5351..7a547ce 100644 --- a/modules/FindMagnumIntegration.cmake +++ b/modules/FindMagnumIntegration.cmake @@ -48,7 +48,7 @@ # This file is part of Magnum. # # Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, -# 2020, 2021, 2022 Vladimír Vondruš +# 2020, 2021, 2022, 2023 Vladimír Vondruš # Copyright © 2018 Konstantinos Chatzilygeroudis # # Permission is hereby granted, free of charge, to any person obtaining a @@ -89,8 +89,14 @@ if(_MAGNUMINTEGRATION_OPTIONAL_DEPENDENCIES) find_package(Magnum OPTIONAL_COMPONENTS ${_MAGNUMINTEGRATION_OPTIONAL_DEPENDENCIES}) endif() -# Global integration include dir -find_path(MAGNUMINTEGRATION_INCLUDE_DIR Magnum +# Global include dir that's unique to Magnum Integration. Often it will be +# installed alongside Magnum, which is why the hint, but if not, it shouldn't +# just pick MAGNUM_INCLUDE_DIR because then _MAGNUMINTEGRATION_*_INCLUDE_DIR +# will fail to be found. In case of CMake subprojects the versionIntegration.h +# is generated inside the build dir so this won't find it, instead +# src/CMakeLists.txt forcibly sets MAGNUMINTEGRATION_INCLUDE_DIR as an internal +# cache value to make that work. +find_path(MAGNUMINTEGRATION_INCLUDE_DIR Magnum/versionIntegration.h HINTS ${MAGNUM_INCLUDE_DIR}) mark_as_advanced(MAGNUMINTEGRATION_INCLUDE_DIR) @@ -314,7 +320,7 @@ if(NOT CMAKE_VERSION VERSION_LESS 3.16) # misleading messages. elseif(NOT _component IN_LIST _MAGNUMINTEGRATION_IMPLICITLY_ENABLED_COMPONENTS) string(TOUPPER ${_component} _COMPONENT) - list(APPEND _MAGNUMINTEGRATION_REASON_FAILURE_MESSAGE "${_component} is not built by default. Make sure you enabled WITH_${_COMPONENT} when building Magnum Integration.") + list(APPEND _MAGNUMINTEGRATION_REASON_FAILURE_MESSAGE "${_component} is not built by default. Make sure you enabled MAGNUM_WITH_${_COMPONENT} when building Magnum Integration.") # Otherwise we have no idea. Better be silent than to print something # misleading. else() diff --git a/modules/FindSDL2.cmake b/modules/FindSDL2.cmake index 311b052..2ce89ca 100644 --- a/modules/FindSDL2.cmake +++ b/modules/FindSDL2.cmake @@ -20,7 +20,7 @@ # This file is part of Magnum. # # Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, -# 2020, 2021, 2022 Vladimír Vondruš +# 2020, 2021, 2022, 2023 Vladimír Vondruš # Copyright © 2018 Jonathan Hale # # Permission is hereby granted, free of charge, to any person obtaining a @@ -138,10 +138,10 @@ else() # which CMake somehow prefers before the SDL2-2.0.dylib file. Making # the dylib first so it is preferred. Not sure how this maps to debug # config though :/ - NAMES SDL2-2.0 SDL2 + NAMES SDL2-2.0 SDL2 SDL2-static PATH_SUFFIXES ${_SDL2_LIBRARY_PATH_SUFFIX}) find_library(SDL2_LIBRARY_DEBUG - NAMES SDL2d + NAMES SDL2d SDL2-staticd PATH_SUFFIXES ${_SDL2_LIBRARY_PATH_SUFFIX}) # FPHSA needs one of the _DEBUG/_RELEASE variables to check that the # library was found -- using SDL_LIBRARY, which will get populated by diff --git a/src/SaveTool/SaveTool.cpp b/src/Application/Application.cpp similarity index 57% rename from src/SaveTool/SaveTool.cpp rename to src/Application/Application.cpp index c9e2315..eaa712d 100644 --- a/src/SaveTool/SaveTool.cpp +++ b/src/Application/Application.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "SaveTool.h" - #include #include @@ -29,14 +27,18 @@ #include -#include +#include +#include #include #include #include "../FontAwesome/IconsFontAwesome5.h" +#include "../Configuration/Configuration.h" #include "../Logger/Logger.h" +#include "Application.h" + using namespace Containers::Literals; extern const ImVec2 center_pivot = {0.5f, 0.5f}; @@ -45,20 +47,24 @@ extern const ImVec2 center_pivot = {0.5f, 0.5f}; Utility::Tweakable tweak; #endif -SaveTool::SaveTool(const Arguments& arguments): +namespace mbst { + +Application::Application(const Arguments& arguments): Platform::Sdl2Application{arguments, - Configuration{}.setTitle("M.A.S.S. Builder Save Tool " SAVETOOL_VERSION " (\"" SAVETOOL_CODENAME "\")") + Configuration{}.setTitle("M.A.S.S. Builder Save Tool " SAVETOOL_VERSION_STRING " (\"" SAVETOOL_CODENAME "\")") .setSize({960, 720})} { #ifdef SAVETOOL_DEBUG_BUILD tweak.enable("", "../../"); #endif + LOG_INFO_FORMAT("Framebuffer size: {}x{}", framebufferSize().x(), framebufferSize().y()); + LOG_INFO_FORMAT("Window size: {}x{}", windowSize().x(), windowSize().y()); + LOG_INFO_FORMAT("DPI scaling: {}x{}", dpiScaling().x(), dpiScaling().y()); + 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); - GL::Renderer::disable(GL::Renderer::Feature::DepthTest); GL::Renderer::setBlendFunction(GL::Renderer::BlendFunction::SourceAlpha, GL::Renderer::BlendFunction::OneMinusSourceAlpha); GL::Renderer::setBlendEquation(GL::Renderer::BlendEquation::Add, @@ -78,9 +84,9 @@ SaveTool::SaveTool(const Arguments& arguments): #endif LOG_INFO("Registering custom events."); - if((_initEventId = SDL_RegisterEvents(3)) == UnsignedInt(-1)) { + if((_initEventId = SDL_RegisterEvents(3)) == std::uint32_t(-1)) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", - "SDL_RegisterEvents() failed in SaveTool::SaveTool(). Exiting...", window()); + "SDL_RegisterEvents() failed in Application::SaveTool(). Exiting...", window()); exit(EXIT_FAILURE); return; } @@ -99,24 +105,10 @@ SaveTool::SaveTool(const Arguments& arguments): initialiseGui(); - if(!initialiseToolDirectories()) { - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app", - _lastError.data(), window()); - exit(EXIT_FAILURE); - return; - } - - if(!findGameDataDirectory()) { - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app", - _lastError.data(), window()); - exit(EXIT_FAILURE); - return; - } - checkGameState(); _gameCheckTimerId = SDL_AddTimer(2000, - [](UnsignedInt interval, void* param)->UnsignedInt{ - static_cast(param)->checkGameState(); + [](std::uint32_t interval, void* param)->std::uint32_t{ + static_cast(param)->checkGameState(); return interval; }, this); if(_gameCheckTimerId == 0) { @@ -128,9 +120,7 @@ SaveTool::SaveTool(const Arguments& arguments): initialiseConfiguration(); - LOG_INFO("Initialising update checker."); - curl_global_init(CURL_GLOBAL_DEFAULT); - if(_checkUpdatesOnStartup) { + if(conf().checkUpdatesOnStartup()) { _queue.addToast(Toast::Type::Default, "Checking for updates..."_s); _updateThread = std::thread{[this]{ checkForUpdates(); }}; } @@ -141,37 +131,28 @@ SaveTool::SaveTool(const Arguments& arguments): GL::DebugOutput::setEnabled(GL::DebugOutput::Source::Api, GL::DebugOutput::Type::Other, {131185}, false); } - if(_skipDisclaimer) { - _uiState = UiState::Initialising; + if(conf().skipDisclaimer()) { + _uiState = UiState::Initialising; _initThread = std::thread{[this]{ initialiseManager(); }}; } _timeline.start(); } -SaveTool::~SaveTool() { +Application::~Application() { LOG_INFO("Cleaning up."); - LOG_INFO("Shutting libcurl down."); - curl_global_cleanup(); - SDL_RemoveTimer(_gameCheckTimerId); LOG_INFO("Saving the configuration."); - _conf.setValue("cheat_mode"_s, _cheatMode); - _conf.setValue("advanced_mode"_s, _advancedMode); - _conf.setValue("startup_update_check"_s, _checkUpdatesOnStartup); - _conf.setValue("skip_disclaimer"_s, _skipDisclaimer); - _conf.setValue("swap_interval"_s, _swapInterval); - _conf.setValue("fps_cap"_s, _fpsCap); - - _conf.save(); + conf().save(); LOG_INFO("Exiting."); } -void SaveTool::drawEvent() { +void +Application::drawEvent() { #ifdef SAVETOOL_DEBUG_BUILD tweak.update(); #endif @@ -182,8 +163,8 @@ void SaveTool::drawEvent() { swapBuffers(); - if(_swapInterval == 0 && _fpsCap < 301.0f) { - while(_timeline.currentFrameDuration() < (1.0f / _fpsCap)); + if(conf().swapInterval() == 0 && conf().fpsCap() < 301.0f) { + while(_timeline.currentFrameDuration() < (1.0f / conf().fpsCap())); } redraw(); @@ -191,45 +172,54 @@ void SaveTool::drawEvent() { _timeline.nextFrame(); } -void SaveTool::viewportEvent(ViewportEvent& event) { +void +Application::viewportEvent(ViewportEvent& event) { GL::defaultFramebuffer.setViewport({{}, event.framebufferSize()}); - const Vector2 size = Vector2{windowSize()}/dpiScaling(); + const auto size = Vector2{windowSize()}/dpiScaling(); _imgui.relayout(size, windowSize(), framebufferSize()); } -void SaveTool::keyPressEvent(KeyEvent& event) { +void +Application::keyPressEvent(KeyEvent& event) { if(_imgui.handleKeyPressEvent(event)) return; } -void SaveTool::keyReleaseEvent(KeyEvent& event) { +void +Application::keyReleaseEvent(KeyEvent& event) { if(_imgui.handleKeyReleaseEvent(event)) return; } -void SaveTool::mousePressEvent(MouseEvent& event) { +void +Application::mousePressEvent(MouseEvent& event) { if(_imgui.handleMousePressEvent(event)) return; } -void SaveTool::mouseReleaseEvent(MouseEvent& event) { +void +Application::mouseReleaseEvent(MouseEvent& event) { if(_imgui.handleMouseReleaseEvent(event)) return; } -void SaveTool::mouseMoveEvent(MouseMoveEvent& event) { +void +Application::mouseMoveEvent(MouseMoveEvent& event) { if(_imgui.handleMouseMoveEvent(event)) return; } -void SaveTool::mouseScrollEvent(MouseScrollEvent& event) { +void +Application::mouseScrollEvent(MouseScrollEvent& event) { if(_imgui.handleMouseScrollEvent(event)) { event.setAccepted(); return; } } -void SaveTool::textInputEvent(TextInputEvent& event) { +void +Application::textInputEvent(TextInputEvent& event) { if(_imgui.handleTextInputEvent(event)) return; } -void SaveTool::anyEvent(SDL_Event& event) { +void +Application::anyEvent(SDL_Event& event) { if(event.type == _initEventId) { initEvent(event); } @@ -241,7 +231,8 @@ void SaveTool::anyEvent(SDL_Event& event) { } } -void SaveTool::drawImGui() { +void +Application::drawImGui() { _imgui.newFrame(); if(ImGui::GetIO().WantTextInput && !isTextInputActive()) { @@ -258,7 +249,8 @@ void SaveTool::drawImGui() { _imgui.drawFrame(); } -void SaveTool::drawGui() { +void +Application::drawGui() { drawMainMenu(); switch(_uiState) { @@ -300,8 +292,9 @@ void SaveTool::drawGui() { _queue.draw(windowSize()); } -void SaveTool::drawDisclaimer() { - ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f}}, ImGuiCond_Always, center_pivot); +void +Application::drawDisclaimer() { + ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f} / dpiScaling()}, ImGuiCond_Always, center_pivot); if(ImGui::Begin("Disclaimer##DisclaimerWindow", nullptr, ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoBringToFrontOnFocus| @@ -330,7 +323,13 @@ void SaveTool::drawDisclaimer() { ImGui::Bullet(); ImGui::SameLine(); - ImGui::TextUnformatted("This version of the application was tested on M.A.S.S. Builder early access version " SUPPORTED_GAME_VERSION ". It may or may not work with other versions of the game."); + ImGui::TextUnformatted("This version of the application was tested on M.A.S.S. Builder early access version " SAVETOOL_SUPPORTED_GAME_VERSION ". It may or may not work with other versions of the game."); + + if(conf().isRunningInWine()) { + ImGui::Bullet(); + ImGui::SameLine(); + ImGui::TextUnformatted("You are currently running this application in Wine/Proton. It hasn't been fully tested, so some issues may arise. Furthermore, features may be unavailable."); + } ImGui::PopTextWrapPos(); @@ -344,11 +343,13 @@ void SaveTool::drawDisclaimer() { ImGui::Dummy({0.0f, 5.0f}); ImGui::Dummy({4.0f, 0.0f}); ImGui::SameLine(); - ImGui::Checkbox("Don't show next time", &_skipDisclaimer); + if(drawCheckbox("Don't show next time", conf().skipDisclaimer())) { + conf().setSkipDisclaimer(!conf().skipDisclaimer()); + } ImGui::TableSetColumnIndex(1); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, {24.0f, 12.0f}); if(ImGui::Button("I understand the risks")) { - _uiState = UiState::Initialising; + _uiState = UiState::Initialising; _initThread = std::thread{[this]{ initialiseManager(); }}; } ImGui::PopStyleVar(); @@ -359,8 +360,9 @@ void SaveTool::drawDisclaimer() { ImGui::End(); } -void SaveTool::drawInitialisation() { - ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f}}, ImGuiCond_Always, center_pivot); +void +Application::drawInitialisation() { + ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f} / dpiScaling()}, ImGuiCond_Always, center_pivot); if(ImGui::BeginPopupModal("##InitPopup", nullptr, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar)) @@ -372,7 +374,8 @@ void SaveTool::drawInitialisation() { ImGui::OpenPopup("##InitPopup"); } -void SaveTool::drawGameState() { +void +Application::drawGameState() { ImGui::TextUnformatted("Game state:"); ImGui::SameLine(); { @@ -393,18 +396,19 @@ void SaveTool::drawGameState() { } } -void SaveTool::drawHelpMarker(Containers::StringView text, Float wrap_pos) { +void +Application::drawHelpMarker(Containers::StringView text, float wrap_pos) { ImGui::TextUnformatted(ICON_FA_QUESTION_CIRCLE); drawTooltip(text, wrap_pos); } -void SaveTool::drawTooltip(Containers::StringView text, Float wrap_pos) { - if(ImGui::IsItemHovered()){ - ImGui::BeginTooltip(); +void +Application::drawTooltip(Containers::StringView text, float wrap_pos) { + if(ImGui::IsItemHovered() && ImGui::BeginTooltip()) { if(wrap_pos > 0.0f) { ImGui::PushTextWrapPos(wrap_pos); } - ImGui::TextUnformatted(text.data()); + ImGui::TextUnformatted(text.cbegin(), text.cend()); if(wrap_pos > 0.0f) { ImGui::PopTextWrapPos(); } @@ -412,11 +416,121 @@ void SaveTool::drawTooltip(Containers::StringView text, Float wrap_pos) { } } -void SaveTool::openUri(Containers::StringView uri) { - ShellExecuteW(nullptr, nullptr, Utility::Unicode::widen(uri.data()), nullptr, nullptr, SW_SHOWDEFAULT); +bool +Application::drawCheckbox(Containers::StringView label, bool value) { + return ImGui::Checkbox(label.data(), &value); } -void SaveTool::checkGameState() { +void +Application::drawVector3Drag(Containers::StringView id, Vector3& vec, float speed, Vector3 min, Vector3 max, + const char* x_format, const char* y_format, const char* z_format, ImGuiSliderFlags_ flags) +{ + ImGui::PushID(id.cbegin()); + ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); + ImGui::DragFloat("##X", &vec.x(), speed, min.x(), max.x(), x_format, flags); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::DragFloat("##Y", &vec.y(), speed, min.y(), max.y(), y_format, flags); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::DragFloat("##Z", &vec.z(), speed, min.z(), max.z(), z_format, flags); + ImGui::PopItemWidth(); + ImGui::PopID(); +} + +void +Application::drawVector3dDrag(Containers::StringView id, Vector3d& vec, float speed, Vector3d min, Vector3d max, + const char* x_format, const char* y_format, const char* z_format, + ImGuiSliderFlags_ flags) +{ + ImGui::PushID(id.cbegin()); + ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); + ImGui::DragScalar("##X", ImGuiDataType_Double, &vec.x(), speed, &min.x(), &max.x(), x_format, flags); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::DragScalar("##Y", ImGuiDataType_Double, &vec.y(), speed, &min.y(), &max.y(), y_format, flags); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::DragScalar("##X", ImGuiDataType_Double, &vec.x(), speed, &min.z(), &max.z(), z_format, flags); + ImGui::PopItemWidth(); + ImGui::PopID(); +} + +void +Application::drawVector3Slider(Containers::StringView id, Vector3& vec, Vector3 min, Vector3 max, const char* x_format, + const char* y_format, const char* z_format, ImGuiSliderFlags_ flags) +{ + ImGui::PushID(id.cbegin()); + ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); + ImGui::SliderFloat("##X", &vec.x(), min.x(), max.x(), x_format, flags); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderFloat("##Y", &vec.y(), min.y(), max.y(), y_format, flags); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderFloat("##Z", &vec.z(), min.z(), max.z(), z_format, flags); + ImGui::PopItemWidth(); + ImGui::PopID(); +} + +void +Application::drawVector3dSlider(Containers::StringView id, Vector3d& vec, Vector3d min, Vector3d max, + const char* x_format, const char* y_format, const char* z_format, + ImGuiSliderFlags_ flags) +{ + ImGui::PushID(id.cbegin()); + ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); + ImGui::SliderScalar("##X", ImGuiDataType_Double, &vec.x(), &min.x(), &max.x(), x_format, flags); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderScalar("##Y", ImGuiDataType_Double, &vec.y(), &min.y(), &max.y(), y_format, flags); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderScalar("##X", ImGuiDataType_Double, &vec.x(), &min.z(), &max.z(), z_format, flags); + ImGui::PopItemWidth(); + ImGui::PopID(); +} + +void +Application::drawVector2Slider(Containers::StringView id, Vector2& vec, Vector2 min, Vector2 max, const char* x_format, + const char* y_format, ImGuiSliderFlags_ flags) +{ + ImGui::PushID(id.cbegin()); + ImGui::PushMultiItemsWidths(2, ImGui::CalcItemWidth()); + ImGui::SliderFloat("##X", &vec.x(), min.x(), max.x(), x_format, flags); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderFloat("##Y", &vec.y(), min.y(), max.y(), y_format, flags); + ImGui::PopItemWidth(); + ImGui::PopID(); +} + +void +Application::drawVector2dSlider(Containers::StringView id, Vector2d& vec, Vector2d min, Vector2d max, + const char* x_format, const char* y_format, ImGuiSliderFlags_ flags) +{ + ImGui::PushID(id.cbegin()); + ImGui::PushMultiItemsWidths(2, ImGui::CalcItemWidth()); + ImGui::SliderScalar("##X", ImGuiDataType_Double, &vec.x(), &min.x(), &max.x(), x_format, flags); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderScalar("##Y", ImGuiDataType_Double, &vec.y(), &min.y(), &max.y(), y_format, flags); + ImGui::PopItemWidth(); + ImGui::PopID(); +} + +void +Application::openUri(Containers::StringView uri) { + if(!conf().isRunningInWine()) { + ShellExecuteA(nullptr, nullptr, uri.data(), nullptr, nullptr, SW_SHOWDEFAULT); + } + else { + std::system(Utility::format("winebrowser.exe {}", uri).cbegin()); + } +} + +void +Application::checkGameState() { WTS_PROCESS_INFOW* process_infos = nullptr; unsigned long process_count = 0; @@ -437,3 +551,5 @@ void SaveTool::checkGameState() { _gameState = GameState::Unknown; } } + +} diff --git a/src/SaveTool/SaveTool.h b/src/Application/Application.h similarity index 51% rename from src/SaveTool/SaveTool.h rename to src/Application/Application.h index c662203..9c16709 100644 --- a/src/SaveTool/SaveTool.h +++ b/src/Application/Application.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,12 +16,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include #include #include #include #include -#include #include #ifdef SAVETOOL_DEBUG_BUILD #include @@ -34,13 +34,15 @@ #include #include -#include #include -#include "../ProfileManager/ProfileManager.h" -#include "../MassManager/MassManager.h" +#include "../Managers/BackupManager.h" +#include "../Managers/MassManager.h" +#include "../Managers/ProfileManager.h" +#include "../Managers/StagedMassManager.h" #include "../ToastQueue/ToastQueue.h" +#include "../UpdateChecker/UpdateChecker.h" #ifdef SAVETOOL_DEBUG_BUILD #define tw CORRADE_TWEAKABLE @@ -50,17 +52,19 @@ using namespace Corrade; using namespace Containers::Literals; using namespace Magnum; -class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener { - public: - explicit SaveTool(const Arguments& arguments); +namespace mbst { - ~SaveTool() override; +class Application: public Platform::Sdl2Application, public efsw::FileWatchListener { + public: + explicit Application(const Arguments& arguments); + + ~Application() override; void handleFileAction(efsw::WatchID watch_id, const std::string& dir, const std::string& filename, efsw::Action action, - std::string old_filename = "") override; + std::string old_filename) override; private: // Events @@ -78,20 +82,15 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener void anyEvent(SDL_Event& event) override; - enum InitStatus: Int { + enum InitStatus: std::int32_t { InitSuccess, ProfileManagerFailure }; void initEvent(SDL_Event& event); - enum UpdateCheckStatus : Int { - CurlInitFailed = 0, - CurlError = 1, - CurlTimeout = 2, - }; void updateCheckEvent(SDL_Event& event); - enum FileEventType: Int { + enum FileEventType: std::int32_t { FileAdded = efsw::Action::Add, FileDeleted = efsw::Action::Delete, FileModified = efsw::Action::Modified, @@ -104,8 +103,6 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener void initialiseConfiguration(); void initialiseGui(); void initialiseManager(); - auto initialiseToolDirectories() -> bool; - auto findGameDataDirectory() -> bool; void initialiseMassManager(); void initialiseFileWatcher(); @@ -117,21 +114,21 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener void drawInitialisation(); void drawProfileManager(); - auto drawBackupListPopup() -> ImGuiID; - auto drawBackupProfilePopup(std::size_t profile_index) -> ImGuiID; - auto drawDeleteProfilePopup(std::size_t profile_index) -> ImGuiID; + void drawBackupListPopup(); + void drawBackupFolder(const Managers::Vfs::Directory& dir); + void drawBackupRestorePopup(std::size_t backup_index); + void drawBackupDeletePopup(std::size_t backup_index); + void drawDeleteProfilePopup(std::size_t profile_index); void drawManager(); - auto drawIntEditPopup(int* value_to_edit, int max) -> bool; - auto drawRenamePopup(Containers::ArrayView name_view) -> bool; + bool drawIntEditPopup(int* value_to_edit, int max); + bool drawRenamePopup(Containers::ArrayView name_view); void drawGeneralInfo(); void drawResearchInventory(); - template - void drawMaterialRow(Containers::StringView name, Int tier, Getter getter, Setter setter); - void drawUnavailableMaterialRow(Containers::StringView name, Int tier); + void drawMaterialRow(Containers::StringView name, std::int32_t tier, GameData::MaterialID id); void drawMassManager(); - auto drawDeleteMassPopup(int mass_index) -> ImGuiID; - auto drawDeleteStagedMassPopup(Containers::StringView filename) -> ImGuiID; + void drawDeleteMassPopup(int mass_index); + void drawDeleteStagedMassPopup(Containers::StringView filename); void drawMassViewer(); void drawFrameInfo(); @@ -140,33 +137,55 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener void drawEyeColourPicker(); void drawCustomFrameStyles(); void drawArmour(); + void drawBLAttachment(); void drawCustomArmourStyles(); void drawWeapons(); - void drawWeaponCategory(Containers::StringView name, Containers::ArrayView weapons_view, bool& dirty, - Containers::StringView payload_type, Containers::StringView payload_tooltip); - void drawWeaponEditor(Weapon& weapon); + void drawWeaponCategory(Containers::StringView name, Containers::ArrayView weapons_view, + bool& dirty, Containers::StringView payload_type, + Containers::StringView payload_tooltip); + void drawWeaponEditor(GameObjects::Weapon& weapon); void drawGlobalStyles(); void drawTuning(); - void drawDecalEditor(Decal& decal); - void drawAccessoryEditor(Accessory& accessory, Containers::ArrayView style_view); - auto getStyleName(Int id, Containers::ArrayView view) -> Containers::StringView; + void drawDecalEditor(GameObjects::Decal& decal); + void drawAccessoryEditor(GameObjects::Accessory& accessory, + Containers::ArrayView style_view); + auto getStyleName(std::int32_t id, Containers::ArrayView view) + -> Containers::StringView; enum DCSResult { DCS_Fail, DCS_ResetStyle, DCS_Save }; - auto drawCustomStyle(CustomStyle& style) -> DCSResult; + auto drawCustomStyle(GameObjects::CustomStyle& style) -> DCSResult; void drawAbout(); void drawGameState(); // Convenience wrappers over ImGui stuff - void drawHelpMarker(Containers::StringView text, Float wrap_pos = 0.0f); - void drawTooltip(Containers::StringView text, Float wrap_pos = 0.0f); + void drawHelpMarker(Containers::StringView text, float wrap_pos = 0.0f); + void drawTooltip(Containers::StringView text, float wrap_pos = 0.0f); + bool drawCheckbox(Containers::StringView label, bool value); + void drawVector3Drag(Containers::StringView id, Vector3& vec, float speed, Vector3 min, Vector3 max, + const char* x_format, const char* y_format, const char* z_format, + ImGuiSliderFlags_ flags = ImGuiSliderFlags_None); + void drawVector3dDrag(Containers::StringView id, Vector3d& vec, float speed, Vector3d min, Vector3d max, + const char* x_format, const char* y_format, const char* z_format, + ImGuiSliderFlags_ flags = ImGuiSliderFlags_None); + void drawVector3Slider(Containers::StringView id, Vector3& vec, Vector3 min, Vector3 max, const char* x_format, + const char* y_format, const char* z_format, + ImGuiSliderFlags_ flags = ImGuiSliderFlags_None); + void drawVector3dSlider(Containers::StringView id, Vector3d& vec, Vector3d min, Vector3d max, + const char* x_format, const char* y_format, const char* z_format, + ImGuiSliderFlags_ flags = ImGuiSliderFlags_None); + void drawVector2Slider(Containers::StringView id, Vector2& vec, Vector2 min, Vector2 max, const char* x_format, + const char* y_format, ImGuiSliderFlags_ flags = ImGuiSliderFlags_None); + void drawVector2dSlider(Containers::StringView id, Vector2d& vec, Vector2d min, Vector2d max, + const char* x_format, const char* y_format, + ImGuiSliderFlags_ flags = ImGuiSliderFlags_None); template - auto drawUnsafeWidget(Functor func, Args... args) -> bool { + bool drawUnsafeWidget(Functor func, Args... args) { GameState game_state = _gameState; // Copying the value to reduce the risk of a data race. ImGui::BeginDisabled(game_state != GameState::NotRunning); bool result = func(std::forward(args)...); @@ -197,7 +216,6 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener void checkForUpdates(); - Utility::Configuration _conf{"MassBuilderSaveTool.ini"_s}; Utility::Resource _rs{"assets"_s}; // GUI-related members @@ -211,11 +229,11 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener MassViewer } _uiState{UiState::Disclaimer}; - bool _aboutPopup{false}; + bool _aboutPopup = false; #ifdef SAVETOOL_DEBUG_BUILD - bool _demoWindow{false}; - bool _styleEditor{false}; - bool _metricsWindow{false}; + bool _demoWindow = false; + bool _styleEditor = false; + bool _metricsWindow = false; #endif ToastQueue _queue; @@ -223,37 +241,29 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener std::thread _initThread; std::thread _updateThread; - UnsignedInt _initEventId; - UnsignedInt _updateEventId; - UnsignedInt _fileEventId; + std::uint32_t _initEventId; + std::uint32_t _updateEventId; + std::uint32_t _fileEventId; Containers::String _lastError; - Containers::String _gameDataDir; - Containers::String _configDir; - Containers::String _saveDir; - Containers::String _screenshotsDir; - - Containers::String _backupsDir; - Containers::String _stagingDir; - //Containers::String _armouryDir; - //Containers::String _armoursDir; - //Containers::String _weaponsDir; - //Containers::String _stylesDir; - - enum class GameState : UnsignedByte { + enum class GameState : std::uint8_t { Unknown, NotRunning, Running } _gameState{GameState::Unknown}; SDL_TimerID _gameCheckTimerId = 0; - Containers::Pointer _profileManager; - Profile* _currentProfile{nullptr}; + Containers::Pointer _profileManager; + GameObjects::Profile* _currentProfile = nullptr; - Containers::Pointer _massManager; - Mass* _currentMass{nullptr}; + Containers::Pointer _backupManager; - Weapon* _currentWeapon = nullptr; + Containers::Pointer _massManager; + GameObjects::Mass* _currentMass = nullptr; + + Containers::Pointer _stagedMassManager; + + GameObjects::Weapon* _currentWeapon = nullptr; Containers::Pointer _fileWatcher; enum watchID { @@ -262,36 +272,29 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener }; Containers::StaticArray<2, efsw::WatchID> _watchIDs; - int _swapInterval = 1; - float _fpsCap = 60.0f; + Containers::Optional _checker{Containers::NullOpt}; + std::mutex _checkerMutex; - bool _skipDisclaimer{false}; - bool _checkUpdatesOnStartup{true}; + bool _modifiedBySaveTool = false; + bool _jointsDirty = false; + bool _stylesDirty = false; + bool _eyeFlareDirty = false; + bool _meleeDirty = false; + bool _shieldsDirty = false; + bool _bShootersDirty = false; + bool _eShootersDirty = false; + bool _bLaunchersDirty = false; + bool _eLaunchersDirty = false; - bool _updateAvailable{false}; - Containers::String _latestVersion; - Containers::String _releaseLink; - Containers::String _downloadLink; - - bool _modifiedBySaveTool{false}; - bool _jointsDirty{false}; - bool _stylesDirty{false}; - bool _eyeFlareDirty{false}; - Containers::StaticArray<38, Int> _selectedArmourDecals{ValueInit}; - Containers::StaticArray<38, Int> _selectedArmourAccessories{ValueInit}; - Int _selectedBLPlacement{0}; - Int _selectedWeaponPart{0}; - Int _selectedWeaponDecal{0}; - Int _selectedWeaponAccessory{0}; - bool _meleeDirty{false}; - bool _shieldsDirty{false}; - bool _bShootersDirty{false}; - bool _eShootersDirty{false}; - bool _bLaunchersDirty{false}; - bool _eLaunchersDirty{false}; - - bool _cheatMode{false}; - bool _advancedMode{false}; + Containers::Optional _selectedArmourSlot{Containers::NullOpt}; + Containers::StaticArray<38, std::int32_t> _selectedArmourDecals{ValueInit}; + Containers::StaticArray<38, std::int32_t> _selectedArmourAccessories{ValueInit}; + std::uint32_t _selectedBLPlacement = 0; + std::int32_t _selectedWeaponPart = 0; + std::int32_t _selectedWeaponDecal = 0; + std::int32_t _selectedWeaponAccessory = 0; Timeline _timeline; }; + +} diff --git a/src/SaveTool/SaveTool_FileWatcher.cpp b/src/Application/Application_FileWatcher.cpp similarity index 84% rename from src/SaveTool/SaveTool_FileWatcher.cpp rename to src/Application/Application_FileWatcher.cpp index 42a3fcd..3348d8e 100644 --- a/src/SaveTool/SaveTool_FileWatcher.cpp +++ b/src/Application/Application_FileWatcher.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,13 +24,13 @@ #include #include -#include "SaveTool.h" +#include "Application.h" -void SaveTool::handleFileAction(efsw::WatchID watch_id, - const std::string&, - const std::string& filename, - efsw::Action action, - std::string old_filename) +namespace mbst { + +void +Application::handleFileAction(efsw::WatchID watch_id, const std::string&, const std::string& filename, + efsw::Action action, std::string old_filename) { SDL_Event event; SDL_zero(event); @@ -48,7 +48,9 @@ void SaveTool::handleFileAction(efsw::WatchID watch_id, return; } // TODO: actually do something when config files will finally be handled - if(!Utility::String::endsWith(filename, Utility::format("Profile{}.sav", _currentProfile->account()).data())) { + if(!Utility::String::endsWith(filename, Utility::format("Profile{}.sav", _currentProfile->account()).data()) && + filename.find("Unit") == std::string::npos) + { return; } @@ -60,19 +62,20 @@ void SaveTool::handleFileAction(efsw::WatchID watch_id, SDL_PushEvent(&event); } -void SaveTool::fileUpdateEvent(SDL_Event& event) { +void +Application::fileUpdateEvent(SDL_Event& event) { Containers::String filename{static_cast(event.user.data1), std::strlen(static_cast(event.user.data1)), nullptr}; if((event.user.code & StagedUpdate) == StagedUpdate) { - _massManager->refreshStagedMass(filename); + _stagedMassManager->refreshMass(filename); return; } Containers::String old_filename; - Int index = 0; - Int old_index = 0; + std::int32_t index = 0; + std::int32_t old_index = 0; bool is_current_profile = filename == _currentProfile->filename(); bool is_unit = filename.hasPrefix(_currentProfile->isDemo() ? "DemoUnit"_s : "Unit"_s); if(is_unit) { @@ -81,9 +84,10 @@ void SaveTool::fileUpdateEvent(SDL_Event& event) { } if(event.user.code == FileMoved) { - old_filename = Containers::String{static_cast(event.user.data2), std::strlen(static_cast(event.user.data2)), nullptr}; + old_filename = Containers::String{static_cast(event.user.data2), + std::strlen(static_cast(event.user.data2)), nullptr}; old_index = ((old_filename[_currentProfile->isDemo() ? 8 : 4] - 0x30) * 10) + - (old_filename[_currentProfile->isDemo() ? 9 : 5] - 0x30); + (old_filename[_currentProfile->isDemo() ? 9 : 5] - 0x30); } switch(event.user.code) { @@ -123,8 +127,8 @@ void SaveTool::fileUpdateEvent(SDL_Event& event) { } else { if(_modifiedBySaveTool && _currentMass->filename() == filename) { - auto handle = CreateFileW(Utility::Unicode::widen(Containers::StringView{filename}), GENERIC_READ, 0, - nullptr, OPEN_EXISTING, 0, nullptr); + auto handle = CreateFileW(Utility::Unicode::widen(Containers::StringView{filename}), + GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr); if(handle && handle != INVALID_HANDLE_VALUE) { CloseHandle(handle); _modifiedBySaveTool = false; @@ -148,3 +152,5 @@ void SaveTool::fileUpdateEvent(SDL_Event& event) { _queue.addToast(Toast::Type::Warning, "Unknown file action type"_s); } } + +} diff --git a/src/Application/Application_Initialisation.cpp b/src/Application/Application_Initialisation.cpp new file mode 100644 index 0000000..c692408 --- /dev/null +++ b/src/Application/Application_Initialisation.cpp @@ -0,0 +1,141 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include +#include + +#include "../Configuration/Configuration.h" +#include "../FontAwesome/IconsFontAwesome5.h" +#include "../FontAwesome/IconsFontAwesome5Brands.h" +#include "../Logger/Logger.h" + +#include "Application.h" + +namespace mbst { + +void +Application::initEvent(SDL_Event& event) { + _initThread.join(); + + switch(event.user.code) { + case InitSuccess: + _uiState = UiState::ProfileManager; + ImGui::CloseCurrentPopup(); + break; + case ProfileManagerFailure: + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error ", + _profileManager->lastError().data(), window()); + exit(EXIT_FAILURE); + break; + default: + break; + } +} + +void +Application::initialiseConfiguration() { + LOG_INFO("Reading configuration file."); + + setSwapInterval(conf().swapInterval()); + setMinimalLoopPeriod(0); +} + +void +Application::initialiseGui() { + LOG_INFO("Initialising Dear ImGui."); + + auto ctx = ImGui::CreateContext(); + auto& 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(reg_font.data()), int(reg_font.size()), + 20.0f * float(framebufferSize().x()) / size.x(), &font_config); + + auto icon_font = _rs.getRaw(FONT_ICON_FILE_NAME_FAS); + static const ImWchar icon_range[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; + ImFontConfig icon_config; + icon_config.FontDataOwnedByAtlas = false; + icon_config.MergeMode = true; + icon_config.PixelSnapH = true; + icon_config.OversampleH = icon_config.OversampleV = 1; + icon_config.GlyphMinAdvanceX = 18.0f; + io.Fonts->AddFontFromMemoryTTF(const_cast(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(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); + io.Fonts->AddFontFromMemoryTTF(const_cast(mono_font.data()), int(mono_font.size()), + 18.0f * float(framebufferSize().x()) / size.x(), &font_config); + + _imgui = ImGuiIntegration::Context(*ctx, Vector2{windowSize()}/dpiScaling(), windowSize(), framebufferSize()); + + io.IniFilename = nullptr; + + auto& style = ImGui::GetStyle(); + + style.WindowTitleAlign = {0.5f, 0.5f}; + style.FrameRounding = 3.2f; + style.Colors[ImGuiCol_WindowBg] = ImColor(0xff1f1f1f); +} + +void +Application::initialiseManager() { + LOG_INFO("Initialising the profile manager."); + + SDL_Event event; + SDL_zero(event); + event.type = _initEventId; + + _profileManager.emplace(); + if(!_profileManager->ready()) { + event.user.code = ProfileManagerFailure; + SDL_PushEvent(&event); + return; + } + + _backupManager.emplace(); + + _stagedMassManager.emplace(); + + event.user.code = InitSuccess; + SDL_PushEvent(&event); +} + +void +Application::initialiseMassManager() { + LOG_INFO("Initialising the M.A.S.S. manager."); + _massManager.emplace(_currentProfile->account(), _currentProfile->isDemo()); +} + +void +Application::initialiseFileWatcher() { + LOG_INFO("Initialising the file watcher."); + _fileWatcher.emplace(); + _watchIDs[SaveDir] = _fileWatcher->addWatch(conf().directories().gameSaves, this, false); + _watchIDs[StagingDir] = _fileWatcher->addWatch(conf().directories().staging, this, false); + _fileWatcher->watch(); +} + +} diff --git a/src/SaveTool/SaveTool_MainManager.cpp b/src/Application/Application_MainManager.cpp similarity index 64% rename from src/SaveTool/SaveTool_MainManager.cpp rename to src/Application/Application_MainManager.cpp index 2f0d1c7..38e52f6 100644 --- a/src/SaveTool/SaveTool_MainManager.cpp +++ b/src/Application/Application_MainManager.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,20 +16,27 @@ #include +#include #include #include +#include + #include +#include "../Configuration/Configuration.h" #include "../FontAwesome/IconsFontAwesome5.h" -#include "../Maps/LastMissionId.h" -#include "../Maps/StoryProgress.h" +#include "../GameData/LastMissionId.h" +#include "../GameData/StoryProgress.h" -#include "SaveTool.h" +#include "Application.h" -void SaveTool::drawManager() { +namespace mbst { + +void +Application::drawManager() { ImGui::SetNextWindowPos({0.0f, ImGui::GetItemRectSize().y}, ImGuiCond_Always); - ImGui::SetNextWindowSize({Float(windowSize().x()), Float(windowSize().y()) - ImGui::GetItemRectSize().y}, + ImGui::SetNextWindowSize(ImVec2{Vector2{float(windowSize().x()), float(windowSize().y()) - ImGui::GetItemRectSize().y} / dpiScaling()}, ImGuiCond_Always); if(!ImGui::Begin("##MainWindow", nullptr, ImGuiWindowFlags_NoDecoration|ImGuiWindowFlags_NoMove| @@ -52,7 +59,7 @@ void SaveTool::drawManager() { if(ImGui::BeginChild("##ProfileInfo", {ImGui::GetContentRegionAvail().x * 0.60f, 0.0f}, - true, ImGuiWindowFlags_MenuBar)) + ImGuiChildFlags_Border, ImGuiWindowFlags_MenuBar)) { if(ImGui::BeginMenuBar()) { ImGui::TextUnformatted("Profile information"); @@ -79,7 +86,7 @@ void SaveTool::drawManager() { ImGui::SameLine(); if(ImGui::BeginChild("##MASSManager", {0.0f, 0.0f}, - true, ImGuiWindowFlags_MenuBar)) + ImGuiChildFlags_Border, ImGuiWindowFlags_MenuBar)) { if(ImGui::BeginMenuBar()) { ImGui::TextUnformatted("M.A.S.S. management"); @@ -94,7 +101,8 @@ void SaveTool::drawManager() { ImGui::End(); } -auto SaveTool::drawIntEditPopup(int* value_to_edit, int max) -> bool { +bool +Application::drawIntEditPopup(int* value_to_edit, int max) { bool apply = false; if(ImGui::BeginPopup("int_edit")) { ImGui::Text("Please enter a value between 0 and %i:", max); @@ -103,7 +111,7 @@ auto SaveTool::drawIntEditPopup(int* value_to_edit, int max) -> bool { drawHelpMarker("You can either drag the widget left or right to change the value,\n" "or click on it while holding Ctrl to edit the value directly."); ImGui::SameLine(); - drawUnsafeWidget([](auto... args){ return ImGui::SliderInt("", args...); }, + drawUnsafeWidget([](auto... args){ return ImGui::SliderInt("##IntSlider", args...); }, value_to_edit, 0, max, "%d", ImGuiSliderFlags_AlwaysClamp); ImGui::SameLine(); if(drawUnsafeWidget([]{ return ImGui::Button("Apply"); })) { @@ -117,7 +125,8 @@ auto SaveTool::drawIntEditPopup(int* value_to_edit, int max) -> bool { return apply; } -auto SaveTool::drawRenamePopup(Containers::ArrayView name_view) -> bool { +bool +Application::drawRenamePopup(Containers::ArrayView name_view) { bool apply = false; if(ImGui::BeginPopup("name_edit")) { ImGui::TextUnformatted("Please enter a new name. Conditions:"); @@ -133,13 +142,15 @@ auto SaveTool::drawRenamePopup(Containers::ArrayView name_view) -> bool { (name_view[0] != ' ' && name_view[len - 1] != ' ') ? ICON_FA_CHECK : ICON_FA_TIMES); static auto callback = [](ImGuiInputTextCallbackData* data)->int { - if(data->EventChar < 256 && std::strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789- ", char(data->EventChar))) { + if(data->EventChar < 256 && + std::strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789- ", char(data->EventChar))) + { return 0; } return 1; }; - drawUnsafeWidget([](auto... args){ return ImGui::InputText("", args...); }, + drawUnsafeWidget([](auto... args){ return ImGui::InputText("##NameField", args...); }, name_view.data(), name_view.size(), ImGuiInputTextFlags_CallbackCharFilter, callback, nullptr); @@ -169,16 +180,17 @@ auto SaveTool::drawRenamePopup(Containers::ArrayView name_view) -> bool { return apply; } -void SaveTool::drawGeneralInfo() { +void +Application::drawGeneralInfo() { if(!_currentProfile) { return; } ImGui::Text("Credits: %i", _currentProfile->credits()); - auto it = std::find_if(story_progress.begin(), story_progress.end(), - [this](const StoryProgressPoint& p){ return p.id == _currentProfile->storyProgress(); }); - if(it != story_progress.end()) + auto it = std::find_if(GameData::story_progress.begin(), GameData::story_progress.end(), + [this](const GameData::StoryProgressPoint& p){ return p.id == _currentProfile->storyProgress(); }); + if(it != GameData::story_progress.end()) { ImGui::TextUnformatted("Story progress:"); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.x / 4.0f); @@ -193,8 +205,8 @@ void SaveTool::drawGeneralInfo() { ImGui::Text("Story progress: 0x%x", _currentProfile->storyProgress()); } - if(mission_id_map.find(_currentProfile->lastMissionId()) != mission_id_map.cend()) { - ImGui::Text("Last mission: %s", mission_id_map.at(_currentProfile->lastMissionId()).data()); + if(GameData::mission_id_map.find(_currentProfile->lastMissionId()) != GameData::mission_id_map.cend()) { + ImGui::Text("Last mission: %s", GameData::mission_id_map.at(_currentProfile->lastMissionId()).data()); } else if(_currentProfile->lastMissionId() == -1) { ImGui::TextUnformatted("Last mission: none"); @@ -205,7 +217,7 @@ void SaveTool::drawGeneralInfo() { drawTooltip("This is the last mission selected in the mission selection screen, not the last mission played.", float(windowSize().x()) * 0.35f); - const Float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); + const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); ImGui::Dummy({ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y - footer_height_to_reserve}); ImGui::Separator(); @@ -224,13 +236,13 @@ void SaveTool::drawGeneralInfo() { } } - if(!_cheatMode) { + if(!conf().cheatMode()) { return; } ImGui::SameLine(); - static Int credits; + static std::int32_t credits; if(drawUnsafeWidget([]{ return ImGui::Button("Edit credits"); })) { credits = _currentProfile->credits(); ImGui::OpenPopup("int_edit"); @@ -251,7 +263,7 @@ void SaveTool::drawGeneralInfo() { if(_gameState != GameState::NotRunning) { ImGui::CloseCurrentPopup(); } - for(const auto& sp : story_progress) { + for(const auto& sp : GameData::story_progress) { if(ImGui::BeginMenu(sp.chapter.data())) { if(!sp.after) { if(ImGui::MenuItem(sp.point.data())) { @@ -277,7 +289,8 @@ void SaveTool::drawGeneralInfo() { } } -void SaveTool::drawResearchInventory() { +void +Application::drawResearchInventory() { if(!_currentProfile) { return; } @@ -292,156 +305,90 @@ void SaveTool::drawResearchInventory() { ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableSetColumnIndex(1); - ImGui::Text("Engine materials"); + ImGui::TextUnformatted("Engine materials"); - drawMaterialRow("Verse steel", 1, - [this]{ return _currentProfile->verseSteel(); }, - [this](Int amount){ return _currentProfile->setVerseSteel(amount); }); - drawMaterialRow("Undinium", 2, - [this]{ return _currentProfile->undinium(); }, - [this](Int amount){ return _currentProfile->setUndinium(amount); }); - drawMaterialRow("Necrium alloy", 3, - [this]{ return _currentProfile->necriumAlloy(); }, - [this](Int amount){ return _currentProfile->setNecriumAlloy(amount); }); - drawMaterialRow("Lunarite", 4, - [this]{ return _currentProfile->lunarite(); }, - [this](Int amount){ return _currentProfile->setLunarite(amount); }); - drawMaterialRow("Asterite", 5, - [this]{ return _currentProfile->asterite(); }, - [this](Int amount){ return _currentProfile->setAsterite(amount); }); - drawMaterialRow("Hallite fragma", 6, - [this]{ return _currentProfile->halliteFragma(); }, - [this](Int amount){ return _currentProfile->setHalliteFragma(amount); }); - drawUnavailableMaterialRow("Unnoctinium", 7); + drawMaterialRow("Verse steel", 1, GameData::MaterialID::VerseSteel); + drawMaterialRow("Undinium", 2, GameData::MaterialID::Undinium); + drawMaterialRow("Necrium alloy", 3, GameData::MaterialID::NecriumAlloy); + drawMaterialRow("Lunarite", 4, GameData::MaterialID::Lunarite); + drawMaterialRow("Asterite", 5, GameData::MaterialID::Asterite); + drawMaterialRow("Hallite fragma", 6, GameData::MaterialID::HalliteFragma); + drawMaterialRow("Unnoctinium", 7, GameData::MaterialID::Unnoctinium); ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableSetColumnIndex(1); - ImGui::Text("OS materials"); + ImGui::TextUnformatted("OS materials"); - drawMaterialRow("Ednil", 1, - [this]{ return _currentProfile->ednil(); }, - [this](Int amount){ return _currentProfile->setEdnil(amount); }); - drawMaterialRow("Nuflalt", 2, - [this]{ return _currentProfile->nuflalt(); }, - [this](Int amount){ return _currentProfile->setNuflalt(amount); }); - drawMaterialRow("Aurelene", 3, - [this]{ return _currentProfile->aurelene(); }, - [this](Int amount){ return _currentProfile->setAurelene(amount); }); - drawMaterialRow("Soldus", 4, - [this]{ return _currentProfile->soldus(); }, - [this](Int amount){ return _currentProfile->setSoldus(amount); }); - drawMaterialRow("Synthesized N", 5, - [this]{ return _currentProfile->synthesisedN(); }, - [this](Int amount){ return _currentProfile->setSynthesisedN(amount); }); - drawMaterialRow("Nanoc", 6, - [this]{ return _currentProfile->nanoc(); }, - [this](Int amount){ return _currentProfile->setNanoc(amount); }); - drawUnavailableMaterialRow("Abyssillite", 7); + drawMaterialRow("Ednil", 1, GameData::MaterialID::Ednil); + drawMaterialRow("Nuflalt", 2, GameData::MaterialID::Nuflalt); + drawMaterialRow("Aurelene", 3, GameData::MaterialID::Aurelene); + drawMaterialRow("Soldus", 4, GameData::MaterialID::Soldus); + drawMaterialRow("Synthesized N", 5, GameData::MaterialID::SynthesisedN); + drawMaterialRow("Nanoc", 6, GameData::MaterialID::Nanoc); + drawMaterialRow("Abyssillite", 7, GameData::MaterialID::Abyssillite); ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableSetColumnIndex(1); - ImGui::Text("Architect materials"); + ImGui::TextUnformatted("Architect materials"); - drawMaterialRow("Alcarbonite", 1, - [this]{ return _currentProfile->alcarbonite(); }, - [this](Int amount){ return _currentProfile->setAlcarbonite(amount); }); - drawMaterialRow("Keripehene", 2, - [this]{ return _currentProfile->keriphene(); }, - [this](Int amount){ return _currentProfile->setKeriphene(amount); }); - drawMaterialRow("Nitinol-CM", 3, - [this]{ return _currentProfile->nitinolCM(); }, - [this](Int amount){ return _currentProfile->setNitinolCM(amount); }); - drawMaterialRow("Quarkium", 4, - [this]{ return _currentProfile->quarkium(); }, - [this](Int amount){ return _currentProfile->setQuarkium(amount); }); - drawMaterialRow("Alterene", 5, - [this]{ return _currentProfile->alterene(); }, - [this](Int amount){ return _currentProfile->setAlterene(amount); }); - drawMaterialRow("Cosmium", 6, - [this]{ return _currentProfile->cosmium(); }, - [this](Int amount){ return _currentProfile->setCosmium(amount); }); - drawUnavailableMaterialRow("Purified quarkium", 7); + drawMaterialRow("Alcarbonite", 1, GameData::MaterialID::Alcarbonite); + drawMaterialRow("Keriphene", 2, GameData::MaterialID::Keriphene); + drawMaterialRow("Nitinol-CM", 3, GameData::MaterialID::NitinolCM); + drawMaterialRow("Quarkium", 4, GameData::MaterialID::Quarkium); + drawMaterialRow("Alterene", 5, GameData::MaterialID::Alterene); + drawMaterialRow("Cosmium", 6, GameData::MaterialID::Cosmium); + drawMaterialRow("Purified quarkium", 7, GameData::MaterialID::PurifiedQuarkium); ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableSetColumnIndex(1); - ImGui::Text("Quark data"); + ImGui::TextUnformatted("Quark data"); - drawMaterialRow("Mixed composition", 1, - [this]{ return _currentProfile->mixedComposition(); }, - [this](Int amount){ return _currentProfile->setMixedComposition(amount); }); - drawMaterialRow("Void residue", 2, - [this]{ return _currentProfile->voidResidue(); }, - [this](Int amount){ return _currentProfile->setVoidResidue(amount); }); - drawMaterialRow("Muscular construction", 3, - [this]{ return _currentProfile->muscularConstruction(); }, - [this](Int amount){ return _currentProfile->setMuscularConstruction(amount); }); - drawMaterialRow("Mineral exoskeletology", 4, - [this]{ return _currentProfile->mineralExoskeletology(); }, - [this](Int amount){ return _currentProfile->setMineralExoskeletology(amount); }); - drawMaterialRow("Carbonized skin", 5, - [this]{ return _currentProfile->carbonisedSkin(); }, - [this](Int amount){ return _currentProfile->setCarbonisedSkin(amount); }); - drawMaterialRow("Isolated void particle", 6, - [this]{ return _currentProfile->isolatedVoidParticle(); }, - [this](Int amount){ return _currentProfile->setIsolatedVoidParticle(amount); }); - drawUnavailableMaterialRow("Weaponised physiology", 7); + drawMaterialRow("Mixed composition", 1, GameData::MaterialID::MixedComposition); + drawMaterialRow("Void residue", 2, GameData::MaterialID::VoidResidue); + drawMaterialRow("Muscular construction", 3, GameData::MaterialID::MuscularConstruction); + drawMaterialRow("Mineral exoskeletology", 4, GameData::MaterialID::MineralExoskeletology); + drawMaterialRow("Carbonized skin", 5, GameData::MaterialID::CarbonisedSkin); + drawMaterialRow("Isolated void particle", 6, GameData::MaterialID::IsolatedVoidParticle); + drawMaterialRow("Weaponised physiology", 7, GameData::MaterialID::WeaponisedPhysiology); ImGui::EndTable(); } } -template -void SaveTool::drawMaterialRow(Containers::StringView name, Int tier, Getter getter, Setter setter) { - static_assert(std::is_same::value, "getter doesn't return an Int, and/or doesn't take zero arguments."); - static_assert(std::is_same::value, "setter doesn't return a bool, and/or doesn't take a single Int as an argument."); - +void +Application::drawMaterialRow(Containers::StringView name, std::int32_t tier, GameData::MaterialID id) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::Text("T%i", tier); ImGui::TableSetColumnIndex(1); - ImGui::TextUnformatted(name.data()); + ImGui::TextUnformatted(name.cbegin(), name.cend()); ImGui::TableSetColumnIndex(2); - if(getter() != -1) { - ImGui::Text("%i", getter()); - if(_cheatMode) { - ImGui::TableSetColumnIndex(3); - ImGui::PushID(name.data()); - static Int var = 0; - if(drawUnsafeWidget(ImGui::SmallButton, ICON_FA_EDIT)) { - (var) = getter(); - ImGui::OpenPopup("int_edit"); - } - drawTooltip("Edit"); - if(drawIntEditPopup(&(var), 9999)) { - if(!setter(var)) { - _queue.addToast(Toast::Type::Error, _currentProfile->lastError()); - } - } - ImGui::PopID(); + ImGui::Text("%i", _currentProfile->material(id)); + if(conf().cheatMode()) { + ImGui::TableSetColumnIndex(3); + ImGui::PushID(name.data()); + static std::int32_t var = 0; + if(drawUnsafeWidget(ImGui::SmallButton, ICON_FA_EDIT)) { + var = _currentProfile->material(id); + ImGui::OpenPopup("int_edit"); } - } - else { - ImGui::TextDisabled("Not found in the save file"); + drawTooltip("Edit"); + if(drawIntEditPopup(&var, 9999)) { + if(!_currentProfile->setMaterial(id, var)) { + _queue.addToast(Toast::Type::Error, _currentProfile->lastError()); + } + } + ImGui::PopID(); } } -void SaveTool::drawUnavailableMaterialRow(Containers::StringView name, Int tier) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::Text("T%i", tier); - ImGui::TableSetColumnIndex(1); - ImGui::TextUnformatted(name.data()); - ImGui::TableSetColumnIndex(2); - ImGui::TextDisabled("Unavailable as of game version " SUPPORTED_GAME_VERSION); -} - -void SaveTool::drawMassManager() { +void +Application::drawMassManager() { if(!_massManager) { return; } static int mass_to_delete = 0; - static ImGuiID mass_deletion_popup_ID = drawDeleteMassPopup(mass_to_delete); if(ImGui::BeginTable("##HangarsTable", 4, ImGuiTableFlags_BordersOuter|ImGuiTableFlags_RowBg|ImGuiTableFlags_ScrollY, @@ -470,7 +417,7 @@ void SaveTool::drawMassManager() { ImGui::TableSetColumnIndex(0); ImGui::Selectable(Utility::format("{:.2d}", i + 1).data(), false, ImGuiSelectableFlags_SpanAllColumns|ImGuiSelectableFlags_AllowItemOverlap); - if(_massManager->hangar(i).state() == Mass::State::Valid && + if(_massManager->hangar(i).state() == GameObjects::Mass::State::Valid && ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) { drag_drop_index = i; @@ -484,21 +431,21 @@ void SaveTool::drawMassManager() { if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("StagedMass")) { if(payload->DataSize != sizeof(Containers::String)) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error", - "payload->DataSize != sizeof(Containers::String) in SaveTool::drawMassManager()", + "payload->DataSize != sizeof(Containers::String) in Application::drawMassManager()", window()); exit(EXIT_FAILURE); } Containers::StringView file = *static_cast(payload->Data); - if(!_massManager->importMass(file, i)) { + if(!_stagedMassManager->import(file, i, _currentProfile->account(), _currentProfile->isDemo())) { _queue.addToast(Toast::Type::Error, _massManager->lastError()); } } else if((payload = ImGui::AcceptDragDropPayload("Mass"))) { if(payload->DataSize != sizeof(int)) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error", - "payload->DataSize != sizeof(int) in SaveTool::drawMassManager()", + "payload->DataSize != sizeof(int) in Application::drawMassManager()", window()); exit(EXIT_FAILURE); } @@ -515,14 +462,15 @@ void SaveTool::drawMassManager() { ImGui::TableSetColumnIndex(1); switch(_massManager->hangar(i).state()) { - case Mass::State::Empty: + case GameObjects::Mass::State::Empty: ImGui::TextDisabled(""); break; - case Mass::State::Invalid: + case GameObjects::Mass::State::Invalid: ImGui::TextDisabled(""); break; - case Mass::State::Valid: - ImGui::TextUnformatted(_massManager->hangar(i).name().data()); + case GameObjects::Mass::State::Valid: + ImGui::TextUnformatted(_massManager->hangar(i).name().cbegin(), + _massManager->hangar(i).name().cend()); break; } @@ -532,10 +480,10 @@ void SaveTool::drawMassManager() { drawTooltip("This is the currently active frame slot."); } - if(_massManager->hangar(i).state() != Mass::State::Empty) { + if(_massManager->hangar(i).state() != GameObjects::Mass::State::Empty) { ImGui::TableSetColumnIndex(3); ImGui::PushID(i); - if(_massManager->hangar(i).state() == Mass::State::Valid) { + if(_massManager->hangar(i).state() == GameObjects::Mass::State::Valid) { if(ImGui::SmallButton(ICON_FA_SEARCH)) { _currentMass = &_massManager->hangar(i); _uiState = UiState::MassViewer; @@ -550,9 +498,10 @@ void SaveTool::drawMassManager() { ImGui::SameLine(0.0f, 2.0f); if(drawUnsafeWidget(ImGui::SmallButton, ICON_FA_TRASH_ALT)) { mass_to_delete = i; - ImGui::OpenPopup(mass_deletion_popup_ID); + ImGui::OpenPopup("Confirmation##DeleteMassConfirmation"); } drawTooltip("Delete"); + drawDeleteMassPopup(mass_to_delete); ImGui::PopID(); } } @@ -562,7 +511,6 @@ void SaveTool::drawMassManager() { drawDeleteMassPopup(mass_to_delete); - static ImGuiID staged_mass_deletion_popup_ID = drawDeleteStagedMassPopup(""); static Containers::StringView staged_mass_to_delete; if(ImGui::BeginTable("##StagingArea", 2, @@ -579,32 +527,33 @@ void SaveTool::drawMassManager() { ImGui::TextUnformatted("Staging area"); ImGui::SameLine(); if(ImGui::SmallButton(ICON_FA_FOLDER_OPEN " Open staging folder")) { - openUri(Utility::Path::toNativeSeparators(_stagingDir)); + openUri(Utility::Path::toNativeSeparators(conf().directories().staging)); } - for(const auto& pair : _massManager->stagedMasses()) { + for(const auto& mass : _stagedMassManager->stagedMasses()) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - Containers::String staged_formatted = Utility::format("{} ({})", pair.second, pair.first); + Containers::String staged_formatted = Utility::format("{} ({})", mass.name, mass.filename); ImGui::Selectable(staged_formatted.data()); if((ImGui::CalcTextSize(staged_formatted.data()).x + ImGui::GetStyle().FramePadding.x) > ImGui::GetContentRegionAvail().x) { drawTooltip(staged_formatted.data()); } if(ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) { - ImGui::SetDragDropPayload("StagedMass", &(pair.first), sizeof(Containers::String)); + ImGui::SetDragDropPayload("StagedMass", &(mass.filename), sizeof(Containers::String)); - ImGui::Text("%s - Staged", pair.second.data()); + ImGui::Text("%s - Staged", mass.name.cbegin()); ImGui::EndDragDropSource(); } ImGui::TableSetColumnIndex(1); - ImGui::PushID(pair.first.data()); + ImGui::PushID(mass.filename.data()); if(ImGui::SmallButton(ICON_FA_TRASH_ALT)) { - staged_mass_to_delete = pair.first; - ImGui::OpenPopup(staged_mass_deletion_popup_ID); + staged_mass_to_delete = mass.filename; + ImGui::OpenPopup("Confirmation##DeleteStagedMassConfirmation"); } drawTooltip("Delete"); + drawDeleteStagedMassPopup(staged_mass_to_delete); ImGui::PopID(); } @@ -614,7 +563,7 @@ void SaveTool::drawMassManager() { if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("Mass")) { if(payload->DataSize != sizeof(int)) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error", - "payload->DataSize != sizeof(int) in SaveTool::drawMassManager()", + "payload->DataSize != sizeof(int) in Application::drawMassManager()", window()); exit(EXIT_FAILURE); } @@ -632,27 +581,28 @@ void SaveTool::drawMassManager() { drawDeleteStagedMassPopup(staged_mass_to_delete); } -auto SaveTool::drawDeleteMassPopup(int mass_index) -> ImGuiID { +void +Application::drawDeleteMassPopup(int mass_index) { if(!ImGui::BeginPopupModal("Confirmation##DeleteMassConfirmation", nullptr, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove)) { - return ImGui::GetID("Confirmation##DeleteMassConfirmation"); + return; } - if(_massManager->hangar(mass_index).state() == Mass::State::Empty) { + Containers::ScopeGuard guard{ImGui::EndPopup}; + + if(_massManager->hangar(mass_index).state() == GameObjects::Mass::State::Empty) { ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - return 0; + return; } if(_gameState != GameState::NotRunning) { ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - return 0; + return; } ImGui::PushTextWrapPos(float(windowSize().x()) * 0.40f); - if(_massManager->hangar(mass_index).state() == Mass::State::Invalid) { + if(_massManager->hangar(mass_index).state() == GameObjects::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); } @@ -682,22 +632,19 @@ auto SaveTool::drawDeleteMassPopup(int mass_index) -> ImGuiID { ImGui::EndTable(); } - - ImGui::EndPopup(); - - return 0; } -auto SaveTool::drawDeleteStagedMassPopup(Containers::StringView filename) -> ImGuiID { +void +Application::drawDeleteStagedMassPopup(Containers::StringView filename) { if(!ImGui::BeginPopupModal("Confirmation##DeleteStagedMassConfirmation", nullptr, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove)) { - return ImGui::GetID("Confirmation##DeleteStagedMassConfirmation"); + return; } 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()); + _stagedMassManager->at(filename).filename.cbegin()); ImGui::PopTextWrapPos(); if(ImGui::BeginTable("##DeleteStagedMassLayout", 2)) { @@ -708,8 +655,9 @@ auto SaveTool::drawDeleteStagedMassPopup(Containers::StringView filename) -> ImG ImGui::TableSetColumnIndex(1); if(ImGui::Button("Yes")) { - if(!_massManager->deleteStagedMass(filename)) { - _queue.addToast(Toast::Type::Error, _massManager->lastError()); + if(!_stagedMassManager->remove(filename)) { + _queue.addToast(Toast::Type::Error, + "Couldn't delete the staged M.A.S.S. at " + filename + ": " + _stagedMassManager->lastError()); } ImGui::CloseCurrentPopup(); } @@ -722,6 +670,6 @@ auto SaveTool::drawDeleteStagedMassPopup(Containers::StringView filename) -> ImG } ImGui::EndPopup(); - - return 0; +} + } diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/Application/Application_MassViewer.cpp similarity index 63% rename from src/SaveTool/SaveTool_MassViewer.cpp rename to src/Application/Application_MassViewer.cpp index 7ff8c5f..1bd38b9 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/Application/Application_MassViewer.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -19,15 +19,22 @@ #include +#include + +#include "../Configuration/Configuration.h" #include "../FontAwesome/IconsFontAwesome5.h" -#include "../Maps/Accessories.h" +#include "../GameData/Accessories.h" #define STYLENAMES_DEFINITION -#include "../Maps/StyleNames.h" +#include "../GameData/StyleNames.h" +#include "../ImportExport/Export.h" -#include "SaveTool.h" +#include "Application.h" -void SaveTool::drawMassViewer() { - if(!_currentMass || _currentMass->state() != Mass::State::Valid) { +namespace mbst { + +void +Application::drawMassViewer() { + if(!_currentMass || _currentMass->state() != GameObjects::Mass::State::Valid) { _currentMass = nullptr; _currentWeapon = nullptr; _uiState = UiState::MainManager; @@ -36,7 +43,7 @@ void SaveTool::drawMassViewer() { } ImGui::SetNextWindowPos({0.0f, ImGui::GetItemRectSize().y}, ImGuiCond_Always); - ImGui::SetNextWindowSize({Float(windowSize().x()), Float(windowSize().y()) - ImGui::GetItemRectSize().y}, + ImGui::SetNextWindowSize(ImVec2{Vector2{float(windowSize().x()), float(windowSize().y()) - ImGui::GetItemRectSize().y} / dpiScaling()}, ImGuiCond_Always); if(!ImGui::Begin("##MassViewer", nullptr, ImGuiWindowFlags_NoDecoration|ImGuiWindowFlags_NoMove| @@ -48,7 +55,7 @@ void SaveTool::drawMassViewer() { if(ImGui::BeginChild("##MassInfo", {0.0f, 0.0f}, - true, ImGuiWindowFlags_MenuBar)) + ImGuiChildFlags_Border, ImGuiWindowFlags_MenuBar)) { if(ImGui::BeginMenuBar()) { if(ImGui::BeginTable("##MassViewerMenuTable", 4)) { @@ -84,8 +91,9 @@ void SaveTool::drawMassViewer() { _jointsDirty = false; _stylesDirty = false; _eyeFlareDirty = false; - _selectedArmourDecals = Containers::StaticArray<38, Int>{ValueInit}; - _selectedArmourAccessories = Containers::StaticArray<38, Int>{ValueInit}; + _selectedArmourSlot = Containers::NullOpt; + _selectedArmourDecals = Containers::StaticArray<38, std::int32_t>{ValueInit}; + _selectedArmourAccessories = Containers::StaticArray<38, std::int32_t>{ValueInit}; _selectedBLPlacement = 0; _selectedWeaponPart = 0; _selectedWeaponDecal = 0; @@ -122,6 +130,13 @@ void SaveTool::drawMassViewer() { ImGui::EndTabItem(); } + if(_currentMass->bulletLauncherAttachmentStyle() != GameObjects::BulletLauncherAttachmentStyle::NotFound && + ImGui::BeginTabItem("Bullet launcher attachment")) + { + drawBLAttachment(); + ImGui::EndTabItem(); + } + if(ImGui::BeginTabItem("Custom armour styles")) { drawCustomArmourStyles(); ImGui::EndTabItem(); @@ -132,7 +147,7 @@ void SaveTool::drawMassViewer() { ImGui::EndTabItem(); } - if(_currentMass->globalStyles().size() != 0 && ImGui::BeginTabItem("Global styles")) { + if(!_currentMass->globalStyles().isEmpty() && ImGui::BeginTabItem("Global styles")) { drawGlobalStyles(); ImGui::EndTabItem(); } @@ -153,8 +168,9 @@ void SaveTool::drawMassViewer() { ImGui::End(); } -void SaveTool::drawGlobalStyles() { - if(!_currentMass || _currentMass->state() != Mass::State::Valid) { +void +Application::drawGlobalStyles() { + if(!_currentMass || _currentMass->state() != GameObjects::Mass::State::Valid) { return; } @@ -165,10 +181,9 @@ 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++) { + for(std::uint32_t i = 0; i < _currentMass->globalStyles().size(); i++) { ImGui::PushID(int(i)); - DCSResult result; - result = drawCustomStyle(_currentMass->globalStyles()[i]); + auto result = drawCustomStyle(_currentMass->globalStyles()[i]); switch(result) { case DCS_ResetStyle: _currentMass->getGlobalStyles(); @@ -189,8 +204,9 @@ void SaveTool::drawGlobalStyles() { ImGui::EndChild(); } -void SaveTool::drawTuning() { - if(!_currentMass || _currentMass->state() != Mass::State::Valid) { +void +Application::drawTuning() { + if(!_currentMass || _currentMass->state() != GameObjects::Mass::State::Valid) { return; } @@ -221,7 +237,7 @@ void SaveTool::drawTuning() { ImGui::TableNextColumn(); ImGui::TextUnformatted("Gears"); - for(UnsignedInt i = 0; i < _currentMass->gears().size(); i++) { + for(std::uint32_t i = 0; i < _currentMass->gears().size(); i++) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("%i", _currentMass->gears()[i]); @@ -247,7 +263,7 @@ void SaveTool::drawTuning() { ImGui::TableNextColumn(); ImGui::TextUnformatted("Modules"); - for(UnsignedInt i = 0; i < _currentMass->modules().size(); i++) { + for(std::uint32_t i = 0; i < _currentMass->modules().size(); i++) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("%i", _currentMass->modules()[i]); @@ -273,7 +289,7 @@ void SaveTool::drawTuning() { ImGui::TableNextColumn(); ImGui::TextUnformatted("Techs"); - for(UnsignedInt i = 0; i < _currentMass->techs().size(); i++) { + for(std::uint32_t i = 0; i < _currentMass->techs().size(); i++) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("%i", _currentMass->techs()[i]); @@ -285,8 +301,9 @@ void SaveTool::drawTuning() { ImGui::EndTable(); } -auto SaveTool::drawCustomStyle(CustomStyle& style) -> DCSResult { - if(!_currentMass || _currentMass->state() != Mass::State::Valid) { +Application::DCSResult +Application::drawCustomStyle(GameObjects::CustomStyle& style) { + if(!_currentMass || _currentMass->state() != GameObjects::Mass::State::Valid) { return DCS_Fail; } @@ -296,13 +313,15 @@ auto SaveTool::drawCustomStyle(CustomStyle& style) -> DCSResult { DCSResult return_value = DCS_Fail; - if(!ImGui::BeginChild("##CustomStyle", {0.0f, 244.0f}, true, ImGuiWindowFlags_MenuBar)) { + if(!ImGui::BeginChild("##CustomStyle", {}, ImGuiChildFlags_Border|ImGuiChildFlags_AutoResizeY, + ImGuiWindowFlags_MenuBar)) + { ImGui::EndChild(); return DCS_Fail; } if(ImGui::BeginMenuBar()) { - ImGui::TextUnformatted(style.name.data()); + ImGui::TextUnformatted(style.name.cbegin(), style.name.cend()); static Containers::StaticArray<33, char> name_buf{ValueInit}; if(ImGui::SmallButton(ICON_FA_EDIT " Rename")) { @@ -316,6 +335,18 @@ auto SaveTool::drawCustomStyle(CustomStyle& style) -> DCSResult { style.name = name_buf.data(); } + if(ImGui::SmallButton(ICON_FA_FILE_EXPORT " Export")) { + if(!ImportExport::exportStyle(_currentMass->name(), style)) { + _queue.addToast(Toast::Type::Error, ImportExport::lastExportError()); + } + else { + _queue.addToast(Toast::Type::Success, "Style exported successfully."); + } + } + if(drawUnsafeWidget(ImGui::SmallButton, ICON_FA_FILE_IMPORT " Import")) { + // TODO: implement once the style manager is ready. + } + ImGui::EndMenuBar(); } @@ -390,13 +421,14 @@ auto SaveTool::drawCustomStyle(CustomStyle& style) -> DCSResult { return return_value; } -void SaveTool::drawDecalEditor(Decal& decal) { +void +Application::drawDecalEditor(GameObjects::Decal& decal) { ImGui::Text("ID: %i", decal.id); - if(ImGui::BeginTable("##DecalTable", _advancedMode ? 2 : 1, ImGuiTableFlags_BordersInnerV)) { + if(ImGui::BeginTable("##DecalTable", conf().advancedMode() ? 2 : 1, ImGuiTableFlags_BordersInnerV)) { ImGui::TableSetupColumn("##Normal", ImGuiTableColumnFlags_WidthStretch); - if(_advancedMode) { + if(conf().advancedMode()) { ImGui::TableSetupColumn("##Advanced", ImGuiTableColumnFlags_WidthStretch); } @@ -420,12 +452,17 @@ void SaveTool::drawDecalEditor(Decal& decal) { ImGui::SameLine(); drawHelpMarker("Right-click for more option, click the coloured square for the full picker."); - ImGui::PushMultiItemsWidths(2, ImGui::CalcItemWidth()); - ImGui::SliderFloat("##OffsetX", &decal.offset.x(), 0.0f, 1.0f, "X: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##OffsetY", &decal.offset.y(), 0.0f, 1.0f, "Y: %.3f"); - ImGui::PopItemWidth(); + std::visit( + [this](auto& arg){ + using T = std::decay_t; + if constexpr (std::is_same_v) { + drawVector2Slider("##Offset", arg, Vector2{0.0f}, Vector2{1.0f}, "X: %.3f", "Y: %.3f"); + } + else if constexpr (std::is_same_v) { + drawVector2dSlider("##Offset", arg, Vector2d{0.0}, Vector2d{1.0}, "X: %.3f", "Y: %.3f"); + } + }, decal.offset + ); ImGui::SameLine(); drawHelpMarker("0.0 = -100 in-game\n1.0 = 100 in-game"); @@ -442,7 +479,7 @@ void SaveTool::drawDecalEditor(Decal& decal) { ImGui::Checkbox("##Wrap", &decal.wrap); ImGui::EndGroup(); - if(_advancedMode) { + if(conf().advancedMode()) { ImGui::TableNextColumn(); ImGui::TextColored(ImColor(255, 255, 0), ICON_FA_EXCLAMATION_TRIANGLE); @@ -458,34 +495,49 @@ void SaveTool::drawDecalEditor(Decal& decal) { 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::PushItemWidth(-1.0f); + std::visit( + [this](auto& arg){ + using T = std::decay_t; + if constexpr (std::is_same_v) { + drawVector3Drag("##Position", arg, 1.0f, Vector3{-FLT_MAX}, Vector3{FLT_MAX}, "X: %.3f", + "Y: %.3f", "Z: %.3f"); + } + else if constexpr (std::is_same_v) { + drawVector3dDrag("##Position", arg, 1.0f, Vector3d{-DBL_MAX}, Vector3d{DBL_MAX}, "X: %.3f", + "Y: %.3f", "Z: %.3f"); + } + }, decal.position + ); - 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(); + std::visit( + [this](auto& arg){ + using T = std::decay_t; + if constexpr (std::is_same_v) { + drawVector3Drag("##U", arg, 1.0f, Vector3{-FLT_MAX}, Vector3{FLT_MAX}, "X: %.3f", "Y: %.3f", + "Z: %.3f"); + } + else if constexpr (std::is_same_v) { + drawVector3dDrag("##U", arg, 1.0f, Vector3d{-DBL_MAX}, Vector3d{DBL_MAX}, "X: %.3f", "Y: %.3f", + "Z: %.3f"); + } + }, decal.uAxis + ); + + std::visit( + [this](auto& arg){ + using T = std::decay_t; + if constexpr (std::is_same_v) { + drawVector3Drag("##V", arg, 1.0f, Vector3{-FLT_MAX}, Vector3{FLT_MAX}, "X: %.3f", "Y: %.3f", + "Z: %.3f"); + } + else if constexpr (std::is_same_v) { + drawVector3dDrag("##V", arg, 1.0f, Vector3d{-DBL_MAX}, Vector3d{DBL_MAX}, "X: %.3f", "Y: %.3f", + "Z: %.3f"); + } + }, decal.vAxis + ); - 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(); } @@ -494,12 +546,13 @@ void SaveTool::drawDecalEditor(Decal& decal) { } } -void SaveTool::drawAccessoryEditor(Accessory& accessory, Containers::ArrayView style_view) { +void +Application::drawAccessoryEditor(GameObjects::Accessory& accessory, Containers::ArrayView style_view) { if(accessory.id < 1) { ImGui::TextUnformatted("Accessory: "); } - else if(accessories.find(accessory.id) != accessories.cend()) { - ImGui::Text("Accessory #%.4i - %s", accessory.id, accessories.at(accessory.id).name.data()); + else if(GameData::accessories.find(accessory.id) != GameData::accessories.cend()) { + ImGui::Text("Accessory #%.4i - %s", accessory.id, GameData::accessories.at(accessory.id).name.data()); } else { ImGui::Text("Accessory #%i", accessory.id); @@ -508,8 +561,8 @@ void SaveTool::drawAccessoryEditor(Accessory& accessory, Containers::ArrayView size = Containers::NullOpt; + static std::int32_t tab = 0; + static Containers::Optional size = Containers::NullOpt; if(ImGui::SmallButton("Change")) { ImGui::OpenPopup("##AccessoryPopup"); if(accessory.id >= 3000) { @@ -532,7 +585,7 @@ void SaveTool::drawAccessoryEditor(Accessory& accessory, Containers::ArrayView= 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)) + for(const auto& [id, acc] : GameData::accessories) { + if(id >= tab * 1000 && id < ((tab + 1) * 1000) && (!size || *size == acc.size)) { + if(ImGui::Selectable(Utility::format("#{:.4d} - {} ({})", id, acc.name, size_labels[acc.size]).data(), + id == accessory.id)) { - accessory.id = acc.first; + accessory.id = id; accessory.attachIndex = 0; } - if(acc.first == accessory.id) { + if(id == accessory.id) { ImGui::SetItemDefaultFocus(); } } @@ -635,11 +688,11 @@ void SaveTool::drawAccessoryEditor(Accessory& accessory, Containers::ArrayView; + if constexpr (std::is_same_v) { + drawVector3Drag("##RelativePosition", arg, 1.0f, Vector3{-FLT_MAX}, Vector3{FLT_MAX}, "X: %.3f", + "Y: %.3f", "Z: %.3f"); + } + else if constexpr (std::is_same_v) { + drawVector3dDrag("##RelativePosition", arg, 1.0f, Vector3d{-DBL_MAX}, Vector3d{DBL_MAX}, "X: %.3f", + "Y: %.3f", "Z: %.3f"); + } + }, accessory.relativePosition + ); } - ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); - ImGui::SliderFloat("##PosOffsetX", &accessory.relativePositionOffset.x(), -500.0f, +500.0f, "X: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##PosOffsetY", &accessory.relativePositionOffset.y(), -500.0f, +500.0f, "Y: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##PosOffsetZ", &accessory.relativePositionOffset.z(), -500.0f, +500.0f, "Z: %.3f"); - ImGui::PopItemWidth(); + std::visit( + [this](auto& arg){ + using T = std::decay_t; + if constexpr (std::is_same_v) { + drawVector3Slider("##OffsetPosition", arg, Vector3{-500.0f}, Vector3{500.0f}, "X: %.3f", "Y: %.3f", + "Z: %.3f"); + } + else if constexpr (std::is_same_v) { + drawVector3dSlider("##OffsetPosition", arg, Vector3d{-500.0}, Vector3d{500.0}, "X: %.3f", "Y: %.3f", + "Z: %.3f"); + } + }, accessory.relativePositionOffset + ); ImGui::SameLine(); drawHelpMarker("+/-500.0 = +/-250 in-game"); - if(_advancedMode) { - ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); - ImGui::DragFloat("##RotX", &accessory.relativeRotation.x(), 1.0f, -FLT_MAX, +FLT_MAX, "Roll: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::DragFloat("##RotY", &accessory.relativeRotation.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Yaw: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::DragFloat("##RotZ", &accessory.relativeRotation.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Pitch: %.3f"); - ImGui::PopItemWidth(); + if(conf().advancedMode()) { + std::visit( + [this](auto& arg){ + using T = std::decay_t; + if constexpr (std::is_same_v) { + drawVector3Drag("##RelativeRotation", arg, 1.0f, Vector3{-FLT_MAX}, Vector3{FLT_MAX}, "Roll: %.3f", + "Yaw: %.3f", "Pitch: %.3f"); + } + else if constexpr (std::is_same_v) { + drawVector3dDrag("##RelativeRotation", arg, 1.0f, Vector3d{-DBL_MAX}, Vector3d{DBL_MAX}, + "Roll: %.3f", "Yaw: %.3f", "Pitch: %.3f"); + } + }, accessory.relativePosition + ); } - ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); - ImGui::SliderFloat("##RotOffsetX", &accessory.relativeRotationOffset.x(), -180.0f, +180.0f, "Roll: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##RotOffsetY", &accessory.relativeRotationOffset.y(), -180.0f, +180.0f, "Yaw: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##RotOffsetZ", &accessory.relativeRotationOffset.z(), -180.0f, +180.0f, "Pitch: %.3f"); - ImGui::PopItemWidth(); + std::visit( + [this](auto& arg){ + using T = std::decay_t; + if constexpr (std::is_same_v) { + drawVector3Slider("##OffsetRotation", arg, Vector3{-180.0f}, Vector3{180.0f}, "Roll: %.3f", + "Yaw: %.3f", "Pitch: %.3f"); + } + else if constexpr (std::is_same_v) { + drawVector3dSlider("##OffsetRotation", arg, Vector3d{-180.0}, Vector3d{180.0}, "Roll: %.3f", + "Yaw: %.3f", "Pitch: %.3f"); + } + }, accessory.relativeRotationOffset + ); - ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); - ImGui::SliderFloat("##ScaleX", &accessory.localScale.x(), -3.0f, +3.0f, "X: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##ScaleY", &accessory.localScale.y(), -3.0f, +3.0f, "Y: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##ScaleZ", &accessory.localScale.z(), -3.0f, +3.0f, "Z: %.3f"); - ImGui::PopItemWidth(); + std::visit( + [this](auto& arg){ + using T = std::decay_t; + if constexpr (std::is_same_v) { + drawVector3Slider("##Scale", arg, Vector3{-3.0f}, Vector3{3.0f}, "X: %.3f", "Y: %.3f", "Z: %.3f"); + } + else if constexpr (std::is_same_v) { + drawVector3dSlider("##Scale", arg, Vector3d{-3.0}, Vector3d{3.0}, "X: %.3f", "Y: %.3f", "Z: %.3f"); + } + }, accessory.localScale + ); ImGui::SameLine(); drawHelpMarker("+/-3.0 = +/-150 in-game"); ImGui::EndGroup(); } -auto SaveTool::getStyleName(Int id, Containers::ArrayView view) -> Containers::StringView { +Containers::StringView +Application::getStyleName(std::int32_t id, Containers::ArrayView view) { if(id >= 0 && id <= 15) { return view[id].name; } @@ -740,6 +812,8 @@ auto SaveTool::getStyleName(Int id, Containers::ArrayView view) -> return _currentMass->globalStyles()[id - 50].name; } else { - return style_names.at(id); + return GameData::builtin_style_names.at(id); } } + +} diff --git a/src/Application/Application_MassViewer_Armour.cpp b/src/Application/Application_MassViewer_Armour.cpp new file mode 100644 index 0000000..a6e5b3b --- /dev/null +++ b/src/Application/Application_MassViewer_Armour.cpp @@ -0,0 +1,392 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include "../FontAwesome/IconsFontAwesome5.h" +#include "../GameData/ArmourSets.h" +#include "../GameData/StyleNames.h" + +#include "Application.h" + +namespace mbst { + +void +Application::drawArmour() { + if(!_currentMass || _currentMass->state() != GameObjects::Mass::State::Valid) { + return; + } + + constexpr static Containers::StringView slot_labels[] = { + #define c(enumerator, strenum, name) name, + #include "../Maps/ArmourSlots.hpp" + #undef c + }; + + auto labels_view = arrayView(slot_labels); + + const static float footer_height_to_reserve = ImGui::GetFrameHeightWithSpacing(); + + ImGui::BeginGroup(); + + if(ImGui::BeginTable("##SlotsTable", 1, + ImGuiTableFlags_ScrollY|ImGuiTableFlags_BordersOuter|ImGuiTableFlags_BordersInnerH, + {ImGui::GetContentRegionAvail().x * 0.15f, -footer_height_to_reserve})) + { + ImGui::TableSetupColumn("##Slots", ImGuiTableColumnFlags_WidthStretch); + + for(std::size_t i = 0; i < labels_view.size(); i++) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if(ImGui::Selectable(labels_view[i].data(), _selectedArmourSlot && (*_selectedArmourSlot) == i, + ImGuiSelectableFlags_SpanAvailWidth)) + { + _selectedArmourSlot = i; + } + } + + ImGui::EndTable(); + } + + if(ImGui::Button(ICON_FA_UNDO_ALT " Reset all")) { + _currentMass->getArmourParts(); + } + + ImGui::EndGroup(); + + ImGui::SameLine(); + + if(!_selectedArmourSlot) { + ImGui::TextUnformatted("No selected armour slot."); + return; + } + + auto& part = _currentMass->armourParts()[*_selectedArmourSlot]; + + ImGui::BeginGroup(); + + if(ImGui::BeginChild("##ArmourEditor", {0.0f, -footer_height_to_reserve})) { + + ImGui::SeparatorText("Part"); + + if(GameData::armour_sets.find(part.id) != GameData::armour_sets.cend()) { + ImGui::Text("Set name: %s", GameData::armour_sets.at(part.id).name.data()); + } + else { + ImGui::Text("Set ID: %u", part.id); + } + + ImGui::SameLine(); + + if(ImGui::SmallButton("Change")) { + ImGui::OpenPopup("##ArmourPartPopup"); + } + if(ImGui::BeginPopup("##ArmourPartPopup")) { + if(ImGui::BeginListBox("##ChangePart")) { + for(auto& set : GameData::armour_sets) { + if(part.slot != GameObjects::ArmourPart::Slot::Neck || set.second.neck_compatible) { + if(ImGui::Selectable(set.second.name.data(), set.first == part.id, + ImGuiSelectableFlags_SpanAvailWidth)) + { + part.id = set.first; + } + } + } + ImGui::EndListBox(); + } + + ImGui::EndPopup(); + } + + ImGui::SeparatorText("Styles"); + + for(std::int32_t i = 0; i < 4; i++) { + drawAlignedText("Slot %d:", i + 1); + + ImGui::SameLine(); + + ImGui::PushID(i); + if(ImGui::BeginCombo("##Style", + getStyleName(part.styles[i], _currentMass->armourCustomStyles()).data())) + { + for(const auto& style : GameData::builtin_style_names) { + if(ImGui::Selectable(getStyleName(style.first, _currentMass->armourCustomStyles()).data(), + part.styles[i] == style.first)) + { + part.styles[i] = style.first; + } + } + + ImGui::EndCombo(); + } + ImGui::PopID(); + } + + ImGui::SeparatorText("Decals"); + + constexpr static float selectable_width = 25.0f; + + drawAlignedText("Showing/editing decal:"); + ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, {0.5f, 0.0f}); + for(std::uint32_t i = 0; i < part.decals.size(); i++) { + ImGui::SameLine(); + if(ImGui::Selectable(std::to_string(i + 1).c_str(), _selectedArmourDecals[*_selectedArmourSlot] == int(i), + ImGuiSelectableFlags_None, {selectable_width, 0.0f})) + { + _selectedArmourDecals[*_selectedArmourSlot] = int(i); + } + if(i != part.decals.size() - 1) { + ImGui::SameLine(); + ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); + } + } + ImGui::PopStyleVar(); + drawDecalEditor(part.decals[_selectedArmourDecals[*_selectedArmourSlot]]); + + if(!part.accessories.isEmpty()) { + ImGui::SeparatorText("Accessories"); + + drawAlignedText("Showing/editing accessory:"); + ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, {0.5f, 0.0f}); + for(std::uint32_t i = 0; i < part.accessories.size(); i++) { + ImGui::SameLine(); + if(ImGui::Selectable((std::string{} + char(i + 65)).c_str(), + _selectedArmourAccessories[*_selectedArmourSlot] == int(i), + ImGuiSelectableFlags_None, {selectable_width, 0.0f})) + { + _selectedArmourAccessories[*_selectedArmourSlot] = int(i); + } + if(i != part.accessories.size() - 1) { + ImGui::SameLine(); + ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); + } + } + ImGui::PopStyleVar(); + drawAccessoryEditor(part.accessories[_selectedArmourAccessories[*_selectedArmourSlot]], + _currentMass->armourCustomStyles()); + } + } + + ImGui::EndChild(); + + if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { + _modifiedBySaveTool = true; + if(!_currentMass->writeArmourPart(part.slot)) { + _modifiedBySaveTool = false; + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); + } + } + + ImGui::EndGroup(); +} + +void +Application::drawBLAttachment() { + if(!_currentMass || _currentMass->state() != GameObjects::Mass::State::Valid) { + return; + } + + drawAlignedText("Attachment style:"_s); + ImGui::SameLine(); + if(ImGui::RadioButton("Active one", + _currentMass->bulletLauncherAttachmentStyle() == GameObjects::BulletLauncherAttachmentStyle::ActiveOne)) + { + _currentMass->bulletLauncherAttachmentStyle() = GameObjects::BulletLauncherAttachmentStyle::ActiveOne; + } + ImGui::SameLine(); + if(ImGui::RadioButton("Active one per slot", + _currentMass->bulletLauncherAttachmentStyle() == GameObjects::BulletLauncherAttachmentStyle::ActiveOnePerSlot)) + { + _currentMass->bulletLauncherAttachmentStyle() = GameObjects::BulletLauncherAttachmentStyle::ActiveOnePerSlot; + } + ImGui::SameLine(); + if(ImGui::RadioButton("All equipped", + _currentMass->bulletLauncherAttachmentStyle() == GameObjects::BulletLauncherAttachmentStyle::AllEquipped)) + { + _currentMass->bulletLauncherAttachmentStyle() = GameObjects::BulletLauncherAttachmentStyle::ActiveOnePerSlot; + } + + ImGui::Separator(); + + constexpr static float selectable_width = 25.0f; + drawAlignedText("Launcher slot:"); + ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, {0.5f, 0.0f}); + for(auto i = 0u; i < _currentMass->bulletLauncherAttachments().size(); i++) { + ImGui::SameLine(); + if(ImGui::Selectable(std::to_string(i).c_str(), _selectedBLPlacement == i, ImGuiSelectableFlags_None, + {selectable_width, 0.0f})) + { + _selectedBLPlacement = i; + } + if(i != _currentMass->bulletLauncherAttachments().size() - 1) { + ImGui::SameLine(); + ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); + } + } + ImGui::PopStyleVar(); + + auto& placement = _currentMass->bulletLauncherAttachments()[_selectedBLPlacement]; + + static const Containers::StringView socket_labels[] = { + #define c(enumerator, enumstr, name) name, + #include "../Maps/BulletLauncherSockets.hpp" + #undef c + }; + + drawAlignedText("Socket:"); + ImGui::SameLine(); + if(ImGui::BeginCombo("##Socket", socket_labels[std::uint32_t(placement.socket)].data())) { + for(std::uint32_t i = 0; i < (sizeof(socket_labels) / sizeof(socket_labels[0])); i++) { + if(ImGui::Selectable(socket_labels[i].data(), i == std::uint32_t(placement.socket), ImGuiSelectableFlags_SpanAvailWidth)) { + placement.socket = static_cast(i); + } + } + ImGui::EndCombo(); + } + + if(placement.socket != GameObjects::BulletLauncherAttachment::Socket::Auto) { + ImGui::BeginGroup(); + drawAlignedText("Relative position:"); + drawAlignedText("Offset position:"); + drawAlignedText("Relative rotation:"); + drawAlignedText("Offset rotation:"); + drawAlignedText("Scale:"); + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + std::visit( + [this](auto& arg){ + using T = std::decay_t; + if constexpr (std::is_same_v) { + drawVector3Drag("##RelativePosition", arg, 1.0f, Vector3{-FLT_MAX}, Vector3{FLT_MAX}, "X: %.3f", + "Y: %.3f", "Z: %.3f"); + } + else if constexpr (std::is_same_v) { + drawVector3dDrag("##RelativePosition", arg, 1.0f, Vector3d{-DBL_MAX}, Vector3d{DBL_MAX}, + "X: %.3f", "Y: %.3f", "Z: %.3f"); + } + }, placement.relativeLocation + ); + + std::visit( + [this](auto& arg){ + using T = std::decay_t; + if constexpr (std::is_same_v) { + drawVector3Slider("##OffsetPosition", arg, Vector3{-500.0f}, Vector3{500.0f}, "X: %.3f", "Y: %.3f", + "Z: %.3f"); + } + else if constexpr (std::is_same_v) { + drawVector3dSlider("##OffsetPosition", arg, Vector3d{-500.0}, Vector3d{500.0}, "X: %.3f", + "Y: %.3f", "Z: %.3f"); + } + }, placement.offsetLocation + ); + ImGui::SameLine(); + drawHelpMarker("+/-500.0 = +/-250 in-game"); + + std::visit( + [this](auto& arg){ + using T = std::decay_t; + if constexpr (std::is_same_v) { + drawVector3Drag("##RelativeRotation", arg, 1.0f, Vector3{-FLT_MAX}, Vector3{FLT_MAX}, + "Pitch: %.3f", "Yaw: %.3f", "Roll: %.3f"); + } + else if constexpr (std::is_same_v) { + drawVector3dDrag("##RelativeRotation", arg, 1.0f, Vector3d{-DBL_MAX}, Vector3d{DBL_MAX}, + "Pitch: %.3f", "Yaw: %.3f", "Roll: %.3f"); + } + }, placement.relativeRotation + ); + + std::visit( + [this](auto& arg){ + using T = std::decay_t; + if constexpr (std::is_same_v) { + drawVector3Slider("##OffsetRotation", arg, Vector3{-30.0f, -30.0f, -180.0f}, + Vector3{30.0f, 30.0f, 180.0f}, "Pitch: %.3f", "Yaw: %.3f", "Roll: %.3f"); + } + else if constexpr (std::is_same_v) { + drawVector3dSlider("##OffsetRotation", arg, Vector3d{-30.0, -30.0, -180.0}, + Vector3d{30.0, 30.0, 180.0}, "Pitch: %.3f", "Yaw: %.3f", "Roll: %.3f"); + } + }, placement.offsetRotation + ); + + std::visit( + [this](auto& arg){ + using T = std::decay_t; + if constexpr (std::is_same_v) { + drawVector3Slider("##Scale", arg, Vector3{0.5f}, Vector3{1.5f}, "X: %.3f", "Y: %.3f", "Z: %.3f"); + } + else if constexpr (std::is_same_v) { + drawVector3dSlider("##Scale", arg, Vector3d{0.5}, Vector3d{1.5}, "X: %.3f", "Y: %.3f", "Z: %.3f"); + } + }, placement.relativeScale + ); + ImGui::SameLine(); + drawHelpMarker("0.5 = 50 in-game\n1.5 = 150 in-game"); + ImGui::EndGroup(); + } + + _modifiedBySaveTool = true; + if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { + _modifiedBySaveTool = true; + if(!_currentMass->writeBulletLauncherAttachments()) { + _modifiedBySaveTool = false; + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); + } + } +} + +void +Application::drawCustomArmourStyles() { + if(!_currentMass || _currentMass->state() != GameObjects::Mass::State::Valid) { + return; + } + + if(!ImGui::BeginChild("##ArmourStyles")) { + ImGui::EndChild(); + return; + } + + ImGui::TextWrapped("In-game values are multiplied by 100. For example, 0.500 here is equal to 50 in-game."); + + for(std::uint32_t i = 0; i < _currentMass->armourCustomStyles().size(); i++) { + ImGui::PushID(int(i)); + auto result = drawCustomStyle(_currentMass->armourCustomStyles()[i]); + switch(result) { + case DCS_ResetStyle: + _currentMass->getArmourCustomStyles(); + break; + case DCS_Save: + _modifiedBySaveTool = true; + if(!_currentMass->writeArmourCustomStyle(i)) { + _modifiedBySaveTool = false; + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); + } + break; + default: + break; + } + ImGui::PopID(); + } + + ImGui::EndChild(); +} + +} diff --git a/src/Application/Application_MassViewer_Frame.cpp b/src/Application/Application_MassViewer_Frame.cpp new file mode 100644 index 0000000..2ccead1 --- /dev/null +++ b/src/Application/Application_MassViewer_Frame.cpp @@ -0,0 +1,294 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include "../FontAwesome/IconsFontAwesome5.h" +#include "../GameData/StyleNames.h" + +#include "Application.h" + +namespace mbst { + +void +Application::drawFrameInfo() { + if(!_currentMass || _currentMass->state() != GameObjects::Mass::State::Valid) { + return; + } + + if(!ImGui::BeginChild("##FrameInfo")) { + ImGui::EndChild(); + return; + } + + ImGui::BeginGroup(); + + if(ImGui::BeginChild("##JointSliders", + {(ImGui::GetContentRegionAvail().x / 2.0f) - (ImGui::GetStyle().WindowPadding.x / 2.0f), + 0.0f}, + ImGuiChildFlags_Border|ImGuiChildFlags_AutoResizeY, ImGuiWindowFlags_MenuBar)) + { + if(ImGui::BeginMenuBar()) { + ImGui::TextUnformatted("Joint sliders"); + drawHelpMarker("In-game values are multiplied by 100. For example, 0.500 here is equal to 50 in-game.", + static_cast(windowSize().x()) / 4.0f); + + ImGui::EndMenuBar(); + } + + drawJointSliders(); + } + ImGui::EndChild(); + + if(ImGui::BeginChild("##FrameStyles", + {(ImGui::GetContentRegionAvail().x / 2.0f) - (ImGui::GetStyle().WindowPadding.x / 2.0f), 0.0f}, + ImGuiChildFlags_Border, ImGuiWindowFlags_MenuBar)) + { + if(ImGui::BeginMenuBar()) { + ImGui::TextUnformatted("Frame styles"); + + ImGui::EndMenuBar(); + } + + drawFrameStyles(); + } + ImGui::EndChild(); + + ImGui::EndGroup(); + + ImGui::SameLine(); + + if(ImGui::BeginChild("##EyeFlare", {}, ImGuiChildFlags_Border, ImGuiWindowFlags_MenuBar)) { + if(ImGui::BeginMenuBar()) { + ImGui::TextUnformatted("Eye flare colour"); + drawHelpMarker("Right-click the picker for more options.", 250.0f); + + ImGui::EndMenuBar(); + } + + drawEyeColourPicker(); + } + ImGui::EndChild(); + + ImGui::EndChild(); +} + +void +Application::drawJointSliders() { + if(!_currentMass || _currentMass->state() != GameObjects::Mass::State::Valid) { + return; + } + + ImGui::BeginGroup(); + drawAlignedText("Neck:"); + drawAlignedText("Body:"); + drawAlignedText("Shoulders:"); + drawAlignedText("Hips:"); + drawAlignedText("Arms:"); + drawAlignedText("Legs:"); + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + ImGui::PushItemWidth(-1.0f); + if(ImGui::SliderFloat("##NeckSlider", &_currentMass->jointSliders().neck, 0.0f, 1.0f)) { + _jointsDirty = true; + } + if(ImGui::SliderFloat("##BodySlider", &_currentMass->jointSliders().body, 0.0f, 1.0f)) { + _jointsDirty = true; + } + if(ImGui::SliderFloat("##ShouldersSlider", &_currentMass->jointSliders().shoulders, 0.0f, 1.0f)) { + _jointsDirty = true; + } + if(ImGui::SliderFloat("##HipsSlider", &_currentMass->jointSliders().hips, 0.0f, 1.0f)) { + _jointsDirty = true; + } + ImGui::PushMultiItemsWidths(2, ImGui::CalcItemWidth()); + if(ImGui::SliderFloat("##UpperArmsSlider", &_currentMass->jointSliders().upperArms, 0.0f, 1.0f, "Upper: %.3f")) { + _jointsDirty = true; + } + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + if(ImGui::SliderFloat("##LowerArmsSlider", &_currentMass->jointSliders().lowerArms, 0.0f, 1.0f, "Lower: %.3f")) { + _jointsDirty = true; + } + ImGui::PopItemWidth(); + ImGui::PushMultiItemsWidths(2, ImGui::CalcItemWidth()); + if(ImGui::SliderFloat("##UpperLegsSlider", &_currentMass->jointSliders().upperLegs, 0.0f, 1.0f, "Upper: %.3f")) { + _jointsDirty = true; + } + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + if(ImGui::SliderFloat("##LowerLegsSlider", &_currentMass->jointSliders().lowerLegs, 0.0f, 1.0f, "Lower: %.3f")) { + _jointsDirty = true; + } + ImGui::PopItemWidth(); + ImGui::PopItemWidth(); + ImGui::EndGroup(); + + if(!_jointsDirty) { + ImGui::BeginDisabled(); + ImGui::Button(ICON_FA_SAVE " Save"); + ImGui::SameLine(); + ImGui::Button(ICON_FA_UNDO " Reset"); + ImGui::EndDisabled(); + } + else { + if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { + _modifiedBySaveTool = true; + if(!_currentMass->writeJointSliders()) { + _modifiedBySaveTool = false; + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); + } + _jointsDirty = false; + } + ImGui::SameLine(); + if(ImGui::Button(ICON_FA_UNDO " Reset")) { + _currentMass->getJointSliders(); + _jointsDirty = false; + } + } +} + +void +Application::drawFrameStyles() { + if(!_currentMass || _currentMass->state() != GameObjects::Mass::State::Valid) { + return; + } + + for(std::int32_t i = 0; i < 4; i++) { + drawAlignedText("Slot %d:", i + 1); + + ImGui::SameLine(); + + ImGui::PushID(i); + + ImGui::PushItemWidth(-1.0f); + if(ImGui::BeginCombo("##Style", + getStyleName(_currentMass->frameStyles()[i], _currentMass->frameCustomStyles()).data())) + { + for(const auto& style : GameData::builtin_style_names) { + if(ImGui::Selectable(getStyleName(style.first, _currentMass->frameCustomStyles()).data(), + _currentMass->frameStyles()[i] == style.first)) + { + _currentMass->frameStyles()[i] = style.first; + _stylesDirty = true; + } + } + + ImGui::EndCombo(); + } + ImGui::PopItemWidth(); + + ImGui::PopID(); + } + + if(!_stylesDirty) { + ImGui::BeginDisabled(); + ImGui::Button(ICON_FA_SAVE " Save"); + ImGui::SameLine(); + ImGui::Button(ICON_FA_UNDO " Reset"); + ImGui::EndDisabled(); + } + else { + if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { + _modifiedBySaveTool = true; + if(!_currentMass->writeFrameStyles()) { + _modifiedBySaveTool = false; + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); + } + _stylesDirty = false; + } + ImGui::SameLine(); + if(ImGui::Button(ICON_FA_UNDO " Reset")) { + _currentMass->getFrameStyles(); + _stylesDirty = false; + } + } +} + +void +Application::drawEyeColourPicker() { + if(!_currentMass || _currentMass->state() != GameObjects::Mass::State::Valid) { + return; + } + + if(ImGui::ColorPicker3("##EyeFlarePicker", &_currentMass->eyeFlareColour().x())) { + _eyeFlareDirty = true; + } + + if(!_eyeFlareDirty) { + ImGui::BeginDisabled(); + ImGui::Button(ICON_FA_SAVE " Save"); + ImGui::SameLine(); + ImGui::Button(ICON_FA_UNDO " Reset"); + ImGui::EndDisabled(); + } + else { + if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { + _modifiedBySaveTool = true; + if(!_currentMass->writeEyeFlareColour()) { + _modifiedBySaveTool = false; + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); + } + _eyeFlareDirty = false; + } + ImGui::SameLine(); + if(ImGui::Button(ICON_FA_UNDO " Reset")) { + _currentMass->getEyeFlareColour(); + _eyeFlareDirty = false; + } + } +} + +void +Application::drawCustomFrameStyles() { + if(!_currentMass || _currentMass->state() != GameObjects::Mass::State::Valid) { + return; + } + + if(!ImGui::BeginChild("##FrameStyles")) { + ImGui::EndChild(); + return; + } + + ImGui::TextWrapped("In-game values are multiplied by 100. For example, 0.500 here is equal to 50 in-game."); + + for(std::uint32_t i = 0; i < _currentMass->frameCustomStyles().size(); i++) { + ImGui::PushID(int(i)); + auto result = drawCustomStyle(_currentMass->frameCustomStyles()[i]); + switch(result) { + case DCS_ResetStyle: + _currentMass->getFrameCustomStyles(); + break; + case DCS_Save: + _modifiedBySaveTool = true; + if(!_currentMass->writeFrameCustomStyle(i)) { + _modifiedBySaveTool = false; + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); + } + break; + default: + break; + } + ImGui::PopID(); + } + + ImGui::EndChild(); +} + +} diff --git a/src/SaveTool/SaveTool_MassViewer_Weapons.cpp b/src/Application/Application_MassViewer_Weapons.cpp similarity index 73% rename from src/SaveTool/SaveTool_MassViewer_Weapons.cpp rename to src/Application/Application_MassViewer_Weapons.cpp index 909712f..7865a0e 100644 --- a/src/SaveTool/SaveTool_MassViewer_Weapons.cpp +++ b/src/Application/Application_MassViewer_Weapons.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,18 +16,21 @@ #include "../FontAwesome/IconsFontAwesome5.h" -#include "../Maps/StyleNames.h" -#include "../Maps/WeaponParts.h" +#include "../GameData/StyleNames.h" +#include "../GameData/WeaponParts.h" -#include "SaveTool.h" +#include "Application.h" -void SaveTool::drawWeapons() { - if(!_currentMass || _currentMass->state() != Mass::State::Valid) { +namespace mbst { + +void +Application::drawWeapons() { + if(!_currentMass || _currentMass->state() != GameObjects::Mass::State::Valid) { _currentWeapon = nullptr; return; } - const Float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); + const float footer_height_to_reserve = ImGui::GetFrameHeightWithSpacing(); ImGui::BeginGroup(); @@ -52,11 +55,9 @@ void SaveTool::drawWeapons() { bool dirty = _meleeDirty || _shieldsDirty || _bShootersDirty || _eShootersDirty || _bLaunchersDirty || _eLaunchersDirty; - if(!dirty) { - ImGui::BeginDisabled(); - } + ImGui::BeginDisabled(!dirty); - if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { + if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save order"); })) { if(_meleeDirty) { _modifiedBySaveTool = true; if(!_currentMass->writeMeleeWeapons()) { @@ -153,9 +154,7 @@ void SaveTool::drawWeapons() { } } - if(!dirty) { - ImGui::EndDisabled(); - } + ImGui::EndDisabled(); ImGui::EndGroup(); @@ -177,42 +176,40 @@ void SaveTool::drawWeapons() { ImGui::EndChild(); - ImGui::Separator(); - if(drawUnsafeWidget([](){ return ImGui::Button(ICON_FA_SAVE " Save changes to weapon category"); })) { _modifiedBySaveTool = true; switch(_currentWeapon->type) { - case WeaponType::Melee: + case GameObjects::Weapon::Type::Melee: if(!_currentMass->writeMeleeWeapons()) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } break; - case WeaponType::Shield: + case GameObjects::Weapon::Type::Shield: if(!_currentMass->writeShields()) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } break; - case WeaponType::BulletShooter: + case GameObjects::Weapon::Type::BulletShooter: if(!_currentMass->writeBulletShooters()) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } break; - case WeaponType::EnergyShooter: + case GameObjects::Weapon::Type::EnergyShooter: if(!_currentMass->writeEnergyShooters()) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } break; - case WeaponType::BulletLauncher: + case GameObjects::Weapon::Type::BulletLauncher: if(!_currentMass->writeBulletLaunchers()) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } break; - case WeaponType::EnergyLauncher: + case GameObjects::Weapon::Type::EnergyLauncher: if(!_currentMass->writeEnergyLaunchers()) { _modifiedBySaveTool = false; _queue.addToast(Toast::Type::Error, _currentMass->lastError()); @@ -228,22 +225,22 @@ void SaveTool::drawWeapons() { if(ImGui::Button(ICON_FA_UNDO_ALT " Reset weapon category")) { switch(_currentWeapon->type) { - case WeaponType::Melee: + case GameObjects::Weapon::Type::Melee: _currentMass->getMeleeWeapons(); break; - case WeaponType::Shield: + case GameObjects::Weapon::Type::Shield: _currentMass->getShields(); break; - case WeaponType::BulletShooter: + case GameObjects::Weapon::Type::BulletShooter: _currentMass->getBulletShooters(); break; - case WeaponType::EnergyShooter: + case GameObjects::Weapon::Type::EnergyShooter: _currentMass->getEnergyShooters(); break; - case WeaponType::BulletLauncher: + case GameObjects::Weapon::Type::BulletLauncher: _currentMass->getBulletLaunchers(); break; - case WeaponType::EnergyLauncher: + case GameObjects::Weapon::Type::EnergyLauncher: _currentMass->getEnergyLaunchers(); break; default: @@ -254,16 +251,17 @@ void SaveTool::drawWeapons() { ImGui::EndGroup(); } -void SaveTool::drawWeaponCategory(Containers::StringView name, Containers::ArrayView weapons_view, bool& dirty, - Containers::StringView payload_type, Containers::StringView payload_tooltip) +void +Application::drawWeaponCategory(Containers::StringView name, Containers::ArrayView weapons_view, bool& dirty, + Containers::StringView payload_type, Containers::StringView payload_tooltip) { ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableNextColumn(); - ImGui::TextUnformatted(name.data()); + ImGui::TextUnformatted(name.cbegin(), name.cend()); ImGui::PushID(payload_type.data()); - for(UnsignedInt i = 0; i < weapons_view.size(); i++) { + for(std::uint32_t i = 0; i < weapons_view.size(); i++) { auto& weapon = weapons_view[i]; ImGui::TableNextRow(); @@ -275,7 +273,7 @@ void SaveTool::drawWeaponCategory(Containers::StringView name, Containers::Array _currentWeapon = &weapon; } if(ImGui::BeginDragDropSource()) { - ImGui::SetDragDropPayload(payload_type.data(), &i, sizeof(UnsignedInt)); + ImGui::SetDragDropPayload(payload_type.data(), &i, sizeof(std::uint32_t)); if(ImGui::GetIO().KeyCtrl) { ImGui::Text("%s %i - %s (copy)", payload_tooltip.data(), i + 1, weapon.name.data()); } @@ -317,8 +315,9 @@ void SaveTool::drawWeaponCategory(Containers::StringView name, Containers::Array ImGui::PopID(); } -void SaveTool::drawWeaponEditor(Weapon& weapon) { - if(!_currentMass || _currentMass->state() != Mass::State::Valid || !_currentWeapon) { +void +Application::drawWeaponEditor(GameObjects::Weapon& weapon) { + if(!_currentMass || _currentMass->state() != GameObjects::Mass::State::Valid || !_currentWeapon) { return; } @@ -328,7 +327,7 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { #undef c }; - drawAlignedText("%s: %s", labels[UnsignedInt(weapon.type)].data(), weapon.name.data()); + drawAlignedText("%s: %s", labels[std::uint32_t(weapon.type)].data(), weapon.name.data()); ImGui::SameLine(); @@ -347,11 +346,11 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { ImGui::BeginGroup(); drawAlignedText("Equipped:"); - if(weapon.type != WeaponType::Shield) { + if(weapon.type != GameObjects::Weapon::Type::Shield) { drawAlignedText("Damage type:"); } - if(weapon.type == WeaponType::Melee) { + if(weapon.type == GameObjects::Weapon::Type::Melee) { drawAlignedText("Dual-wield:"); drawAlignedText("Custom effect mode:"); @@ -365,48 +364,48 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { ImGui::BeginGroup(); ImGui::Checkbox("##EquippedCheckbox", &weapon.attached); - if(weapon.type != WeaponType::Shield) { - if(weapon.type == WeaponType::Melee && - ImGui::RadioButton("Physical##NoElement", weapon.damageType == DamageType::Physical)) + if(weapon.type != GameObjects::Weapon::Type::Shield) { + if(weapon.type == GameObjects::Weapon::Type::Melee && + ImGui::RadioButton("Physical##NoElement", weapon.damageType == GameObjects::Weapon::DamageType::Physical)) { - weapon.damageType = DamageType::Physical; + weapon.damageType = GameObjects::Weapon::DamageType::Physical; } - else if((weapon.type == WeaponType::BulletShooter || weapon.type == WeaponType::BulletLauncher) && - ImGui::RadioButton("Piercing##NoElement", weapon.damageType == DamageType::Piercing)) + else if((weapon.type == GameObjects::Weapon::Type::BulletShooter || weapon.type == GameObjects::Weapon::Type::BulletLauncher) && + ImGui::RadioButton("Piercing##NoElement", weapon.damageType == GameObjects::Weapon::DamageType::Piercing)) { - weapon.damageType = DamageType::Piercing; + weapon.damageType = GameObjects::Weapon::DamageType::Piercing; } - else if((weapon.type == WeaponType::EnergyShooter || weapon.type == WeaponType::EnergyLauncher) && - ImGui::RadioButton("Plasma##NoElement", weapon.damageType == DamageType::Plasma)) + else if((weapon.type == GameObjects::Weapon::Type::EnergyShooter || weapon.type == GameObjects::Weapon::Type::EnergyLauncher) && + ImGui::RadioButton("Plasma##NoElement", weapon.damageType == GameObjects::Weapon::DamageType::Plasma)) { - weapon.damageType = DamageType::Plasma; + weapon.damageType = GameObjects::Weapon::DamageType::Plasma; } ImGui::SameLine(); - if(ImGui::RadioButton("Heat##Heat", weapon.damageType == DamageType::Heat)) { - weapon.damageType = DamageType::Heat; + if(ImGui::RadioButton("Heat##Heat", weapon.damageType == GameObjects::Weapon::DamageType::Heat)) { + weapon.damageType = GameObjects::Weapon::DamageType::Heat; } ImGui::SameLine(); - if(ImGui::RadioButton("Freeze##Freeze", weapon.damageType == DamageType::Freeze)) { - weapon.damageType = DamageType::Freeze; + if(ImGui::RadioButton("Freeze##Freeze", weapon.damageType == GameObjects::Weapon::DamageType::Freeze)) { + weapon.damageType = GameObjects::Weapon::DamageType::Freeze; } ImGui::SameLine(); - if(ImGui::RadioButton("Shock##Shock", weapon.damageType == DamageType::Shock)) { - weapon.damageType = DamageType::Shock; + if(ImGui::RadioButton("Shock##Shock", weapon.damageType == GameObjects::Weapon::DamageType::Shock)) { + weapon.damageType = GameObjects::Weapon::DamageType::Shock; } } - if(weapon.type == WeaponType::Melee) { + if(weapon.type == GameObjects::Weapon::Type::Melee) { ImGui::Checkbox("##DualWield", &weapon.dualWield); - if(ImGui::RadioButton("Default##Default", weapon.effectColourMode == EffectColourMode::Default)) { - weapon.effectColourMode = EffectColourMode::Default; + if(ImGui::RadioButton("Default##Default", weapon.effectColourMode == GameObjects::Weapon::EffectColourMode::Default)) { + weapon.effectColourMode = GameObjects::Weapon::EffectColourMode::Default; } ImGui::SameLine(); - if(ImGui::RadioButton("Custom##Custom", weapon.effectColourMode == EffectColourMode::Custom)) { - weapon.effectColourMode = EffectColourMode::Custom; + if(ImGui::RadioButton("Custom##Custom", weapon.effectColourMode == GameObjects::Weapon::EffectColourMode::Custom)) { + weapon.effectColourMode = GameObjects::Weapon::EffectColourMode::Custom; } - bool custom_effect = (weapon.effectColourMode == EffectColourMode::Custom); + bool custom_effect = (weapon.effectColourMode == GameObjects::Weapon::EffectColourMode::Custom); if(!custom_effect) { ImGui::BeginDisabled(); } @@ -425,8 +424,8 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { if(ImGui::CollapsingHeader("Weapon parts")) { drawAlignedText("Viewing/editing part:"); - for(Int i = 0; UnsignedLong(i) < weapon.parts.size(); i++) { - if(UnsignedLong(_selectedWeaponPart) >= weapon.parts.size()) { + for(std::int32_t i = 0; std::size_t(i) < weapon.parts.size(); i++) { + if(std::size_t(_selectedWeaponPart) >= weapon.parts.size()) { _selectedWeaponPart = 0; } ImGui::SameLine(); @@ -435,20 +434,20 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { auto& part = weapon.parts[_selectedWeaponPart]; - const auto* map = [this, &weapon]()-> const std::map* { + const auto* map = [this, &weapon]()-> const std::map* { switch(weapon.type) { - case WeaponType::Melee: - return _selectedWeaponPart == 0 ? &melee_grips : &melee_assaulters; - case WeaponType::Shield: - return _selectedWeaponPart == 0 ? &shield_handles : &shield_shells; - case WeaponType::BulletShooter: - return _selectedWeaponPart == 0 ? &bshooter_triggers : &bshooter_barrels; - case WeaponType::EnergyShooter: - return _selectedWeaponPart == 0 ? &eshooter_triggers : &eshooter_busters; - case WeaponType::BulletLauncher: - return _selectedWeaponPart == 0 ? &blauncher_pods : &blauncher_projectiles; - case WeaponType::EnergyLauncher: - return _selectedWeaponPart == 0 ? &elauncher_generators : &elauncher_pods; + case GameObjects::Weapon::Type::Melee: + return _selectedWeaponPart == 0 ? &GameData::melee_grips : &GameData::melee_assaulters; + case GameObjects::Weapon::Type::Shield: + return _selectedWeaponPart == 0 ? &GameData::shield_handles : &GameData::shield_shells; + case GameObjects::Weapon::Type::BulletShooter: + return _selectedWeaponPart == 0 ? &GameData::bshooter_triggers : &GameData::bshooter_barrels; + case GameObjects::Weapon::Type::EnergyShooter: + return _selectedWeaponPart == 0 ? &GameData::eshooter_triggers : &GameData::eshooter_busters; + case GameObjects::Weapon::Type::BulletLauncher: + return _selectedWeaponPart == 0 ? &GameData::blauncher_pods : &GameData::blauncher_projectiles; + case GameObjects::Weapon::Type::EnergyLauncher: + return _selectedWeaponPart == 0 ? &GameData::elauncher_generators : &GameData::elauncher_pods; } return nullptr; @@ -459,7 +458,7 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { } if(map->find(part.id) != map->cend()) { - ImGui::TextUnformatted(map->at(part.id).data()); + ImGui::TextUnformatted(map->at(part.id).cbegin(), map->at(part.id).cend()); } else if(part.id == -1) { ImGui::TextUnformatted(""); @@ -490,14 +489,14 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { } } - if(weapon.type == WeaponType::Shield || - (weapon.type == WeaponType::BulletLauncher && _selectedWeaponPart != 0)) + if(weapon.type == GameObjects::Weapon::Type::Shield || + (weapon.type == GameObjects::Weapon::Type::BulletLauncher && _selectedWeaponPart != 0)) { ImGui::SameLine(); if(ImGui::SmallButton("Unequip")) { part.id = -1; } - if(weapon.type == WeaponType::Shield && _selectedWeaponPart == 0) { + if(weapon.type == GameObjects::Weapon::Type::Shield && _selectedWeaponPart == 0) { drawTooltip("This will make the whole shield and its accessories invisible."); } else { @@ -505,10 +504,10 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { } } - if(ImGui::BeginChild("##PartDetails", {0.0f, 0.0f}, true)) { + if(ImGui::BeginChild("##PartDetails", {}, ImGuiChildFlags_Border)) { ImGui::TextUnformatted("Styles:"); - for(Int i = 0; i < 4; i++) { + for(std::int32_t i = 0; i < 4; i++) { drawAlignedText("Slot %d:", i + 1); ImGui::SameLine(); @@ -516,7 +515,7 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { ImGui::PushID(i); if(ImGui::BeginCombo("##Style", getStyleName(part.styles[i], weapon.customStyles).data())) { - for(const auto& style: style_names) { + for(const auto& style: GameData::builtin_style_names) { if(ImGui::Selectable(getStyleName(style.first, weapon.customStyles).data(), part.styles[i] == style.first)) { part.styles[i] = style.first; @@ -534,7 +533,7 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { ImGui::PushID("Decal"); drawAlignedText("Showing/editing decal"); - for(UnsignedLong i = 0; i < part.decals.size(); i++) { + for(std::size_t i = 0; i < part.decals.size(); i++) { ImGui::SameLine(); ImGui::RadioButton(std::to_string(i + 1).c_str(), &_selectedWeaponDecal, int(i)); } @@ -543,13 +542,13 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { ImGui::PopID(); - if(part.accessories.size() != 0) { + if(!part.accessories.isEmpty()) { ImGui::Separator(); ImGui::PushID("Accessory"); drawAlignedText("Showing/editing accessory"); - for(UnsignedLong i = 0; i < part.accessories.size(); i++) { + for(std::size_t i = 0; i < part.accessories.size(); i++) { ImGui::SameLine(); ImGui::RadioButton(std::string{char(65 + i)}.c_str(), &_selectedWeaponAccessory, int(i)); } @@ -563,3 +562,5 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { ImGui::EndChild(); } } + +} diff --git a/src/SaveTool/SaveTool_ProfileManager.cpp b/src/Application/Application_ProfileManager.cpp similarity index 58% rename from src/SaveTool/SaveTool_ProfileManager.cpp rename to src/Application/Application_ProfileManager.cpp index 89b7bdf..ce103a0 100644 --- a/src/SaveTool/SaveTool_ProfileManager.cpp +++ b/src/Application/Application_ProfileManager.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -20,14 +20,17 @@ #include "../FontAwesome/IconsFontAwesome5.h" -#include "SaveTool.h" +#include "Application.h" extern const ImVec2 center_pivot; -void SaveTool::drawProfileManager() { +namespace mbst { + +void +Application::drawProfileManager() { static std::size_t profile_index = 0; - ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f}}, ImGuiCond_Always, center_pivot); + ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f} / dpiScaling()}, ImGuiCond_Always, center_pivot); if(ImGui::Begin("Profile management##ProfileManager", nullptr, ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoBringToFrontOnFocus| ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_MenuBar)) @@ -37,10 +40,6 @@ void SaveTool::drawProfileManager() { ImGui::EndMenuBar(); } - static ImGuiID backup_list_popup_id = drawBackupListPopup(); - static ImGuiID backup_popup_id = drawBackupProfilePopup(profile_index); - static ImGuiID delete_popup_id = drawDeleteProfilePopup(profile_index); - if(ImGui::BeginTable("##ManagerLayout", 2)) { ImGui::TableSetupColumn("##Label", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("##Refresh", ImGuiTableColumnFlags_WidthFixed); @@ -59,9 +58,10 @@ void SaveTool::drawProfileManager() { } ImGui::SameLine(); if(ImGui::SmallButton("Backups")) { - _profileManager->refreshBackups(); - ImGui::OpenPopup(backup_list_popup_id); + _backupManager->refresh(); + ImGui::OpenPopup("Backups##BackupsModal"); } + drawBackupListPopup(); ImGui::EndTable(); } @@ -80,7 +80,7 @@ void SaveTool::drawProfileManager() { ImGui::TextUnformatted("Actions"); for(std::size_t i = 0; i < _profileManager->profiles().size(); ++i) { - Profile& profile = _profileManager->profiles()[i]; + GameObjects::Profile& profile = _profileManager->profiles()[i]; ImGui::TableNextRow(); @@ -100,16 +100,21 @@ void SaveTool::drawProfileManager() { ImGui::TableSetColumnIndex(2); if(ImGui::SmallButton(ICON_FA_FILE_ARCHIVE)) { - profile_index = i; - ImGui::OpenPopup(backup_popup_id); + if(!_backupManager->create(_profileManager->profiles()[i])) { + _queue.addToast(Toast::Type::Error, _backupManager->lastError(), std::chrono::seconds{5}); + } + else { + _queue.addToast(Toast::Type::Success, "Backup created successfully!"_s); + } } drawTooltip("Backup"); ImGui::SameLine(0.0f, 2.0f); if(drawUnsafeWidget(ImGui::SmallButton, ICON_FA_TRASH_ALT)) { profile_index = i; - ImGui::OpenPopup(delete_popup_id); + ImGui::OpenPopup("Confirmation##DeleteProfileConfirmation"); } drawTooltip("Delete"); + drawDeleteProfilePopup(profile_index); ImGui::PopID(); } ImGui::EndTable(); @@ -118,107 +123,20 @@ void SaveTool::drawProfileManager() { ImGui::TextUnformatted("Click a profile to manage it."); } - drawBackupListPopup(); - drawBackupProfilePopup(profile_index); - drawDeleteProfilePopup(profile_index); - ImGui::End(); } -auto SaveTool::drawBackupListPopup() -> ImGuiID { - ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f}}, ImGuiCond_Always, center_pivot); +void +Application::drawBackupListPopup() { + ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f} / dpiScaling()}, ImGuiCond_Always, center_pivot); if(!ImGui::BeginPopupModal("Backups##BackupsModal", nullptr, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove)) { - return ImGui::GetID("Backups##BackupsModal"); + return; } static std::size_t backup_index; - if(ImGui::BeginPopupModal("Restore backup", nullptr, - ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize)) - { - 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, - _profileManager->backups()[backup_index].timestamp.month, - _profileManager->backups()[backup_index].timestamp.day, - _profileManager->backups()[backup_index].timestamp.hour, - _profileManager->backups()[backup_index].timestamp.minute, - _profileManager->backups()[backup_index].timestamp.second); - ImGui::PopTextWrapPos(); - - if(ImGui::BeginTable("##RestoreBackupLayout", 2)) { - ImGui::TableSetupColumn("##Dummy", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("##YesNo", ImGuiTableColumnFlags_WidthFixed); - - ImGui::TableNextRow(); - - ImGui::TableSetColumnIndex(1); - if(ImGui::Button("Yes")) { - if(!_profileManager->restoreBackup(backup_index)) { - _queue.addToast(Toast::Type::Error, _profileManager->lastError()); - } - if(!_profileManager->refreshProfiles()) { - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", - _profileManager->lastError().data(), window()); - exit(EXIT_FAILURE); - } - ImGui::CloseCurrentPopup(); - } - ImGui::SameLine(); - if(ImGui::Button("No", ImGui::GetItemRectSize())) { - ImGui::CloseCurrentPopup(); - } - - ImGui::EndTable(); - } - - ImGui::EndPopup(); - } - - if(ImGui::BeginPopupModal("Delete backup", nullptr, - ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize)) - { - 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, - _profileManager->backups()[backup_index].timestamp.month, - _profileManager->backups()[backup_index].timestamp.day, - _profileManager->backups()[backup_index].timestamp.hour, - _profileManager->backups()[backup_index].timestamp.minute, - _profileManager->backups()[backup_index].timestamp.second); - ImGui::PopTextWrapPos(); - - if(ImGui::BeginTable("##DeleteBackupLayout", 2)) { - ImGui::TableSetupColumn("##Dummy", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("##YesNo", ImGuiTableColumnFlags_WidthFixed); - - ImGui::TableNextRow(); - - ImGui::TableSetColumnIndex(1); - if(ImGui::Button("Yes")) { - if(!_profileManager->deleteBackup(backup_index)) { - _queue.addToast(Toast::Type::Error, _profileManager->lastError()); - } - ImGui::CloseCurrentPopup(); - } - ImGui::SameLine(); - if(ImGui::Button("No", ImGui::GetItemRectSize())) { - ImGui::CloseCurrentPopup(); - } - - ImGui::EndTable(); - } - - ImGui::EndPopup(); - } - - static ImGuiID restore_backup_popup_id = ImGui::GetID("Restore backup"); - static ImGuiID delete_backup_popup_id = ImGui::GetID("Delete backup"); - if(ImGui::BeginTable("##BackupsLabelLayout", 2)) { ImGui::TableSetupColumn("##Label", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("##Refresh", ImGuiTableColumnFlags_WidthFixed); @@ -229,13 +147,13 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID { ImGui::TableSetColumnIndex(1); if(ImGui::SmallButton("Refresh")) { - _profileManager->refreshBackups(); + _backupManager->refresh(); } ImGui::EndTable(); } - if(_profileManager->backups().isEmpty()) { + if(_backupManager->backups().isEmpty()) { ImGui::TextDisabled("No backups were found."); } else if(ImGui::BeginTable("##Backups", 4, @@ -257,16 +175,17 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID { ImGui::TableSetColumnIndex(3); ImGui::TextUnformatted("Actions"); - for(std::size_t i = 0; i < _profileManager->backups().size(); ++i) { - auto& backup = _profileManager->backups()[i]; + //drawBackupFolder(_backupManager->vfs()); + + for(std::size_t i = 0; i < _backupManager->backups().size(); ++i) { + auto& backup = _backupManager->backups()[i]; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - ImGui::TextUnformatted(backup.company.data()); - if(ImGui::IsItemHovered()) { - ImGui::BeginTooltip(); + ImGui::TextUnformatted(backup.company.cbegin(), backup.company.cend()); + if(ImGui::IsItemHovered() && ImGui::BeginTooltip()) { for(const auto& file : backup.includedFiles) { - ImGui::TextUnformatted(file.data()); + ImGui::TextUnformatted(file.cbegin()); } ImGui::EndTooltip(); } @@ -281,27 +200,29 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID { backup.timestamp.second); ImGui::TableSetColumnIndex(2); - ImGui::TextUnformatted(backup.type == ProfileType::Demo ? "Demo" : "Full"); + ImGui::TextUnformatted(backup.demo ? "Demo" : "Full"); ImGui::TableSetColumnIndex(3); ImGui::PushID(int(i)); if(ImGui::SmallButton(ICON_FA_UNDO)) { backup_index = i; - ImGui::OpenPopup(restore_backup_popup_id); + ImGui::OpenPopup("Restore backup##RestoreBackupModal"); } drawTooltip("Restore"); + drawBackupRestorePopup(backup_index); ImGui::SameLine(0.0f, 2.0f); if(ImGui::SmallButton(ICON_FA_TRASH_ALT)) { backup_index = i; - ImGui::OpenPopup(delete_backup_popup_id); + ImGui::OpenPopup("Delete backup##DeleteBackupModal"); } drawTooltip("Delete"); + drawBackupDeletePopup(backup_index); ImGui::PopID(); } ImGui::EndTable(); } - ImGui::PushTextWrapPos(ImGui::GetWindowContentRegionWidth()); + ImGui::PushTextWrapPos(ImGui::GetContentRegionAvail().x); ImGui::TextUnformatted("Hover over a company name to see which files are included in the backup."); ImGui::PopTextWrapPos(); @@ -320,20 +241,91 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID { } ImGui::EndPopup(); - - return 0; } -auto SaveTool::drawBackupProfilePopup(std::size_t profile_index) -> ImGuiID { - if(!ImGui::BeginPopupModal("Include builds ?##IncludeBuildsDialog", nullptr, - ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove)) - { - return ImGui::GetID("Include builds ?##IncludeBuildsDialog"); +void +Application::drawBackupFolder(const Managers::Vfs::Directory& dir) { + if(dir.files().isEmpty() && dir.subdirs().isEmpty()) { + return; } - ImGui::TextUnformatted("Should builds be added to the backup ?"); + for(auto& subdir : dir.subdirs()) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + bool open = ImGui::TreeNodeEx(subdir.name().cbegin(), ImGuiTreeNodeFlags_SpanAllColumns); - if(ImGui::BeginTable("##NameBackupLayout", 2)) { + ImGui::TableNextColumn(); + ImGui::TextDisabled("--"); + + ImGui::TableNextColumn(); + ImGui::TextDisabled("--"); + + ImGui::TableNextColumn(); + ImGui::TextDisabled("--"); + + if(open) { + drawBackupFolder(subdir); + ImGui::TreePop(); + } + } + + for(auto file : dir.files()) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TreeNodeEx(file->company.cbegin(), ImGuiTreeNodeFlags_SpanAllColumns|ImGuiTreeNodeFlags_Leaf| + ImGuiTreeNodeFlags_NoTreePushOnOpen); + + ImGui::TableNextColumn(); + ImGui::Text("%.4i-%.2i-%.2i %.2i:%.2i:%.2i", + file->timestamp.year, + file->timestamp.month, + file->timestamp.day, + file->timestamp.hour, + file->timestamp.minute, + file->timestamp.second); + + ImGui::TableNextColumn(); + ImGui::TextUnformatted(file->demo ? "Demo" : "Full"); + + ImGui::TableNextColumn(); + ImGui::PushID(file); + if(ImGui::SmallButton(ICON_FA_UNDO)) { + //ImGui::OpenPopup("Restore backup##RestoreBackupModal"); + } + drawTooltip("Restore"); + //drawBackupRestorePopup(backup_index); + ImGui::SameLine(0.0f, 2.0f); + if(ImGui::SmallButton(ICON_FA_TRASH_ALT)) { + //ImGui::OpenPopup("Delete backup##DeleteBackupModal"); + } + drawTooltip("Delete"); + //drawBackupDeletePopup(backup_index); + ImGui::PopID(); + } +} + + +void +Application::drawBackupRestorePopup(std::size_t backup_index) { + if(!ImGui::BeginPopupModal("Restore backup##RestoreBackupModal", nullptr, + ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize)) + { + return; + } + + ImGui::PushTextWrapPos(float(windowSize().x()) * 0.50f); + ImGui::Text("Are you sure you want to restore the %s backup from %.4i-%.2i-%.2i %.2i:%.2i:%.2i ?\n\n" + "Any existing data will be overwritten.", + _backupManager->backups()[backup_index].company.data(), + _backupManager->backups()[backup_index].timestamp.year, + _backupManager->backups()[backup_index].timestamp.month, + _backupManager->backups()[backup_index].timestamp.day, + _backupManager->backups()[backup_index].timestamp.hour, + _backupManager->backups()[backup_index].timestamp.minute, + _backupManager->backups()[backup_index].timestamp.second); + ImGui::PopTextWrapPos(); + + if(ImGui::BeginTable("##RestoreBackupLayout", 2)) { ImGui::TableSetupColumn("##Dummy", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("##YesNo", ImGuiTableColumnFlags_WidthFixed); @@ -341,22 +333,18 @@ auto SaveTool::drawBackupProfilePopup(std::size_t profile_index) -> ImGuiID { ImGui::TableSetColumnIndex(1); if(ImGui::Button("Yes")) { - if(!_profileManager->backupProfile(profile_index, true)) { + if(!_backupManager->restore(backup_index)) { + _queue.addToast(Toast::Type::Error, _profileManager->lastError()); + } + if(!_profileManager->refreshProfiles()) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", _profileManager->lastError().data(), window()); + exit(EXIT_FAILURE); } ImGui::CloseCurrentPopup(); } ImGui::SameLine(); if(ImGui::Button("No", ImGui::GetItemRectSize())) { - if(!_profileManager->backupProfile(profile_index, false)) { - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", - _profileManager->lastError().data(), window()); - } - ImGui::CloseCurrentPopup(); - } - ImGui::SameLine(); - if(ImGui::Button("Cancel")) { ImGui::CloseCurrentPopup(); } @@ -364,15 +352,58 @@ auto SaveTool::drawBackupProfilePopup(std::size_t profile_index) -> ImGuiID { } ImGui::EndPopup(); - - return 0; } -auto SaveTool::drawDeleteProfilePopup(std::size_t profile_index) -> ImGuiID { +void +Application::drawBackupDeletePopup(std::size_t backup_index) { + if(!ImGui::BeginPopupModal("Delete backup##DeleteBackupModal", nullptr, + ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize)) + { + return; + } + + ImGui::PushTextWrapPos(float(windowSize().x()) * 0.50f); + ImGui::Text("Are you sure you want to delete the %s backup from %.4i-%.2i-%.2i %.2i:%.2i:%.2i ?\n\n" + "This operation is irreversible.", + _backupManager->backups()[backup_index].company.data(), + _backupManager->backups()[backup_index].timestamp.year, + _backupManager->backups()[backup_index].timestamp.month, + _backupManager->backups()[backup_index].timestamp.day, + _backupManager->backups()[backup_index].timestamp.hour, + _backupManager->backups()[backup_index].timestamp.minute, + _backupManager->backups()[backup_index].timestamp.second); + ImGui::PopTextWrapPos(); + + if(ImGui::BeginTable("##DeleteBackupLayout", 2)) { + ImGui::TableSetupColumn("##Dummy", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("##YesNo", ImGuiTableColumnFlags_WidthFixed); + + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex(1); + if(ImGui::Button("Yes")) { + if(!_backupManager->remove(backup_index)) { + _queue.addToast(Toast::Type::Error, _profileManager->lastError()); + } + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if(ImGui::Button("No", ImGui::GetItemRectSize())) { + ImGui::CloseCurrentPopup(); + } + + ImGui::EndTable(); + } + + ImGui::EndPopup(); +} + +void +Application::drawDeleteProfilePopup(std::size_t profile_index) { if(!ImGui::BeginPopupModal("Confirmation##DeleteProfileConfirmation", nullptr, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove)) { - return ImGui::GetID("Confirmation##DeleteProfileConfirmation"); + return; } static bool delete_builds = false; @@ -410,6 +441,6 @@ auto SaveTool::drawDeleteProfilePopup(std::size_t profile_index) -> ImGuiID { } ImGui::EndPopup(); - - return 0; +} + } diff --git a/src/Application/Application_UpdateChecker.cpp b/src/Application/Application_UpdateChecker.cpp new file mode 100644 index 0000000..828c7a6 --- /dev/null +++ b/src/Application/Application_UpdateChecker.cpp @@ -0,0 +1,95 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include + +#include "../Logger/Logger.h" + +#include "Application.h" + +namespace mbst { + +void +Application::updateCheckEvent(SDL_Event& event) { + _updateThread.join(); + + switch(static_cast(event.user.code)) { + case UpdateChecker::Success: + _checkerMutex.lock(); + if(_checker->updateAvailable()) { + using namespace std::chrono_literals; + _queue.addToast(Toast::Type::Warning, + "Your version is out of date and thus unsupported.\nCheck the settings for more information."_s, 5s); + } + else { + if(_checker->version() == current_version || (current_version > _checker->version() && current_version.prerelease)) { + _queue.addToast(Toast::Type::Success, "The application is already up to date."_s); + } + else if(_checker->version() > current_version && !current_version.prerelease) { + _queue.addToast(Toast::Type::Warning, + "Your version is more recent than the latest one in the repo. How???"_s); + } + } + _checkerMutex.unlock(); + break; + case UpdateChecker::HttpError: + _checkerMutex.lock(); + _queue.addToast(Toast::Type::Error, _checker->error()); + LOG_ERROR(_checker->error()); + _checkerMutex.unlock(); + break; + case UpdateChecker::CurlInitFailed: + _queue.addToast(Toast::Type::Error, "Couldn't initialise libcurl. Update check aborted."_s); + LOG_ERROR("Couldn't initialise libcurl. Update check aborted."); + break; + case UpdateChecker::CurlError: + { + using namespace std::chrono_literals; + _checkerMutex.lock(); + _queue.addToast(Toast::Type::Error, _checker->error(), 10s); + LOG_ERROR(_checker->error()); + _checkerMutex.unlock(); + } + break; + case UpdateChecker::CurlTimeout: + _queue.addToast(Toast::Type::Error, "The request timed out."_s); + LOG_ERROR("The request timed out."); + break; + } +} + +void +Application::checkForUpdates() { + SDL_Event event; + SDL_zero(event); + event.type = _updateEventId; + + _checkerMutex.lock(); + + if(!_checker) { + _checker.emplace(); + } + + event.user.code = _checker->check(); + + _checkerMutex.unlock(); + + SDL_PushEvent(&event); +} + +} diff --git a/src/SaveTool/SaveTool_drawAbout.cpp b/src/Application/Application_drawAbout.cpp similarity index 73% rename from src/SaveTool/SaveTool_drawAbout.cpp rename to src/Application/Application_drawAbout.cpp index 1904d1b..c8ef16c 100644 --- a/src/SaveTool/SaveTool_drawAbout.cpp +++ b/src/Application/Application_drawAbout.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "SaveTool.h" - #include #include @@ -29,9 +27,14 @@ #include "../FontAwesome/IconsFontAwesome5.h" #include "../FontAwesome/IconsFontAwesome5Brands.h" +#include "Application.h" + extern const ImVec2 center_pivot; -void SaveTool::drawAbout() { +namespace mbst { + +void +Application::drawAbout() { ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f}}, ImGuiCond_Always, center_pivot); ImGui::SetNextWindowSize({float(windowSize().x()) * 0.8f, float(windowSize().y()) * 0.75f}, ImGuiCond_Always); @@ -59,7 +62,7 @@ void SaveTool::drawAbout() { ImGui::TextWrapped("This application, made for the M.A.S.S. Builder community by Guillaume Jacquemin (aka William JCM), " "is a rewrite of the wxWidgets-powered M.A.S.S. Builder Save Tool (formerly known as wxMASSManager)."); - auto website = "https://williamjcm.ovh/coding/mbst"; + auto website = "https://williamjcm.ovh/mbst"; drawAlignedText(ICON_FA_GLOBE " %s", website); ImGui::SameLine(); if(ImGui::Button("Copy to clipboard")) { @@ -69,7 +72,7 @@ void SaveTool::drawAbout() { if(ImGui::Button("Open in browser")) { openUri(website); } - auto repo = "https://williamjcm.ovh/git/williamjcm/MassBuilderSaveTool"; + auto repo = "https://git.williamjcm.ovh/williamjcm/MassBuilderSaveTool"; drawAlignedText(ICON_FA_GIT_ALT " %s", repo); ImGui::SameLine(); if(ImGui::Button("Copy to clipboard")) { @@ -83,12 +86,13 @@ void SaveTool::drawAbout() { ImGui::Separator(); 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:"); + 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, float(windowSize().y()) * 0.3f}, true)) { + if(ImGui::BeginChild("##GPL", {0.0f, float(windowSize().y()) * 0.3f}, ImGuiChildFlags_Border)) { static auto licence = _rs.getRaw("COPYING"); ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]); - ImGui::TextEx(licence.data(), licence.data() + licence.size(), ImGuiTextFlags_None); + ImGui::TextUnformatted(licence.cbegin(), licence.cend()); ImGui::PopFont(); } ImGui::EndChild(); @@ -114,14 +118,6 @@ void SaveTool::drawAbout() { ImGui::TextUnformatted("Licence: MIT"); - static auto corrade_licence = _rs.getRaw("COPYING.Corrade"); - if(ImGui::BeginChild("##CorradeLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) { - ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]); - ImGui::TextEx(corrade_licence.data(), corrade_licence.data() + corrade_licence.size(), ImGuiTextFlags_None); - ImGui::PopFont(); - } - ImGui::EndChild(); - ImGui::TreePop(); } @@ -142,14 +138,6 @@ void SaveTool::drawAbout() { ImGui::TextUnformatted("Licence: MIT"); - static auto magnum_licence = _rs.getRaw("COPYING.Magnum"); - if(ImGui::BeginChild("##MagnumLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) { - ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]); - ImGui::TextEx(magnum_licence.data(), magnum_licence.data() + magnum_licence.size(), ImGuiTextFlags_None); - ImGui::PopFont(); - } - ImGui::EndChild(); - ImGui::TreePop(); } @@ -168,14 +156,6 @@ void SaveTool::drawAbout() { ImGui::TextUnformatted("Licence: MIT"); - static auto imgui_licence = _rs.getRaw("LICENSE.ImGui"); - if(ImGui::BeginChild("##ImGuiLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) { - ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]); - ImGui::TextEx(imgui_licence.data(), imgui_licence.data() + imgui_licence.size(), ImGuiTextFlags_None); - ImGui::PopFont(); - } - ImGui::EndChild(); - ImGui::TreePop(); } @@ -194,14 +174,6 @@ void SaveTool::drawAbout() { ImGui::TextUnformatted("Licence: zlib"); - static auto sdl_licence = _rs.getRaw("LICENSE.SDL"); - if(ImGui::BeginChild("##SDLLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) { - ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]); - ImGui::TextEx(sdl_licence.data(), sdl_licence.data() + sdl_licence.size(), ImGuiTextFlags_None); - ImGui::PopFont(); - } - ImGui::EndChild(); - ImGui::TreePop(); } @@ -220,14 +192,6 @@ void SaveTool::drawAbout() { ImGui::TextUnformatted("Licence: 3-clause BSD"); - static auto libzip_licence = _rs.getRaw("LICENSE.libzip"); - if(ImGui::BeginChild("##libzipLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) { - ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]); - ImGui::TextEx(libzip_licence.data(), libzip_licence.data() + libzip_licence.size(), ImGuiTextFlags_None); - ImGui::PopFont(); - } - ImGui::EndChild(); - ImGui::TreePop(); } @@ -245,14 +209,6 @@ void SaveTool::drawAbout() { ImGui::TextUnformatted("Licence: MIT"); - static auto efsw_licence = _rs.getRaw("LICENSE.efsw"); - if(ImGui::BeginChild("##efswLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) { - ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]); - ImGui::TextEx(efsw_licence.data(), efsw_licence.data() + efsw_licence.size(), ImGuiTextFlags_None); - ImGui::PopFont(); - } - ImGui::EndChild(); - ImGui::TreePop(); } @@ -271,14 +227,6 @@ void SaveTool::drawAbout() { ImGui::TextUnformatted("Licence: MIT/X derivative"); - static auto curl_licence = _rs.getRaw("LICENSE.curl"); - if(ImGui::BeginChild("##libcurlLicence", {0.0f, float(windowSize().y()) * 0.3f}, true)) { - ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]); - ImGui::TextEx(curl_licence.data(), curl_licence.data() + curl_licence.size(), ImGuiTextFlags_None); - ImGui::PopFont(); - } - ImGui::EndChild(); - ImGui::TreePop(); } @@ -322,3 +270,5 @@ void SaveTool::drawAbout() { ImGui::EndPopup(); } + +} diff --git a/src/Application/Application_drawMainMenu.cpp b/src/Application/Application_drawMainMenu.cpp new file mode 100644 index 0000000..957089e --- /dev/null +++ b/src/Application/Application_drawMainMenu.cpp @@ -0,0 +1,277 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include "../Configuration/Configuration.h" +#include "../FontAwesome/IconsFontAwesome5.h" +#include "../FontAwesome/IconsFontAwesome5Brands.h" + +#include "Application.h" + +namespace mbst { + +void +Application::drawMainMenu() { + if(!ImGui::BeginMainMenuBar()) { + return; + } + + if(ImGui::BeginMenu("Save Tool##SaveToolMenu")) { + if(ImGui::BeginMenu(ICON_FA_FOLDER_OPEN " Open game data directory")) { + if(ImGui::MenuItem(ICON_FA_COG " Configuration", nullptr, false, + Utility::Path::exists(conf().directories().gameConfig))) + { + openUri(Utility::Path::toNativeSeparators(conf().directories().gameConfig)); + } + + if(ImGui::MenuItem(ICON_FA_SAVE " Saves", nullptr, false, + Utility::Path::exists(conf().directories().gameSaves))) + { + openUri(Utility::Path::toNativeSeparators(conf().directories().gameSaves)); + } + + if(ImGui::MenuItem(ICON_FA_IMAGE " Screenshots", nullptr, false, + Utility::Path::exists(conf().directories().gameScreenshots))) + { + openUri(Utility::Path::toNativeSeparators(conf().directories().gameScreenshots)); + } + + ImGui::EndMenu(); + } + + if(ImGui::BeginMenu(ICON_FA_FOLDER_OPEN " Open manager directory")) { + if(ImGui::MenuItem(ICON_FA_FILE_ARCHIVE " Profile backups", nullptr, false, + Utility::Path::exists(conf().directories().backups))) + { + openUri(Utility::Path::toNativeSeparators(conf().directories().backups)); + } + + if(ImGui::MenuItem(ICON_FA_EXCHANGE_ALT " Staging area", nullptr, false, + Utility::Path::exists(conf().directories().staging))) + { + openUri(Utility::Path::toNativeSeparators(conf().directories().staging)); + } + + if(ImGui::BeginMenu(ICON_FA_BOXES " Armoury")) { + if(ImGui::MenuItem(ICON_FA_SHIELD_ALT " Armour parts", nullptr, false, + Utility::Path::exists(conf().directories().armours))) + { + openUri(Utility::Path::toNativeSeparators(conf().directories().armours)); + } + + if(ImGui::MenuItem(ICON_FA_HAMMER " Weapons", nullptr, false, + Utility::Path::exists(conf().directories().weapons))) + { + openUri(Utility::Path::toNativeSeparators(conf().directories().weapons)); + } + + if(ImGui::MenuItem(ICON_FA_PALETTE " Custom styles", nullptr, false, + Utility::Path::exists(conf().directories().styles))) + { + openUri(Utility::Path::toNativeSeparators(conf().directories().styles)); + } + + ImGui::EndMenu(); + } + + ImGui::EndMenu(); + } + + ImGui::Separator(); + + if(ImGui::BeginMenu(ICON_FA_COG " Settings")) { + ImGui::BeginGroup(); + drawAlignedText("Vertical sync:"); + if(conf().swapInterval() == 0) { + drawAlignedText("FPS cap:"); + } + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + + static const char* framelimit_labels[] = { + "Off", + "Every VBLANK", + "Every second VBLANK", + "Every third VBLANK", + }; + + ImGui::PushItemWidth(300.0f); + + if(ImGui::BeginCombo("##FrameLimit", framelimit_labels[conf().swapInterval()])) { + for(int i = 0; i <= 3; i++) { + if(ImGui::Selectable(framelimit_labels[i], conf().swapInterval() == i)) { + conf().setSwapInterval(i); + setSwapInterval(i); + if(i == 0) { + setMinimalLoopPeriod(0); + } + } + } + + ImGui::EndCombo(); + } + + if(conf().swapInterval() == 0) { + static float fps_cap = conf().fpsCap(); + if(ImGui::SliderFloat("##FpsCapSlider", &fps_cap, 15.0f, 301.0f, + conf().fpsCap() != 301.0f ? "%.0f" : "Uncapped", ImGuiSliderFlags_AlwaysClamp)) + { + conf().setFpsCap(fps_cap); + } + } + + ImGui::PopItemWidth(); + + ImGui::EndGroup(); + + if(drawCheckbox("Cheat mode", conf().cheatMode())) { + conf().setCheatMode(!conf().cheatMode()); + } + ImGui::SameLine(); + ImGui::AlignTextToFramePadding(); + drawHelpMarker("This gives access to save edition features that can be considered cheats.", + float(windowSize().x()) * 0.4f); + + if(drawCheckbox("Advanced mode", conf().advancedMode())) { + conf().setAdvancedMode(!conf().advancedMode()); + } + ImGui::SameLine(); + ImGui::AlignTextToFramePadding(); + drawHelpMarker("This gives access to editing values that have unknown purposes or are undocumented.", + float(windowSize().x()) * 0.4f); + + if(drawCheckbox("Check for updates on startup", conf().checkUpdatesOnStartup())) { + conf().setCheckUpdatesOnStartup(!conf().checkUpdatesOnStartup()); + } + ImGui::SameLine(); + if(ImGui::Button(ICON_FA_SYNC_ALT " Check now")) { + _queue.addToast(Toast::Type::Default, "Checking for updates..."); + _updateThread = std::thread{[this]{ checkForUpdates(); }}; + } + + if(_checker && _checkerMutex.try_lock()) { + if(_checker->updateAvailable()) { + drawAlignedText("Version %s is available.", Containers::String{_checker->version()}.data()); + if(ImGui::Button(ICON_FA_FILE_SIGNATURE " Release notes")) { + openUri("https://williamjcm.ovh/mbst"); + } + ImGui::SameLine(); + if(ImGui::Button(ICON_FA_DOWNLOAD " Download now")) { + openUri(_checker->downloadLink()); + } + } + _checkerMutex.unlock(); + } + + if(drawCheckbox("Skip disclaimer", conf().skipDisclaimer())) { + conf().setSkipDisclaimer(!conf().skipDisclaimer()); + } + + ImGui::EndMenu(); + } + + ImGui::Separator(); + + if(ImGui::MenuItem(ICON_FA_SIGN_OUT_ALT " Quit##QuitMenuItem")) { + exit(EXIT_SUCCESS); + } + + ImGui::EndMenu(); + } + + ImGui::BeginDisabled(conf().isRunningInWine()); + if(ImGui::BeginMenu("Game##GameMenu")) { + if(ImGui::MenuItem(ICON_FA_PLAY " Run demo##RunDemoMenuItem")) { + openUri("steam://run/1048390"); + } + drawTooltip("Will not work if you have the full game."); + + if(ImGui::MenuItem(ICON_FA_PLAY " Run full game##RunFullGameMenuItem")) { + openUri("steam://run/956680"); + } + + ImGui::Separator(); + + if(ImGui::BeginMenu(ICON_FA_DISCORD " Discord communities")) { + if(ImGui::MenuItem("Official server")) { + openUri("https://discord.gg/sekai-project"); + } + + if(ImGui::MenuItem("Community server")) { + openUri("https://discord.gg/massbuildercommunity"); + } + + ImGui::EndMenu(); + } + + ImGui::EndMenu(); + } + ImGui::EndDisabled(); + if(conf().isRunningInWine() && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { + ImGui::SetTooltip("Not available when running in Wine."); + } + + #ifdef SAVETOOL_DEBUG_BUILD + if(ImGui::BeginMenu("Debug tools")) { + ImGui::MenuItem("ImGui demo window", nullptr, &_demoWindow); + ImGui::MenuItem("ImGui style editor", nullptr, &_styleEditor); + ImGui::MenuItem("ImGui metrics window", nullptr, &_metricsWindow); + + ImGui::EndMenu(); + } + #endif + + if(ImGui::BeginMenu("Help")) { + if(ImGui::BeginMenu(ICON_FA_KEYBOARD " Keyboard shortcuts")) { + ImGui::BulletText("CTRL+Click on a slider or drag box to input value as text."); + ImGui::BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields."); + ImGui::BulletText("While inputing text:\n"); + ImGui::Indent(); + ImGui::BulletText("CTRL+Left/Right to word jump."); + ImGui::BulletText("CTRL+A or double-click to select all."); + ImGui::BulletText("CTRL+X/C/V to use clipboard cut/copy/paste."); + ImGui::BulletText("CTRL+Z,CTRL+Y to undo/redo."); + ImGui::BulletText("ESCAPE to revert."); + ImGui::BulletText("You can apply arithmetic operators +,*,/ on numerical values.\nUse +- to subtract."); + ImGui::Unindent(); + ImGui::EndMenu(); + } + ImGui::MenuItem(ICON_FA_INFO_CIRCLE " About", nullptr, &_aboutPopup); + + ImGui::EndMenu(); + } + + if(_gameCheckTimerId != 0) { + if(ImGui::BeginTable("##MainMenuLayout", 2)) { + ImGui::TableSetupColumn("##Dummy", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("##GameState", ImGuiTableColumnFlags_WidthFixed); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(1); + drawGameState(); + + ImGui::EndTable(); + } + } + + ImGui::EndMainMenuBar(); +} + +} diff --git a/src/BinaryIo/BinaryIo.h b/src/BinaryIo/BinaryIo.h new file mode 100644 index 0000000..8cc09a6 --- /dev/null +++ b/src/BinaryIo/BinaryIo.h @@ -0,0 +1,24 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +namespace BinaryIo { + +class Reader; +class Writer; + +} diff --git a/src/BinaryIo/Reader.cpp b/src/BinaryIo/Reader.cpp new file mode 100644 index 0000000..c00edfc --- /dev/null +++ b/src/BinaryIo/Reader.cpp @@ -0,0 +1,150 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include +#include + +#include "../Logger/Logger.h" + +#include "Reader.h" + +namespace BinaryIo { + +Reader::Reader(Containers::StringView filename) { + _file = std::fopen(filename.data(), "rb"); + + if(!_file) { + LOG_ERROR_FORMAT("Couldn't open {} for reading: {}", filename, std::strerror(errno)); + } +} + +Reader::~Reader() { + closeFile(); +} + +bool +Reader::open() { + return _file; +} + +bool +Reader::eof() { + return std::feof(_file) != 0; +} + +std::int64_t +Reader::position() { + return _ftelli64(_file); +} + +bool +Reader::seek(std::int64_t position) { + return _fseeki64(_file, position, SEEK_SET) == 0; +} + +void +Reader::closeFile() { + std::fclose(_file); + _file = nullptr; +} + +bool +Reader::readChar(char& value) { + return std::fread(&value, sizeof(char), 1, _file) == 1; +} + +bool +Reader::readInt8(std::int8_t& value) { + return std::fread(&value, sizeof(std::int8_t), 1, _file) == 1; +} + +bool +Reader::readUint8(std::uint8_t& value) { + return std::fread(&value, sizeof(std::uint8_t), 1, _file) == 1; +} + +bool +Reader::readInt16(std::int16_t& value) { + return std::fread(&value, sizeof(std::int16_t), 1, _file) == 1; +} + +bool +Reader::readUint16(std::uint16_t& value) { + return std::fread(&value, sizeof(std::uint16_t), 1, _file) == 1; +} + +bool +Reader::readInt32(std::int32_t& value) { + return std::fread(&value, sizeof(std::int32_t), 1, _file) == 1; +} + +bool +Reader::readUint32(std::uint32_t& value) { + return std::fread(&value, sizeof(std::uint32_t), 1, _file) == 1; +} + +bool +Reader::readInt64(std::int64_t& value) { + return std::fread(&value, sizeof(std::int64_t), 1, _file) == 1; +} + +bool +Reader::readUint64(std::uint64_t& value) { + return std::fread(&value, sizeof(std::uint64_t), 1, _file) == 1; +} + +bool +Reader::readFloat(float& value) { + return std::fread(&value, sizeof(float), 1, _file) == 1; +} + +bool +Reader::readDouble(double& value) { + return std::fread(&value, sizeof(double), 1, _file) == 1; +} + +bool +Reader::readArray(Containers::Array& array, std::size_t count) { + if(array.size() < count) { + array = Containers::Array{ValueInit, count}; + } + + return std::fread(array.data(), sizeof(char), count, _file) == count; +} + +bool +Reader::readUEString(Containers::String& str) { + std::uint32_t length = 0; + if(!readUint32(length) || length == 0) { + return false; + } + + str = Containers::String{ValueInit, length - 1}; + + return std::fread(str.data(), sizeof(char), length, _file) == length; +} + +std::int32_t +Reader::peekChar() { + std::int32_t c; + c = std::fgetc(_file); + std::ungetc(c, _file); + return c; +} + +} diff --git a/src/BinaryIo/Reader.h b/src/BinaryIo/Reader.h new file mode 100644 index 0000000..6a00886 --- /dev/null +++ b/src/BinaryIo/Reader.h @@ -0,0 +1,80 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include +#include + +#include +#include +#include + +using namespace Corrade; + +namespace BinaryIo { + +class Reader { + public: + explicit Reader(Containers::StringView filename); + ~Reader(); + + Reader(const Reader& other) = delete; + Reader& operator=(const Reader& other) = delete; + + Reader(Reader&& other) = default; + Reader& operator=(Reader&& other) = default; + + bool open(); + bool eof(); + auto position() -> std::int64_t; + + bool seek(std::int64_t position); + + void closeFile(); + + bool readChar(char& value); + bool readInt8(std::int8_t& value); + bool readUint8(std::uint8_t& value); + bool readInt16(std::int16_t& value); + bool readUint16(std::uint16_t& value); + bool readInt32(std::int32_t& value); + bool readUint32(std::uint32_t& value); + bool readInt64(std::int64_t& value); + bool readUint64(std::uint64_t& value); + bool readFloat(float& value); + bool readDouble(double& value); + bool readArray(Containers::Array& array, std::size_t count); + + template + bool readValue(T& value) { + return fread(&value, sizeof(T), 1, _file) == 1; + } + + template + bool readStaticArray(Containers::StaticArray& array) { + return std::fread(array.data(), sizeof(char), S, _file) == S; + } + + bool readUEString(Containers::String& str); + + auto peekChar() -> std::int32_t; + + private: + std::FILE* _file = nullptr; +}; + +} diff --git a/src/BinaryIo/Writer.cpp b/src/BinaryIo/Writer.cpp new file mode 100644 index 0000000..4a0bb5d --- /dev/null +++ b/src/BinaryIo/Writer.cpp @@ -0,0 +1,163 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include "../Logger/Logger.h" + +#include "Writer.h" + +using namespace Containers::Literals; + +namespace BinaryIo { + +Writer::Writer(Containers::StringView filename) { + _file = std::fopen(filename.data(), "wb"); + if(!_file) { + LOG_ERROR_FORMAT("Couldn't open {} for reading: {}", filename, std::strerror(errno)); + } +} + +Writer::~Writer() { + closeFile(); +} + +bool +Writer::open() { + return _file; +} + +void +Writer::closeFile() { + std::fflush(_file); + std::fclose(_file); + _file = nullptr; +} + +std::int64_t +Writer::position() { + return _ftelli64(_file); +} + +Containers::ArrayView +Writer::array() const { + return _data; +} + +std::size_t +Writer::arrayPosition() const { + return _index; +} + +bool +Writer::flushToFile() { + bool ret = writeArray(_data); + std::fflush(_file); + _data = Containers::Array{}; + _index = 0; + return ret; +} + +bool +Writer::writeChar(char value) { + return std::fwrite(&value, sizeof(char), 1, _file) == 1; +} + +bool +Writer::writeInt8(std::int8_t value) { + return std::fwrite(&value, sizeof(std::int8_t), 1, _file) == 1; +} + +bool +Writer::writeUint8(std::uint8_t value) { + return std::fwrite(&value, sizeof(std::uint8_t), 1, _file) == 1; +} + +bool +Writer::writeInt16(std::int16_t value) { + return std::fwrite(&value, sizeof(std::int16_t), 1, _file) == 1; +} + +bool +Writer::writeUint16(std::uint16_t value) { + return std::fwrite(&value, sizeof(std::uint16_t), 1, _file) == 1; +} + +bool +Writer::writeInt32(std::int32_t value) { + return std::fwrite(&value, sizeof(std::int32_t), 1, _file) == 1; +} + +bool +Writer::writeUint32(std::uint32_t value) { + return std::fwrite(&value, sizeof(std::uint32_t), 1, _file) == 1; +} + +bool +Writer::writeInt64(std::int64_t value) { + return std::fwrite(&value, sizeof(std::int64_t), 1, _file) == 1; +} + +bool +Writer::writeUint64(std::uint64_t value) { + return std::fwrite(&value, sizeof(std::uint64_t), 1, _file) == 1; +} + +bool +Writer::writeFloat(float value) { + return std::fwrite(&value, sizeof(float), 1, _file) == 1; +} + +bool +Writer::writeDouble(double value) { + return std::fwrite(&value, sizeof(double), 1, _file) == 1; +} + +bool +Writer::writeArray(Containers::ArrayView array) { + if(array.isEmpty()) { + return false; + } + + return std::fwrite(array.data(), sizeof(char), array.size(), _file) == array.size(); +} + +bool +Writer::writeUEString(Containers::StringView str) { + if(str.size() > UINT32_MAX) { + LOG_ERROR_FORMAT("String is too big. Expected size() < UINT32_MAX, got {} instead.", str.size()); + return false; + } + + writeUint32(static_cast(str.size()) + 1); + + if(str.size() > 0) { + std::size_t count = std::fwrite(str.data(), sizeof(char), str.size(), _file); + if(count != str.size()) { + return false; + } + } + return writeChar('\0'); +} + +std::size_t +Writer::writeUEStringToArray(Containers::StringView value) { + return writeValueToArray(std::uint32_t(value.size()) + 1u) + + writeDataToArray(Containers::ArrayView{value}) + + writeValueToArray('\0'); +} + +} diff --git a/src/UESaveFile/BinaryWriter.h b/src/BinaryIo/Writer.h similarity index 55% rename from src/UESaveFile/BinaryWriter.h rename to src/BinaryIo/Writer.h index efdc4ed..763622e 100644 --- a/src/UESaveFile/BinaryWriter.h +++ b/src/BinaryIo/Writer.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,6 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include #include #include @@ -23,81 +24,80 @@ #include #include -#include - using namespace Corrade; -using namespace Magnum; -class BinaryWriter { +namespace BinaryIo { + +class Writer { public: - explicit BinaryWriter(Containers::StringView filename); - ~BinaryWriter(); + explicit Writer(Containers::StringView filename); + ~Writer(); - BinaryWriter(const BinaryWriter& other) = delete; - BinaryWriter& operator=(const BinaryWriter& other) = delete; + Writer(const Writer& other) = delete; + Writer& operator=(const Writer& other) = delete; - BinaryWriter(BinaryWriter&& other) = default; - BinaryWriter& operator=(BinaryWriter&& other) = default; + Writer(Writer&& other) = default; + Writer& operator=(Writer&& other) = default; - auto open() -> bool; + bool open(); void closeFile(); - auto position() -> Long; + auto position() -> std::int64_t; auto array() const -> Containers::ArrayView; - auto arrayPosition() const -> UnsignedLong; - auto flushToFile() -> bool; + auto arrayPosition() const -> std::size_t; + bool flushToFile(); - auto writeByte(Byte value) -> bool; - auto writeChar(char value) -> bool; - auto writeUnsignedByte(UnsignedByte value) -> bool; - auto writeShort(Short value) -> bool; - auto writeUnsignedShort(UnsignedShort value) -> bool; - auto writeInt(Int value) -> bool; - auto writeUnsignedInt(UnsignedInt value) -> bool; - auto writeLong(Long value) -> bool; - auto writeUnsignedLong(UnsignedLong value) -> bool; - auto writeFloat(Float value) -> bool; - auto writeDouble(Double value) -> bool; - auto writeArray(Containers::ArrayView array) -> bool; + bool writeChar(char value); + bool writeInt8(std::int8_t value); + bool writeUint8(std::uint8_t value); + bool writeInt16(std::int16_t value); + bool writeUint16(std::uint16_t value); + bool writeInt32(std::int32_t value); + bool writeUint32(std::uint32_t value); + bool writeInt64(std::int64_t value); + bool writeUint64(std::uint64_t value); + bool writeFloat(float value); + bool writeDouble(double value); + bool writeArray(Containers::ArrayView array); template - auto writeString(const char(&str)[size]) -> bool { + bool writeString(const char(&str)[size]) { return writeArray({str, size - 1}); } template - auto writeStaticArray(Containers::StaticArrayView array) -> bool { + bool writeStaticArray(Containers::StaticArrayView array) { return std::fwrite(array.data(), sizeof(char), S, _file) == S; } - auto writeUEString(Containers::StringView str) -> bool; + bool writeUEString(Containers::StringView str); template::value, T, T&>> - auto writeValueToArray(U value) -> UnsignedLong { + auto writeValueToArray(U value) -> std::size_t { Containers::ArrayView view{&value, 1}; return writeDataToArray(view); } - auto writeUEStringToArray(Containers::StringView value) -> UnsignedLong; + auto writeUEStringToArray(Containers::StringView value) -> std::size_t; template - void writeValueToArrayAt(T& value, UnsignedLong position) { + void writeValueToArrayAt(T& value, std::size_t position) { Containers::ArrayView view{&value, 1}; writeDataToArrayAt(view, position); } template - auto writeDataToArray(Containers::ArrayView view) -> UnsignedLong { + auto writeDataToArray(Containers::ArrayView view) -> std::size_t { arrayAppend(_data, Containers::arrayCast(view)); _index += sizeof(T) * view.size(); return sizeof(T) * view.size(); } template - void writeDataToArrayAt(Containers::ArrayView view, UnsignedLong position) { + void writeDataToArrayAt(Containers::ArrayView view, std::size_t position) { auto casted_view = Containers::arrayCast(view); - for(UnsignedLong i = 0; i < casted_view.size(); i++) { + for(std::size_t i = 0; i < casted_view.size(); i++) { _data[position + i] = casted_view[i]; } } @@ -106,5 +106,7 @@ class BinaryWriter { FILE* _file = nullptr; Containers::Array _data; - UnsignedLong _index = 0; + std::size_t _index = 0; }; + +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 93d4596..8fbd5ec 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,5 @@ # MassBuilderSaveTool -# Copyright (C) 2021-2022 Guillaume Jacquemin +# Copyright (C) 2021-2024 Guillaume Jacquemin # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,21 +14,36 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -set(SAVETOOL_PROJECT_VERSION 1.4.3) +set(SAVETOOL_PROJECT_VERSION 1.5.0) -find_package(Corrade REQUIRED Main Containers Utility) +find_package(Corrade REQUIRED Containers Utility) +if(CORRADE_TARGET_WINDOWS) + find_package(Corrade REQUIRED Main) +endif() find_package(Magnum REQUIRED GL Sdl2Application) find_package(MagnumIntegration REQUIRED ImGui) +if(SAVETOOL_USE_SYSTEM_LIBZIP) + find_package(libzip REQUIRED) +endif(SAVETOOL_USE_SYSTEM_LIBZIP) + +if(SAVETOOL_USE_SYSTEM_EFSW) + find_package(efsw REQUIRED) +endif(SAVETOOL_USE_SYSTEM_EFSW) + +if(SAVETOOL_USE_SYSTEM_LIBCURL) + find_package(CURL REQUIRED HTTPS) +endif(SAVETOOL_USE_SYSTEM_LIBCURL) + set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON) corrade_add_resource(Assets assets.conf) -add_library(Logger STATIC EXCLUDE_FROM_ALL +set(Logger_SOURCES Logger/Logger.h Logger/Logger.cpp Logger/EntryType.h @@ -36,164 +51,200 @@ add_library(Logger STATIC EXCLUDE_FROM_ALL Logger/MagnumLogBuffer.cpp ) -target_link_libraries(Logger PRIVATE - Corrade::Utility - Magnum::Magnum +set(BinaryIo_SOURCES + BinaryIo/BinaryIo.h + BinaryIo/Reader.h + BinaryIo/Reader.cpp + BinaryIo/Writer.h + BinaryIo/Writer.cpp ) -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 +set(Gvas_SOURCES + Gvas/Serialisers/Serialisers.h + Gvas/Serialisers/AbstractUnrealCollectionProperty.h + Gvas/Serialisers/AbstractUnrealProperty.h + Gvas/Serialisers/AbstractUnrealStruct.h + Gvas/Serialisers/ArrayProperty.h + Gvas/Serialisers/ArrayProperty.cpp + Gvas/Serialisers/BoolProperty.h + Gvas/Serialisers/BoolProperty.cpp + Gvas/Serialisers/ByteProperty.h + Gvas/Serialisers/ByteProperty.cpp + Gvas/Serialisers/ColourProperty.h + Gvas/Serialisers/ColourProperty.cpp + Gvas/Serialisers/DateTimeProperty.h + Gvas/Serialisers/DateTimeProperty.cpp + Gvas/Serialisers/EnumProperty.h + Gvas/Serialisers/EnumProperty.cpp + Gvas/Serialisers/FloatProperty.h + Gvas/Serialisers/FloatProperty.cpp + Gvas/Serialisers/GuidProperty.h + Gvas/Serialisers/GuidProperty.cpp + Gvas/Serialisers/IntProperty.h + Gvas/Serialisers/IntProperty.cpp + Gvas/Serialisers/MapProperty.h + Gvas/Serialisers/MapProperty.cpp + Gvas/Serialisers/ResourceProperty.h + Gvas/Serialisers/ResourceProperty.cpp + Gvas/Serialisers/RotatorProperty.h + Gvas/Serialisers/RotatorProperty.cpp + Gvas/Serialisers/StringProperty.h + Gvas/Serialisers/StringProperty.cpp + Gvas/Serialisers/SetProperty.h + Gvas/Serialisers/SetProperty.cpp + Gvas/Serialisers/Struct.h + Gvas/Serialisers/Struct.cpp + Gvas/Serialisers/TextProperty.h + Gvas/Serialisers/TextProperty.cpp + Gvas/Serialisers/UnrealProperty.h + Gvas/Serialisers/VectorProperty.h + Gvas/Serialisers/VectorProperty.cpp + Gvas/Serialisers/Vector2DProperty.h + Gvas/Serialisers/Vector2DProperty.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 + Gvas/Types/Types.h + Gvas/Types/ArrayProperty.h + Gvas/Types/BoolProperty.h + Gvas/Types/ByteProperty.h + Gvas/Types/ColourStructProperty.h + Gvas/Types/DateTimeStructProperty.h + Gvas/Types/EnumProperty.h + Gvas/Types/FloatProperty.h + Gvas/Types/GenericStructProperty.h + Gvas/Types/GuidStructProperty.h + Gvas/Types/IntProperty.h + Gvas/Types/MapProperty.h + Gvas/Types/NoneProperty.h + Gvas/Types/RotatorStructProperty.h + Gvas/Types/SetProperty.h + Gvas/Types/StringProperty.h + Gvas/Types/StructProperty.h + Gvas/Types/ResourceItemValue.h + Gvas/Types/TextProperty.h + Gvas/Types/UnrealProperty.h + Gvas/Types/UnrealPropertyBase.h + Gvas/Types/Vector2DStructProperty.h + Gvas/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 + Gvas/Gvas.h + Gvas/Debug.h + Gvas/Debug.cpp + Gvas/File.h + Gvas/File.cpp + Gvas/PropertySerialiser.h + Gvas/PropertySerialiser.cpp ) -target_link_libraries(UESaveFile PRIVATE - Corrade::Containers - Corrade::Utility - Magnum::Magnum - Logger +set(ImportExport_SOURCES + ImportExport/Import.h + ImportExport/Import.cpp + ImportExport/Export.h + ImportExport/Export.cpp + ImportExport/Keys.h ) -add_executable(MassBuilderSaveTool WIN32 +if(CORRADE_TARGET_WINDOWS) + set(SAVETOOL_RC_FILE resource.rc) +endif() + +add_executable(MassBuilderSaveTool main.cpp - SaveTool/SaveTool.h - SaveTool/SaveTool.cpp - SaveTool/SaveTool_drawAbout.cpp - SaveTool/SaveTool_drawMainMenu.cpp - SaveTool/SaveTool_FileWatcher.cpp - SaveTool/SaveTool_Initialisation.cpp - SaveTool/SaveTool_MainManager.cpp - SaveTool/SaveTool_MassViewer.cpp - SaveTool/SaveTool_MassViewer_Frame.cpp - SaveTool/SaveTool_MassViewer_Armour.cpp - SaveTool/SaveTool_MassViewer_Weapons.cpp - SaveTool/SaveTool_ProfileManager.cpp - SaveTool/SaveTool_UpdateChecker.cpp - ProfileManager/ProfileManager.h - ProfileManager/ProfileManager.cpp - Profile/Profile.h - Profile/Profile.cpp - Profile/PropertyNames.h - Profile/ResourceIDs.h - MassManager/MassManager.h - MassManager/MassManager.cpp - Mass/Accessory.h - Mass/ArmourPart.h - Mass/BulletLauncherAttachment.h - Mass/CustomStyle.h - Mass/Decal.h - Mass/Joints.h - Mass/Mass.h - Mass/Mass.cpp - Mass/Mass_Frame.cpp - Mass/Mass_Armour.cpp - Mass/Mass_Weapons.cpp - Mass/Mass_Styles.cpp - Mass/Mass_DecalsAccessories.cpp - Mass/PropertyNames.h - Mass/Weapon.h - Mass/Weapon.cpp - Mass/WeaponPart.h - Maps/Accessories.h - Maps/ArmourSets.h + Application/Application.h + Application/Application.cpp + Application/Application_drawAbout.cpp + Application/Application_drawMainMenu.cpp + Application/Application_FileWatcher.cpp + Application/Application_Initialisation.cpp + Application/Application_MainManager.cpp + Application/Application_MassViewer.cpp + Application/Application_MassViewer_Frame.cpp + Application/Application_MassViewer_Armour.cpp + Application/Application_MassViewer_Weapons.cpp + Application/Application_ProfileManager.cpp + Application/Application_UpdateChecker.cpp + Configuration/Configuration.h + Configuration/Configuration.cpp + FontAwesome/IconsFontAwesome5.h + FontAwesome/IconsFontAwesome5Brands.h + GameData/Accessories.h + GameData/ArmourSets.h + GameData/LastMissionId.h + GameData/ResourceIDs.h + GameData/StoryProgress.h + GameData/StyleNames.h + GameData/WeaponParts.h + GameObjects/Accessory.h + GameObjects/ArmourPart.h + GameObjects/BulletLauncherAttachment.h + GameObjects/CustomStyle.h + GameObjects/Decal.h + GameObjects/Joints.h + GameObjects/Mass.h + GameObjects/Mass.cpp + GameObjects/Mass_Frame.cpp + GameObjects/Mass_Armour.cpp + GameObjects/Mass_Weapons.cpp + GameObjects/Mass_Styles.cpp + GameObjects/Mass_DecalsAccessories.cpp + GameObjects/Profile.h + GameObjects/Profile.cpp + GameObjects/PropertyNames.h + GameObjects/Weapon.h + GameObjects/Weapon.cpp + GameObjects/WeaponPart.h + Managers/Backup.h + Managers/BackupManager.h + Managers/BackupManager.cpp + Managers/MassManager.h + Managers/MassManager.cpp + Managers/ProfileManager.h + Managers/ProfileManager.cpp + Managers/StagedMass.h + Managers/StagedMassManager.h + Managers/StagedMassManager.cpp + Managers/Vfs/Directory.h Maps/ArmourSlots.hpp Maps/BulletLauncherAttachmentStyles.hpp Maps/BulletLauncherSockets.hpp Maps/DamageTypes.hpp Maps/EffectColourModes.hpp - Maps/LastMissionId.h - Maps/StoryProgress.h - Maps/StyleNames.h - Maps/WeaponParts.h Maps/WeaponTypes.hpp ToastQueue/ToastQueue.h ToastQueue/ToastQueue.cpp + UpdateChecker/UpdateChecker.h + UpdateChecker/UpdateChecker.cpp Utilities/Crc32.h - FontAwesome/IconsFontAwesome5.h - FontAwesome/IconsFontAwesome5Brands.h - resource.rc + Utilities/Crc32.cpp + Utilities/Temp.h + Utilities/Temp.cpp + Version/Version.h + Version/Version.cpp + ${Logger_SOURCES} + ${BinaryIo_SOURCES} + ${Gvas_SOURCES} + ${ImportExport_SOURCES} + ${SAVETOOL_RC_FILE} ${Assets} ) -if(CMAKE_BUILD_TYPE STREQUAL Debug) - add_compile_definitions(SAVETOOL_DEBUG_BUILD) +if(CORRADE_TARGET_WINDOWS) + set_target_properties(${PROJECT_NAME} PROPERTIES WIN32_EXECUTABLE $) endif() -add_compile_definitions( - SAVETOOL_VERSION="${SAVETOOL_PROJECT_VERSION}" - SAVETOOL_CODENAME="Enigmatic Ellenier" - SUPPORTED_GAME_VERSION="0.9.x" + +target_compile_definitions(MassBuilderSaveTool PRIVATE + SAVETOOL_VERSION_STRING="${SAVETOOL_PROJECT_VERSION}" + SAVETOOL_VERSION_MAJOR=1 + SAVETOOL_VERSION_MINOR=5 + SAVETOOL_VERSION_PATCH=0 + SAVETOOL_VERSION_PRERELEASE=false + SAVETOOL_CODENAME="Fuckin' UE5..." + SAVETOOL_SUPPORTED_GAME_VERSION="0.11.x" ) +if(CMAKE_BUILD_TYPE STREQUAL Debug) + target_compile_definitions(MassBuilderSaveTool PRIVATE SAVETOOL_DEBUG_BUILD) +endif() + if(CMAKE_BUILD_TYPE STREQUAL Release) set_target_properties(MassBuilderSaveTool PROPERTIES OUTPUT_NAME MassBuilderSaveTool-${SAVETOOL_PROJECT_VERSION}) endif() @@ -207,16 +258,29 @@ endif() target_link_libraries(MassBuilderSaveTool PRIVATE Corrade::Containers Corrade::Utility - Corrade::Main Magnum::Magnum Magnum::GL Magnum::Sdl2Application MagnumIntegration::ImGui - Logger - UESaveFile - efsw - zip - libcurl - imm32 - wtsapi32 + CURL::libcurl_static ) + +if(SAVETOOL_USE_SYSTEM_LIBZIP) + target_link_libraries(MassBuilderSaveTool PRIVATE libzip::zip) +else() + target_link_libraries(MassBuilderSaveTool PRIVATE zip) +endif() + +if(SAVETOOL_USE_SYSTEM_EFSW) + target_link_libraries(MassBuilderSaveTool PRIVATE efsw::efsw) +else() + target_link_libraries(MassBuilderSaveTool PRIVATE efsw) +endif() + +if(CORRADE_TARGET_WINDOWS) + target_link_libraries(MassBuilderSaveTool PRIVATE + Corrade::Main + imm32 + wtsapi32 + ) +endif() diff --git a/src/Configuration/Configuration.cpp b/src/Configuration/Configuration.cpp new file mode 100644 index 0000000..f2c8887 --- /dev/null +++ b/src/Configuration/Configuration.cpp @@ -0,0 +1,292 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../Logger/Logger.h" + +#include "Configuration.h" + +namespace mbst { + +Configuration::Configuration() { + Containers::String exe_path = Utility::Path::split(*Utility::Path::executableLocation()).first(); + _conf = Utility::Configuration{Utility::Path::join(exe_path, "MassBuilderSaveTool.ini")}; + + if(_conf.hasValue("swap_interval")) { + _swapInterval = _conf.value("swap_interval"); + } + else { + _conf.setValue("swap_interval", 1); + } + + if(_conf.hasValue("frame_limit")) { + std::string frame_limit = _conf.value("frame_limit"); + if(frame_limit == "half_vsync") { + _swapInterval = 2; + } + _conf.removeValue("frame_limit"); + } + + if(_conf.hasValue("fps_cap")) { + _fpsCap = _conf.value("fps_cap"); + } + else { + _conf.setValue("fps_cap", 60.0f); + } + + if(_conf.hasValue("cheat_mode")) { + _cheatMode = _conf.value("cheat_mode"); + } + else { + _conf.setValue("cheat_mode", _cheatMode); + } + + if(_conf.hasValue("advanced_mode")) { + _advancedMode = _conf.value("advanced_mode"); + } + else { + _conf.setValue("advanced_mode", _advancedMode); + } + + if(_conf.hasValue("startup_update_check")) { + _checkUpdatesOnStartup = _conf.value("startup_update_check"); + } + else { + _conf.setValue("startup_update_check", _checkUpdatesOnStartup); + } + + if(_conf.hasValue("skip_disclaimer")) { + _skipDisclaimer = _conf.value("skip_disclaimer"); + } + else { + _conf.setValue("skip_disclaimer", _skipDisclaimer); + } + + LOG_INFO("Searching for the game's save directory."); + wchar_t* localappdata_path = nullptr; + Containers::ScopeGuard guard{localappdata_path, CoTaskMemFree}; + auto result = SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_DEFAULT, nullptr, &localappdata_path); + if(result != S_OK) + { + char* message_buffer = nullptr; + auto size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, result, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), + reinterpret_cast(&message_buffer), 0, nullptr); + String message{message_buffer, size}; + LocalFree(message_buffer); + + _lastError = Utility::format("SHGetKnownFolderPath() failed with error code {}: {}", result, message); + LOG_ERROR(_lastError); + return; + } + + auto game_data_dir = Utility::Path::join( + Utility::Path::fromNativeSeparators(Utility::Unicode::narrow(localappdata_path)), + "MASS_Builder/Saved"_s + ); + if(!Utility::Path::exists(game_data_dir)) { + LOG_ERROR(_lastError = game_data_dir + " wasn't found. Make sure to play the game at least once."_s); + return; + } + + _directories.gameConfig = Utility::Path::join(game_data_dir, "Config/WindowsNoEditor"_s); + _directories.gameSaves = Utility::Path::join(game_data_dir, "SaveGames"_s); + _directories.gameScreenshots = Utility::Path::join(game_data_dir, "Screenshots/WindowsNoEditor"_s); + + LOG_INFO("Initialising Save Tool directories."); + Containers::String executable_location = Utility::Path::split(*Utility::Path::executableLocation()).first(); + _directories.backups = Utility::Path::join(executable_location, "backups"); + _directories.staging = Utility::Path::join(executable_location, "staging"); + auto armoury_dir = Utility::Path::join(executable_location, "armoury"); + _directories.armours = Utility::Path::join(armoury_dir, "armours"); + _directories.weapons = Utility::Path::join(armoury_dir, "weapons"); + _directories.styles = Utility::Path::join(armoury_dir, "styles"); + _directories.temp = Utility::Path::join(executable_location, "temp"); + + if(!Utility::Path::exists(_directories.backups)) { + LOG_WARNING("Backups directory not found, creating..."); + if(!Utility::Path::make(_directories.backups)) { + LOG_ERROR(_lastError = "Couldn't create the backups directory."); + return; + } + } + if(!Utility::Path::exists(_directories.staging)) { + LOG_WARNING("Staging directory not found, creating..."); + if(!Utility::Path::make(_directories.staging)) { + LOG_ERROR(_lastError = "Couldn't create the staging directory."); + return; + } + } + if(!Utility::Path::exists(_directories.armours)) { + LOG_WARNING("Armours directory not found, creating..."); + if(!Utility::Path::make(_directories.armours)) { + LOG_ERROR(_lastError = "Couldn't create the armours directory."); + return; + } + } + if(!Utility::Path::exists(_directories.weapons)) { + LOG_WARNING("Weapons directory not found, creating..."); + if(!Utility::Path::make(_directories.weapons)) { + LOG_ERROR(_lastError = "Couldn't create the weapons directory."); + return; + } + } + if(!Utility::Path::exists(_directories.styles)) { + LOG_WARNING("Styles directory not found, creating..."); + if(!Utility::Path::make(_directories.styles)) { + LOG_ERROR(_lastError = "Couldn't create the styles directory."); + return; + } + } + + if(!Utility::Path::exists(_directories.temp)) { + LOG_WARNING("Temporary directory not found, creating..."); + if(!Utility::Path::make(_directories.temp)) { + LOG_ERROR(_lastError = "Couldn't create the temporary directory."); + return; + } + } + + _valid = true; +} + +Configuration::~Configuration() { + save(); +} + +bool +Configuration::valid() const { + return _valid; +} + +Containers::StringView +Configuration::lastError() const { + return _lastError; +} + +void +Configuration::save() { + _conf.save(); +} + +int +Configuration::swapInterval() const { + return _swapInterval; +} + +void +Configuration::setSwapInterval(int interval) { + _swapInterval = interval; + _conf.setValue("swap_interval", _swapInterval); + _conf.save(); +} + +float +Configuration::fpsCap() const { + return _fpsCap; +} + +void +Configuration::setFpsCap(float cap) { + _fpsCap = cap; + _conf.setValue("fps_cap", _fpsCap); + _conf.save(); +} + +bool +Configuration::cheatMode() const { + return _cheatMode; +} + +void +Configuration::setCheatMode(bool enabled) { + _cheatMode = enabled; + _conf.setValue("cheat_mode", _cheatMode); + _conf.save(); +} + +bool +Configuration::advancedMode() const { + return _advancedMode; +} + +void +Configuration::setAdvancedMode(bool enabled) { + _advancedMode = enabled; + _conf.setValue("advanced_mode", _advancedMode); + _conf.save(); +} + +bool +Configuration::checkUpdatesOnStartup() const { + return _checkUpdatesOnStartup; +} + +void +Configuration::setCheckUpdatesOnStartup(bool mode) { + _checkUpdatesOnStartup = mode; + _conf.setValue("startup_update_check", _checkUpdatesOnStartup); + _conf.save(); +} + +bool +Configuration::skipDisclaimer() const { + return _skipDisclaimer; +} + +void +Configuration::setSkipDisclaimer(bool mode) { + _skipDisclaimer = mode; + _conf.setValue("skip_disclaimer", _skipDisclaimer); + _conf.save(); +} + +bool +Configuration::isRunningInWine() const { + return _isRunningInWine; +} + +void +Configuration::setRunningInWine(bool wine) { + _isRunningInWine = wine; +} + +Configuration::Directories const& +Configuration::directories() const { + return _directories; +} + +Configuration& +Configuration::instance() { + static Configuration conf{}; + return conf; +} + +Configuration& +conf() { + return Configuration::instance(); +} + +} diff --git a/src/Configuration/Configuration.h b/src/Configuration/Configuration.h new file mode 100644 index 0000000..79ec469 --- /dev/null +++ b/src/Configuration/Configuration.h @@ -0,0 +1,94 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +using namespace Corrade; + +namespace mbst { + +class Configuration { + public: + static auto instance() -> Configuration&; + + ~Configuration(); + + bool valid() const; + + auto lastError() const -> Containers::StringView; + + void save(); + + auto swapInterval() const -> int; + void setSwapInterval(int interval); + + auto fpsCap() const -> float; + void setFpsCap(float cap); + + bool cheatMode() const; + void setCheatMode(bool enabled); + + bool advancedMode() const; + void setAdvancedMode(bool enabled); + + bool checkUpdatesOnStartup() const; + void setCheckUpdatesOnStartup(bool mode); + + bool skipDisclaimer() const; + void setSkipDisclaimer(bool mode); + + bool isRunningInWine() const; + void setRunningInWine(bool wine); + + struct Directories { + Containers::String gameSaves; + Containers::String gameConfig; + Containers::String gameScreenshots; + Containers::String backups; + Containers::String staging; + Containers::String armours; + Containers::String weapons; + Containers::String styles; + Containers::String temp; + }; + auto directories() const -> Directories const&; + + private: + explicit Configuration(); + + Utility::Configuration _conf; + + Containers::String _lastError; + + int _swapInterval = 1; + float _fpsCap = 60.0f; + bool _cheatMode = false; + bool _advancedMode = false; + bool _checkUpdatesOnStartup = true; + bool _skipDisclaimer = false; + + bool _isRunningInWine = false; + + bool _valid = false; + + Directories _directories; +}; + +Configuration& conf(); + +} diff --git a/src/GameData/Accessories.h b/src/GameData/Accessories.h new file mode 100644 index 0000000..0eb4463 --- /dev/null +++ b/src/GameData/Accessories.h @@ -0,0 +1,783 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include + +#include + +using namespace Corrade; +using namespace Containers::Literals; + +namespace mbst::GameData { + +struct Accessory { + Containers::StringView name; + enum Size { + S, + M, + L, + XL + }; + Size size{}; +}; + +static const std::map accessories{ + // region Primitives + {1, {"Cube"_s, Accessory::Size::S}}, + {2, {"Pentagon"_s, Accessory::Size::S}}, + {3, {"Hexagon"_s, Accessory::Size::S}}, + {4, {"Cylinder"_s, Accessory::Size::S}}, + {5, {"Sphere"_s, Accessory::Size::S}}, + {6, {"TriPyramid"_s, Accessory::Size::S}}, + {7, {"SquPyramid"_s, Accessory::Size::S}}, + {8, {"PenPyramid"_s, Accessory::Size::S}}, + {9, {"HexPyramid"_s, Accessory::Size::S}}, + {10, {"Cone"_s, Accessory::Size::S}}, + {11, {"SquStick"_s, Accessory::Size::S}}, + {12, {"PenStick"_s, Accessory::Size::S}}, + {13, {"HexStick"_s, Accessory::Size::S}}, + {14, {"CycStick"_s, Accessory::Size::S}}, + {15, {"Capsule"_s, Accessory::Size::S}}, + {16, {"Decal Pad 01"_s, Accessory::Size::S}}, + {17, {"Decal Pad 02"_s, Accessory::Size::S}}, + {18, {"Decal Pad 03"_s, Accessory::Size::S}}, + {19, {"Decal Pad 04"_s, Accessory::Size::S}}, + {20, {"Decal Pad 05"_s, Accessory::Size::S}}, + {21, {"Triangle"_s, Accessory::Size::S}}, + {22, {"ThinStar"_s, Accessory::Size::S}}, + {23, {"Star"_s, Accessory::Size::S}}, + {24, {"SixSideStar"_s, Accessory::Size::S}}, + {25, {"Asterisk"_s, Accessory::Size::S}}, + {26, {"Ring"_s, Accessory::Size::S}}, + {27, {"SawedRing"_s, Accessory::Size::S}}, + {28, {"HalfRing"_s, Accessory::Size::S}}, + {29, {"Cresent"_s, Accessory::Size::S}}, + {30, {"Donut"_s, Accessory::Size::S}}, + {31, {"FiveCogWheel"_s, Accessory::Size::S}}, + {32, {"SixCogWheel"_s, Accessory::Size::S}}, + {33, {"SevenCogWheel"_s, Accessory::Size::S}}, + {34, {"EightCogWheel"_s, Accessory::Size::S}}, + {35, {"TwelveCogWheel"_s, Accessory::Size::S}}, + + {51, {"SquBevel"_s, Accessory::Size::S}}, + {52, {"TriBevel"_s, Accessory::Size::S}}, + {53, {"PenBevel"_s, Accessory::Size::S}}, + {54, {"HexBevel"_s, Accessory::Size::S}}, + {55, {"CycBevel"_s, Accessory::Size::S}}, + {56, {"RecBevel"_s, Accessory::Size::S}}, + {57, {"DaiBevel"_s, Accessory::Size::S}}, + {58, {"MonBevel"_s, Accessory::Size::S}}, + {59, {"CofBevel"_s, Accessory::Size::S}}, + {60, {"JevBevel"_s, Accessory::Size::S}}, + {61, {"SquEmboss"_s, Accessory::Size::S}}, + {62, {"TriEmboss"_s, Accessory::Size::S}}, + {63, {"PenEmboss"_s, Accessory::Size::S}}, + {64, {"HexEmboss"_s, Accessory::Size::S}}, + {65, {"CycEmboss"_s, Accessory::Size::S}}, + {66, {"RecEmboss"_s, Accessory::Size::S}}, + {67, {"DaiEmboss"_s, Accessory::Size::S}}, + {68, {"MonEmboss"_s, Accessory::Size::S}}, + {69, {"CofEmboss"_s, Accessory::Size::S}}, + {70, {"JevEmboss"_s, Accessory::Size::S}}, + + {101, {"Flat Hex Pin"_s, Accessory::Size::S}}, + {102, {"Cross Circle Pin"_s, Accessory::Size::S}}, + {103, {"Flat Circle Pin"_s, Accessory::Size::S}}, + {104, {"Hex Circle Pin"_s, Accessory::Size::S}}, + {105, {"Circle Button Pin"_s, Accessory::Size::S}}, + {106, {"Hexagon Pin"_s, Accessory::Size::S}}, + {107, {"Cross Square Pin"_s, Accessory::Size::S}}, + {108, {"Flat Square Pin"_s, Accessory::Size::S}}, + {109, {"Quad Corner Pin"_s, Accessory::Size::S}}, + {110, {"Bi Corner Pin"_s, Accessory::Size::S}}, + {111, {"Circle Pin"_s, Accessory::Size::S}}, + {112, {"Flat End Pin"_s, Accessory::Size::S}}, + {113, {"Flat Cut Pin"_s, Accessory::Size::S}}, + {114, {"Radial Pin"_s, Accessory::Size::S}}, + {115, {"Diamiter Pin"_s, Accessory::Size::S}}, + + {151, {"TriPoint"_s, Accessory::Size::S}}, + {152, {"SquPoint"_s, Accessory::Size::S}}, + {153, {"PenPoint"_s, Accessory::Size::S}}, + {154, {"HexPoint"_s, Accessory::Size::S}}, + {155, {"CycPoint"_s, Accessory::Size::S}}, + {156, {"Bevel SquCutPoint"_s, Accessory::Size::S}}, + {157, {"Bevel HexCutPoint"_s, Accessory::Size::S}}, + {158, {"Bevel HexPoint"_s, Accessory::Size::S}}, + {159, {"Bevel CycCutPoint"_s, Accessory::Size::S}}, + {160, {"Bevel CycPoint"_s, Accessory::Size::S}}, + + {201, {"Shaped Edge 01"_s, Accessory::Size::M}}, + {202, {"Shaped Edge 02"_s, Accessory::Size::M}}, + {203, {"Shaped Edge 03"_s, Accessory::Size::M}}, + {204, {"Shaped Edge 04"_s, Accessory::Size::M}}, + {205, {"Shaped Edge 05"_s, Accessory::Size::M}}, + {206, {"Shaped Edge 06"_s, Accessory::Size::M}}, + {207, {"Shaped Edge 07"_s, Accessory::Size::M}}, + {208, {"Shaped Edge 08"_s, Accessory::Size::M}}, + {209, {"Shaped Edge 09"_s, Accessory::Size::M}}, + {210, {"Shaped Edge 10"_s, Accessory::Size::M}}, + {211, {"Shaped Edge 11"_s, Accessory::Size::M}}, + {212, {"Shaped Edge 12"_s, Accessory::Size::M}}, + {213, {"Shaped Edge 13"_s, Accessory::Size::M}}, + {214, {"Shaped Edge 14"_s, Accessory::Size::M}}, + {215, {"Shaped Edge 15"_s, Accessory::Size::M}}, + {216, {"Shaped Edge 16"_s, Accessory::Size::M}}, + {217, {"Shaped Edge 17"_s, Accessory::Size::M}}, + {218, {"Shaped Edge 18"_s, Accessory::Size::M}}, + {219, {"Shaped Edge 19"_s, Accessory::Size::M}}, + {220, {"Shaped Edge 20"_s, Accessory::Size::M}}, + + {251, {"Fish Tail 01"_s, Accessory::Size::M}}, + {252, {"Fish Tail 02"_s, Accessory::Size::M}}, + {253, {"Fish Tail 03"_s, Accessory::Size::M}}, + {254, {"Fish Tail 04"_s, Accessory::Size::M}}, + {255, {"Fish Tail 05"_s, Accessory::Size::M}}, + {256, {"Based Separator 01"_s, Accessory::Size::M}}, + {257, {"Based Separator 02"_s, Accessory::Size::M}}, + {258, {"Based Separator 03"_s, Accessory::Size::M}}, + {259, {"Based Separator 04"_s, Accessory::Size::M}}, + {260, {"Based Separator 05"_s, Accessory::Size::M}}, + {261, {"Based Separator 06"_s, Accessory::Size::M}}, + {262, {"Based Separator 07"_s, Accessory::Size::M}}, + {263, {"Based Separator 08"_s, Accessory::Size::M}}, + {264, {"Based Separator 09"_s, Accessory::Size::M}}, + {265, {"Based Separator 10"_s, Accessory::Size::M}}, + + {301, {"Rectangular Box 01"_s, Accessory::Size::M}}, + {302, {"Rectangular Box 02"_s, Accessory::Size::M}}, + {303, {"Rectangular Box 03"_s, Accessory::Size::M}}, + {304, {"Rectangular Box 04"_s, Accessory::Size::M}}, + {305, {"Rectangular Box 05"_s, Accessory::Size::M}}, + {306, {"CofBox 01"_s, Accessory::Size::M}}, + {307, {"CofBox 02"_s, Accessory::Size::M}}, + {308, {"CofBox 03"_s, Accessory::Size::M}}, + {309, {"CofBox 04"_s, Accessory::Size::M}}, + {310, {"CofBox 05"_s, Accessory::Size::M}}, + {311, {"Triangular Box 01"_s, Accessory::Size::M}}, + {312, {"Triangular Box 02"_s, Accessory::Size::M}}, + {313, {"Triangular Box 03"_s, Accessory::Size::M}}, + {314, {"Triangular Box 04"_s, Accessory::Size::M}}, + {315, {"Triangular Box 05"_s, Accessory::Size::M}}, + {316, {"Diagonal Box A01"_s, Accessory::Size::M}}, + {317, {"Diagonal Box A02"_s, Accessory::Size::M}}, + {318, {"Diagonal Box A03"_s, Accessory::Size::M}}, + {319, {"Diagonal Box A04"_s, Accessory::Size::M}}, + {320, {"Diagonal Box A05"_s, Accessory::Size::M}}, + {321, {"Diagonal Box B01"_s, Accessory::Size::M}}, + {322, {"Diagonal Box B02"_s, Accessory::Size::M}}, + {323, {"Diagonal Box B03"_s, Accessory::Size::M}}, + {324, {"Diagonal Box B04"_s, Accessory::Size::M}}, + {325, {"Diagonal Box B05"_s, Accessory::Size::M}}, + // endregion + + // region Armours + {1001, {"Short Layer 01"_s, Accessory::Size::M}}, + {1002, {"Short Layer 02"_s, Accessory::Size::M}}, + {1003, {"Short Layer 03"_s, Accessory::Size::M}}, + {1004, {"Short Layer 04"_s, Accessory::Size::M}}, + {1005, {"Short Layer 05"_s, Accessory::Size::M}}, + {1006, {"Long Layer 01"_s, Accessory::Size::M}}, + {1007, {"Long Layer 02"_s, Accessory::Size::M}}, + {1008, {"Long Layer 03"_s, Accessory::Size::M}}, + {1009, {"Long Layer 04"_s, Accessory::Size::M}}, + {1010, {"Long Layer 05"_s, Accessory::Size::M}}, + {1011, {"Diagonal Long Layer 01"_s, Accessory::Size::M}}, + {1012, {"Diagonal Long Layer 02"_s, Accessory::Size::M}}, + {1013, {"Diagonal Long Layer 03"_s, Accessory::Size::M}}, + {1014, {"Diagonal Long Layer 04"_s, Accessory::Size::M}}, + {1015, {"Diagonal Long Layer 05"_s, Accessory::Size::M}}, + + {1051, {"Sloped Layer 01"_s, Accessory::Size::M}}, + {1052, {"Sloped Layer 02"_s, Accessory::Size::M}}, + {1053, {"Sloped Layer 03"_s, Accessory::Size::M}}, + {1054, {"Sloped Layer 04"_s, Accessory::Size::M}}, + {1055, {"Sloped Layer 05"_s, Accessory::Size::M}}, + {1056, {"Sloped Layer 06"_s, Accessory::Size::M}}, + {1057, {"Sloped Layer 07"_s, Accessory::Size::M}}, + {1058, {"Sloped Layer 08"_s, Accessory::Size::M}}, + {1059, {"Sloped Layer 09"_s, Accessory::Size::M}}, + {1060, {"Sloped Layer 10"_s, Accessory::Size::M}}, + {1061, {"Sloped Layer 11"_s, Accessory::Size::M}}, + {1062, {"Sloped Layer 12"_s, Accessory::Size::M}}, + {1063, {"Sloped Layer 13"_s, Accessory::Size::M}}, + {1064, {"Sloped Layer 14"_s, Accessory::Size::M}}, + {1065, {"Sloped Layer 15"_s, Accessory::Size::M}}, + + {1101, {"Raised Center 01"_s, Accessory::Size::M}}, + {1102, {"Raised Center 02"_s, Accessory::Size::M}}, + {1103, {"Raised Center 03"_s, Accessory::Size::M}}, + {1104, {"Raised Center 04"_s, Accessory::Size::M}}, + {1105, {"Raised Center 05"_s, Accessory::Size::M}}, + {1106, {"Raised Block 01"_s, Accessory::Size::M}}, + {1107, {"Raised Block 02"_s, Accessory::Size::M}}, + {1108, {"Raised Block 03"_s, Accessory::Size::M}}, + {1109, {"Raised Pointed"_s, Accessory::Size::M}}, + {1110, {"Raised Cover"_s, Accessory::Size::M}}, + {1111, {"Raised Slant 01"_s, Accessory::Size::M}}, + {1112, {"Raised Slant 02"_s, Accessory::Size::M}}, + {1113, {"Raised Slant 03"_s, Accessory::Size::M}}, + {1114, {"Raised Slant 04"_s, Accessory::Size::M}}, + {1115, {"Raised Slant 05"_s, Accessory::Size::M}}, + + {1151, {"Wide Patch 01"_s, Accessory::Size::L}}, + {1152, {"Wide Patch 02"_s, Accessory::Size::L}}, + {1153, {"Wide Patch 03"_s, Accessory::Size::L}}, + {1154, {"Wide Patch 04"_s, Accessory::Size::L}}, + {1155, {"Wide Patch 05"_s, Accessory::Size::L}}, + + {1201, {"Pointed Armour 01"_s, Accessory::Size::L}}, + {1202, {"Pointed Armour 02"_s, Accessory::Size::L}}, + {1203, {"Pointed Armour 03"_s, Accessory::Size::L}}, + {1204, {"Pointed Armour 04"_s, Accessory::Size::L}}, + {1205, {"Pointed Armour 05"_s, Accessory::Size::L}}, + {1206, {"Pointed Armour 06"_s, Accessory::Size::L}}, + {1207, {"Pointed Armour 07"_s, Accessory::Size::L}}, + {1208, {"Pointed Armour 08"_s, Accessory::Size::L}}, + {1209, {"Pointed Armour 09"_s, Accessory::Size::L}}, + {1210, {"Pointed Armour 10"_s, Accessory::Size::L}}, + {1211, {"Pointed Armour 11"_s, Accessory::Size::L}}, + {1212, {"Pointed Armour 12"_s, Accessory::Size::L}}, + {1213, {"Pointed Armour 13"_s, Accessory::Size::L}}, + {1214, {"Pointed Armour 14"_s, Accessory::Size::L}}, + {1215, {"Pointed Armour 15"_s, Accessory::Size::L}}, + {1216, {"Pointed Armour 16"_s, Accessory::Size::L}}, + {1217, {"Pointed Armour 17"_s, Accessory::Size::L}}, + {1218, {"Pointed Armour 18"_s, Accessory::Size::L}}, + {1219, {"Pointed Armour 19"_s, Accessory::Size::L}}, + {1220, {"Pointed Armour 20"_s, Accessory::Size::L}}, + + {1251, {"E Limb Cover 01"_s, Accessory::Size::L}}, + {1252, {"E Limb Cover 02"_s, Accessory::Size::L}}, + {1253, {"E Limb Cover 03"_s, Accessory::Size::L}}, + {1254, {"E Limb Cover 04"_s, Accessory::Size::L}}, + {1255, {"E Limb Cover 05"_s, Accessory::Size::L}}, + {1256, {"E Limb Cover 06"_s, Accessory::Size::L}}, + {1257, {"E Limb Cover 07"_s, Accessory::Size::L}}, + {1258, {"E Limb Cover 08"_s, Accessory::Size::L}}, + {1259, {"E Limb Cover 09"_s, Accessory::Size::L}}, + {1260, {"E Limb Cover 10"_s, Accessory::Size::L}}, + + {1301, {"C Limb Cover 01"_s, Accessory::Size::L}}, + {1302, {"C Limb Cover 02"_s, Accessory::Size::L}}, + {1303, {"C Limb Cover 03"_s, Accessory::Size::L}}, + {1304, {"C Limb Cover 04"_s, Accessory::Size::L}}, + {1305, {"C Limb Cover 05"_s, Accessory::Size::L}}, + {1306, {"C Limb Cover 06"_s, Accessory::Size::L}}, + {1307, {"C Limb Cover 07"_s, Accessory::Size::L}}, + {1308, {"C Limb Cover 08"_s, Accessory::Size::L}}, + {1309, {"C Limb Cover 09"_s, Accessory::Size::L}}, + {1310, {"C Limb Cover 10"_s, Accessory::Size::L}}, + {1311, {"C Limb Cover 11"_s, Accessory::Size::L}}, + {1312, {"C Limb Cover 12"_s, Accessory::Size::L}}, + {1313, {"C Limb Cover 13"_s, Accessory::Size::L}}, + {1314, {"C Limb Cover 14"_s, Accessory::Size::L}}, + {1315, {"C Limb Cover 15"_s, Accessory::Size::L}}, + {1316, {"C Limb Cover 16"_s, Accessory::Size::L}}, + {1317, {"C Limb Cover 17"_s, Accessory::Size::L}}, + {1318, {"C Limb Cover 18"_s, Accessory::Size::L}}, + {1319, {"C Limb Cover 19"_s, Accessory::Size::L}}, + {1320, {"C Limb Cover 20"_s, Accessory::Size::L}}, + + {1351, {"P Limb Cover 01"_s, Accessory::Size::XL}}, + {1352, {"P Limb Cover 02"_s, Accessory::Size::XL}}, + {1353, {"P Limb Cover 03"_s, Accessory::Size::XL}}, + {1354, {"P Limb Cover 04"_s, Accessory::Size::XL}}, + {1355, {"P Limb Cover 05"_s, Accessory::Size::XL}}, + + {1401, {"Flat Cover 01"_s, Accessory::Size::XL}}, + {1402, {"Flat Cover 02"_s, Accessory::Size::XL}}, + {1403, {"Flat Cover 03"_s, Accessory::Size::XL}}, + {1404, {"Flat Cover 04"_s, Accessory::Size::XL}}, + {1405, {"Flat Cover 05"_s, Accessory::Size::XL}}, + {1406, {"Flat Cover 06"_s, Accessory::Size::XL}}, + {1407, {"Flat Cover 07"_s, Accessory::Size::XL}}, + {1408, {"Flat Cover 08"_s, Accessory::Size::XL}}, + {1409, {"Flat Cover 09"_s, Accessory::Size::XL}}, + {1410, {"Flat Cover 10"_s, Accessory::Size::XL}}, + + {1451, {"L Side Opening 01"_s, Accessory::Size::XL}}, + {1452, {"L Side Opening 02"_s, Accessory::Size::XL}}, + {1453, {"L Side Opening 03"_s, Accessory::Size::XL}}, + {1454, {"L Side Opening 04"_s, Accessory::Size::XL}}, + {1455, {"L Side Opening 05"_s, Accessory::Size::XL}}, + {1456, {"L Side Opening 06"_s, Accessory::Size::XL}}, + {1457, {"L Side Opening 07"_s, Accessory::Size::XL}}, + {1458, {"L Side Opening 08"_s, Accessory::Size::XL}}, + {1459, {"L Side Opening 09"_s, Accessory::Size::XL}}, + {1460, {"L Side Opening 10"_s, Accessory::Size::XL}}, + // endregion + + // region Components + {2001, {"Disc Padding 01"_s, Accessory::Size::M}}, + {2002, {"Disc Padding 02"_s, Accessory::Size::M}}, + {2003, {"Disc Padding 03"_s, Accessory::Size::M}}, + {2004, {"Disc Padding 04"_s, Accessory::Size::M}}, + {2005, {"Disc Padding 05"_s, Accessory::Size::M}}, + {2006, {"Thin Padding 01"_s, Accessory::Size::M}}, + {2007, {"Thin Padding 02"_s, Accessory::Size::M}}, + {2008, {"Thin Padding 03"_s, Accessory::Size::M}}, + {2009, {"Thin Padding 04"_s, Accessory::Size::M}}, + {2010, {"Thin Padding 05"_s, Accessory::Size::M}}, + {2011, {"Thick Padding 01"_s, Accessory::Size::M}}, + {2012, {"Thick Padding 02"_s, Accessory::Size::M}}, + {2013, {"Thick Padding 03"_s, Accessory::Size::M}}, + {2014, {"Thick Padding 04"_s, Accessory::Size::M}}, + {2015, {"Thick Padding 05"_s, Accessory::Size::M}}, + {2016, {"Thick Padding 06"_s, Accessory::Size::M}}, + {2017, {"Thick Padding 07"_s, Accessory::Size::M}}, + {2018, {"Thick Padding 08"_s, Accessory::Size::M}}, + {2019, {"Thick Padding 09"_s, Accessory::Size::M}}, + {2020, {"Thick Padding 10"_s, Accessory::Size::M}}, + {2021, {"CSide Padding 01"_s, Accessory::Size::M}}, + {2022, {"CSide Padding 02"_s, Accessory::Size::M}}, + {2023, {"CSide Padding 03"_s, Accessory::Size::M}}, + {2024, {"CSide Padding 04"_s, Accessory::Size::M}}, + {2025, {"CSide Padding 05"_s, Accessory::Size::M}}, + + {2051, {"Container 01"_s, Accessory::Size::L}}, + {2052, {"Container 02"_s, Accessory::Size::L}}, + {2053, {"Container 03"_s, Accessory::Size::L}}, + {2054, {"Container 04"_s, Accessory::Size::L}}, + {2055, {"Container 05"_s, Accessory::Size::L}}, + + {2101, {"Plating 01"_s, Accessory::Size::L}}, + {2102, {"Plating 02"_s, Accessory::Size::L}}, + {2103, {"Plating 03"_s, Accessory::Size::L}}, + {2104, {"Plating 04"_s, Accessory::Size::L}}, + {2105, {"Plating 05"_s, Accessory::Size::L}}, + + {2126, {"Curved Plating 01"_s, Accessory::Size::L}}, + {2127, {"Curved Plating 02"_s, Accessory::Size::L}}, + {2128, {"Curved Plating 03"_s, Accessory::Size::L}}, + {2129, {"Curved Plating 04"_s, Accessory::Size::L}}, + {2130, {"Curved Plating 05"_s, Accessory::Size::L}}, + + {2151, {"Complex Base 01"_s, Accessory::Size::L}}, + {2152, {"Complex Base 02"_s, Accessory::Size::L}}, + {2153, {"Complex Base 03"_s, Accessory::Size::L}}, + {2154, {"Complex Base 04"_s, Accessory::Size::L}}, + {2155, {"Complex Base 05"_s, Accessory::Size::L}}, + {2156, {"Complex Base 06"_s, Accessory::Size::L}}, + {2157, {"Complex Base 07"_s, Accessory::Size::L}}, + {2158, {"Complex Base 08"_s, Accessory::Size::L}}, + {2159, {"Complex Base 09"_s, Accessory::Size::L}}, + {2160, {"Complex Base 10"_s, Accessory::Size::L}}, + + {2201, {"Long Base 01"_s, Accessory::Size::XL}}, + {2202, {"Long Base 02"_s, Accessory::Size::XL}}, + {2203, {"Long Base 03"_s, Accessory::Size::XL}}, + {2204, {"Long Base 04"_s, Accessory::Size::XL}}, + {2205, {"Long Base 05"_s, Accessory::Size::XL}}, + + {2251, {"Straight Wing 01"_s, Accessory::Size::XL}}, + {2252, {"Straight Wing 02"_s, Accessory::Size::XL}}, + {2253, {"Straight Wing 03"_s, Accessory::Size::XL}}, + {2254, {"Straight Wing 04"_s, Accessory::Size::XL}}, + {2255, {"Straight Wing 05"_s, Accessory::Size::XL}}, + {2256, {"Straight Wing 06"_s, Accessory::Size::XL}}, + {2257, {"Straight Wing 07"_s, Accessory::Size::XL}}, + {2258, {"Straight Wing 08"_s, Accessory::Size::XL}}, + {2259, {"Straight Wing 09"_s, Accessory::Size::XL}}, + {2260, {"Straight Wing 10"_s, Accessory::Size::XL}}, + + {2301, {"Triangular Wing 01"_s, Accessory::Size::XL}}, + {2302, {"Triangular Wing 02"_s, Accessory::Size::XL}}, + {2303, {"Triangular Wing 03"_s, Accessory::Size::XL}}, + {2304, {"Triangular Wing 04"_s, Accessory::Size::XL}}, + {2305, {"Triangular Wing 05"_s, Accessory::Size::XL}}, + {2306, {"Triangular Wing 06"_s, Accessory::Size::XL}}, + {2307, {"Triangular Wing 07"_s, Accessory::Size::XL}}, + {2308, {"Triangular Wing 08"_s, Accessory::Size::XL}}, + {2309, {"Triangular Wing 09"_s, Accessory::Size::XL}}, + {2310, {"Triangular Wing 10"_s, Accessory::Size::XL}}, + {2311, {"Triangular Wing 11"_s, Accessory::Size::L}}, + {2312, {"Triangular Wing 12"_s, Accessory::Size::L}}, + {2313, {"Triangular Wing 13"_s, Accessory::Size::L}}, + {2314, {"Triangular Wing 14"_s, Accessory::Size::L}}, + {2315, {"Triangular Wing 15"_s, Accessory::Size::L}}, + + {2351, {"Complex Wing 01"_s, Accessory::Size::XL}}, + {2352, {"Complex Wing 02"_s, Accessory::Size::XL}}, + {2353, {"Complex Wing 03"_s, Accessory::Size::XL}}, + {2354, {"Complex Wing 04"_s, Accessory::Size::XL}}, + {2355, {"Complex Wing 05"_s, Accessory::Size::XL}}, + {2356, {"Complex Wing 06"_s, Accessory::Size::L}}, + {2357, {"Complex Wing 07"_s, Accessory::Size::L}}, + {2358, {"Complex Wing 08"_s, Accessory::Size::L}}, + {2359, {"Complex Wing 09"_s, Accessory::Size::L}}, + {2360, {"Complex Wing 10"_s, Accessory::Size::L}}, + + {2401, {"Blade 01"_s, Accessory::Size::XL}}, + {2402, {"Blade 02"_s, Accessory::Size::XL}}, + {2403, {"Blade 03"_s, Accessory::Size::XL}}, + {2404, {"Blade 04"_s, Accessory::Size::XL}}, + {2405, {"Blade 05"_s, Accessory::Size::XL}}, + {2406, {"Blade 06"_s, Accessory::Size::XL}}, + {2407, {"Blade 07"_s, Accessory::Size::XL}}, + {2408, {"Blade 08"_s, Accessory::Size::XL}}, + {2409, {"Blade 09"_s, Accessory::Size::XL}}, + {2410, {"Blade 10"_s, Accessory::Size::XL}}, + {2411, {"Blade 11"_s, Accessory::Size::XL}}, + {2412, {"Blade 12"_s, Accessory::Size::XL}}, + {2413, {"Blade 13"_s, Accessory::Size::XL}}, + {2414, {"Blade 14"_s, Accessory::Size::XL}}, + {2415, {"Blade 15"_s, Accessory::Size::XL}}, + + {2426, {"Curved Blade 01"_s, Accessory::Size::XL}}, + {2427, {"Curved Blade 02"_s, Accessory::Size::XL}}, + {2428, {"Curved Blade 03"_s, Accessory::Size::XL}}, + {2429, {"Curved Blade 04"_s, Accessory::Size::XL}}, + {2430, {"Curved Blade 05"_s, Accessory::Size::XL}}, + {2431, {"Axe Head 01"_s, Accessory::Size::XL}}, + {2432, {"Axe Head 02"_s, Accessory::Size::XL}}, + {2433, {"Axe Head 03"_s, Accessory::Size::XL}}, + {2434, {"Axe Head 04"_s, Accessory::Size::XL}}, + {2435, {"Axe Head 05"_s, Accessory::Size::XL}}, + + {2451, {"Horn 01"_s, Accessory::Size::M}}, + {2452, {"Horn 02"_s, Accessory::Size::M}}, + {2453, {"Horn 03"_s, Accessory::Size::M}}, + {2454, {"Horn 04"_s, Accessory::Size::M}}, + {2455, {"Horn 05"_s, Accessory::Size::M}}, + {2456, {"Horn 06"_s, Accessory::Size::M}}, + {2457, {"Horn 07"_s, Accessory::Size::M}}, + {2458, {"Horn 08"_s, Accessory::Size::M}}, + {2459, {"Horn 09"_s, Accessory::Size::M}}, + {2460, {"Horn 10"_s, Accessory::Size::M}}, + {2461, {"Horn 11"_s, Accessory::Size::M}}, + {2462, {"Horn 12"_s, Accessory::Size::M}}, + {2463, {"Horn 13"_s, Accessory::Size::M}}, + {2464, {"Horn 14"_s, Accessory::Size::M}}, + {2465, {"Horn 15"_s, Accessory::Size::M}}, + + {2471, {"Mask"_s, Accessory::Size::M}}, + {2472, {"Droplet"_s, Accessory::Size::M}}, + {2473, {"Thigh"_s, Accessory::Size::M}}, + {2474, {"LegS"_s, Accessory::Size::M}}, + {2475, {"LegTH"_s, Accessory::Size::M}}, + {2476, {"Plume 01"_s, Accessory::Size::M}}, + {2477, {"Plume 02"_s, Accessory::Size::M}}, + {2478, {"Plume 03"_s, Accessory::Size::M}}, + {2479, {"Plume 04"_s, Accessory::Size::M}}, + {2480, {"Plume 05"_s, Accessory::Size::M}}, + + {2491, {"Tail 01"_s, Accessory::Size::XL}}, + {2492, {"Tail 02"_s, Accessory::Size::XL}}, + {2493, {"Tail 03"_s, Accessory::Size::XL}}, + {2494, {"Tail 04"_s, Accessory::Size::XL}}, + {2495, {"Tail 05"_s, Accessory::Size::XL}}, + + {2501, {"Finger 01"_s, Accessory::Size::M}}, + {2502, {"Finger 02"_s, Accessory::Size::M}}, + {2503, {"Finger 03"_s, Accessory::Size::M}}, + {2504, {"Finger 04"_s, Accessory::Size::M}}, + {2505, {"Finger 05"_s, Accessory::Size::M}}, + + {2510, {"Clamp 01"_s, Accessory::Size::M}}, + {2511, {"Clamp 02"_s, Accessory::Size::M}}, + {2512, {"Clamp 03"_s, Accessory::Size::M}}, + {2513, {"Clamp 04"_s, Accessory::Size::M}}, + {2514, {"Clamp 05"_s, Accessory::Size::M}}, + + {2521, {"Fabric 01"_s, Accessory::Size::XL}}, + {2522, {"Fabric 02"_s, Accessory::Size::XL}}, + {2523, {"Fabric 03"_s, Accessory::Size::XL}}, + {2524, {"Fabric 04"_s, Accessory::Size::XL}}, + {2525, {"Fabric 05"_s, Accessory::Size::XL}}, + + {2551, {"Energy Barrel 01"_s, Accessory::Size::XL}}, + {2552, {"Energy Barrel 02"_s, Accessory::Size::XL}}, + {2553, {"Energy Barrel 03"_s, Accessory::Size::XL}}, + {2554, {"Energy Barrel 04"_s, Accessory::Size::XL}}, + {2555, {"Energy Barrel 05"_s, Accessory::Size::XL}}, + + {2560, {"Wire Head 01"_s, Accessory::Size::M}}, + {2561, {"Wire Head 02"_s, Accessory::Size::M}}, + {2562, {"Wire Head 03"_s, Accessory::Size::M}}, + {2563, {"Wire Head 04"_s, Accessory::Size::M}}, + {2564, {"Wire Head 05"_s, Accessory::Size::M}}, + {2565, {"Wire Head 06"_s, Accessory::Size::M}}, + {2566, {"Wire Head 07"_s, Accessory::Size::M}}, + {2567, {"Wire Head 08"_s, Accessory::Size::M}}, + {2568, {"Wire Head 09"_s, Accessory::Size::M}}, + {2569, {"Wire Head 10"_s, Accessory::Size::M}}, + + {2601, {"L Bullet Barrel 01"_s, Accessory::Size::XL}}, + {2602, {"L Bullet Barrel 02"_s, Accessory::Size::XL}}, + {2603, {"L Bullet Barrel 03"_s, Accessory::Size::XL}}, + {2604, {"L Bullet Barrel 04"_s, Accessory::Size::XL}}, + {2605, {"L Bullet Barrel 05"_s, Accessory::Size::XL}}, + {2606, {"S Bullet Barrel 01"_s, Accessory::Size::XL}}, + {2607, {"S Bullet Barrel 02"_s, Accessory::Size::XL}}, + {2608, {"S Bullet Barrel 03"_s, Accessory::Size::XL}}, + {2609, {"S Bullet Barrel 04"_s, Accessory::Size::XL}}, + {2610, {"S Bullet Barrel 05"_s, Accessory::Size::XL}}, + {2611, {"B Bullet Barrel 01"_s, Accessory::Size::XL}}, + {2612, {"B Bullet Barrel 02"_s, Accessory::Size::XL}}, + {2613, {"B Bullet Barrel 03"_s, Accessory::Size::XL}}, + {2614, {"B Bullet Barrel 04"_s, Accessory::Size::XL}}, + {2615, {"B Bullet Barrel 05"_s, Accessory::Size::XL}}, + {2616, {"B Bullet Barrel 06"_s, Accessory::Size::XL}}, + {2617, {"B Bullet Barrel 07"_s, Accessory::Size::XL}}, + {2618, {"B Bullet Barrel 08"_s, Accessory::Size::XL}}, + {2619, {"B Bullet Barrel 09"_s, Accessory::Size::XL}}, + {2620, {"B Bullet Barrel 10"_s, Accessory::Size::XL}}, + + {2651, {"Cylinder Scope 01"_s, Accessory::Size::M}}, + {2652, {"Cylinder Scope 02"_s, Accessory::Size::M}}, + {2653, {"Cylinder Scope 03"_s, Accessory::Size::M}}, + {2654, {"Cylinder Scope 04"_s, Accessory::Size::M}}, + {2655, {"Cylinder Scope 05"_s, Accessory::Size::M}}, + {2656, {"Elec Scope 01"_s, Accessory::Size::M}}, + {2657, {"Elec Scope 02"_s, Accessory::Size::M}}, + {2658, {"Elec Scope 03"_s, Accessory::Size::M}}, + {2659, {"Elec Scope 04"_s, Accessory::Size::M}}, + {2660, {"Elec Scope 05"_s, Accessory::Size::M}}, + {2661, {"Mark Scope 01"_s, Accessory::Size::S}}, + {2662, {"Mark Scope 02"_s, Accessory::Size::S}}, + {2663, {"Mark Scope 03"_s, Accessory::Size::S}}, + {2664, {"Mark Scope 04"_s, Accessory::Size::S}}, + {2665, {"Mark Scope 05"_s, Accessory::Size::S}}, + + {2701, {"S Single Weaponry"_s, Accessory::Size::M}}, + {2702, {"S Packed Weaponry 01"_s, Accessory::Size::M}}, + {2703, {"S Packed Weaponry 02"_s, Accessory::Size::M}}, + {2704, {"S Packed Weaponry 03"_s, Accessory::Size::M}}, + {2705, {"S Packed Weaponry 04"_s, Accessory::Size::M}}, + {2706, {"L Single Weaponry"_s, Accessory::Size::XL}}, + {2707, {"L Packed Weaponry 01"_s, Accessory::Size::XL}}, + {2708, {"L Packed Weaponry 02"_s, Accessory::Size::XL}}, + {2709, {"L Packed Weaponry 03"_s, Accessory::Size::XL}}, + {2710, {"L Packed Weaponry 04"_s, Accessory::Size::XL}}, + {2711, {"Atk Single Weaponry"_s, Accessory::Size::XL}}, + {2712, {"Atk Packed Weaponry 01"_s, Accessory::Size::XL}}, + {2713, {"Atk Packed Weaponry 02"_s, Accessory::Size::XL}}, + {2714, {"Atk Packed Weaponry 03"_s, Accessory::Size::XL}}, + {2715, {"Atk Packed Weaponry 04"_s, Accessory::Size::XL}}, + + {2721, {"Double Pod"_s, Accessory::Size::L}}, + {2722, {"Triple Pod"_s, Accessory::Size::L}}, + {2723, {"Vertical T Pod"_s, Accessory::Size::L}}, + {2724, {"Quadruple Pod"_s, Accessory::Size::L}}, + {2725, {"Vertical Q Pod"_s, Accessory::Size::L}}, + + {2741, {"Grenade 01"_s, Accessory::Size::M}}, + {2742, {"Grenade 02"_s, Accessory::Size::M}}, + {2743, {"Grenade 03"_s, Accessory::Size::M}}, + {2744, {"Grenade 04"_s, Accessory::Size::M}}, + {2745, {"Grenade 05"_s, Accessory::Size::M}}, + + {2751, {"Vent 01"_s, Accessory::Size::M}}, + {2752, {"Vent 02"_s, Accessory::Size::M}}, + {2753, {"Vent 03"_s, Accessory::Size::M}}, + {2754, {"Vent 04"_s, Accessory::Size::M}}, + {2755, {"Vent 05"_s, Accessory::Size::M}}, + {2756, {"Vent 06"_s, Accessory::Size::M}}, + {2757, {"Vent 07"_s, Accessory::Size::M}}, + {2758, {"Vent 08"_s, Accessory::Size::M}}, + {2759, {"Vent 09"_s, Accessory::Size::M}}, + {2760, {"Vent 10"_s, Accessory::Size::M}}, + {2761, {"Cooling Tile 01"_s, Accessory::Size::L}}, + {2762, {"Cooling Tile 02"_s, Accessory::Size::L}}, + {2763, {"Cooling Tile 03"_s, Accessory::Size::L}}, + {2764, {"Cooling Tile 04"_s, Accessory::Size::L}}, + {2765, {"Cooling Tile 05"_s, Accessory::Size::L}}, + + {2901, {"Complex Construct 01"_s, Accessory::Size::L}}, + {2902, {"Complex Construct 02"_s, Accessory::Size::L}}, + {2903, {"Complex Construct 03"_s, Accessory::Size::L}}, + {2904, {"Complex Construct 04"_s, Accessory::Size::L}}, + {2905, {"Complex Construct 05"_s, Accessory::Size::L}}, + {2906, {"Grating 01"_s, Accessory::Size::M}}, + {2907, {"Grating 02"_s, Accessory::Size::M}}, + {2908, {"Grating 03"_s, Accessory::Size::M}}, + {2909, {"Grating 04"_s, Accessory::Size::M}}, + {2910, {"Grating 05"_s, Accessory::Size::M}}, + {2911, {"Wireframe 01"_s, Accessory::Size::L}}, + {2912, {"Wireframe 02"_s, Accessory::Size::L}}, + {2913, {"Wireframe 03"_s, Accessory::Size::L}}, + {2914, {"Wireframe 04"_s, Accessory::Size::L}}, + {2915, {"Wireframe 05"_s, Accessory::Size::L}}, + + {2926, {"Complex Armour 01"_s, Accessory::Size::L}}, + {2927, {"Complex Armour 02"_s, Accessory::Size::L}}, + {2928, {"Complex Armour 03"_s, Accessory::Size::L}}, + {2929, {"Complex Armour 04"_s, Accessory::Size::L}}, + {2930, {"Complex Armour 05"_s, Accessory::Size::L}}, + + {2950, {"Q Mask 01"_s, Accessory::Size::M}}, + {2951, {"Q Mask 02"_s, Accessory::Size::M}}, + {2952, {"Q Mask 03"_s, Accessory::Size::M}}, + {2953, {"Q Mask 04"_s, Accessory::Size::M}}, + {2954, {"Q Mask 05"_s, Accessory::Size::M}}, + // endregion + + // region Connectors + {3001, {"Circular Vent 01"_s, Accessory::Size::M}}, + {3002, {"Circular Vent 02"_s, Accessory::Size::M}}, + {3003, {"Circular Vent 03"_s, Accessory::Size::M}}, + {3004, {"Circular Vent 04"_s, Accessory::Size::M}}, + {3005, {"Circular Vent 05"_s, Accessory::Size::M}}, + {3006, {"Circular Vent 06"_s, Accessory::Size::M}}, + {3007, {"Circular Vent 07"_s, Accessory::Size::M}}, + {3008, {"Circular Vent 08"_s, Accessory::Size::M}}, + {3009, {"Circular Vent 09"_s, Accessory::Size::M}}, + {3010, {"Circular Vent 10"_s, Accessory::Size::M}}, + {3011, {"Circular Vent 11"_s, Accessory::Size::M}}, + {3012, {"Circular Vent 12"_s, Accessory::Size::M}}, + {3013, {"Circular Vent 13"_s, Accessory::Size::M}}, + {3014, {"Circular Vent 14"_s, Accessory::Size::M}}, + {3015, {"Circular Vent 15"_s, Accessory::Size::M}}, + + {3051, {"Reactor 01"_s, Accessory::Size::L}}, + {3052, {"Reactor 02"_s, Accessory::Size::L}}, + {3053, {"Reactor 03"_s, Accessory::Size::L}}, + {3054, {"Reactor 04"_s, Accessory::Size::L}}, + {3055, {"Reactor 05"_s, Accessory::Size::L}}, + + {3101, {"Connecting Tube 01"_s, Accessory::Size::XL}}, + {3102, {"Connecting Tube 02"_s, Accessory::Size::XL}}, + {3103, {"Connecting Tube 03"_s, Accessory::Size::XL}}, + {3104, {"Connecting Tube 04"_s, Accessory::Size::XL}}, + {3105, {"Connecting Tube 05"_s, Accessory::Size::XL}}, + + {3151, {"Latch 01"_s, Accessory::Size::M}}, + {3152, {"Latch 02"_s, Accessory::Size::M}}, + {3153, {"Latch 03"_s, Accessory::Size::M}}, + {3154, {"Latch 04"_s, Accessory::Size::M}}, + {3155, {"Latch 05"_s, Accessory::Size::M}}, + {3156, {"Latch 06"_s, Accessory::Size::M}}, + {3157, {"Latch 07"_s, Accessory::Size::M}}, + {3158, {"Latch 08"_s, Accessory::Size::M}}, + {3159, {"Latch 09"_s, Accessory::Size::M}}, + {3160, {"Latch 10"_s, Accessory::Size::M}}, + {3161, {"Latch 11"_s, Accessory::Size::M}}, + {3162, {"Latch 12"_s, Accessory::Size::M}}, + {3163, {"Latch 13"_s, Accessory::Size::M}}, + {3164, {"Latch 14"_s, Accessory::Size::M}}, + {3165, {"Latch 15"_s, Accessory::Size::M}}, + + {3201, {"Short Connector 01"_s, Accessory::Size::M}}, + {3202, {"Short Connector 02"_s, Accessory::Size::M}}, + {3203, {"Short Connector 03"_s, Accessory::Size::M}}, + {3204, {"Short Connector 04"_s, Accessory::Size::M}}, + {3205, {"Short Connector 05"_s, Accessory::Size::M}}, + {3206, {"Antenna 01"_s, Accessory::Size::S}}, + {3207, {"Antenna 02"_s, Accessory::Size::S}}, + {3208, {"Antenna 03"_s, Accessory::Size::S}}, + {3209, {"Antenna 04"_s, Accessory::Size::S}}, + {3210, {"Antenna 05"_s, Accessory::Size::S}}, + + {3226, {"Long Connector 01"_s, Accessory::Size::XL}}, + {3227, {"Long Connector 02"_s, Accessory::Size::XL}}, + {3228, {"Long Connector 03"_s, Accessory::Size::XL}}, + {3229, {"Long Connector 04"_s, Accessory::Size::XL}}, + {3230, {"Long Connector 05"_s, Accessory::Size::XL}}, + {3231, {"Long Connector 06"_s, Accessory::Size::XL}}, + {3232, {"Long Connector 07"_s, Accessory::Size::XL}}, + {3233, {"Long Connector 08"_s, Accessory::Size::XL}}, + {3234, {"Long Connector 09"_s, Accessory::Size::XL}}, + {3235, {"Long Connector 10"_s, Accessory::Size::XL}}, + + {3251, {"Complex Connector 01"_s, Accessory::Size::XL}}, + {3252, {"Complex Connector 02"_s, Accessory::Size::XL}}, + {3253, {"Complex Connector 03"_s, Accessory::Size::XL}}, + {3254, {"Complex Connector 04"_s, Accessory::Size::XL}}, + {3255, {"Complex Connector 05"_s, Accessory::Size::XL}}, + + {3301, {"Tube Line 01"_s, Accessory::Size::L}}, + {3302, {"Tube Line 02"_s, Accessory::Size::L}}, + {3303, {"Tube Line 03"_s, Accessory::Size::L}}, + {3304, {"Tube Line 04"_s, Accessory::Size::XL}}, + {3305, {"Tube Line 05"_s, Accessory::Size::XL}}, + {3306, {"Tube Line 06"_s, Accessory::Size::M}}, + {3307, {"Tube Line 07"_s, Accessory::Size::M}}, + {3308, {"Tube Line 08"_s, Accessory::Size::M}}, + {3309, {"Tube Line 09"_s, Accessory::Size::L}}, + {3310, {"Tube Line 10"_s, Accessory::Size::L}}, + + {3351, {"Radar Plate 01"_s, Accessory::Size::M}}, + {3352, {"Radar Plate 02"_s, Accessory::Size::M}}, + {3353, {"Radar Plate 03"_s, Accessory::Size::M}}, + {3354, {"Radar Plate 04"_s, Accessory::Size::M}}, + {3355, {"Radar Plate 05"_s, Accessory::Size::M}}, + {3356, {"Radar Pod 01"_s, Accessory::Size::M}}, + {3357, {"Radar Pod 02"_s, Accessory::Size::M}}, + {3358, {"Radar Pod 03"_s, Accessory::Size::M}}, + {3359, {"Radar Pod 04"_s, Accessory::Size::M}}, + {3360, {"Radar Pod 05"_s, Accessory::Size::M}}, + {3361, {"Radar Ring 01"_s, Accessory::Size::XL}}, + {3362, {"Radar Ring 02"_s, Accessory::Size::XL}}, + {3363, {"Radar Ring 03"_s, Accessory::Size::XL}}, + {3364, {"Radar Ring 04"_s, Accessory::Size::XL}}, + {3365, {"Radar Ring 05"_s, Accessory::Size::XL}}, + + {3401, {"Tri Pod 01"_s, Accessory::Size::M}}, + {3402, {"Tri Pod 02"_s, Accessory::Size::M}}, + {3403, {"Tri Pod 03"_s, Accessory::Size::M}}, + {3404, {"Tri Pod 04"_s, Accessory::Size::M}}, + {3405, {"Tri Pod 05"_s, Accessory::Size::M}}, + {3406, {"Signal Pod 01"_s, Accessory::Size::M}}, + {3407, {"Signal Pod 02"_s, Accessory::Size::M}}, + {3408, {"Signal Pod 03"_s, Accessory::Size::M}}, + {3409, {"Signal Pod 04"_s, Accessory::Size::M}}, + {3410, {"Signal Pod 05"_s, Accessory::Size::M}}, + + {3451, {"Track Wheel 01"_s, Accessory::Size::XL}}, + {3452, {"Track Wheel 02"_s, Accessory::Size::XL}}, + {3453, {"Track Wheel 03"_s, Accessory::Size::XL}}, + {3454, {"Track Wheel 04"_s, Accessory::Size::XL}}, + {3455, {"Track Wheel 05"_s, Accessory::Size::XL}}, + {3456, {"Track Chain 01"_s, Accessory::Size::XL}}, + {3457, {"Track Chain 02"_s, Accessory::Size::XL}}, + {3458, {"Track Chain 03"_s, Accessory::Size::XL}}, + {3459, {"Track Chain 04"_s, Accessory::Size::XL}}, + {3460, {"Track Chain 05"_s, Accessory::Size::XL}}, + {3461, {"Track Chain 06"_s, Accessory::Size::XL}}, + {3462, {"Track Chain 07"_s, Accessory::Size::XL}}, + {3463, {"Track Chain 08"_s, Accessory::Size::XL}}, + {3464, {"Track Chain 09"_s, Accessory::Size::XL}}, + {3465, {"Track Chain 10"_s, Accessory::Size::XL}}, + {3466, {"Track Chain 11"_s, Accessory::Size::XL}}, + {3467, {"Track Chain 12"_s, Accessory::Size::XL}}, + {3468, {"Track Chain 13"_s, Accessory::Size::XL}}, + {3469, {"Track Chain 14"_s, Accessory::Size::XL}}, + {3470, {"Track Chain 15"_s, Accessory::Size::XL}}, + + {3500, {"Wire Module 05"_s, Accessory::Size::XL}}, + {3501, {"Wire Module 06"_s, Accessory::Size::XL}}, + {3502, {"Wire Module 07"_s, Accessory::Size::XL}}, + {3503, {"Wire Module 08"_s, Accessory::Size::XL}}, + {3504, {"Wire Module 09"_s, Accessory::Size::XL}}, + {3505, {"Wire Module 10"_s, Accessory::Size::XL}}, + {3506, {"Wire Module 11"_s, Accessory::Size::XL}}, + {3507, {"Wire Module 12"_s, Accessory::Size::XL}}, + {3508, {"Wire Module 13"_s, Accessory::Size::XL}}, + {3509, {"Wire Module 14"_s, Accessory::Size::XL}}, + {3510, {"Wire Module 15"_s, Accessory::Size::XL}}, + {3511, {"Wire Module 06"_s, Accessory::Size::XL}}, + {3512, {"Wire Module 07"_s, Accessory::Size::XL}}, + {3513, {"Wire Module 08"_s, Accessory::Size::XL}}, + {3514, {"Wire Module 09"_s, Accessory::Size::XL}}, + // endregion +}; + +} diff --git a/src/Maps/ArmourSets.h b/src/GameData/ArmourSets.h similarity index 86% rename from src/Maps/ArmourSets.h rename to src/GameData/ArmourSets.h index 8051709..97daa02 100644 --- a/src/Maps/ArmourSets.h +++ b/src/GameData/ArmourSets.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,22 +16,23 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include + #include #include -#include - using namespace Corrade; using namespace Containers::Literals; -using namespace Magnum; + +namespace mbst::GameData { struct ArmourSet { Containers::StringView name; bool neck_compatible; }; -static const std::map armour_sets { +static const std::map armour_sets { {-1, {""_s, true}}, {0, {"Vanguard"_s, true}}, {1, {"Assault Mk.I"_s, true}}, @@ -55,4 +56,9 @@ static const std::map armour_sets { {25, {"Axial Core R-Type"_s, true}}, {26, {"Axial Core S-Type"_s, false}}, {27, {"Axial Core X-Type"_s, false}}, + {28, {"Zenith-X"_s, true}}, + {29, {"Zenith-Y"_s, false}}, + {30, {"Zenith-Z"_s, false}}, }; + +} diff --git a/src/GameData/LastMissionId.h b/src/GameData/LastMissionId.h new file mode 100644 index 0000000..b329b42 --- /dev/null +++ b/src/GameData/LastMissionId.h @@ -0,0 +1,70 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include + +using namespace Corrade; +using namespace Containers::Literals; + +namespace mbst::GameData { + +static const std::map mission_id_map {{ + // Story missions + {100, "Mission 1 - Training"_s}, + {101, "Mission 2 - Patrol Operation"_s}, + {102, "Mission 3 - Fusion Cells in the Snow"_s}, + {103, "Mission 4 - Earning Changes"_s}, + {104, "Mission 5 - Unexpected Coordination"_s}, + {105, "Mission 6 - Empowering Void"_s}, + {106, "Mission 7 - Logisitics Obstacles"_s}, + {107, "Mission 8 - Wrath of the Wastelands"_s}, + {108, "Mission 9 - Suspicious Originator"_s}, + {109, "Mission 10 - Researchers Data Recovery"_s}, + {110, "Mission 11 - Tempestuous Sector"_s}, + {111, "Mission 12 - Clashes of Metal"_s}, + {112, "Mission 13 - The Sandstorm Glutton"_s}, + {113, "Mission 14 - An Icy Investigation"_s}, + {114, "Mission 15 - Outposts Line of Defense"_s}, + {115, "Mission 16 - Hidden in the Pass"_s}, + {116, "Mission 17 - Homebase Security"_s}, + {117, "Mission 18 - Molewarp Protection Deal"_s}, + {118, "Mission 19 - Behind the Walls of Ice"_s}, + {119, "Mission 20 - Odin in the Sea of Flames"_s}, + {120, "Mission 21 - Retracing Ruined Shelter"_s}, + {121, "Mission 22 - The Traitor"_s}, + {122, "Mission 23 - Duel of Aces"_s}, + + // Hunting grounds + {200, "Hunt 1 - Desert Pathway Safety"_s}, + {201, "Hunt 2 - Snowfield Custodian"_s}, + {202, "Hunt 3 - Abandoned Valley Raid"_s}, + {203, "Hunt 4 - Depths of the Machineries"_s}, + {204, "Hunt 5 - Crater Crashers"_s}, + {205, "Hunt 6 - Prototype Performance Tests"_s}, + {206, "Hunt 7 - A Mess in Manufacturing"_s}, + {207, "Hunt 8 - Visitors in Volcanic Fissures"_s}, + + // Challenges + {300, "Challenge 1 - Redline Battlefront"_s}, + {320, "Challenge 2 - Void Convergence"_s}, + {400, "Challenge 3 - Gates of Ascension"_s} +}}; + +} diff --git a/src/Profile/ResourceIDs.h b/src/GameData/ResourceIDs.h similarity index 74% rename from src/Profile/ResourceIDs.h rename to src/GameData/ResourceIDs.h index 3b03d11..7f8095e 100644 --- a/src/Profile/ResourceIDs.h +++ b/src/GameData/ResourceIDs.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,17 +16,16 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include +namespace mbst::GameData { -using namespace Magnum; - -enum MaterialID : Int { +enum MaterialID : std::int32_t { VerseSteel = 0xC3500, Undinium = 0xC3501, NecriumAlloy = 0xC3502, Lunarite = 0xC3503, Asterite = 0xC3504, HalliteFragma = 0xC3505, + Unnoctinium = 0xC3506, Ednil = 0xC350A, Nuflalt = 0xC350B, @@ -34,13 +33,15 @@ enum MaterialID : Int { Soldus = 0xC350D, SynthesisedN = 0xC350E, Nanoc = 0xC350F, + Abyssillite = 0xC3510, - Alcarbonite = 0xC3514, - Keriphene = 0xC3515, - NitinolCM = 0xC3516, - Quarkium = 0xC3517, - Alterene = 0xC3518, - Cosmium = 0xC3519, + Alcarbonite = 0xC3514, + Keriphene = 0xC3515, + NitinolCM = 0xC3516, + Quarkium = 0xC3517, + Alterene = 0xC3518, + Cosmium = 0xC3519, + PurifiedQuarkium = 0xC351A, MixedComposition = 0xDBBA0, VoidResidue = 0xDBBA1, @@ -48,4 +49,7 @@ enum MaterialID : Int { MineralExoskeletology = 0xDBBA3, CarbonisedSkin = 0xDBBA4, IsolatedVoidParticle = 0xDBBA5, + WeaponisedPhysiology = 0xDBBA6, }; + +} diff --git a/src/Maps/StoryProgress.h b/src/GameData/StoryProgress.h similarity index 80% rename from src/Maps/StoryProgress.h rename to src/GameData/StoryProgress.h index 2e9e28d..5973afa 100644 --- a/src/Maps/StoryProgress.h +++ b/src/GameData/StoryProgress.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -19,16 +19,15 @@ #include #include -#include - using namespace Corrade; using namespace Containers::Literals; -using namespace Magnum; + +namespace mbst::GameData { struct StoryProgressPoint { - Int id; - Containers::StringView chapter; - Containers::StringView point; + std::int32_t id{}; + Containers::StringView chapter = nullptr; + Containers::StringView point = nullptr; Containers::StringView after = nullptr; }; @@ -109,6 +108,28 @@ static const Corrade::Containers::Array story_progress {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}, + {0x0709, "Chapter 3"_s, "Returned to hangar"_s, "After mission 17"_s}, {0x070A, "Chapter 3"_s, "Got hunt 6 briefing"_s, "After mission 17"_s}, + {0x070B, "Chapter 3"_s, "Returned to hangar"_s, "After mission 17"_s}, + {0x070C, "Chapter 3"_s, "Got mission 18 briefing"_s, "After mission 17"_s}, + {0x076C, "Chapter 3"_s, "Debriefing"_s, "After mission 18"_s}, + {0x076D, "Chapter 3"_s, "Returned to hangar"_s, "After mission 18"_s}, + {0x076E, "Chapter 3"_s, "Got hunt 7 and mission 19 briefing"_s, "After mission 18"_s}, + + {0x07D0, "Chapter 4"_s, "Debriefing"_s, "After mission 19"_s}, + {0x07D1, "Chapter 4"_s, "Returned to hangar"_s, "After mission 19"_s}, + {0x07D2, "Chapter 4"_s, "Got mission 20 briefing"_s, "After mission 19"_s}, + {0x0834, "Chapter 4"_s, "Debriefing"_s, "After mission 20"_s}, + {0x0835, "Chapter 4"_s, "Returned to hangar"_s, "After mission 20"_s}, + {0x0836, "Chapter 4"_s, "Got hunt 8 and mission 21 briefing"_s, "After mission 20"_s}, + {0x0898, "Chapter 4"_s, "Debriefing"_s, "After mission 21"_s}, + {0x0899, "Chapter 4"_s, "Returned to hangar"_s, "After mission 21"_s}, + {0x089A, "Chapter 4"_s, "Got mission 22 briefing"_s, "After mission 21"_s}, + {0x08FC, "Chapter 4"_s, "Debriefing"_s, "After mission 22"_s}, + {0x08FD, "Chapter 4"_s, "Returned to hangar"_s, "After mission 22"_s}, + {0x08FE, "Chapter 4"_s, "Got mission 23 briefing"_s, "After mission 22"_s}, + {0x0960, "Chapter 4"_s, "Returned to hangar"_s, "After mission 23"_s}, } }; + +} diff --git a/src/Maps/StyleNames.h b/src/GameData/StyleNames.h similarity index 77% rename from src/Maps/StyleNames.h rename to src/GameData/StyleNames.h index e62188f..a64a73a 100644 --- a/src/Maps/StyleNames.h +++ b/src/GameData/StyleNames.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,53 +16,20 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include + #include #include -#include - using namespace Corrade; using namespace Containers::Literals; -using namespace Magnum; -extern const std::map style_names +namespace mbst::GameData { + +extern const std::map builtin_style_names #ifdef STYLENAMES_DEFINITION { - {0, "Custom Style 1"_s}, - {1, "Custom Style 2"_s}, - {2, "Custom Style 3"_s}, - {3, "Custom Style 4"_s}, - {4, "Custom Style 5"_s}, - {5, "Custom Style 6"_s}, - {6, "Custom Style 7"_s}, - {7, "Custom Style 8"_s}, - {8, "Custom Style 9"_s}, - {9, "Custom Style 10"_s}, - {10, "Custom Style 11"_s}, - {11, "Custom Style 12"_s}, - {12, "Custom Style 13"_s}, - {13, "Custom Style 14"_s}, - {14, "Custom Style 15"_s}, - {15, "Custom Style 16"_s}, - - {50, "Global Style 1"_s}, - {51, "Global Style 2"_s}, - {52, "Global Style 3"_s}, - {53, "Global Style 4"_s}, - {54, "Global Style 5"_s}, - {55, "Global Style 6"_s}, - {56, "Global Style 7"_s}, - {57, "Global Style 8"_s}, - {58, "Global Style 9"_s}, - {59, "Global Style 10"_s}, - {60, "Global Style 11"_s}, - {61, "Global Style 12"_s}, - {62, "Global Style 13"_s}, - {63, "Global Style 14"_s}, - {64, "Global Style 15"_s}, - {65, "Global Style 16"_s}, - {100, "Iron"_s}, {101, "Silver"_s}, {102, "Gold"_s}, @@ -194,3 +161,5 @@ extern const std::map style_names } #endif ; + +} diff --git a/src/Maps/WeaponParts.h b/src/GameData/WeaponParts.h similarity index 81% rename from src/Maps/WeaponParts.h rename to src/GameData/WeaponParts.h index 3455943..36451eb 100644 --- a/src/Maps/WeaponParts.h +++ b/src/GameData/WeaponParts.h @@ -1,6 +1,6 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -15,19 +15,20 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include + #include #include -#include - using namespace Corrade; -using namespace Magnum; using namespace Containers::Literals; +namespace mbst::GameData { + // region Melee -static const std::map melee_grips { +static const std::map melee_grips { {0, "Combat Grip (1H)"_s}, {1, "Knuckle Guard Grip (1H)"_s}, {2, "Dual Guard Grip (1H)"_s}, @@ -36,6 +37,7 @@ static const std::map melee_grips { {5, "Guardian Grip (1H)"_s}, {6, "Knight Guard Grip (1H)"_s}, {7, "Saber Guard Grip (1H)"_s}, + {8, "Base Grip (1H)"_s}, {100, "Combat Side Grip (1H)"_s}, {101, "Hollowed Side Grip (1H)"_s}, @@ -43,42 +45,63 @@ static const std::map melee_grips { {103, "Plated Side Grip (1H)"_s}, {104, "Locked Side Grip (1H)"_s}, {105, "Longpoint Side Grip (1H)"_s}, + {106, "Concave Side Grip (1H)"_s}, + {107, "Polehead Side Grip (1H)"_s}, + {108, "Base Side Grip (1H)"_s}, {200, "Combat Dual Grip (1H)"_s}, {201, "Hollowed Dual Grip (1H)"_s}, {202, "Plated Dual Grip (1H)"_s}, + {203, "Concave Dual Grip (1H)"_s}, + {204, "Polehead Dual Grip (1H)"_s}, {400, "Combat Twin Grip (1H)"_s}, {401, "Sepal Twin Grip (1H)"_s}, {402, "Hollowed Twin Grip (1H)"_s}, {403, "Knuckle Guard Twin Grip (1H)"_s}, {404, "Arched Twin Grip (1H)"_s}, + {405, "Handguard Twin Grip (1H)"_s}, + {406, "Fullguard Twin Grip (1H)"_s}, + {407, "Base Twin Grip (1H)"_s}, {1000, "Combat Knuckle (R/L)"_s}, {1001, "Battle Fist (R/L)"_s}, {1002, "Guard Knuckle (R/L)"_s}, + {1003, "Heavy Fist (R/L)"_s}, + {1004, "Thick Fist (R/L)"_s}, + {1005, "Base Fist (R/L)"_s}, {2000, "Combat Polearm (2H)"_s}, {2001, "Dual Guard Polearm (2H)"_s}, {2002, "Sepal Polearm (2H)"_s}, {2003, "Fin Polearm (2H)"_s}, {2004, "Arched Polearm (2H)"_s}, + {2005, "Sharp Polearm (2H)"_s}, + {2006, "Ring Polearm (2H)"_s}, + {2007, "Base Polearm (2H)"_s}, {2100, "Combat Side Polearm (2H)"_s}, {2101, "Plated Side Polearm (2H)"_s}, {2102, "Locked Side Polearm (2H)"_s}, {2103, "Fin Side Polearm (2H)"_s}, + {2104, "Heavy Side Polearm (2H)"_s}, + {2105, "Base Side Polearm (2H)"_s}, {2200, "Combat Dual Polearm (2H)"_s}, + {2201, "Studded Dual Polearm (2H)"_s}, + {2202, "Circular Dual Polearm (2H)"_s}, {2400, "Combat Twin Blade (2H)"_s}, {2401, "Guard Twin Blade (2H)"_s}, {2402, "Sepal Twin Blade (2H)"_s}, {2403, "Fin Twin Blade (2H)"_s}, {2404, "Arched Twin Blade (2H)"_s}, + {2405, "Holder Twin Blade (2H)"_s}, + {2406, "Ring Twin Blade (2H)"_s}, + {2407, "Base Twin Blade (2H)"_s}, }; -static const std::map melee_assaulters { +static const std::map melee_assaulters { {0, "Long Metal Blade"_s}, {1, "Long Assault Blade"_s}, {2, "Long Fin Blade"_s}, @@ -91,6 +114,7 @@ static const std::map melee_assaulters { {9, "Long Flat Gouger"_s}, {10, "Long Curved Blade"_s}, {11, "Long Broad Blade"_s}, + {12, "Base L Sword"_s}, {20, "Long Combat Edge"_s}, {21, "Long Attached Edge"_s}, @@ -104,6 +128,7 @@ static const std::map melee_assaulters { {100, "Short Metal Blade"_s}, {101, "Short Assault Blade"_s}, {102, "Short Fin Blade"_s}, + {103, "Base S Sword"_s}, {120, "Short Combat Edge"_s}, @@ -119,6 +144,7 @@ static const std::map melee_assaulters { {200, "Bracer"_s}, {201, "Custom Bracer"_s}, + {202, "Base Hammer"_s}, {210, "Expanded Bracer"_s}, {211, "Expanded Custom Bracer"_s}, @@ -126,10 +152,18 @@ static const std::map melee_assaulters { {300, "Heavy Smasher"_s}, {301, "Heavy Basher"_s}, {302, "Heavy Torch Mace"_s}, + {303, "Heavy Spike Club"_s}, + {304, "Heavy Diamond Smasher"_s}, + {305, "Heavy Spinning Smasher (Motion)"_s}, + {306, "Base L Mace"_s}, {400, "Light Smasher"_s}, {401, "Light Basher"_s}, {402, "Light Torch Mace"_s}, + {403, "Light Spike Club"_s}, + {404, "Light Diamond Smasher"_s}, + {405, "Light Spinning Smasher"_s}, + {406, "Base S Mace"_s}, {420, "War Hammer"_s}, {421, "Great Hammer"_s}, @@ -141,13 +175,16 @@ static const std::map melee_assaulters { {500, "Combat Lance"_s}, {501, "Gouger Lance"_s}, - + {502, "Pointy Lance"_s}, + {503, "Spinning Pointy Lance (Motion)"_s}, + {504, "Crystal Lance"_s}, {510, "Piercer"_s}, {600, "Short Combat Lance"_s}, - + {601, "Short Pointy Lance"_s}, + {602, "Short Spinning Pointy Lance (Motion)"_s}, + {603, "Short Crystal Lance"_s}, {605, "Short Combat Drill (Motion)"_s}, - {610, "Short Piercer"_s}, {700, "Combat Axe"_s}, @@ -172,7 +209,7 @@ static const std::map melee_assaulters { // endregion // region Shields -static const std::map shield_handles { +static const std::map shield_handles { {0, "Balanced Handle"_s}, {1, "Expanded Handle"_s}, {2, "Lowguard Handle"_s}, @@ -191,7 +228,7 @@ static const std::map shield_handles { {101, "Star Handle"_s}, }; -static const std::map shield_shells { +static const std::map shield_shells { {0, "Balanced Shell"_s}, {1, "Compass Shell"_s}, {2, "Uppoint Shell"_s}, @@ -212,7 +249,7 @@ static const std::map shield_shells { // endregion // region Bullet Shooters -static const std::map bshooter_triggers { +static const std::map bshooter_triggers { {0, "BL-Combat Trigger (1H)"_s}, {1, "Light Machine Trigger (1H)"_s}, {2, "Tactical Trigger (1H)"_s}, @@ -230,7 +267,7 @@ static const std::map bshooter_triggers { {199, "2H Base Trigger (2H)"_s}, }; -static const std::map bshooter_barrels { +static const std::map bshooter_barrels { {0, "BL-Combat Barrel (1 shot)"_s}, {1, "Shock Absorb Barrel (1 shot) (Motion)"_s}, {2, "Muzzlemod Barrel (1 shot)"_s}, @@ -263,11 +300,16 @@ static const std::map bshooter_barrels { {397, "Short D Base Barrel (Detonate)"_s}, {398, "Medium D Base Barrel (Detonate)"_s}, {399, "Long D Base Barrel (Detonate)"_s}, + + {400, "Heavy Burst Barrel (Pile Bunker) (Motion)"_s}, + {401, "Under Guard Barrel (Pile Bunker) (Motion)"_s}, + {402, "Facthold Barrel (Pile Bunker) (Motion)"_s}, + {499, "Long P Base Barrel (Pile Bunker) (Motion)"_s}, }; // endregion //region Energy Shooters -static const std::map eshooter_triggers { +static const std::map eshooter_triggers { {0, "EN-Rifle Trigger (1H)"_s}, {1, "Underarm Trigger (1H)"_s}, {2, "EN-Inverted Trigger (1H)"_s}, @@ -285,7 +327,7 @@ static const std::map eshooter_triggers { {199, "2H Base EnTrigger (2H)"_s}, }; -static const std::map eshooter_busters { +static const std::map eshooter_busters { {0, "EN-Combat Buster (1 shot)"_s}, {1, "Delta Cycler (1 shot) (Motion)"_s}, {2, "EN-Longbarrel Buster (1 shot)"_s}, @@ -317,11 +359,16 @@ static const std::map eshooter_busters { {397, "Short W Base Buster (Wave)"_s}, {398, "Medium W Base Buster (Wave)"_s}, {399, "Long W Base Buster (Wave)"_s}, + + {400, "Wiredcharge Buster (Prism) (Motion)"_s}, + {402, "Heavyclamp Buster (Prism) (Motion)"_s}, + {402, "Curlescent Buster (Prism) (Motion)"_s}, + {499, "Long P Base Buster (Prism) (Motion)"_s}, }; // endregion // region Bullet Launchers -static const std::map blauncher_pods { +static const std::map blauncher_pods { {0, "BL-Delta Pack Launcher (Missile x12)"_s}, {1, "BL-Twin Pack Launcher (Missile x12)"_s}, {2, "Detector Launcher (Missile x12)"_s}, @@ -351,7 +398,7 @@ static const std::map blauncher_pods { {399, "C Base Pod (Cluster x40)"_s}, }; -static const std::map blauncher_projectiles { +static const std::map blauncher_projectiles { {0, "Flathead Missile"_s}, {1, "Warhead Missile"_s}, {2, "Pointhead Missile"_s}, @@ -361,7 +408,7 @@ static const std::map blauncher_projectiles { // endregion // region Energy Launchers -static const std::map elauncher_generators { +static const std::map elauncher_generators { {0, "Fly Unit"_s}, {1, "Assault Unit (Motion)"_s}, {2, "Falcon Unit"_s}, @@ -393,7 +440,7 @@ static const std::map elauncher_generators { {99, "Base Generator"}, }; -static const std::map elauncher_pods { +static const std::map elauncher_pods { {0, "EN-Dual Claw Launcher (Echo) (Motion)"_s}, {1, "EN-Assault Launcher (Echo)"_s}, {2, "EN-Tactical Launcher (Echo)"_s}, @@ -431,3 +478,5 @@ static const std::map elauncher_pods { {399, "P Base EPod (Photon)"_s}, }; // endregion + +} diff --git a/src/Mass/Accessory.h b/src/GameObjects/Accessory.h similarity index 61% rename from src/Mass/Accessory.h rename to src/GameObjects/Accessory.h index cca271d..fbc967d 100644 --- a/src/Mass/Accessory.h +++ b/src/GameObjects/Accessory.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,6 +16,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include + #include #include @@ -24,13 +26,19 @@ using namespace Corrade; using namespace Magnum; +typedef std::variant Vector3Variant; + +namespace mbst::GameObjects { + struct Accessory { - Int attachIndex = -1; - Int id = -1; - Containers::StaticArray<2, Int> styles{ValueInit}; - Vector3 relativePosition{0.0f}; - Vector3 relativePositionOffset{0.0f}; - Vector3 relativeRotation{0.0f}; - Vector3 relativeRotationOffset{0.0f}; - Vector3 localScale{1.0f}; + std::int32_t attachIndex = -1; + std::int32_t id = -1; + Containers::StaticArray<2, std::int32_t> styles{ValueInit}; + Vector3Variant relativePosition{Vector3{}}; + Vector3Variant relativePositionOffset{Vector3{}}; + Vector3Variant relativeRotation{Vector3{}}; + Vector3Variant relativeRotationOffset{Vector3{}}; + Vector3Variant localScale{Vector3{1.0f}}; }; + +} diff --git a/src/Mass/ArmourPart.h b/src/GameObjects/ArmourPart.h similarity index 73% rename from src/Mass/ArmourPart.h rename to src/GameObjects/ArmourPart.h index 9f7a96c..03d2fc5 100644 --- a/src/Mass/ArmourPart.h +++ b/src/GameObjects/ArmourPart.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -19,24 +19,24 @@ #include #include -#include - #include "Decal.h" #include "Accessory.h" using namespace Corrade; -using namespace Magnum; -enum class ArmourSlot { - #define c(enumerator, enumstr, name) enumerator, - #include "../Maps/ArmourSlots.hpp" - #undef c -}; +namespace mbst::GameObjects { struct ArmourPart { - ArmourSlot slot = ArmourSlot::Face; - Int id = 0; - Containers::StaticArray<4, Int> styles{ValueInit}; + enum class Slot { + #define c(enumerator, enumstr, name) enumerator, + #include "../Maps/ArmourSlots.hpp" + #undef c + }; + Slot slot = Slot::Face; + std::int32_t id = 0; + Containers::StaticArray<4, std::int32_t> styles{ValueInit}; Containers::Array decals; Containers::Array accessories; }; + +} diff --git a/src/Mass/BulletLauncherAttachment.h b/src/GameObjects/BulletLauncherAttachment.h similarity index 65% rename from src/Mass/BulletLauncherAttachment.h rename to src/GameObjects/BulletLauncherAttachment.h index 028c7a4..6f0234a 100644 --- a/src/Mass/BulletLauncherAttachment.h +++ b/src/GameObjects/BulletLauncherAttachment.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include +#include #include #include @@ -24,23 +24,28 @@ using namespace Corrade; using namespace Magnum; +typedef std::variant Vector3Variant; + +namespace mbst::GameObjects { + enum class BulletLauncherAttachmentStyle { #define c(enumerator, enumstr) enumerator, #include "../Maps/BulletLauncherAttachmentStyles.hpp" #undef c }; -enum class BulletLauncherSocket { - #define c(enumerator, enumstr, name) enumerator, - #include "../Maps/BulletLauncherSockets.hpp" - #undef c +struct BulletLauncherAttachment { + enum class Socket { + #define c(enumerator, enumstr, name) enumerator, + #include "../Maps/BulletLauncherSockets.hpp" + #undef c + }; + Socket socket = Socket::Auto; + Vector3Variant relativeLocation; + Vector3Variant offsetLocation; + Vector3Variant relativeRotation; + Vector3Variant offsetRotation; + Vector3Variant relativeScale; }; -struct BulletLauncherAttachment { - BulletLauncherSocket socket = BulletLauncherSocket::Auto; - Vector3 relativeLocation; - Vector3 offsetLocation; - Vector3 relativeRotation; - Vector3 offsetRotation; - Vector3 relativeScale; -}; +} diff --git a/src/Mass/CustomStyle.h b/src/GameObjects/CustomStyle.h similarity index 66% rename from src/Mass/CustomStyle.h rename to src/GameObjects/CustomStyle.h index 7fcc9c3..29e1f1d 100644 --- a/src/Mass/CustomStyle.h +++ b/src/GameObjects/CustomStyle.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -25,16 +25,29 @@ using namespace Corrade; using namespace Magnum; +namespace mbst::GameObjects { + struct CustomStyle { Containers::String name; Color4 colour{0.0f}; - Float metallic = 0.5f; - Float gloss = 0.5f; + float metallic = 0.5f; + float gloss = 0.5f; bool glow = false; - Int patternId = 0; - Float opacity = 0.5f; + std::int32_t patternId = 0; + float opacity = 0.5f; Vector2 offset{0.5f}; - Float rotation = 0.0f; - Float scale = 0.5f; + float rotation = 0.0f; + float scale = 0.5f; + + // This is only used to know which style array the current style is located in when exporting a standalone style. + enum class Type: std::uint8_t { + Unknown, + Frame, + Armour, + Weapon, + Global + } type = Type::Unknown; }; + +} diff --git a/src/Mass/Decal.h b/src/GameObjects/Decal.h similarity index 65% rename from src/Mass/Decal.h rename to src/GameObjects/Decal.h index 3758b75..ed11afc 100644 --- a/src/Mass/Decal.h +++ b/src/GameObjects/Decal.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,21 +16,31 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include + #include #include #include +#include using namespace Magnum; +typedef std::variant Vector2Variant; +typedef std::variant Vector3Variant; + +namespace mbst::GameObjects { + struct Decal { - Int id = -1; + std::int32_t id = -1; Color4 colour{0.0f}; - Vector3 position{0.0f}; - Vector3 uAxis{0.0f}; - Vector3 vAxis{0.0f}; - Vector2 offset{0.5f}; - Float scale = 0.5f; - Float rotation = 0.0f; + Vector3Variant position{Vector3{}}; + Vector3Variant uAxis{Vector3{}}; + Vector3Variant vAxis{Vector3{}}; + Vector2Variant offset{Vector2{0.5f}}; + float scale = 0.5f; + float rotation = 0.0f; bool flip = false; bool wrap = false; }; + +} diff --git a/src/Mass/Joints.h b/src/GameObjects/Joints.h similarity index 68% rename from src/Mass/Joints.h rename to src/GameObjects/Joints.h index 8cc58c2..ebb294a 100644 --- a/src/Mass/Joints.h +++ b/src/GameObjects/Joints.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,17 +16,17 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include - -using namespace Magnum; +namespace mbst::GameObjects { struct Joints { - Float neck = 0.0f; - Float body = 0.0f; - Float shoulders = 0.0f; - Float hips = 0.0f; - Float upperArms = 0.0f; - Float lowerArms = 0.0f; - Float upperLegs = 0.0f; - Float lowerLegs = 0.0f; + float neck = 0.0f; + float body = 0.0f; + float shoulders = 0.0f; + float hips = 0.0f; + float upperArms = 0.0f; + float lowerArms = 0.0f; + float upperLegs = 0.0f; + float lowerLegs = 0.0f; }; + +} diff --git a/src/Mass/Mass.cpp b/src/GameObjects/Mass.cpp similarity index 76% rename from src/Mass/Mass.cpp rename to src/GameObjects/Mass.cpp index 2608948..6025185 100644 --- a/src/Mass/Mass.cpp +++ b/src/GameObjects/Mass.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -23,17 +23,19 @@ #include "PropertyNames.h" #include "../Logger/Logger.h" -#include "../UESaveFile/Types/ArrayProperty.h" -#include "../UESaveFile/Types/BoolProperty.h" -#include "../UESaveFile/Types/ColourStructProperty.h" -#include "../UESaveFile/Types/GenericStructProperty.h" -#include "../UESaveFile/Types/IntProperty.h" -#include "../UESaveFile/Types/StringProperty.h" +#include "../Gvas/Types/ArrayProperty.h" +#include "../Gvas/Types/BoolProperty.h" +#include "../Gvas/Types/ColourStructProperty.h" +#include "../Gvas/Types/GenericStructProperty.h" +#include "../Gvas/Types/IntProperty.h" +#include "../Gvas/Types/StringProperty.h" #include "Mass.h" using namespace Containers::Literals; +namespace mbst::GameObjects { + Mass::Mass(Containers::StringView path) { auto split = Utility::Path::split(path); _folder = split.first(); @@ -42,31 +44,33 @@ Mass::Mass(Containers::StringView path) { refreshValues(); } -auto Mass::lastError() -> Containers::StringView { +Containers::StringView +Mass::lastError() { return _lastError; } -auto Mass::getNameFromFile(Containers::StringView path) -> Containers::Optional { +Containers::Optional +Mass::getNameFromFile(Containers::StringView path) { if(!Utility::Path::exists(path)) { LOG_ERROR_FORMAT("{} couldn't be found.", path); return Containers::NullOpt; } - UESaveFile mass{path}; + Gvas::File mass{path}; if(!mass.valid()) { LOG_ERROR_FORMAT("{} is invalid: {}", path, mass.lastError()); return Containers::NullOpt; } - auto unit_data = mass.at(MASS_UNIT_DATA); + auto unit_data = mass.at(MASS_UNIT_DATA); if(!unit_data) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, path); return Containers::NullOpt; } - auto name_prop = unit_data->at(MASS_NAME); + auto name_prop = unit_data->at(MASS_NAME); if(!name_prop) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_NAME, path); @@ -76,7 +80,8 @@ auto Mass::getNameFromFile(Containers::StringView path) -> Containers::Optional< return {name_prop->value}; } -void Mass::refreshValues() { +void +Mass::refreshValues() { LOG_INFO_FORMAT("Refreshing values for {}.", _filename); logger().lockMutex(); @@ -122,7 +127,7 @@ void Mass::refreshValues() { } LOG_INFO("Getting the unit data."); - auto unit_data = _mass->at(MASS_UNIT_DATA); + auto unit_data = _mass->at(MASS_UNIT_DATA); if(!unit_data) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename); _state = State::Invalid; @@ -130,7 +135,7 @@ void Mass::refreshValues() { } LOG_INFO("Reading the M.A.S.S. name."); - auto name_prop = unit_data->at(MASS_NAME); + auto name_prop = unit_data->at(MASS_NAME); if(!name_prop) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_NAME, _filename); @@ -217,7 +222,7 @@ void Mass::refreshValues() { } LOG_INFO("Getting the associated account."); - auto account_prop = _mass->at("Account"_s); + auto account_prop = _mass->at("Account"_s); if(!account_prop) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_ACCOUNT, _filename); _state = State::Invalid; @@ -229,19 +234,22 @@ void Mass::refreshValues() { _state = State::Valid; } -auto Mass::filename() -> Containers::StringView { +Containers::StringView +Mass::filename() { return _filename; } -auto Mass::name() -> Containers::StringView { +Containers::StringView +Mass::name() { CORRADE_INTERNAL_ASSERT(_name); return *_name; } -auto Mass::setName(Containers::StringView new_name) -> bool { +bool +Mass::setName(Containers::StringView new_name) { _name = {new_name}; - auto unit_data = _mass->at("UnitData"_s); + auto unit_data = _mass->at("UnitData"_s); if(!unit_data) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename); @@ -249,7 +257,7 @@ auto Mass::setName(Containers::StringView new_name) -> bool { return false; } - auto name_prop = unit_data->at("Name_45_A037C5D54E53456407BDF091344529BB"_s); + auto name_prop = unit_data->at("Name_45_A037C5D54E53456407BDF091344529BB"_s); if(!name_prop) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_NAME, _filename); @@ -267,19 +275,22 @@ auto Mass::setName(Containers::StringView new_name) -> bool { return true; } -auto Mass::state() -> State { +Mass::State +Mass::state() { return _state; } -auto Mass::dirty() const -> bool { +bool Mass::dirty() const { return _dirty; } -void Mass::setDirty(bool dirty) { +void +Mass::setDirty(bool dirty) { _dirty = dirty; } -void Mass::getTuning() { +void +Mass::getTuning() { getTuningCategory(MASS_ENGINE, _tuning.engineId, MASS_GEARS, _tuning.gearIds); if(_state == State::Invalid) { @@ -299,38 +310,46 @@ void Mass::getTuning() { } } -auto Mass::engine() -> Int& { +std::int32_t& +Mass::engine() { return _tuning.engineId; } -auto Mass::gears() -> Containers::ArrayView { +Containers::ArrayView +Mass::gears() { return _tuning.gearIds; } -auto Mass::os() -> Int& { +std::int32_t& +Mass::os() { return _tuning.osId; } -auto Mass::modules() -> Containers::ArrayView { +Containers::ArrayView +Mass::modules() { return _tuning.moduleIds; } -auto Mass::architecture() -> Int& { +std::int32_t& +Mass::architecture() { return _tuning.archId; } -auto Mass::techs() -> Containers::ArrayView { +Containers::ArrayView +Mass::techs() { return _tuning.techIds; } -auto Mass::account() -> Containers::StringView { +Containers::StringView +Mass::account() { return _account; } -auto Mass::updateAccount(Containers::StringView new_account) -> bool { +bool +Mass::updateAccount(Containers::StringView new_account) { _account = new_account; - auto account = _mass->at(MASS_ACCOUNT); + auto account = _mass->at(MASS_ACCOUNT); if(!account) { _lastError = "Couldn't find the " MASS_ACCOUNT " property."_s; _state = State::Invalid; @@ -347,12 +366,14 @@ auto Mass::updateAccount(Containers::StringView new_account) -> bool { return true; } -void Mass::getTuningCategory(Containers::StringView big_node_prop_name, Int& big_node_id, - Containers::StringView small_nodes_prop_name, Containers::ArrayView small_nodes_ids) +void +Mass::getTuningCategory(Containers::StringView big_node_prop_name, std::int32_t& big_node_id, + Containers::StringView small_nodes_prop_name, + Containers::ArrayView small_nodes_ids) { LOG_INFO_FORMAT("Getting tuning data ({}, {}).", big_node_prop_name, small_nodes_prop_name); - auto node_id = _mass->at(big_node_prop_name); + auto node_id = _mass->at(big_node_prop_name); if(!node_id) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", big_node_prop_name, _filename); _state = State::Invalid; @@ -360,7 +381,7 @@ void Mass::getTuningCategory(Containers::StringView big_node_prop_name, Int& big } big_node_id = node_id->value; - auto node_ids = _mass->at(small_nodes_prop_name); + auto node_ids = _mass->at(small_nodes_prop_name); if(!node_ids) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", small_nodes_prop_name, _filename); _state = State::Invalid; @@ -374,9 +395,11 @@ void Mass::getTuningCategory(Containers::StringView big_node_prop_name, Int& big return; } - for(UnsignedInt i = 0; i < small_nodes_ids.size(); i++) { - auto small_node_id = node_ids->at(i); + for(std::uint32_t i = 0; i < small_nodes_ids.size(); i++) { + auto small_node_id = node_ids->at(i); CORRADE_INTERNAL_ASSERT(small_node_id); small_nodes_ids[i] = small_node_id->value; } } + +} diff --git a/src/Mass/Mass.h b/src/GameObjects/Mass.h similarity index 68% rename from src/Mass/Mass.h rename to src/GameObjects/Mass.h index 733c07f..13edb42 100644 --- a/src/Mass/Mass.h +++ b/src/GameObjects/Mass.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -36,19 +36,16 @@ #include "WeaponPart.h" #include "Weapon.h" -#include "../UESaveFile/UESaveFile.h" +#include "../Gvas/File.h" +#include "../Gvas/Types/Types.h" using namespace Corrade; using namespace Magnum; -struct ArrayProperty; +namespace mbst::GameObjects { class Mass { public: - enum class State : UnsignedByte { - Empty, Invalid, Valid - }; - explicit Mass(Containers::StringView path); Mass(const Mass&) = delete; @@ -66,101 +63,106 @@ class Mass { auto filename() -> Containers::StringView; auto name() -> Containers::StringView; - auto setName(Containers::StringView new_name) -> bool; + bool setName(Containers::StringView new_name); + + enum class State: std::uint8_t { + Empty, Invalid, Valid + }; auto state() -> State; - auto dirty() const -> bool; + bool dirty() const; void setDirty(bool dirty = true); auto jointSliders() -> Joints&; void getJointSliders(); - auto writeJointSliders() -> bool; + bool writeJointSliders(); - auto frameStyles() -> Containers::ArrayView; + auto frameStyles() -> Containers::ArrayView; void getFrameStyles(); - auto writeFrameStyles() -> bool; + bool writeFrameStyles(); auto eyeFlareColour() -> Color4&; void getEyeFlareColour(); - auto writeEyeFlareColour() -> bool; + bool writeEyeFlareColour(); auto frameCustomStyles() -> Containers::ArrayView; void getFrameCustomStyles(); - auto writeFrameCustomStyle(UnsignedLong index) -> bool; + bool writeFrameCustomStyle(std::size_t index); auto armourParts() -> Containers::ArrayView; void getArmourParts(); - auto writeArmourPart(ArmourSlot slot) -> bool; + bool writeArmourPart(ArmourPart::Slot slot); auto bulletLauncherAttachmentStyle() -> BulletLauncherAttachmentStyle&; auto bulletLauncherAttachments() -> Containers::ArrayView; void getBulletLauncherAttachments(); - auto writeBulletLauncherAttachments() -> bool; + bool writeBulletLauncherAttachments(); auto armourCustomStyles() -> Containers::ArrayView; void getArmourCustomStyles(); - auto writeArmourCustomStyle(UnsignedLong index) -> bool; + bool writeArmourCustomStyle(std::size_t index); auto meleeWeapons() -> Containers::ArrayView; void getMeleeWeapons(); - auto writeMeleeWeapons() -> bool; + bool writeMeleeWeapons(); auto shields() -> Containers::ArrayView; void getShields(); - auto writeShields() -> bool; + bool writeShields(); auto bulletShooters() -> Containers::ArrayView; void getBulletShooters(); - auto writeBulletShooters() -> bool; + bool writeBulletShooters(); auto energyShooters() -> Containers::ArrayView; void getEnergyShooters(); - auto writeEnergyShooters() -> bool; + bool writeEnergyShooters(); auto bulletLaunchers() -> Containers::ArrayView; void getBulletLaunchers(); - auto writeBulletLaunchers() -> bool; + bool writeBulletLaunchers(); auto energyLaunchers() -> Containers::ArrayView; void getEnergyLaunchers(); - auto writeEnergyLaunchers() -> bool; + bool writeEnergyLaunchers(); auto globalStyles() -> Containers::ArrayView; void getGlobalStyles(); - auto writeGlobalStyle(UnsignedLong index) -> bool; + bool writeGlobalStyle(std::size_t index); void getTuning(); - auto engine() -> Int&; - auto gears() -> Containers::ArrayView; + auto engine() -> std::int32_t&; + auto gears() -> Containers::ArrayView; - auto os() -> Int&; - auto modules() -> Containers::ArrayView; + auto os() -> std::int32_t&; + auto modules() -> Containers::ArrayView; - auto architecture() -> Int&; - auto techs() -> Containers::ArrayView; + auto architecture() -> std::int32_t&; + auto techs() -> Containers::ArrayView; auto account() -> Containers::StringView; - auto updateAccount(Containers::StringView new_account) -> bool; + bool updateAccount(Containers::StringView new_account); private: - void getCustomStyles(Containers::ArrayView styles, ArrayProperty* style_array); - auto writeCustomStyle(const CustomStyle& style, UnsignedLong index, ArrayProperty* style_array) -> bool; + void getCustomStyles(Containers::ArrayView styles, Gvas::Types::ArrayProperty* style_array); + bool writeCustomStyle(const CustomStyle& style, std::size_t index, Gvas::Types::ArrayProperty* style_array); - void getDecals(Containers::ArrayView decals, ArrayProperty* decal_array); - void writeDecals(Containers::ArrayView decals, ArrayProperty* decal_array); + void getDecals(Containers::ArrayView decals, Gvas::Types::ArrayProperty* decal_array); + void writeDecals(Containers::ArrayView decals, Gvas::Types::ArrayProperty* decal_array); - void getAccessories(Containers::ArrayView accessories, ArrayProperty* accessory_array); - void writeAccessories(Containers::ArrayView accessories, ArrayProperty* accs_array); + void getAccessories(Containers::ArrayView accessories, Gvas::Types::ArrayProperty* accessory_array); + void writeAccessories(Containers::ArrayView accessories, Gvas::Types::ArrayProperty* accs_array); void getWeaponType(Containers::StringView prop_name, Containers::ArrayView weapon_array); - auto writeWeaponType(Containers::StringView prop_name, Containers::ArrayView weapon_array) -> bool; + bool writeWeaponType(Containers::StringView prop_name, Containers::ArrayView weapon_array); - void getTuningCategory(Containers::StringView big_node_prop_name, Int& big_node_id, - Containers::StringView small_nodes_prop_name, Containers::ArrayView small_nodes_ids); + void getTuningCategory(Containers::StringView big_node_prop_name, std::int32_t& big_node_id, + Containers::StringView small_nodes_prop_name, + Containers::ArrayView small_nodes_ids); - Containers::Optional _mass; + Containers::Optional _mass; Containers::String _lastError; @@ -174,7 +176,7 @@ class Mass { struct { Joints joints{}; - Containers::StaticArray<4, Int> styles{ValueInit}; + Containers::StaticArray<4, std::int32_t> styles{ValueInit}; Color4 eyeFlare{0.0f}; Containers::StaticArray<16, CustomStyle> customStyles; } _frame; @@ -198,15 +200,17 @@ class Mass { Containers::Array _globalStyles; struct { - Int engineId; - Containers::StaticArray<7, Int> gearIds; + std::int32_t engineId; + Containers::StaticArray<7, std::int32_t> gearIds; - Int osId; - Containers::StaticArray<7, Int> moduleIds; + std::int32_t osId; + Containers::StaticArray<7, std::int32_t> moduleIds; - Int archId; - Containers::StaticArray<7, Int> techIds; + std::int32_t archId; + Containers::StaticArray<7, std::int32_t> techIds; } _tuning; Containers::String _account; }; + +} diff --git a/src/Mass/Mass_Armour.cpp b/src/GameObjects/Mass_Armour.cpp similarity index 60% rename from src/Mass/Mass_Armour.cpp rename to src/GameObjects/Mass_Armour.cpp index 8af925a..6a27f0a 100644 --- a/src/Mass/Mass_Armour.cpp +++ b/src/GameObjects/Mass_Armour.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -18,32 +18,36 @@ #include "PropertyNames.h" #include "../Logger/Logger.h" -#include "../UESaveFile/Types/ArrayProperty.h" -#include "../UESaveFile/Types/ByteProperty.h" -#include "../UESaveFile/Types/GenericStructProperty.h" -#include "../UESaveFile/Types/IntProperty.h" -#include "../UESaveFile/Types/StringProperty.h" -#include "../UESaveFile/Types/VectorStructProperty.h" +#include "../Gvas/Types/ArrayProperty.h" +#include "../Gvas/Types/ByteProperty.h" +#include "../Gvas/Types/GenericStructProperty.h" +#include "../Gvas/Types/IntProperty.h" +#include "../Gvas/Types/StringProperty.h" +#include "../Gvas/Types/VectorStructProperty.h" #include "Mass.h" using namespace Containers::Literals; -auto Mass::armourParts() -> Containers::ArrayView { +namespace mbst::GameObjects { + +Containers::ArrayView +Mass::armourParts() { return _armour.parts; } -void Mass::getArmourParts() { +void +Mass::getArmourParts() { LOG_INFO("Getting armour parts."); - auto unit_data = _mass->at(MASS_UNIT_DATA); + auto unit_data = _mass->at(MASS_UNIT_DATA); if(!unit_data) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename); _state = State::Invalid; return; } - auto armour_array = unit_data->at(MASS_ARMOUR_PARTS); + auto armour_array = unit_data->at(MASS_ARMOUR_PARTS); if(!armour_array) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_ARMOUR_PARTS, _filename); _state = State::Invalid; @@ -57,12 +61,12 @@ void Mass::getArmourParts() { return; } - for(UnsignedInt i = 0; i < armour_array->items.size(); i++) { - auto part_prop = armour_array->at(i); + for(std::uint32_t i = 0; i < armour_array->items.size(); i++) { + auto part_prop = armour_array->at(i); auto& part = _armour.parts[i]; - auto& armour_slot = part_prop->at(MASS_ARMOUR_SLOT)->enumValue; - #define c(enumerator, strenum, name) if(armour_slot == (strenum)) { part.slot = ArmourSlot::enumerator; } else + auto& armour_slot = part_prop->at(MASS_ARMOUR_SLOT)->enumValue; + #define c(enumerator, strenum, name) if(armour_slot == (strenum)) { part.slot = ArmourPart::Slot::enumerator; } else #include "../Maps/ArmourSlots.hpp" #undef c { @@ -71,9 +75,9 @@ void Mass::getArmourParts() { return; } - part.id = part_prop->at(MASS_ARMOUR_ID)->value; + part.id = part_prop->at(MASS_ARMOUR_ID)->value; - auto part_styles = part_prop->at(MASS_ARMOUR_STYLES); + auto part_styles = part_prop->at(MASS_ARMOUR_STYLES); if(!part_styles) { LOG_ERROR_FORMAT("Part styles not found for part number {}.", i); _state = State::Invalid; @@ -87,11 +91,11 @@ void Mass::getArmourParts() { return; } - for(UnsignedInt j = 0; j < part_styles->items.size(); j++) { - part.styles[j] = part_styles->at(j)->value; + for(std::uint32_t j = 0; j < part_styles->items.size(); j++) { + part.styles[j] = part_styles->at(j)->value; } - auto decals_array = part_prop->at(MASS_ARMOUR_DECALS); + auto decals_array = part_prop->at(MASS_ARMOUR_DECALS); if(!decals_array) { LOG_ERROR_FORMAT("Part decals not found for part number {}.", i); _state = State::Invalid; @@ -102,7 +106,7 @@ void Mass::getArmourParts() { getDecals(part.decals, decals_array); - auto accs_array = part_prop->at(MASS_ARMOUR_ACCESSORIES); + auto accs_array = part_prop->at(MASS_ARMOUR_ACCESSORIES); if(!accs_array) { LOG_WARNING_FORMAT("Part accessories not found for part number {}.", i); part.accessories = Containers::Array{}; @@ -117,12 +121,13 @@ void Mass::getArmourParts() { } } -auto Mass::writeArmourPart(ArmourSlot slot) -> bool { +bool +Mass::writeArmourPart(ArmourPart::Slot slot) { LOG_INFO_FORMAT("Writing armour part in slot {}.", static_cast(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(MASS_UNIT_DATA); + auto unit_data = _mass->at(MASS_UNIT_DATA); if(!unit_data) { _lastError = "Couldn't find the unit data in " + _filename + "."; LOG_ERROR(_lastError); @@ -130,7 +135,7 @@ auto Mass::writeArmourPart(ArmourSlot slot) -> bool { return false; } - auto armour_array = unit_data->at(MASS_ARMOUR_PARTS); + auto armour_array = unit_data->at(MASS_ARMOUR_PARTS); if(!armour_array) { _lastError = "Couldn't find the armour part array in " + _filename + "."; LOG_ERROR(_lastError); @@ -140,18 +145,18 @@ auto Mass::writeArmourPart(ArmourSlot slot) -> bool { Containers::StringView slot_str = nullptr; switch(slot) { - #define c(enumerator, strenum, name) case ArmourSlot::enumerator: \ + #define c(enumerator, strenum, name) case ArmourPart::Slot::enumerator: \ slot_str = strenum; \ break; #include "../Maps/ArmourSlots.hpp" #undef c } - GenericStructProperty* part_prop = nullptr; + Gvas::Types::GenericStructProperty* part_prop = nullptr; - for(UnsignedInt i = 0; i < armour_array->items.size(); i++) { - part_prop = armour_array->at(i); - if(slot_str == part_prop->at(MASS_ARMOUR_SLOT)->enumValue) { + for(std::uint32_t i = 0; i < armour_array->items.size(); i++) { + part_prop = armour_array->at(i); + if(slot_str == part_prop->at(MASS_ARMOUR_SLOT)->enumValue) { break; } else { @@ -162,7 +167,7 @@ auto Mass::writeArmourPart(ArmourSlot slot) -> bool { if(!part_prop) { auto prefix = "Couldn't find the armour part for slot "_s; switch(slot) { - #define c(enumerator, strenum, name) case ArmourSlot::enumerator: \ + #define c(enumerator, strenum, name) case ArmourPart::Slot::enumerator: \ _lastError = prefix + "ArmourSlot::" #enumerator "."_s; \ break; #include "../Maps/ArmourSlots.hpp" @@ -172,18 +177,18 @@ auto Mass::writeArmourPart(ArmourSlot slot) -> bool { return false; } - part_prop->at(MASS_ARMOUR_ID)->value = part.id; + part_prop->at(MASS_ARMOUR_ID)->value = part.id; - auto part_styles = part_prop->at(MASS_ARMOUR_STYLES); - for(UnsignedInt i = 0; i < part.styles.size(); i++) { - part_styles->at(i)->value = part.styles[i]; + auto part_styles = part_prop->at(MASS_ARMOUR_STYLES); + for(std::uint32_t i = 0; i < part.styles.size(); i++) { + part_styles->at(i)->value = part.styles[i]; } - auto decals_array = part_prop->at(MASS_ARMOUR_DECALS); + auto decals_array = part_prop->at(MASS_ARMOUR_DECALS); writeDecals(part.decals, decals_array); - if(part.accessories.size() != 0) { - auto accs_array = part_prop->at(MASS_ARMOUR_ACCESSORIES); + if(!part.accessories.isEmpty()) { + auto accs_array = part_prop->at(MASS_ARMOUR_ACCESSORIES); writeAccessories(part.accessories, accs_array); } @@ -195,26 +200,29 @@ auto Mass::writeArmourPart(ArmourSlot slot) -> bool { return true; } -auto Mass::bulletLauncherAttachmentStyle() -> BulletLauncherAttachmentStyle& { +BulletLauncherAttachmentStyle& +Mass::bulletLauncherAttachmentStyle() { return _armour.blAttachmentStyle; } -auto Mass::bulletLauncherAttachments() -> Containers::ArrayView { +Containers::ArrayView +Mass::bulletLauncherAttachments() { return _armour.blAttachment; } -void Mass::getBulletLauncherAttachments() { +void +Mass::getBulletLauncherAttachments() { LOG_INFO("Getting the bullet launcher attachment data."); - auto unit_data = _mass->at(MASS_UNIT_DATA); + auto unit_data = _mass->at(MASS_UNIT_DATA); if(!unit_data) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename); _state = State::Invalid; return; } - auto attach_style_prop = unit_data->at(MASS_BL_ATTACHMENT_STYLE); - auto attach_array = unit_data->at(MASS_BL_ATTACHMENTS); + auto attach_style_prop = unit_data->at(MASS_BL_ATTACHMENT_STYLE); + auto attach_array = unit_data->at(MASS_BL_ATTACHMENTS); if(!attach_style_prop && !attach_array) { LOG_WARNING_FORMAT("No bullet launcher attachment data found in {}.", _filename); @@ -232,12 +240,12 @@ void Mass::getBulletLauncherAttachments() { if(attach_array->items.size() == _weapons.bulletLaunchers.size() && attach_array->items.size() == _armour.blAttachment.size()) { - for(UnsignedInt i = 0; i < attach_array->items.size(); i++) { - auto attachment_prop = attach_array->at(i); + for(std::uint32_t i = 0; i < attach_array->items.size(); i++) { + auto attachment_prop = attach_array->at(i); auto& attachment = _armour.blAttachment[i]; - Containers::StringView socket = attachment_prop->at(MASS_BL_ATTACHMENT_SOCKET)->value; - #define c(enumerator, strenum, name) if(socket == (strenum)) { attachment.socket = BulletLauncherSocket::enumerator; } else + Containers::StringView socket = attachment_prop->at(MASS_BL_ATTACHMENT_SOCKET)->value; + #define c(enumerator, strenum, name) if(socket == (strenum)) { attachment.socket = BulletLauncherAttachment::Socket::enumerator; } else #include "../Maps/BulletLauncherSockets.hpp" #undef c { @@ -246,16 +254,16 @@ void Mass::getBulletLauncherAttachments() { return; } - auto rel_loc_prop = attachment_prop->at(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(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(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(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(MASS_BL_ATTACHMENT_RELSCALE); - attachment.relativeScale = Vector3{rel_scale_prop->x, rel_scale_prop->y, rel_scale_prop->z}; + auto rel_loc_prop = attachment_prop->at(MASS_BL_ATTACHMENT_RELLOC); + attachment.relativeLocation = rel_loc_prop->vector; + auto off_loc_prop = attachment_prop->at(MASS_BL_ATTACHMENT_OFFLOC); + attachment.offsetLocation = off_loc_prop->vector; + auto rel_rot_prop = attachment_prop->at(MASS_BL_ATTACHMENT_RELROT); + attachment.relativeRotation = rel_rot_prop->vector; + auto off_rot_prop = attachment_prop->at(MASS_BL_ATTACHMENT_OFFROT); + attachment.offsetRotation = off_rot_prop->vector; + auto rel_scale_prop = attachment_prop->at(MASS_BL_ATTACHMENT_RELSCALE); + attachment.relativeScale = rel_scale_prop->vector; } } @@ -274,10 +282,11 @@ void Mass::getBulletLauncherAttachments() { } } -auto Mass::writeBulletLauncherAttachments() -> bool { +bool +Mass::writeBulletLauncherAttachments() { LOG_INFO("Writing bullet launcher attachments."); - auto unit_data = _mass->at(MASS_UNIT_DATA); + auto unit_data = _mass->at(MASS_UNIT_DATA); if(!unit_data) { _lastError = "No unit data in " + _filename; LOG_ERROR(_lastError); @@ -285,8 +294,8 @@ auto Mass::writeBulletLauncherAttachments() -> bool { return false; } - auto attach_style_prop = unit_data->at(MASS_BL_ATTACHMENT_STYLE); - auto attach_array = unit_data->at(MASS_BL_ATTACHMENTS); + auto attach_style_prop = unit_data->at(MASS_BL_ATTACHMENT_STYLE); + auto attach_array = unit_data->at(MASS_BL_ATTACHMENTS); if(!attach_style_prop && !attach_array) { _lastError = "No attachment properties to write to in " + _filename; @@ -306,13 +315,13 @@ auto Mass::writeBulletLauncherAttachments() -> bool { if(attach_array->items.size() == _weapons.bulletLaunchers.size() && attach_array->items.size() == _armour.blAttachment.size()) { - for(UnsignedInt i = 0; i < attach_array->items.size(); i++) { - auto attachment_prop = attach_array->at(i); + for(std::uint32_t i = 0; i < attach_array->items.size(); i++) { + auto attachment_prop = attach_array->at(i); auto& attachment = _armour.blAttachment[i]; - auto& socket = attachment_prop->at(MASS_BL_ATTACHMENT_SOCKET)->value; + auto& socket = attachment_prop->at(MASS_BL_ATTACHMENT_SOCKET)->value; switch(attachment.socket) { - #define c(enumerator, strenum, name) case BulletLauncherSocket::enumerator: socket = strenum; break; + #define c(enumerator, strenum, name) case BulletLauncherAttachment::Socket::enumerator: socket = strenum; break; #include "../Maps/BulletLauncherSockets.hpp" #undef c default: @@ -321,35 +330,25 @@ auto Mass::writeBulletLauncherAttachments() -> bool { return false; } - auto rel_loc_prop = attachment_prop->at(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(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(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(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(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(); + auto rel_loc_prop = attachment_prop->at(MASS_BL_ATTACHMENT_RELLOC); + rel_loc_prop->vector = attachment.relativeLocation; + auto off_loc_prop = attachment_prop->at(MASS_BL_ATTACHMENT_OFFLOC); + off_loc_prop->vector = attachment.offsetLocation; + auto rel_rot_prop = attachment_prop->at(MASS_BL_ATTACHMENT_RELROT); + rel_rot_prop->vector = attachment.relativeRotation; + auto off_rot_prop = attachment_prop->at(MASS_BL_ATTACHMENT_OFFROT); + off_rot_prop->vector = attachment.offsetRotation; + auto rel_scale_prop = attachment_prop->at(MASS_BL_ATTACHMENT_RELSCALE); + rel_scale_prop->vector = attachment.relativeScale; } } if(!attach_style_prop) { - attach_style_prop = new ByteProperty; + attach_style_prop = new Gvas::Types::ByteProperty; attach_style_prop->name.emplace(MASS_BL_ATTACHMENT_STYLE); attach_style_prop->enumType = "enuBLAttachmentStyle"_s; - ByteProperty::ptr prop{attach_style_prop}; - arrayAppend(unit_data->properties, std::move(prop)); + Gvas::Types::ByteProperty::ptr prop{attach_style_prop}; + arrayAppend(unit_data->properties, Utility::move(prop)); } auto& attach_style = attach_style_prop->enumValue; @@ -373,21 +372,23 @@ auto Mass::writeBulletLauncherAttachments() -> bool { return true; } -auto Mass::armourCustomStyles() -> Containers::ArrayView { +Containers::ArrayView +Mass::armourCustomStyles() { return _armour.customStyles; } -void Mass::getArmourCustomStyles() { +void +Mass::getArmourCustomStyles() { LOG_INFO("Getting the custom armour styles."); - auto unit_data = _mass->at(MASS_UNIT_DATA); + auto unit_data = _mass->at(MASS_UNIT_DATA); if(!unit_data) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename); _state = State::Invalid; return; } - auto armour_styles = unit_data->at(MASS_CUSTOM_ARMOUR_STYLES); + auto armour_styles = unit_data->at(MASS_CUSTOM_ARMOUR_STYLES); if(!armour_styles) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_CUSTOM_ARMOUR_STYLES, _filename); _state = State::Invalid; @@ -402,9 +403,14 @@ void Mass::getArmourCustomStyles() { } getCustomStyles(_armour.customStyles, armour_styles); + + for(auto& style : _armour.customStyles) { + style.type = CustomStyle::Type::Armour; + } } -auto Mass::writeArmourCustomStyle(UnsignedLong index) -> bool { +bool +Mass::writeArmourCustomStyle(std::size_t index) { LOG_INFO_FORMAT("Writing custom armour style {}.", index); if(index > _armour.customStyles.size()) { @@ -413,7 +419,7 @@ auto Mass::writeArmourCustomStyle(UnsignedLong index) -> bool { return false; } - auto unit_data = _mass->at(MASS_UNIT_DATA); + auto unit_data = _mass->at(MASS_UNIT_DATA); if(!unit_data) { _lastError = "Couldn't find unit data in "_s + _filename; LOG_ERROR(_lastError); @@ -421,7 +427,7 @@ auto Mass::writeArmourCustomStyle(UnsignedLong index) -> bool { return false; } - auto armour_styles = unit_data->at(MASS_CUSTOM_ARMOUR_STYLES); + auto armour_styles = unit_data->at(MASS_CUSTOM_ARMOUR_STYLES); if(!armour_styles) { _lastError = "Couldn't find armour custom styles in "_s + _filename; LOG_ERROR(_lastError); @@ -431,3 +437,5 @@ auto Mass::writeArmourCustomStyle(UnsignedLong index) -> bool { return writeCustomStyle(_armour.customStyles[index], index, armour_styles); } + +} diff --git a/src/GameObjects/Mass_DecalsAccessories.cpp b/src/GameObjects/Mass_DecalsAccessories.cpp new file mode 100644 index 0000000..02ed1b2 --- /dev/null +++ b/src/GameObjects/Mass_DecalsAccessories.cpp @@ -0,0 +1,139 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include "PropertyNames.h" +#include "../Gvas/Types/ArrayProperty.h" +#include "../Gvas/Types/BoolProperty.h" +#include "../Gvas/Types/ColourStructProperty.h" +#include "../Gvas/Types/FloatProperty.h" +#include "../Gvas/Types/GenericStructProperty.h" +#include "../Gvas/Types/RotatorStructProperty.h" +#include "../Gvas/Types/VectorStructProperty.h" +#include "../Gvas/Types/Vector2DStructProperty.h" +#include "../Gvas/Types/IntProperty.h" + +#include "Mass.h" + +using namespace Containers::Literals; + +namespace mbst::GameObjects { + +void +Mass::getDecals(Containers::ArrayView decals, Gvas::Types::ArrayProperty* decal_array) { + for(std::uint32_t i = 0; i < decal_array->items.size(); i++) { + auto decal_prop = decal_array->at(i); + CORRADE_INTERNAL_ASSERT(decal_prop); + auto& decal = decals[i]; + + decal.id = decal_prop->at(MASS_DECAL_ID)->value; + auto colour_prop = decal_prop->at(MASS_DECAL_COLOUR); + decal.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; + auto pos_prop = decal_prop->at(MASS_DECAL_POSITION); + decal.position = pos_prop->vector; + auto u_prop = decal_prop->at(MASS_DECAL_UAXIS); + decal.uAxis = u_prop->vector; + auto v_prop = decal_prop->at(MASS_DECAL_VAXIS); + decal.vAxis = v_prop->vector; + auto offset_prop = decal_prop->at(MASS_DECAL_OFFSET); + decal.offset = offset_prop->vector; + decal.scale = decal_prop->at(MASS_DECAL_SCALE)->value; + decal.rotation = decal_prop->at(MASS_DECAL_ROTATION)->value; + decal.flip = decal_prop->at(MASS_DECAL_FLIP)->value; + decal.wrap = decal_prop->at(MASS_DECAL_WRAP)->value; + } +} + +void +Mass::writeDecals(Containers::ArrayView decals, Gvas::Types::ArrayProperty* decal_array) { + for(std::uint32_t i = 0; i < decal_array->items.size(); i++) { + auto decal_prop = decal_array->at(i); + CORRADE_INTERNAL_ASSERT(decal_prop); + auto& decal = decals[i]; + + decal_prop->at(MASS_DECAL_ID)->value = decal.id; + auto colour_prop = decal_prop->at(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(MASS_DECAL_POSITION); + pos_prop->vector = decal.position; + auto u_prop = decal_prop->at(MASS_DECAL_UAXIS); + u_prop->vector = decal.uAxis; + auto v_prop = decal_prop->at(MASS_DECAL_VAXIS); + v_prop->vector = decal.vAxis; + auto offset_prop = decal_prop->at(MASS_DECAL_OFFSET); + offset_prop->vector = decal.offset; + decal_prop->at(MASS_DECAL_SCALE)->value = decal.scale; + decal_prop->at(MASS_DECAL_ROTATION)->value = decal.rotation; + decal_prop->at(MASS_DECAL_FLIP)->value = decal.flip; + decal_prop->at(MASS_DECAL_WRAP)->value = decal.wrap; + } +} + +void +Mass::getAccessories(Containers::ArrayView accessories, Gvas::Types::ArrayProperty* accessory_array) { + for(std::uint32_t i = 0; i < accessory_array->items.size(); i++) { + auto acc_prop = accessory_array->at(i); + CORRADE_INTERNAL_ASSERT(acc_prop); + auto& accessory = accessories[i]; + + accessory.attachIndex = acc_prop->at(MASS_ACCESSORY_ATTACH_INDEX)->value; + accessory.id = acc_prop->at(MASS_ACCESSORY_ID)->value; + auto acc_styles = acc_prop->at(MASS_ACCESSORY_STYLES); + for(std::uint32_t j = 0; j < acc_styles->items.size(); j++) { + accessory.styles[j] = acc_styles->at(j)->value; + } + auto rel_pos_prop = acc_prop->at(MASS_ACCESSORY_RELPOS); + accessory.relativePosition = rel_pos_prop->vector; + auto rel_pos_offset_prop = acc_prop->at(MASS_ACCESSORY_OFFPOS); + accessory.relativePositionOffset = rel_pos_offset_prop->vector; + auto rel_rot_prop = acc_prop->at(MASS_ACCESSORY_RELROT); + accessory.relativeRotation = rel_rot_prop->vector; + auto rel_rot_offset_prop = acc_prop->at(MASS_ACCESSORY_OFFROT); + accessory.relativeRotationOffset = rel_rot_offset_prop->vector; + auto local_scale_prop = acc_prop->at(MASS_ACCESSORY_SCALE); + accessory.localScale = local_scale_prop->vector; + } +} + +void +Mass::writeAccessories(Containers::ArrayView accessories, Gvas::Types::ArrayProperty* accs_array) { + for(std::uint32_t i = 0; i < accs_array->items.size(); i++) { + auto acc_prop = accs_array->at(i); + CORRADE_INTERNAL_ASSERT(acc_prop); + auto& accessory = accessories[i]; + + acc_prop->at(MASS_ACCESSORY_ATTACH_INDEX)->value = accessory.attachIndex; + acc_prop->at(MASS_ACCESSORY_ID)->value = accessory.id; + auto acc_styles = acc_prop->at(MASS_ACCESSORY_STYLES); + for(std::uint32_t j = 0; j < acc_styles->items.size(); j++) { + acc_styles->at(j)->value = accessory.styles[j]; + } + auto rel_pos_prop = acc_prop->at(MASS_ACCESSORY_RELPOS); + rel_pos_prop->vector = accessory.relativePosition; + auto rel_pos_offset_prop = acc_prop->at(MASS_ACCESSORY_OFFPOS); + rel_pos_offset_prop->vector = accessory.relativePositionOffset; + auto rel_rot_prop = acc_prop->at(MASS_ACCESSORY_RELROT); + rel_rot_prop->vector = accessory.relativeRotation; + auto rel_rot_offset_prop = acc_prop->at(MASS_ACCESSORY_OFFROT); + rel_rot_offset_prop->vector = accessory.relativeRotationOffset; + auto local_scale_prop = acc_prop->at(MASS_ACCESSORY_SCALE); + local_scale_prop->vector = accessory.localScale; + } +} + +} diff --git a/src/Mass/Mass_Frame.cpp b/src/GameObjects/Mass_Frame.cpp similarity index 62% rename from src/Mass/Mass_Frame.cpp rename to src/GameObjects/Mass_Frame.cpp index c585a19..cae1676 100644 --- a/src/Mass/Mass_Frame.cpp +++ b/src/GameObjects/Mass_Frame.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,59 +16,64 @@ #include "PropertyNames.h" #include "../Logger/Logger.h" -#include "../UESaveFile/Types/ArrayProperty.h" -#include "../UESaveFile/Types/ColourStructProperty.h" -#include "../UESaveFile/Types/FloatProperty.h" -#include "../UESaveFile/Types/GenericStructProperty.h" -#include "../UESaveFile/Types/IntProperty.h" +#include "../Gvas/Types/ArrayProperty.h" +#include "../Gvas/Types/ColourStructProperty.h" +#include "../Gvas/Types/FloatProperty.h" +#include "../Gvas/Types/GenericStructProperty.h" +#include "../Gvas/Types/IntProperty.h" #include "Mass.h" using namespace Containers::Literals; -auto Mass::jointSliders() -> Joints& { +namespace mbst::GameObjects { + +Joints& +Mass::jointSliders() { return _frame.joints; } -void Mass::getJointSliders() { +void +Mass::getJointSliders() { LOG_INFO("Getting joint sliders."); - auto unit_data = _mass->at(MASS_UNIT_DATA); + auto unit_data = _mass->at(MASS_UNIT_DATA); if(!unit_data) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename); _state = State::Invalid; return; } - auto frame_prop = unit_data->at(MASS_FRAME); + auto frame_prop = unit_data->at(MASS_FRAME); if(!frame_prop) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_FRAME, _filename); _state = State::Invalid; return; } - auto length = frame_prop->at(MASS_JOINT_NECK); + auto length = frame_prop->at(MASS_JOINT_NECK); _frame.joints.neck = (length ? length->value : 0.0f); - length = frame_prop->at(MASS_JOINT_BODY); + length = frame_prop->at(MASS_JOINT_BODY); _frame.joints.body = (length ? length->value : 0.0f); - length = frame_prop->at(MASS_JOINT_SHOULDER); + length = frame_prop->at(MASS_JOINT_SHOULDER); _frame.joints.shoulders = (length ? length->value : 0.0f); - length = frame_prop->at(MASS_JOINT_HIP); + length = frame_prop->at(MASS_JOINT_HIP); _frame.joints.hips = (length ? length->value : 0.0f); - length = frame_prop->at(MASS_JOINT_ARM_UPPER); + length = frame_prop->at(MASS_JOINT_ARM_UPPER); _frame.joints.upperArms = (length ? length->value : 0.0f); - length = frame_prop->at(MASS_JOINT_ARM_LOWER); + length = frame_prop->at(MASS_JOINT_ARM_LOWER); _frame.joints.lowerArms = (length ? length->value : 0.0f); - length = frame_prop->at(MASS_JOINT_LEG_UPPER); + length = frame_prop->at(MASS_JOINT_LEG_UPPER); _frame.joints.upperLegs = (length ? length->value : 0.0f); - length = frame_prop->at(MASS_JOINT_LEG_LOWER); + length = frame_prop->at(MASS_JOINT_LEG_LOWER); _frame.joints.lowerLegs = (length ? length->value : 0.0f); } -auto Mass::writeJointSliders() -> bool { +bool +Mass::writeJointSliders() { LOG_INFO("Writing joint sliders"); - auto unit_data = _mass->at(MASS_UNIT_DATA); + auto unit_data = _mass->at(MASS_UNIT_DATA); if(!unit_data) { _lastError = "No unit data in "_s + _filename; LOG_ERROR(_lastError); @@ -76,7 +81,7 @@ auto Mass::writeJointSliders() -> bool { return false; } - auto frame_prop = unit_data->at(MASS_FRAME); + auto frame_prop = unit_data->at(MASS_FRAME); if(!frame_prop) { _lastError = "No frame data in "_s + _filename; @@ -85,93 +90,93 @@ auto Mass::writeJointSliders() -> bool { return false; } - Containers::Array temp; + Containers::Array temp; - auto length = frame_prop->atMove(MASS_JOINT_NECK); + auto length = frame_prop->atMove(MASS_JOINT_NECK); if(_frame.joints.neck != 0.0f) { if(!length) { length.emplace(); length->name.emplace(MASS_JOINT_NECK); } length->value = _frame.joints.neck; - arrayAppend(temp, std::move(length)); + arrayAppend(temp, Utility::move(length)); } - length = frame_prop->atMove(MASS_JOINT_BODY); + length = frame_prop->atMove(MASS_JOINT_BODY); if(_frame.joints.body != 0.0f) { if(!length) { length.emplace(); length->name.emplace(MASS_JOINT_BODY); } length->value = _frame.joints.body; - arrayAppend(temp, std::move(length)); + arrayAppend(temp, Utility::move(length)); } - length = frame_prop->atMove(MASS_JOINT_SHOULDER); + length = frame_prop->atMove(MASS_JOINT_SHOULDER); if(_frame.joints.shoulders != 0.0f) { if(!length) { length.emplace(); length->name.emplace(MASS_JOINT_SHOULDER); } length->value = _frame.joints.shoulders; - arrayAppend(temp, std::move(length)); + arrayAppend(temp, Utility::move(length)); } - length = frame_prop->atMove(MASS_JOINT_ARM_UPPER); + length = frame_prop->atMove(MASS_JOINT_ARM_UPPER); if(_frame.joints.upperArms != 0.0f) { if(!length) { length.emplace(); length->name.emplace(MASS_JOINT_ARM_UPPER); } length->value = _frame.joints.upperArms; - arrayAppend(temp, std::move(length)); + arrayAppend(temp, Utility::move(length)); } - length = frame_prop->atMove(MASS_JOINT_ARM_LOWER); + length = frame_prop->atMove(MASS_JOINT_ARM_LOWER); if(_frame.joints.lowerArms != 0.0f) { if(!length) { length.emplace(); length->name.emplace(MASS_JOINT_ARM_LOWER); } length->value = _frame.joints.lowerArms; - arrayAppend(temp, std::move(length)); + arrayAppend(temp, Utility::move(length)); } - length = frame_prop->atMove(MASS_JOINT_HIP); + length = frame_prop->atMove(MASS_JOINT_HIP); if(_frame.joints.hips != 0.0f) { if(!length) { length.emplace(); length->name.emplace(MASS_JOINT_HIP); } length->value = _frame.joints.hips; - arrayAppend(temp, std::move(length)); + arrayAppend(temp, Utility::move(length)); } - length = frame_prop->atMove(MASS_JOINT_LEG_UPPER); + length = frame_prop->atMove(MASS_JOINT_LEG_UPPER); if(_frame.joints.upperLegs != 0.0f) { if(!length) { length.emplace(); length->name.emplace(MASS_JOINT_LEG_UPPER); } length->value = _frame.joints.upperLegs; - arrayAppend(temp, std::move(length)); + arrayAppend(temp, Utility::move(length)); } - length = frame_prop->atMove(MASS_JOINT_LEG_LOWER); + length = frame_prop->atMove(MASS_JOINT_LEG_LOWER); if(_frame.joints.lowerLegs != 0.0f) { if(!length) { length.emplace(); length->name.emplace(MASS_JOINT_LEG_LOWER); } length->value = _frame.joints.lowerLegs; - arrayAppend(temp, std::move(length)); + arrayAppend(temp, Utility::move(length)); } - arrayAppend(temp, std::move(frame_prop->properties[frame_prop->properties.size() - 3])); - arrayAppend(temp, std::move(frame_prop->properties[frame_prop->properties.size() - 2])); - arrayAppend(temp, std::move(frame_prop->properties[frame_prop->properties.size() - 1])); + arrayAppend(temp, Utility::move(frame_prop->properties[frame_prop->properties.size() - 3])); + arrayAppend(temp, Utility::move(frame_prop->properties[frame_prop->properties.size() - 2])); + arrayAppend(temp, Utility::move(frame_prop->properties[frame_prop->properties.size() - 1])); - frame_prop->properties = std::move(temp); + frame_prop->properties = Utility::move(temp); if(!_mass->saveToFile()) { _lastError = _mass->lastError(); @@ -181,28 +186,30 @@ auto Mass::writeJointSliders() -> bool { return true; } -auto Mass::frameStyles() -> Containers::ArrayView { +Containers::ArrayView +Mass::frameStyles() { return _frame.styles; } -void Mass::getFrameStyles() { +void +Mass::getFrameStyles() { LOG_INFO("Getting frame styles."); - auto unit_data = _mass->at(MASS_UNIT_DATA); + auto unit_data = _mass->at(MASS_UNIT_DATA); if(!unit_data) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename); _state = State::Invalid; return; } - auto frame_prop = unit_data->at(MASS_FRAME); + auto frame_prop = unit_data->at(MASS_FRAME); if(!frame_prop) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_FRAME, _filename); _state = State::Invalid; return; } - auto frame_styles = frame_prop->at(MASS_FRAME_STYLES); + auto frame_styles = frame_prop->at(MASS_FRAME_STYLES); if(!frame_styles) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_FRAME_STYLES, _filename); _state = State::Invalid; @@ -216,15 +223,16 @@ void Mass::getFrameStyles() { return; } - for(UnsignedInt i = 0; i < frame_styles->items.size(); i++) { - _frame.styles[i] = frame_styles->at(i)->value; + for(std::uint32_t i = 0; i < frame_styles->items.size(); i++) { + _frame.styles[i] = frame_styles->at(i)->value; } } -auto Mass::writeFrameStyles() -> bool { +bool +Mass::writeFrameStyles() { LOG_INFO("Writing frame styles."); - auto unit_data = _mass->at(MASS_UNIT_DATA); + auto unit_data = _mass->at(MASS_UNIT_DATA); if(!unit_data) { _lastError = "No unit data in "_s + _filename; LOG_ERROR(_lastError); @@ -232,7 +240,7 @@ auto Mass::writeFrameStyles() -> bool { return false; } - auto frame = unit_data->at(MASS_FRAME); + auto frame = unit_data->at(MASS_FRAME); if(!frame) { _lastError = "No frame data in "_s + _filename; LOG_ERROR(_lastError); @@ -240,7 +248,7 @@ auto Mass::writeFrameStyles() -> bool { return false; } - auto frame_styles = frame->at(MASS_FRAME_STYLES); + auto frame_styles = frame->at(MASS_FRAME_STYLES); if(!frame_styles) { _lastError = "No frame styles in "_s + _filename; LOG_ERROR(_lastError); @@ -248,8 +256,8 @@ auto Mass::writeFrameStyles() -> bool { return false; } - for(UnsignedInt i = 0; i < frame_styles->items.size(); i++) { - frame_styles->at(i)->value = _frame.styles[i]; + for(std::uint32_t i = 0; i < frame_styles->items.size(); i++) { + frame_styles->at(i)->value = _frame.styles[i]; } if(!_mass->saveToFile()) { @@ -260,28 +268,30 @@ auto Mass::writeFrameStyles() -> bool { return true; } -auto Mass::eyeFlareColour() -> Color4& { +Color4& +Mass::eyeFlareColour() { return _frame.eyeFlare; } -void Mass::getEyeFlareColour() { +void +Mass::getEyeFlareColour() { LOG_INFO("Getting the eye flare colour."); - auto unit_data = _mass->at(MASS_UNIT_DATA); + auto unit_data = _mass->at(MASS_UNIT_DATA); if(!unit_data) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename); _state = State::Invalid; return; } - auto frame_prop = unit_data->at(MASS_FRAME); + auto frame_prop = unit_data->at(MASS_FRAME); if(!frame_prop) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_FRAME, _filename); _state = State::Invalid; return; } - auto eye_flare_prop = frame_prop->at(MASS_EYE_FLARE); + auto eye_flare_prop = frame_prop->at(MASS_EYE_FLARE); if(!eye_flare_prop) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_EYE_FLARE, _filename); _state = State::Invalid; @@ -291,10 +301,11 @@ void Mass::getEyeFlareColour() { _frame.eyeFlare = Color4{eye_flare_prop->r, eye_flare_prop->g, eye_flare_prop->b, eye_flare_prop->a}; } -auto Mass::writeEyeFlareColour() -> bool { +bool +Mass::writeEyeFlareColour() { LOG_INFO("Writing the eye flare colour."); - auto unit_data = _mass->at(MASS_UNIT_DATA); + auto unit_data = _mass->at(MASS_UNIT_DATA); if(!unit_data) { _lastError = "No unit data in "_s + _filename; LOG_ERROR(_lastError); @@ -302,7 +313,7 @@ auto Mass::writeEyeFlareColour() -> bool { return false; } - auto frame = unit_data->at(MASS_FRAME); + auto frame = unit_data->at(MASS_FRAME); if(!frame) { _lastError = "No frame data in "_s + _filename; LOG_ERROR(_lastError); @@ -310,7 +321,7 @@ auto Mass::writeEyeFlareColour() -> bool { return false; } - auto eye_flare_prop = frame->at(MASS_EYE_FLARE); + auto eye_flare_prop = frame->at(MASS_EYE_FLARE); if(!eye_flare_prop) { _lastError = "No eye flare property in "_s + _filename; LOG_ERROR(_lastError); @@ -331,21 +342,23 @@ auto Mass::writeEyeFlareColour() -> bool { return true; } -auto Mass::frameCustomStyles() -> Containers::ArrayView { +Containers::ArrayView +Mass::frameCustomStyles() { return _frame.customStyles; } -void Mass::getFrameCustomStyles() { +void +Mass::getFrameCustomStyles() { LOG_INFO("Getting the frame's custom styles."); - auto unit_data = _mass->at(MASS_UNIT_DATA); + auto unit_data = _mass->at(MASS_UNIT_DATA); if(!unit_data) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename); _state = State::Invalid; return; } - auto frame_styles = unit_data->at(MASS_CUSTOM_FRAME_STYLES); + auto frame_styles = unit_data->at(MASS_CUSTOM_FRAME_STYLES); if(!frame_styles) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_CUSTOM_FRAME_STYLES, _filename); _state = State::Invalid; @@ -360,9 +373,14 @@ void Mass::getFrameCustomStyles() { } getCustomStyles(_frame.customStyles, frame_styles); + + for(auto& style : _frame.customStyles) { + style.type = CustomStyle::Type::Frame; + } } -auto Mass::writeFrameCustomStyle(UnsignedLong index) -> bool { +bool +Mass::writeFrameCustomStyle(std::size_t index) { LOG_INFO_FORMAT("Writing frame custom style number {}.", index); if(index > _frame.customStyles.size()) { @@ -371,7 +389,7 @@ auto Mass::writeFrameCustomStyle(UnsignedLong index) -> bool { return false; } - auto unit_data = _mass->at(MASS_UNIT_DATA); + auto unit_data = _mass->at(MASS_UNIT_DATA); if(!unit_data) { _lastError = "No unit data in "_s + _filename; LOG_ERROR(_lastError); @@ -379,7 +397,7 @@ auto Mass::writeFrameCustomStyle(UnsignedLong index) -> bool { return false; } - auto frame_styles = unit_data->at(MASS_CUSTOM_FRAME_STYLES); + auto frame_styles = unit_data->at(MASS_CUSTOM_FRAME_STYLES); if(!frame_styles) { _lastError = "No frame styles in "_s + _filename; LOG_ERROR(_lastError); @@ -389,3 +407,5 @@ auto Mass::writeFrameCustomStyle(UnsignedLong index) -> bool { return writeCustomStyle(_frame.customStyles[index], index, frame_styles); } + +} diff --git a/src/GameObjects/Mass_Styles.cpp b/src/GameObjects/Mass_Styles.cpp new file mode 100644 index 0000000..25311aa --- /dev/null +++ b/src/GameObjects/Mass_Styles.cpp @@ -0,0 +1,158 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include "PropertyNames.h" +#include "../Logger/Logger.h" +#include "../Gvas/Types/ArrayProperty.h" +#include "../Gvas/Types/ColourStructProperty.h" +#include "../Gvas/Types/FloatProperty.h" +#include "../Gvas/Types/GenericStructProperty.h" +#include "../Gvas/Types/IntProperty.h" +#include "../Gvas/Types/StringProperty.h" + +#include "Mass.h" + +using namespace Containers::Literals; + +namespace mbst::GameObjects { + +Containers::ArrayView +Mass::globalStyles() { + return _globalStyles; +} + +void +Mass::getGlobalStyles() { + LOG_INFO("Getting global styles."); + + auto unit_data = _mass->at(MASS_UNIT_DATA); + if(!unit_data) { + LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename); + _state = State::Invalid; + return; + } + + auto global_styles = unit_data->at(MASS_GLOBAL_STYLES); + if(!global_styles) { + LOG_WARNING_FORMAT("Couldn't find global styles in {}.", _filename); + _globalStyles = Containers::Array{0}; + return; + } + + if(global_styles->items.size() != _globalStyles.size()) { + _globalStyles = Containers::Array{global_styles->items.size()}; + } + + getCustomStyles(_globalStyles, global_styles); + + for(auto& style : _globalStyles) { + style.type = CustomStyle::Type::Global; + } +} + +bool +Mass::writeGlobalStyle(std::size_t index) { + 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(MASS_UNIT_DATA); + if(!unit_data) { + _lastError = "No unit data found in "_s + _filename; + LOG_ERROR(_lastError); + _state = State::Invalid; + return false; + } + + auto global_styles = unit_data->at(MASS_GLOBAL_STYLES); + if(!global_styles) { + _lastError = "No global styles found in "_s + _filename; + LOG_ERROR(_lastError); + _state = State::Invalid; + return false; + } + + return writeCustomStyle(_globalStyles[index], index, global_styles); +} + +void +Mass::getCustomStyles(Containers::ArrayView styles, Gvas::Types::ArrayProperty* style_array) { + for(std::uint32_t i = 0; i < style_array->items.size(); i++) { + auto style_prop = style_array->at(i); + auto& style = styles[i]; + + style.name = style_prop->at(MASS_STYLE_NAME)->value; + auto colour_prop = style_prop->at(MASS_STYLE_COLOUR); + style.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; + style.metallic = style_prop->at(MASS_STYLE_METALLIC)->value; + style.gloss = style_prop->at(MASS_STYLE_GLOSS)->value; + style.glow = colour_prop->a != 0.0f; + + style.patternId = style_prop->at(MASS_STYLE_PATTERN_ID)->value; + style.opacity = style_prop->at(MASS_STYLE_PATTERN_OPACITY)->value; + style.offset = Vector2{ + style_prop->at(MASS_STYLE_PATTERN_OFFSETX)->value, + style_prop->at(MASS_STYLE_PATTERN_OFFSETY)->value + }; + style.rotation = style_prop->at(MASS_STYLE_PATTERN_ROTATION)->value; + style.scale = style_prop->at(MASS_STYLE_PATTERN_SCALE)->value; + } +} + +bool +Mass::writeCustomStyle(const CustomStyle& style, std::size_t index, Gvas::Types::ArrayProperty* style_array) { + if(!style_array) { + _lastError = "style_array is null."_s; + LOG_ERROR(_lastError); + return false; + } + + auto style_prop = style_array->at(index); + if(!style_prop) { + _lastError = "Style index is out of range in "_s + _filename; + LOG_ERROR(_lastError); + return false; + } + + style_prop->at(MASS_STYLE_NAME)->value = style.name; + auto colour_prop = style_prop->at(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(MASS_STYLE_METALLIC)->value = style.metallic; + style_prop->at(MASS_STYLE_GLOSS)->value = style.gloss; + + style_prop->at(MASS_STYLE_PATTERN_ID)->value = style.patternId; + style_prop->at(MASS_STYLE_PATTERN_OPACITY)->value = style.opacity; + style_prop->at(MASS_STYLE_PATTERN_OFFSETX)->value = style.offset.x(); + style_prop->at(MASS_STYLE_PATTERN_OFFSETY)->value = style.offset.y(); + style_prop->at(MASS_STYLE_PATTERN_ROTATION)->value = style.rotation; + style_prop->at(MASS_STYLE_PATTERN_SCALE)->value = style.scale; + + if(!_mass->saveToFile()) { + _lastError = _mass->lastError(); + return false; + } + + return true; +} + +} diff --git a/src/Mass/Mass_Weapons.cpp b/src/GameObjects/Mass_Weapons.cpp similarity index 60% rename from src/Mass/Mass_Weapons.cpp rename to src/GameObjects/Mass_Weapons.cpp index 62826e6..5b29e38 100644 --- a/src/Mass/Mass_Weapons.cpp +++ b/src/GameObjects/Mass_Weapons.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,111 +16,132 @@ #include "PropertyNames.h" #include "../Logger/Logger.h" -#include "../UESaveFile/Types/ArrayProperty.h" -#include "../UESaveFile/Types/BoolProperty.h" -#include "../UESaveFile/Types/ByteProperty.h" -#include "../UESaveFile/Types/ColourStructProperty.h" -#include "../UESaveFile/Types/GenericStructProperty.h" -#include "../UESaveFile/Types/IntProperty.h" -#include "../UESaveFile/Types/StringProperty.h" +#include "../Gvas/Types/ArrayProperty.h" +#include "../Gvas/Types/BoolProperty.h" +#include "../Gvas/Types/ByteProperty.h" +#include "../Gvas/Types/ColourStructProperty.h" +#include "../Gvas/Types/GenericStructProperty.h" +#include "../Gvas/Types/IntProperty.h" +#include "../Gvas/Types/StringProperty.h" #include "Mass.h" using namespace Containers::Literals; -auto Mass::meleeWeapons() -> Containers::ArrayView { +namespace mbst::GameObjects { + +Containers::ArrayView +Mass::meleeWeapons() { return _weapons.melee; } -void Mass::getMeleeWeapons() { +void +Mass::getMeleeWeapons() { LOG_INFO("Getting melee weapons."); getWeaponType(MASS_WEAPONS_MELEE, _weapons.melee); } -auto Mass::writeMeleeWeapons() -> bool { +bool +Mass::writeMeleeWeapons() { LOG_INFO("Writing melee weapons."); return writeWeaponType(MASS_WEAPONS_MELEE, _weapons.melee); } -auto Mass::shields() -> Containers::ArrayView { +Containers::ArrayView +Mass::shields() { return _weapons.shields; } -void Mass::getShields() { +void +Mass::getShields() { LOG_INFO("Getting shields."); getWeaponType(MASS_WEAPONS_SHIELD, _weapons.shields); } -auto Mass::writeShields() -> bool { +bool +Mass::writeShields() { LOG_INFO("Writing shields."); return writeWeaponType(MASS_WEAPONS_SHIELD, _weapons.shields); } -auto Mass::bulletShooters() -> Containers::ArrayView { +Containers::ArrayView +Mass::bulletShooters() { return _weapons.bulletShooters; } -void Mass::getBulletShooters() { +void +Mass::getBulletShooters() { LOG_INFO("Getting bullet shooters."); getWeaponType(MASS_WEAPONS_BSHOOTER, _weapons.bulletShooters); } -auto Mass::writeBulletShooters() -> bool { +bool +Mass::writeBulletShooters() { LOG_INFO("Writing bullet shooters."); return writeWeaponType(MASS_WEAPONS_BSHOOTER, _weapons.bulletShooters); } -auto Mass::energyShooters() -> Containers::ArrayView { +Containers::ArrayView +Mass::energyShooters() { return _weapons.energyShooters; } -void Mass::getEnergyShooters() { +void +Mass::getEnergyShooters() { LOG_INFO("Getting energy shooters."); getWeaponType(MASS_WEAPONS_ESHOOTER, _weapons.energyShooters); } -auto Mass::writeEnergyShooters() -> bool { +bool +Mass::writeEnergyShooters() { LOG_INFO("Writing energy shooters."); return writeWeaponType(MASS_WEAPONS_ESHOOTER, _weapons.energyShooters); } -auto Mass::bulletLaunchers() -> Containers::ArrayView { +Containers::ArrayView +Mass::bulletLaunchers() { return _weapons.bulletLaunchers; } -void Mass::getBulletLaunchers() { +void +Mass::getBulletLaunchers() { LOG_INFO("Getting bullet launchers."); getWeaponType(MASS_WEAPONS_BLAUNCHER, _weapons.bulletLaunchers); } -auto Mass::writeBulletLaunchers() -> bool { +bool +Mass::writeBulletLaunchers() { LOG_INFO("Writing bullet launchers."); return writeWeaponType(MASS_WEAPONS_BLAUNCHER, _weapons.bulletLaunchers); } -auto Mass::energyLaunchers() -> Containers::ArrayView { +Containers::ArrayView +Mass::energyLaunchers() { return _weapons.energyLaunchers; } -void Mass::getEnergyLaunchers() { +void +Mass::getEnergyLaunchers() { LOG_INFO("Getting energy launchers."); getWeaponType(MASS_WEAPONS_ELAUNCHER, _weapons.energyLaunchers); } -auto Mass::writeEnergyLaunchers() -> bool { +bool +Mass::writeEnergyLaunchers() { LOG_INFO("Writing energy launchers."); return writeWeaponType(MASS_WEAPONS_ELAUNCHER, _weapons.energyLaunchers); } -void Mass::getWeaponType(Containers::StringView prop_name, Containers::ArrayView weapon_array) { - auto unit_data = _mass->at(MASS_UNIT_DATA); +void +Mass::getWeaponType(Containers::StringView prop_name, Containers::ArrayView weapon_array) { + auto unit_data = _mass->at(MASS_UNIT_DATA); if(!unit_data) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename); _state = State::Invalid; return; } - auto prop = unit_data->at(prop_name); + auto prop = unit_data->at(prop_name); if(!prop) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", prop_name, _filename); _state = State::Invalid; @@ -134,13 +155,13 @@ void Mass::getWeaponType(Containers::StringView prop_name, Containers::ArrayView return; } - for(UnsignedInt i = 0; i < weapon_array.size(); i++) { - auto weapon_prop = prop->at(i); + for(std::uint32_t i = 0; i < weapon_array.size(); i++) { + auto weapon_prop = prop->at(i); auto& weapon = weapon_array[i]; - weapon.name = weapon_prop->at(MASS_WEAPON_NAME)->value; - auto& weapon_type = weapon_prop->at(MASS_WEAPON_TYPE)->enumValue; - #define c(enumerator, strenum, name) if(weapon_type == (strenum)) { weapon.type = WeaponType::enumerator; } else + weapon.name = weapon_prop->at(MASS_WEAPON_NAME)->value; + auto& weapon_type = weapon_prop->at(MASS_WEAPON_TYPE)->enumValue; + #define c(enumerator, strenum, name) if(weapon_type == (strenum)) { weapon.type = Weapon::Type::enumerator; } else #include "../Maps/WeaponTypes.hpp" #undef c { @@ -149,28 +170,28 @@ void Mass::getWeaponType(Containers::StringView prop_name, Containers::ArrayView return; } - auto parts_prop = weapon_prop->at(MASS_WEAPON_ELEMENT); + auto parts_prop = weapon_prop->at(MASS_WEAPON_ELEMENT); weapon.parts = Containers::Array{ValueInit, parts_prop->items.size()}; - for(UnsignedInt j = 0; j < parts_prop->items.size(); j++) { - auto part_prop = parts_prop->at(j); + for(std::uint32_t j = 0; j < parts_prop->items.size(); j++) { + auto part_prop = parts_prop->at(j); auto& part = weapon.parts[j]; - part.id = part_prop->at(MASS_WEAPON_PART_ID)->value; + part.id = part_prop->at(MASS_WEAPON_PART_ID)->value; - auto part_styles = part_prop->at(MASS_WEAPON_PART_STYLES); - for(UnsignedInt k = 0; k < part_styles->items.size(); k++) { - part.styles[k] = part_styles->at(k)->value; + auto part_styles = part_prop->at(MASS_WEAPON_PART_STYLES); + for(std::uint32_t k = 0; k < part_styles->items.size(); k++) { + part.styles[k] = part_styles->at(k)->value; } - auto part_decals = part_prop->at(MASS_WEAPON_PART_DECALS); + auto part_decals = part_prop->at(MASS_WEAPON_PART_DECALS); if(part_decals->items.size() != part.decals.size()) { part.decals = Containers::Array{part_decals->items.size()}; } getDecals(part.decals, part_decals); - auto part_accs = part_prop->at(MASS_WEAPON_PART_ACCESSORIES); + auto part_accs = part_prop->at(MASS_WEAPON_PART_ACCESSORIES); if(!part_accs) { part.accessories = Containers::Array{0}; continue; @@ -182,7 +203,7 @@ void Mass::getWeaponType(Containers::StringView prop_name, Containers::ArrayView getAccessories(part.accessories, part_accs); } - auto custom_styles = weapon_prop->at(MASS_CUSTOM_WEAPON_STYLES); + auto custom_styles = weapon_prop->at(MASS_CUSTOM_WEAPON_STYLES); if(!custom_styles) { LOG_ERROR_FORMAT("Can't find weapon custom styles in {}", _filename); _state = State::Invalid; @@ -198,9 +219,13 @@ void Mass::getWeaponType(Containers::StringView prop_name, Containers::ArrayView getCustomStyles(weapon.customStyles, custom_styles); - weapon.attached = weapon_prop->at(MASS_WEAPON_ATTACH)->value; - auto& damage_type = weapon_prop->at(MASS_WEAPON_DAMAGE_TYPE)->enumValue; - #define c(enumerator, strenum) if(damage_type == (strenum)) { weapon.damageType = DamageType::enumerator; } else + for(auto& style : weapon.customStyles) { + style.type = CustomStyle::Type::Weapon; + } + + weapon.attached = weapon_prop->at(MASS_WEAPON_ATTACH)->value; + auto& damage_type = weapon_prop->at(MASS_WEAPON_DAMAGE_TYPE)->enumValue; + #define c(enumerator, strenum) if(damage_type == (strenum)) { weapon.damageType = Weapon::DamageType::enumerator; } else #include "../Maps/DamageTypes.hpp" #undef c { @@ -208,9 +233,9 @@ void Mass::getWeaponType(Containers::StringView prop_name, Containers::ArrayView _state = State::Invalid; return; } - weapon.dualWield = weapon_prop->at(MASS_WEAPON_DUAL_WIELD)->value; - auto& effect_colour_mode = weapon_prop->at(MASS_WEAPON_COLOUR_EFX_MODE)->enumValue; - #define c(enumerator, strenum) if(effect_colour_mode == (strenum)) { weapon.effectColourMode = EffectColourMode::enumerator; } else + weapon.dualWield = weapon_prop->at(MASS_WEAPON_DUAL_WIELD)->value; + auto& effect_colour_mode = weapon_prop->at(MASS_WEAPON_COLOUR_EFX_MODE)->enumValue; + #define c(enumerator, strenum) if(effect_colour_mode == (strenum)) { weapon.effectColourMode = Weapon::EffectColourMode::enumerator; } else #include "../Maps/EffectColourModes.hpp" #undef c { @@ -218,13 +243,14 @@ void Mass::getWeaponType(Containers::StringView prop_name, Containers::ArrayView _state = State::Invalid; return; } - auto effect_colour = weapon_prop->at(MASS_WEAPON_COLOUR_EFX); + auto effect_colour = weapon_prop->at(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_array) -> bool { - auto unit_data = _mass->at(MASS_UNIT_DATA); +bool +Mass::writeWeaponType(Containers::StringView prop_name, Containers::ArrayView weapon_array) { + auto unit_data = _mass->at(MASS_UNIT_DATA); if(!unit_data) { _lastError = "No unit data in "_s + _filename; LOG_ERROR(_lastError); @@ -232,7 +258,7 @@ auto Mass::writeWeaponType(Containers::StringView prop_name, Containers::ArrayVi return false; } - auto prop = unit_data->at(prop_name); + auto prop = unit_data->at(prop_name); if(!prop) { _lastError = prop_name + " not found in "_s + _filename; LOG_ERROR(_lastError); @@ -248,13 +274,13 @@ auto Mass::writeWeaponType(Containers::StringView prop_name, Containers::ArrayVi return false; } - for(UnsignedInt i = 0; i < weapon_array.size(); i++) { - auto weapon_prop = prop->at(i); + for(std::uint32_t i = 0; i < weapon_array.size(); i++) { + auto weapon_prop = prop->at(i); auto& weapon = weapon_array[i]; - weapon_prop->at(MASS_WEAPON_NAME)->value = weapon.name; + weapon_prop->at(MASS_WEAPON_NAME)->value = weapon.name; switch(weapon.type) { - #define c(enumerator, strenum, name) case WeaponType::enumerator: weapon_prop->at(MASS_WEAPON_TYPE)->enumValue = strenum; break; + #define c(enumerator, strenum, name) case Weapon::Type::enumerator: weapon_prop->at(MASS_WEAPON_TYPE)->enumValue = strenum; break; #include "../Maps/WeaponTypes.hpp" #undef c default: @@ -263,7 +289,7 @@ auto Mass::writeWeaponType(Containers::StringView prop_name, Containers::ArrayVi return false; } - auto parts_prop = weapon_prop->at(MASS_WEAPON_ELEMENT); + auto parts_prop = weapon_prop->at(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()); @@ -272,21 +298,21 @@ auto Mass::writeWeaponType(Containers::StringView prop_name, Containers::ArrayVi return false; } - for(UnsignedInt j = 0; j < parts_prop->items.size(); j++) { - auto part_prop = parts_prop->at(j); + for(std::uint32_t j = 0; j < parts_prop->items.size(); j++) { + auto part_prop = parts_prop->at(j); auto& part = weapon.parts[j]; - part_prop->at(MASS_WEAPON_PART_ID)->value = part.id; + part_prop->at(MASS_WEAPON_PART_ID)->value = part.id; - auto part_styles = part_prop->at(MASS_WEAPON_PART_STYLES); - for(UnsignedInt k = 0; k < part_styles->items.size(); k++) { - part_styles->at(k)->value = part.styles[k]; + auto part_styles = part_prop->at(MASS_WEAPON_PART_STYLES); + for(std::uint32_t k = 0; k < part_styles->items.size(); k++) { + part_styles->at(k)->value = part.styles[k]; } - auto part_decals = part_prop->at(MASS_WEAPON_PART_DECALS); + auto part_decals = part_prop->at(MASS_WEAPON_PART_DECALS); writeDecals(part.decals, part_decals); - auto part_accs = part_prop->at(MASS_WEAPON_PART_ACCESSORIES); + auto part_accs = part_prop->at(MASS_WEAPON_PART_ACCESSORIES); if(!part_accs) { continue; } @@ -302,7 +328,7 @@ auto Mass::writeWeaponType(Containers::StringView prop_name, Containers::ArrayVi writeAccessories(part.accessories, part_accs); } - auto custom_styles = weapon_prop->at(MASS_CUSTOM_WEAPON_STYLES); + auto custom_styles = weapon_prop->at(MASS_CUSTOM_WEAPON_STYLES); if(!custom_styles) { _lastError = "No custom styles found for weapon."_s; LOG_ERROR(_lastError); @@ -318,13 +344,13 @@ auto Mass::writeWeaponType(Containers::StringView prop_name, Containers::ArrayVi return false; } - for(UnsignedInt j = 0; j < weapon.customStyles.size(); j++) { + for(std::uint32_t j = 0; j < weapon.customStyles.size(); j++) { writeCustomStyle(weapon.customStyles[j], j, custom_styles); } - weapon_prop->at(MASS_WEAPON_ATTACH)->value = weapon.attached; + weapon_prop->at(MASS_WEAPON_ATTACH)->value = weapon.attached; switch(weapon.damageType) { - #define c(enumerator, strenum) case DamageType::enumerator: weapon_prop->at(MASS_WEAPON_DAMAGE_TYPE)->enumValue = strenum; break; + #define c(enumerator, strenum) case Weapon::DamageType::enumerator: weapon_prop->at(MASS_WEAPON_DAMAGE_TYPE)->enumValue = strenum; break; #include "../Maps/DamageTypes.hpp" #undef c default: @@ -332,10 +358,10 @@ auto Mass::writeWeaponType(Containers::StringView prop_name, Containers::ArrayVi LOG_ERROR(_lastError); return false; } - weapon_prop->at(MASS_WEAPON_DUAL_WIELD)->value = weapon.dualWield; + weapon_prop->at(MASS_WEAPON_DUAL_WIELD)->value = weapon.dualWield; switch(weapon.effectColourMode) { - #define c(enumerator, enumstr) case EffectColourMode::enumerator: \ - weapon_prop->at(MASS_WEAPON_COLOUR_EFX_MODE)->enumValue = enumstr; \ + #define c(enumerator, enumstr) case Weapon::EffectColourMode::enumerator: \ + weapon_prop->at(MASS_WEAPON_COLOUR_EFX_MODE)->enumValue = enumstr; \ break; #include "../Maps/EffectColourModes.hpp" #undef c @@ -344,7 +370,7 @@ auto Mass::writeWeaponType(Containers::StringView prop_name, Containers::ArrayVi LOG_ERROR(_lastError); return false; } - auto effect_colour = weapon_prop->at(MASS_WEAPON_COLOUR_EFX); + auto effect_colour = weapon_prop->at(MASS_WEAPON_COLOUR_EFX); effect_colour->r = weapon.effectColour.r(); effect_colour->g = weapon.effectColour.g(); effect_colour->b = weapon.effectColour.b(); @@ -358,3 +384,5 @@ auto Mass::writeWeaponType(Containers::StringView prop_name, Containers::ArrayVi return true; } + +} diff --git a/src/GameObjects/Profile.cpp b/src/GameObjects/Profile.cpp new file mode 100644 index 0000000..62a6c50 --- /dev/null +++ b/src/GameObjects/Profile.cpp @@ -0,0 +1,325 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include +#include + +#include "PropertyNames.h" +#include "../Logger/Logger.h" +#include "../Gvas/Types/ArrayProperty.h" +#include "../Gvas/Types/ResourceItemValue.h" +#include "../Gvas/Types/IntProperty.h" +#include "../Gvas/Types/StringProperty.h" + +#include "Profile.h" + +using namespace Corrade; +using namespace Containers::Literals; + +namespace mbst::GameObjects { + +Profile::Profile(Containers::StringView path): + _profile(path) +{ + LOG_INFO_FORMAT("Reading profile at {}.", path); + + if(!_profile.valid()) { + _lastError = _profile.lastError(); + _valid = false; + return; + } + + _filename = Utility::Path::split(path).second(); + + if(_filename.hasPrefix("Demo"_s)) { + _type = Type::Demo; + } + else { + _type = Type::FullGame; + } + + auto account_prop = _profile.at(PROFILE_ACCOUNT); + if(!account_prop) { + _lastError = "Couldn't find an account ID in "_s + _filename; + _valid = false; + return; + } + _account = account_prop->value; + + refreshValues(); +} + +bool +Profile::valid() const { + return _valid; +} + +Containers::StringView +Profile::lastError() const { + return _lastError; +} + +Containers::StringView +Profile::filename() const { + return _filename; +} + +Profile::Type +Profile::type() const { + return _type; +} + +bool +Profile::isDemo() const { + return _type == Type::Demo; +} + +Containers::StringView +Profile::account() const { + return _account; +} + +void +Profile::refreshValues() { + if(!_profile.reloadData()) { + LOG_ERROR(_profile.lastError()); + _valid = false; + return; + } + + if(_profile.saveType() != "/Game/Core/Save/bpSaveGameProfile.bpSaveGameProfile_C"_s) { + LOG_ERROR_FORMAT("{} is not a valid profile save.", _filename); + _valid = false; + return; + } + + LOG_INFO("Getting the company name."); + auto name_prop = _profile.at(PROFILE_NAME); + if(!name_prop) { + _lastError = "No company name in "_s + _filename; + LOG_ERROR(_lastError); + _valid = false; + return; + } + _name = name_prop->value; + + LOG_INFO("Getting the active frame slot."); + auto prop = _profile.at(PROFILE_ACTIVE_FRAME_SLOT); + _activeFrameSlot = prop ? prop->value : 0; + + LOG_INFO("Getting the credits."); + prop = _profile.at(PROFILE_CREDITS); + _credits = prop ? prop->value : 0; + + LOG_INFO("Getting the story progress."); + prop = _profile.at(PROFILE_STORY_PROGRESS); + _storyProgress = prop ? prop->value : 0; + + LOG_INFO("Getting the last mission ID."); + prop = _profile.at(PROFILE_LAST_MISSION_ID); + _lastMissionId = prop ? prop->value : 0; + + LOG_INFO("Getting the materials."); + _materials[GameData::MaterialID::VerseSteel] = getResource(PROFILE_MATERIAL, GameData::MaterialID::VerseSteel); + _materials[GameData::MaterialID::Undinium] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Undinium); + _materials[GameData::MaterialID::NecriumAlloy] = getResource(PROFILE_MATERIAL, GameData::MaterialID::NecriumAlloy); + _materials[GameData::MaterialID::Lunarite] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Lunarite); + _materials[GameData::MaterialID::Asterite] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Asterite); + _materials[GameData::MaterialID::HalliteFragma] = getResource(PROFILE_MATERIAL, GameData::MaterialID::HalliteFragma); + _materials[GameData::MaterialID::Unnoctinium] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Unnoctinium); + + _materials[GameData::MaterialID::Ednil] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Ednil); + _materials[GameData::MaterialID::Nuflalt] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Nuflalt); + _materials[GameData::MaterialID::Aurelene] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Aurelene); + _materials[GameData::MaterialID::Soldus] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Soldus); + _materials[GameData::MaterialID::SynthesisedN] = getResource(PROFILE_MATERIAL, GameData::MaterialID::SynthesisedN); + _materials[GameData::MaterialID::Nanoc] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Nanoc); + _materials[GameData::MaterialID::Abyssillite] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Abyssillite); + + _materials[GameData::MaterialID::Alcarbonite] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Alcarbonite); + _materials[GameData::MaterialID::Keriphene] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Keriphene); + _materials[GameData::MaterialID::NitinolCM] = getResource(PROFILE_MATERIAL, GameData::MaterialID::NitinolCM); + _materials[GameData::MaterialID::Quarkium] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Quarkium); + _materials[GameData::MaterialID::Alterene] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Alterene); + _materials[GameData::MaterialID::Cosmium] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Cosmium); + _materials[GameData::MaterialID::PurifiedQuarkium] = getResource(PROFILE_MATERIAL, GameData::MaterialID::PurifiedQuarkium); + + _materials[GameData::MaterialID::MixedComposition] = getResource(PROFILE_QUARK_DATA, GameData::MaterialID::MixedComposition); + _materials[GameData::MaterialID::VoidResidue] = getResource(PROFILE_QUARK_DATA, GameData::MaterialID::VoidResidue); + _materials[GameData::MaterialID::MuscularConstruction] = getResource(PROFILE_QUARK_DATA, GameData::MaterialID::MuscularConstruction); + _materials[GameData::MaterialID::MineralExoskeletology] = getResource(PROFILE_QUARK_DATA, GameData::MaterialID::MineralExoskeletology); + _materials[GameData::MaterialID::CarbonisedSkin] = getResource(PROFILE_QUARK_DATA, GameData::MaterialID::CarbonisedSkin); + _materials[GameData::MaterialID::IsolatedVoidParticle] = getResource(PROFILE_QUARK_DATA, GameData::MaterialID::IsolatedVoidParticle); + _materials[GameData::MaterialID::WeaponisedPhysiology] = getResource(PROFILE_QUARK_DATA, GameData::MaterialID::WeaponisedPhysiology); + + _valid = true; +} + +Containers::StringView +Profile::companyName() const { + return _name; +} + +bool +Profile::renameCompany(Containers::StringView new_name) { + auto name_prop = _profile.at(PROFILE_NAME); + if(!name_prop) { + _lastError = "No company name in "_s + _filename; + LOG_ERROR(_lastError); + _valid = false; + return false; + } + + name_prop->value = new_name; + + if(!_profile.saveToFile()) { + _lastError = _profile.lastError(); + return false; + } + + return true; +} + +std::int32_t +Profile::activeFrameSlot() const { + return _activeFrameSlot; +} + +std::int32_t +Profile::credits() const { + return _credits; +} + +bool +Profile::setCredits(std::int32_t amount) { + auto credits_prop = _profile.at(PROFILE_CREDITS); + + if(!credits_prop) { + credits_prop = new Gvas::Types::IntProperty; + credits_prop->name.emplace("Credit"_s); + credits_prop->valueLength = sizeof(std::int32_t); + _profile.appendProperty(Gvas::Types::IntProperty::ptr{credits_prop}); + } + + credits_prop->value = amount; + + if(!_profile.saveToFile()) { + _lastError = _profile.lastError(); + return false; + } + + return true; +} + +std::int32_t +Profile::storyProgress() const { + return _storyProgress; +} + +bool +Profile::setStoryProgress(std::int32_t progress) { + auto story_progress_prop = _profile.at("StoryProgress"_s); + + if(!story_progress_prop) { + story_progress_prop = new Gvas::Types::IntProperty; + story_progress_prop->name.emplace("StoryProgress"_s); + story_progress_prop->valueLength = sizeof(std::int32_t); + _profile.appendProperty(Gvas::Types::IntProperty::ptr{story_progress_prop}); + } + + story_progress_prop->value = progress; + + if(!_profile.saveToFile()) { + _lastError = _profile.lastError(); + return false; + } + + return true; +} + +std::int32_t +Profile::lastMissionId() const { + return _lastMissionId; +} + +std::int32_t +Profile::material(GameData::MaterialID id) const { + return _materials.at(id); +} + +bool +Profile::setMaterial(GameData::MaterialID id, std::int32_t amount) { + Containers::StringView container = id >= GameData::MaterialID::MixedComposition ? PROFILE_QUARK_DATA : PROFILE_MATERIAL; + auto mats_prop = _profile.at(container); + + if(!mats_prop) { + mats_prop = new Gvas::Types::ArrayProperty; + mats_prop->name.emplace(container); + mats_prop->itemType = "StructProperty"; + _profile.appendProperty(Gvas::Types::ArrayProperty::ptr{mats_prop}); + } + + auto predicate = [&id](Gvas::Types::UnrealPropertyBase::ptr& prop){ + auto res_prop = dynamic_cast(prop.get()); + return res_prop->id == id; + }; + + auto it = std::find_if(mats_prop->items.begin(), mats_prop->items.end(), predicate); + + Gvas::Types::ResourceItemValue* res_prop; + if(it == mats_prop->items.end()) { + res_prop = new Gvas::Types::ResourceItemValue; + if(mats_prop->items.isEmpty()) { + res_prop->name.emplace(container); + } + res_prop->id = id; + Gvas::Types::ResourceItemValue::ptr prop{res_prop}; + arrayAppend(mats_prop->items, Utility::move(prop)); + } + else { + res_prop = dynamic_cast(it->get()); + } + + res_prop->quantity = amount; + + if(!_profile.saveToFile()) { + _lastError = _profile.lastError(); + return false; + } + + return true; +} + +std::int32_t +Profile::getResource(Containers::StringView container, GameData::MaterialID id) { + auto mats_prop = _profile.at(container); + + if(!mats_prop) { + return 0; + } + + auto predicate = [&id](Gvas::Types::UnrealPropertyBase::ptr& prop){ + auto res_prop = dynamic_cast(prop.get()); + return res_prop->id == id; + }; + + auto it = std::find_if(mats_prop->items.begin(), mats_prop->items.end(), predicate); + return it != mats_prop->items.end() ? dynamic_cast(it->get())->quantity : 0; +} + +} diff --git a/src/GameObjects/Profile.h b/src/GameObjects/Profile.h new file mode 100644 index 0000000..2735b01 --- /dev/null +++ b/src/GameObjects/Profile.h @@ -0,0 +1,92 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include +#include + +#include "../GameData/ResourceIDs.h" +#include "../Gvas/File.h" + +using namespace Corrade; + +namespace mbst::GameObjects { + +class Profile { + public: + explicit Profile(Containers::StringView path); + + bool valid() const; + + auto lastError() const -> Containers::StringView; + + auto filename() const -> Containers::StringView; + + enum class Type: std::uint8_t { + Demo, + FullGame + }; + + auto type() const -> Type; + bool isDemo() const; + + auto account() const -> Containers::StringView; + + void refreshValues(); + + auto companyName() const -> Containers::StringView; + bool renameCompany(Containers::StringView new_name); + + auto activeFrameSlot() const -> std::int32_t; + + auto credits() const -> std::int32_t; + bool setCredits(std::int32_t credits); + + auto storyProgress() const -> std::int32_t; + bool setStoryProgress(std::int32_t progress); + + auto lastMissionId() const -> std::int32_t; + + auto material(GameData::MaterialID id) const -> std::int32_t; + bool setMaterial(GameData::MaterialID id, std::int32_t amount); + + private: + auto getResource(Containers::StringView container, GameData::MaterialID id) -> std::int32_t; + + Containers::String _filename; + + Type _type; + + Gvas::File _profile; + + Containers::String _name; + std::int32_t _activeFrameSlot = 0; + std::int32_t _credits = 0; + std::int32_t _storyProgress = 0; + std::int32_t _lastMissionId = 0; + + std::map _materials; + + Containers::String _account; + + bool _valid = false; + Containers::String _lastError; +}; + +} diff --git a/src/Mass/PropertyNames.h b/src/GameObjects/PropertyNames.h similarity index 85% rename from src/Mass/PropertyNames.h rename to src/GameObjects/PropertyNames.h index 862163b..2d36876 100644 --- a/src/Mass/PropertyNames.h +++ b/src/GameObjects/PropertyNames.h @@ -1,5 +1,32 @@ #pragma once +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +// Profile stuff +#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" + +// Basic MASS stuff #define MASS_UNIT_DATA "UnitData" #define MASS_NAME "Name_45_A037C5D54E53456407BDF091344529BB" #define MASS_ACCOUNT "Account" diff --git a/src/Mass/Weapon.cpp b/src/GameObjects/Weapon.cpp similarity index 86% rename from src/Mass/Weapon.cpp rename to src/GameObjects/Weapon.cpp index 5864282..cc9d646 100644 --- a/src/Mass/Weapon.cpp +++ b/src/GameObjects/Weapon.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,11 +16,13 @@ #include "Weapon.h" +namespace mbst::GameObjects { + Weapon::Weapon(const Weapon& other) { name = other.name; type = other.type; parts = Containers::Array{other.parts.size()}; - for(UnsignedInt i = 0; i < parts.size(); i++) { + for(std::uint32_t i = 0; i < parts.size(); i++) { parts[i] = other.parts[i]; } customStyles = other.customStyles; @@ -31,11 +33,12 @@ Weapon::Weapon(const Weapon& other) { effectColour = other.effectColour; } -Weapon& Weapon::operator=(const Weapon& other) { +Weapon& +Weapon::operator=(const Weapon& other) { name = other.name; type = other.type; parts = Containers::Array{other.parts.size()}; - for(UnsignedInt i = 0; i < parts.size(); i++) { + for(std::uint32_t i = 0; i < parts.size(); i++) { parts[i] = other.parts[i]; } customStyles = other.customStyles; @@ -47,3 +50,5 @@ Weapon& Weapon::operator=(const Weapon& other) { return *this; } + +} diff --git a/src/Mass/Weapon.h b/src/GameObjects/Weapon.h similarity index 79% rename from src/Mass/Weapon.h rename to src/GameObjects/Weapon.h index 7fc8c45..f72f660 100644 --- a/src/Mass/Weapon.h +++ b/src/GameObjects/Weapon.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -29,19 +29,7 @@ using namespace Corrade; using namespace Magnum; -#define c(enumerator, ...) enumerator, -enum class WeaponType { - #include "../Maps/WeaponTypes.hpp" -}; - -enum class DamageType { - #include "../Maps/DamageTypes.hpp" -}; - -enum class EffectColourMode { - #include "../Maps/EffectColourModes.hpp" -}; -#undef c +namespace mbst::GameObjects { struct Weapon { Weapon() = default; @@ -52,8 +40,20 @@ struct Weapon { Weapon(Weapon&& other) = default; Weapon& operator=(Weapon&& other) = default; + #define c(enumerator, ...) enumerator, + enum class Type { + #include "../Maps/WeaponTypes.hpp" + }; + enum class DamageType { + #include "../Maps/DamageTypes.hpp" + }; + enum class EffectColourMode { + #include "../Maps/EffectColourModes.hpp" + }; + #undef c + Containers::String name; - WeaponType type = WeaponType::Melee; + Type type = Type::Melee; Containers::Array parts; Containers::StaticArray<16, CustomStyle> customStyles{ValueInit}; bool attached = false; @@ -62,3 +62,5 @@ struct Weapon { EffectColourMode effectColourMode = EffectColourMode::Default; Color4 effectColour{0.0f}; }; + +} diff --git a/src/Mass/WeaponPart.h b/src/GameObjects/WeaponPart.h similarity index 81% rename from src/Mass/WeaponPart.h rename to src/GameObjects/WeaponPart.h index 227b5af..d6e519c 100644 --- a/src/Mass/WeaponPart.h +++ b/src/GameObjects/WeaponPart.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -19,13 +19,12 @@ #include #include -#include - #include "Decal.h" #include "Accessory.h" using namespace Corrade; -using namespace Magnum; + +namespace mbst::GameObjects { struct WeaponPart { WeaponPart() = default; @@ -34,11 +33,11 @@ struct WeaponPart { id = other.id; styles = other.styles; decals = Containers::Array{other.decals.size()}; - for(UnsignedInt i = 0; i < decals.size(); i++) { + for(auto i = 0u; i < decals.size(); i++) { decals[i] = other.decals[i]; } accessories = Containers::Array{other.accessories.size()}; - for(UnsignedInt i = 0; i < accessories.size(); i++) { + for(auto i = 0u; i < accessories.size(); i++) { accessories[i] = other.accessories[i]; } } @@ -46,11 +45,11 @@ struct WeaponPart { id = other.id; styles = other.styles; decals = Containers::Array{other.decals.size()}; - for(UnsignedInt i = 0; i < decals.size(); i++) { + for(auto i = 0u; i < decals.size(); i++) { decals[i] = other.decals[i]; } accessories = Containers::Array{other.accessories.size()}; - for(UnsignedInt i = 0; i < accessories.size(); i++) { + for(auto i = 0u; i < accessories.size(); i++) { accessories[i] = other.accessories[i]; } return *this; @@ -59,8 +58,10 @@ struct WeaponPart { WeaponPart(WeaponPart&& other) = default; WeaponPart& operator=(WeaponPart&& other) = default; - Int id = 0; - Containers::StaticArray<4, Int> styles{ValueInit}; + std::int32_t id = 0; + Containers::StaticArray<4, std::int32_t> styles{ValueInit}; Containers::Array decals{}; Containers::Array accessories{}; }; + +} diff --git a/src/UESaveFile/Debug.cpp b/src/Gvas/Debug.cpp similarity index 73% rename from src/UESaveFile/Debug.cpp rename to src/Gvas/Debug.cpp index 31bc2ab..8afbb74 100644 --- a/src/UESaveFile/Debug.cpp +++ b/src/Gvas/Debug.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -22,17 +22,20 @@ #include "Debug.h" -Utility::Debug& operator<<(Utility::Debug& debug, const ArrayProperty* prop) { +Utility::Debug& +operator<<(Utility::Debug& debug, const Gvas::Types::ArrayProperty* prop) { return debug << (*prop->name) << Utility::Debug::nospace << ":" << prop->propertyType << "of" << prop->items.size() << prop->itemType; } -Utility::Debug& operator<<(Utility::Debug& debug, const SetProperty* prop) { +Utility::Debug& +operator<<(Utility::Debug& debug, const Gvas::Types::SetProperty* prop) { return debug << (*prop->name) << Utility::Debug::nospace << ":" << prop->propertyType << "of" << prop->items.size() << prop->itemType; } -Utility::Debug& operator<<(Utility::Debug& debug, const GenericStructProperty* prop) { +Utility::Debug& +operator<<(Utility::Debug& debug, const Gvas::Types::GenericStructProperty* prop) { debug << (*prop->name) << Utility::Debug::nospace << ":" << prop->structType << "(" << Utility::Debug::nospace << prop->propertyType << Utility::Debug::nospace << ") Contents:"; @@ -42,8 +45,9 @@ Utility::Debug& operator<<(Utility::Debug& debug, const GenericStructProperty* p return debug; } -Utility::Debug& operator<<(Utility::Debug& debug, const StructProperty* prop) { - auto cast = dynamic_cast(prop); +Utility::Debug& +operator<<(Utility::Debug& debug, const Gvas::Types::StructProperty* prop) { + auto cast = dynamic_cast(prop); if(cast) { return debug << cast; } @@ -52,21 +56,22 @@ Utility::Debug& operator<<(Utility::Debug& debug, const StructProperty* prop) { prop->structType << "(" << Utility::Debug::nospace << prop->propertyType << Utility::Debug::nospace << ")"; } -Utility::Debug& operator<<(Utility::Debug& debug, const UnrealPropertyBase* prop) { +Utility::Debug& +operator<<(Utility::Debug& debug, const Gvas::Types::UnrealPropertyBase* prop) { if(prop->propertyType == "ArrayProperty") { - auto array_prop = dynamic_cast(prop); + auto array_prop = dynamic_cast(prop); if(array_prop) { return debug << array_prop; } } else if(prop->propertyType == "SetProperty") { - auto set_prop = dynamic_cast(prop); + auto set_prop = dynamic_cast(prop); if(set_prop) { return debug << set_prop; } } else if(prop->propertyType == "StructProperty") { - auto struct_prop = dynamic_cast(prop); + auto struct_prop = dynamic_cast(prop); if(struct_prop) { return debug << struct_prop; } diff --git a/src/Gvas/Debug.h b/src/Gvas/Debug.h new file mode 100644 index 0000000..f26c87b --- /dev/null +++ b/src/Gvas/Debug.h @@ -0,0 +1,29 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include "Types/Types.h" + +using namespace Corrade; + +Utility::Debug& operator<<(Utility::Debug& debug, const Gvas::Types::ArrayProperty* prop); +Utility::Debug& operator<<(Utility::Debug& debug, const Gvas::Types::SetProperty* prop); +Utility::Debug& operator<<(Utility::Debug& debug, const Gvas::Types::GenericStructProperty* prop); +Utility::Debug& operator<<(Utility::Debug& debug, const Gvas::Types::StructProperty* prop); +Utility::Debug& operator<<(Utility::Debug& debug, const Gvas::Types::UnrealPropertyBase* prop); diff --git a/src/UESaveFile/UESaveFile.cpp b/src/Gvas/File.cpp similarity index 57% rename from src/UESaveFile/UESaveFile.cpp rename to src/Gvas/File.cpp index 44942ba..e99ffd2 100644 --- a/src/UESaveFile/UESaveFile.cpp +++ b/src/Gvas/File.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -17,56 +17,66 @@ #include #include -#include "BinaryReader.h" -#include "BinaryWriter.h" #include "../Logger/Logger.h" -#include "UESaveFile.h" +#include "../BinaryIo/Reader.h" +#include "../BinaryIo/Writer.h" + +#include "File.h" using namespace Containers::Literals; -UESaveFile::UESaveFile(Containers::String filepath): +namespace Gvas { + +File::File(Containers::String filepath): _propSerialiser{PropertySerialiser::instance()} { - _filepath = std::move(filepath); + _filepath = Utility::move(filepath); loadData(); } -auto UESaveFile::valid() const -> bool { +bool +File::valid() const { return _valid; } -auto UESaveFile::lastError() const -> Containers::StringView { +Containers::StringView +File::lastError() const { return _lastError; } -auto UESaveFile::reloadData() -> bool { +bool +File::reloadData() { if(_noReloadAfterSave) { _noReloadAfterSave = false; return valid(); } - _properties = Containers::Array{}; + _properties = Containers::Array{}; loadData(); return valid(); } -auto UESaveFile::saveType() -> Containers::StringView { +Containers::StringView +File::saveType() { return _saveType; } -void UESaveFile::appendProperty(UnrealPropertyBase::ptr prop) { - auto none_prop = std::move(_properties.back()); - _properties.back() = std::move(prop); - arrayAppend(_properties, std::move(none_prop)); +void +File::appendProperty(Types::UnrealPropertyBase::ptr prop) { + auto none_prop = Utility::move(_properties.back()); + _properties.back() = Utility::move(prop); + arrayAppend(_properties, Utility::move(none_prop)); } -auto UESaveFile::props() -> Containers::ArrayView { +Containers::ArrayView +File::props() { return _properties; } -auto UESaveFile::saveToFile() -> bool { +bool +File::saveToFile() { LOG_INFO_FORMAT("Writing to {}.", _filepath); bool temp_file = _filepath.hasSuffix(".tmp"); @@ -77,7 +87,7 @@ auto UESaveFile::saveToFile() -> bool { return false; } - BinaryWriter writer{_filepath + ".tmp"_s}; + BinaryIo::Writer writer{_filepath + ".tmp"_s}; if(!writer.open()) { _lastError = "Couldn't open the file for saving."_s; @@ -85,32 +95,53 @@ auto UESaveFile::saveToFile() -> bool { return false; } - if(!writer.writeArray(arrayView(_magicBytes)) || - !writer.writeUnsignedInt(_saveVersion) || - !writer.writeUnsignedInt(_packageVersion) || - !writer.writeUnsignedShort(_engineVersion.major) || - !writer.writeUnsignedShort(_engineVersion.minor) || - !writer.writeUnsignedShort(_engineVersion.patch) || - !writer.writeUnsignedInt(_engineVersion.build) || - !writer.writeUEString(_engineVersion.buildId)) - { - _lastError = "Couldn't write the header."_s; + if(!writer.writeArray(arrayView(_magicBytes))) { + _lastError = "Couldn't write the magic bytes."_s; LOG_ERROR(_lastError); return false; } - if(!writer.writeUnsignedInt(_customFormatVersion) || - !writer.writeUnsignedInt(_customFormatData.size())) + if(!writer.writeUint32(_saveVersion)) { + _lastError = "Couldn't write the save version."_s; + LOG_ERROR(_lastError); + return false; + } + + if(!writer.writeUint32(_packageVersion)) { + _lastError = "Couldn't write the package version."_s; + LOG_ERROR(_lastError); + return false; + } + + if(_saveVersion == 3) { + if(!writer.writeUint32(_unknown)) { + _lastError = "Couldn't write some unknown bytes."_s; + LOG_ERROR(_lastError); + return false; + } + } + + if(!writer.writeUint16(_engineVersion.major) || + !writer.writeUint16(_engineVersion.minor) || + !writer.writeUint16(_engineVersion.patch) || + !writer.writeUint32(_engineVersion.build) || + !writer.writeUEString(_engineVersion.buildId)) + { + _lastError = "Couldn't write the engine version."_s; + LOG_ERROR(_lastError); + return false; + } + + if(!writer.writeUint32(_customFormatVersion) || + !writer.writeUint32(_customFormatData.size())) { _lastError = "Couldn't write the custom format data."_s; LOG_ERROR(_lastError); return false; } - for(UnsignedLong i = 0; i < _customFormatData.size(); i++) { - if(!writer.writeStaticArray(Containers::StaticArrayView<16, const char>{_customFormatData[i].id}) || - !writer.writeUnsignedInt(_customFormatData[i].value)) - { + for(auto& i : _customFormatData) { + if(!writer.writeStaticArray<16>(staticArrayView(i.id)) || !writer.writeUint32(i.value)) { _lastError = "Couldn't write the custom format data."_s; LOG_ERROR(_lastError); return false; @@ -124,7 +155,7 @@ auto UESaveFile::saveToFile() -> bool { } for(auto& prop : _properties) { - UnsignedLong bytes_written = 0; + std::size_t 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); @@ -138,7 +169,7 @@ auto UESaveFile::saveToFile() -> bool { } } - writer.writeUnsignedInt(0); + writer.writeUint32(0u); writer.closeFile(); @@ -155,7 +186,8 @@ auto UESaveFile::saveToFile() -> bool { return true; } -void UESaveFile::loadData() { +void +File::loadData() { LOG_INFO_FORMAT("Reading data from {}.", _filepath); _valid = false; @@ -166,7 +198,7 @@ void UESaveFile::loadData() { return; } - BinaryReader reader{_filepath}; + BinaryIo::Reader reader{_filepath}; if(!reader.open()) { _lastError = _filepath + " couldn't be opened."_s; @@ -189,28 +221,66 @@ void UESaveFile::loadData() { return; } - if(!reader.readUnsignedInt(_saveVersion) || - !reader.readUnsignedInt(_packageVersion) || - !reader.readUnsignedShort(_engineVersion.major) || - !reader.readUnsignedShort(_engineVersion.minor) || - !reader.readUnsignedShort(_engineVersion.patch) || - !reader.readUnsignedInt(_engineVersion.build) || - !reader.readUEString(_engineVersion.buildId)) - { - _lastError = "Couldn't read version data."; + if(!reader.readUint32(_saveVersion)) { + _lastError = "Couldn't read save version."; LOG_ERROR(_lastError); return; } - if(!reader.readUnsignedInt(_customFormatVersion)) { + if(!reader.readUint32(_packageVersion)) { + _lastError = "Couldn't read package version."; + LOG_ERROR(_lastError); + return; + } + + if(_saveVersion == 3) { + if(!reader.readUint32(_unknown)) { + _lastError = "Couldn't read some unknown bytes."; + LOG_ERROR(_lastError); + return; + } + } + + if(!reader.readUint16(_engineVersion.major)) { + _lastError = "Couldn't read major engine version."; + LOG_ERROR(_lastError); + return; + } + + if(!reader.readUint16(_engineVersion.minor)) { + _lastError = "Couldn't read minor engine version."; + LOG_ERROR(_lastError); + return; + } + + if(!reader.readUint16(_engineVersion.patch)) { + _lastError = "Couldn't read patch engine version."; + LOG_ERROR(_lastError); + return; + } + + if(!reader.readUint32(_engineVersion.build)) { + _lastError = "Couldn't read engine build."; + LOG_ERROR(_lastError); + return; + } + + if(!reader.readUEString(_engineVersion.buildId)) + { + _lastError = "Couldn't read engine build ID string."; + LOG_ERROR(_lastError); + return; + } + + if(!reader.readUint32(_customFormatVersion)) { _lastError = "Couldn't read the custom format version."; LOG_ERROR(_lastError); return; } - UnsignedInt custom_format_data_size = 0; + std::uint32_t custom_format_data_size = 0; - if(!reader.readUnsignedInt(custom_format_data_size)) { + if(!reader.readUint32(custom_format_data_size)) { _lastError = "Couldn't read the custom format data size."; LOG_ERROR(_lastError); return; @@ -218,18 +288,14 @@ void UESaveFile::loadData() { _customFormatData = Containers::Array{custom_format_data_size}; - for(UnsignedInt i = 0; i < custom_format_data_size; i++) { - CustomFormatDataEntry entry; - + for(auto& entry : _customFormatData) { if(!reader.readStaticArray(entry.id) || - !reader.readInt(entry.value)) + !reader.readInt32(entry.value)) { _lastError = "Couldn't read the custom format data"; LOG_ERROR(_lastError); return; } - - _customFormatData[i] = std::move(entry); } if(!reader.readUEString(_saveType)) { @@ -238,9 +304,9 @@ void UESaveFile::loadData() { return; } - UnrealPropertyBase::ptr prop; + Types::UnrealPropertyBase::ptr prop; while((prop = _propSerialiser->read(reader)) != nullptr) { - arrayAppend(_properties, std::move(prop)); + arrayAppend(_properties, Utility::move(prop)); } if(_properties.isEmpty()) { @@ -259,3 +325,5 @@ void UESaveFile::loadData() { _valid = true; } + +} diff --git a/src/UESaveFile/UESaveFile.h b/src/Gvas/File.h similarity index 68% rename from src/UESaveFile/UESaveFile.h rename to src/Gvas/File.h index a281369..0dce428 100644 --- a/src/UESaveFile/UESaveFile.h +++ b/src/Gvas/File.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -17,34 +17,32 @@ // along with this program. If not, see . #include -#include #include #include #include #include -#include - #include "Types/UnrealPropertyBase.h" #include "PropertySerialiser.h" using namespace Corrade; -using namespace Magnum; -class UESaveFile { +namespace Gvas { + +class File { public: - explicit UESaveFile(Containers::String filepath); + explicit File(Containers::String filepath); - auto valid() const -> bool; + bool valid() const; auto lastError() const -> Containers::StringView; - auto reloadData() -> bool; + bool reloadData(); auto saveType() -> Containers::StringView; template - std::enable_if_t::value, T*> + std::enable_if_t::value, T*> at(Containers::StringView name) { for(auto& prop : _properties) { if(prop->name == name) { @@ -54,11 +52,11 @@ class UESaveFile { return nullptr; } - void appendProperty(UnrealPropertyBase::ptr prop); + void appendProperty(Types::UnrealPropertyBase::ptr prop); - auto props() -> Containers::ArrayView; + auto props() -> Containers::ArrayView; - auto saveToFile() -> bool; + bool saveToFile(); private: void loadData(); @@ -72,26 +70,29 @@ class UESaveFile { Containers::StaticArray<4, char> _magicBytes{'G', 'V', 'A', 'S'}; - UnsignedInt _saveVersion = 0; - UnsignedInt _packageVersion = 0; + std::uint32_t _saveVersion = 0; + std::uint32_t _packageVersion = 0; + std::uint32_t _unknown = 0; struct { - UnsignedShort major = 0; - UnsignedShort minor = 0; - UnsignedShort patch = 0; - UnsignedInt build = 0; + std::uint16_t major = 0; + std::uint16_t minor = 0; + std::uint16_t patch = 0; + std::uint32_t build = 0; Containers::String buildId; } _engineVersion; - UnsignedInt _customFormatVersion = 0; + std::uint32_t _customFormatVersion = 0; struct CustomFormatDataEntry { Containers::StaticArray<16, char> id; - Int value = 0; + std::int32_t value = 0; }; Containers::Array _customFormatData; Containers::String _saveType; - Containers::Array _properties; + Containers::Array _properties; Containers::Reference _propSerialiser; }; + +} diff --git a/src/Gvas/Gvas.h b/src/Gvas/Gvas.h new file mode 100644 index 0000000..cec8a20 --- /dev/null +++ b/src/Gvas/Gvas.h @@ -0,0 +1,24 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +namespace Gvas { + +class File; +class PropertySerialiser; + +} diff --git a/src/Gvas/PropertySerialiser.cpp b/src/Gvas/PropertySerialiser.cpp new file mode 100644 index 0000000..c57faad --- /dev/null +++ b/src/Gvas/PropertySerialiser.cpp @@ -0,0 +1,276 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include "../Logger/Logger.h" + +#include "Serialisers/ArrayProperty.h" +#include "Serialisers/BoolProperty.h" +#include "Serialisers/ByteProperty.h" +#include "Serialisers/ColourProperty.h" +#include "Serialisers/DateTimeProperty.h" +#include "Serialisers/EnumProperty.h" +#include "Serialisers/FloatProperty.h" +#include "Serialisers/GuidProperty.h" +#include "Serialisers/IntProperty.h" +#include "Serialisers/MapProperty.h" +#include "Serialisers/ResourceProperty.h" +#include "Serialisers/RotatorProperty.h" +#include "Serialisers/StringProperty.h" +#include "Serialisers/SetProperty.h" +#include "Serialisers/Struct.h" +#include "Serialisers/TextProperty.h" +#include "Serialisers/VectorProperty.h" +#include "Serialisers/Vector2DProperty.h" + +#include "Types/NoneProperty.h" + +#include "../BinaryIo/Reader.h" +#include "../BinaryIo/Writer.h" + +#include "PropertySerialiser.h" + +namespace Gvas { + +PropertySerialiser::PropertySerialiser() { + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + + arrayAppend(_collectionSerialisers, Containers::pointer()); +} + +PropertySerialiser& +PropertySerialiser::instance() { + static PropertySerialiser serialiser; + return serialiser; +} + +Types::UnrealPropertyBase::ptr +PropertySerialiser::read(BinaryIo::Reader& reader) { + if(reader.peekChar() < 0 || reader.eof()) { + return nullptr; + } + + Containers::String name; + if(!reader.readUEString(name)) { + return nullptr; + } + + if(name == "None") { + return Containers::pointer(); + } + + Containers::String type; + if(!reader.readUEString(type)) { + return nullptr; + } + + std::size_t value_length; + if(!reader.readUint64(value_length)) { + return nullptr; + } + + return deserialise(Utility::move(name), Utility::move(type), value_length, reader); +} + +Types::UnrealPropertyBase::ptr +PropertySerialiser::readItem(BinaryIo::Reader& reader, Containers::String type, std::size_t value_length, + Containers::String name) +{ + if(reader.peekChar() < 0 || reader.eof()) { + return nullptr; + } + + return deserialise(Utility::move(name), Utility::move(type), value_length, reader); +} + +Containers::Array +PropertySerialiser::readSet(BinaryIo::Reader& reader, Containers::StringView item_type, std::uint32_t count) { + if(reader.peekChar() < 0 || reader.eof()) { + return nullptr; + } + + auto serialiser = getCollectionSerialiser(item_type); + + Containers::Array array; + + if(serialiser) { + Containers::String name; + if(!reader.readUEString(name)) { + return nullptr; + } + + Containers::String type; + if(!reader.readUEString(type)) { + return nullptr; + } + + std::size_t value_length; + if(!reader.readUint64(value_length)) { + return nullptr; + } + + array = serialiser->deserialise(name, type, value_length, count, reader, *this); + + for(auto& item : array) { + if(item->name == Containers::NullOpt) { + item->name.emplace(name); + } + } + } + else { + for(std::uint32_t i = 0; i < count; i++) { + auto item = readItem(reader, item_type, std::size_t(-1), ""); + arrayAppend(array, Utility::move(item)); + } + } + + return array; +} + +Types::UnrealPropertyBase::ptr +PropertySerialiser::deserialise(Containers::String name, Containers::String type, std::size_t value_length, + BinaryIo::Reader& reader) +{ + Types::UnrealPropertyBase::ptr prop; + auto serialiser = getSerialiser(type); + + if(serialiser == nullptr) { + return nullptr; + } + + prop = serialiser->deserialise(name, type, value_length, reader, *this); + + if(!prop) { + LOG_ERROR("No property."); + return nullptr; + } + + prop->name = Utility::move(name); + prop->propertyType = Utility::move(type); + + return prop; +} + +bool PropertySerialiser::serialise(Types::UnrealPropertyBase::ptr& prop, Containers::StringView item_type, + std::size_t& bytes_written, BinaryIo::Writer& writer) +{ + auto serialiser = getSerialiser(item_type); + if(!serialiser) { + return false; + } + return serialiser->serialise(prop, bytes_written, writer, *this); +} + +bool +PropertySerialiser::write(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, BinaryIo::Writer& writer) { + if(prop->name == "None" && prop->propertyType == "NoneProperty" && dynamic_cast(prop.get())) { + bytes_written += writer.writeUEStringToArray(*prop->name); + return true; + } + + bytes_written += writer.writeUEStringToArray(*prop->name); + bytes_written += writer.writeUEStringToArray(prop->propertyType); + + std::size_t value_length = 0; + std::size_t vl_position = writer.arrayPosition(); + + bytes_written += writer.writeValueToArray(value_length); + + bool ret = serialise(prop, prop->propertyType, value_length, writer); + + writer.writeValueToArrayAt(value_length, vl_position); + + bytes_written += value_length; + + return ret; +} + +bool +PropertySerialiser::writeItem(Types::UnrealPropertyBase::ptr& prop, Containers::StringView item_type, + std::size_t& bytes_written, BinaryIo::Writer& writer) +{ + if(prop->name == "None" && prop->propertyType == "NoneProperty" && dynamic_cast(prop.get())) { + bytes_written += writer.writeUEStringToArray(*prop->name); + return true; + } + + return serialise(prop, item_type, bytes_written, writer); +} + +bool PropertySerialiser::writeSet(Containers::ArrayView props, + Containers::StringView item_type, std::size_t& bytes_written, + BinaryIo::Writer& writer) +{ + auto serialiser = getCollectionSerialiser(item_type); + if(serialiser) { + return serialiser->serialise(props, item_type, bytes_written, writer, *this); + } + else { + for(auto& prop : props) { + if(!writeItem(prop, item_type, bytes_written, writer)) { + return false; + } + } + + return true; + } +} + +Serialisers::AbstractUnrealProperty* +PropertySerialiser::getSerialiser(Containers::StringView item_type) { + for(auto& item : _serialisers) { + for(auto serialiser_type : item->types()) { + if(item_type == serialiser_type) { + return item.get(); + } + } + } + + return nullptr; +} + +Serialisers::AbstractUnrealCollectionProperty* +PropertySerialiser::getCollectionSerialiser(Containers::StringView item_type) { + for(auto& item : _collectionSerialisers) { + for(Containers::StringView serialiser_type : item->types()) { + if(item_type == serialiser_type) { + return item.get(); + } + } + } + + return nullptr; +} + +} diff --git a/src/Gvas/PropertySerialiser.h b/src/Gvas/PropertySerialiser.h new file mode 100644 index 0000000..99b3cd6 --- /dev/null +++ b/src/Gvas/PropertySerialiser.h @@ -0,0 +1,64 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include +#include +#include + +#include "Serialisers/AbstractUnrealProperty.h" +#include "Serialisers/AbstractUnrealCollectionProperty.h" + +#include "Types/UnrealPropertyBase.h" + +#include "../BinaryIo/BinaryIo.h" + +using namespace Corrade; + +namespace Gvas { + +class PropertySerialiser { + public: + static auto instance() -> PropertySerialiser&; + + auto read(BinaryIo::Reader& reader) -> Types::UnrealPropertyBase::ptr; + auto readItem(BinaryIo::Reader& reader, Containers::String type, std::size_t value_length, Containers::String name) + -> Types::UnrealPropertyBase::ptr; + auto readSet(BinaryIo::Reader& reader, Containers::StringView item_type, std::uint32_t count) + -> Containers::Array; + auto deserialise(Containers::String name, Containers::String type, std::size_t value_length, + BinaryIo::Reader& reader) -> Types::UnrealPropertyBase::ptr; + + bool serialise(Types::UnrealPropertyBase::ptr& prop, Containers::StringView item_type, + std::size_t& bytes_written, BinaryIo::Writer& writer); + bool write(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, BinaryIo::Writer& writer); + bool writeItem(Types::UnrealPropertyBase::ptr& prop, Containers::StringView item_type, + std::size_t& bytes_written, BinaryIo::Writer& writer); + bool writeSet(Containers::ArrayView props, Containers::StringView item_type, + std::size_t& bytes_written, BinaryIo::Writer& writer); + + private: + PropertySerialiser(); + + auto getSerialiser(Containers::StringView item_type) -> Serialisers::AbstractUnrealProperty*; + auto getCollectionSerialiser(Containers::StringView item_type) -> Serialisers::AbstractUnrealCollectionProperty*; + + Containers::Array _serialisers; + Containers::Array _collectionSerialisers; +}; + +} diff --git a/src/UESaveFile/Serialisers/AbstractUnrealCollectionPropertySerialiser.h b/src/Gvas/Serialisers/AbstractUnrealCollectionProperty.h similarity index 51% rename from src/UESaveFile/Serialisers/AbstractUnrealCollectionPropertySerialiser.h rename to src/Gvas/Serialisers/AbstractUnrealCollectionProperty.h index ffa273d..e7d6095 100644 --- a/src/UESaveFile/Serialisers/AbstractUnrealCollectionPropertySerialiser.h +++ b/src/Gvas/Serialisers/AbstractUnrealCollectionProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -21,29 +21,33 @@ #include #include -#include - +#include "../Gvas.h" +#include "../../BinaryIo/BinaryIo.h" #include "../Types/UnrealPropertyBase.h" using namespace Corrade; -using namespace Magnum; -class BinaryReader; -class BinaryWriter; -class PropertySerialiser; +namespace Gvas::Serialisers { -class AbstractUnrealCollectionPropertySerialiser { +using PropertyArray = Containers::Array; +using PropertyArrayView = Containers::ArrayView; +using StringArrayView = Containers::ArrayView; + +class AbstractUnrealCollectionProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; - virtual ~AbstractUnrealCollectionPropertySerialiser() = default; + virtual ~AbstractUnrealCollectionProperty() = default; - virtual auto types() -> Containers::ArrayView = 0; + virtual auto types() -> StringArrayView = 0; - virtual auto deserialise(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, UnsignedInt count, BinaryReader& reader, - PropertySerialiser& serialiser) -> Containers::Array = 0; + virtual auto deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length, + std::uint32_t count, BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> PropertyArray = 0; - virtual auto serialise(Containers::ArrayView props, Containers::StringView item_type, - UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool = 0; + virtual bool serialise(PropertyArrayView props, Containers::StringView item_type, + std::size_t& bytes_written, BinaryIo::Writer& writer, + PropertySerialiser& serialiser) = 0; }; + +} diff --git a/src/UESaveFile/Serialisers/AbstractUnrealPropertySerialiser.h b/src/Gvas/Serialisers/AbstractUnrealProperty.h similarity index 54% rename from src/UESaveFile/Serialisers/AbstractUnrealPropertySerialiser.h rename to src/Gvas/Serialisers/AbstractUnrealProperty.h index 0ddbfd1..a8030c4 100644 --- a/src/UESaveFile/Serialisers/AbstractUnrealPropertySerialiser.h +++ b/src/Gvas/Serialisers/AbstractUnrealProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -20,28 +20,30 @@ #include #include -#include - +#include "../Gvas.h" +#include "../../BinaryIo/BinaryIo.h" #include "../Types/UnrealPropertyBase.h" using namespace Corrade; -using namespace Magnum; -class BinaryReader; -class BinaryWriter; -class PropertySerialiser; +namespace Gvas::Serialisers { -class AbstractUnrealPropertySerialiser { +using StringArrayView = Containers::ArrayView; + +class AbstractUnrealProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; - virtual ~AbstractUnrealPropertySerialiser() = default; + virtual ~AbstractUnrealProperty() = default; - virtual auto types() -> Containers::ArrayView = 0; + virtual auto types() -> StringArrayView = 0; - virtual auto deserialise(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr = 0; + virtual auto deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr = 0; - virtual auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool = 0; + virtual bool serialise(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) = 0; }; + +} diff --git a/src/UESaveFile/Serialisers/AbstractUnrealStructSerialiser.h b/src/Gvas/Serialisers/AbstractUnrealStruct.h similarity index 59% rename from src/UESaveFile/Serialisers/AbstractUnrealStructSerialiser.h rename to src/Gvas/Serialisers/AbstractUnrealStruct.h index 7e62fc6..b9d1e08 100644 --- a/src/UESaveFile/Serialisers/AbstractUnrealStructSerialiser.h +++ b/src/Gvas/Serialisers/AbstractUnrealStruct.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -21,26 +21,26 @@ #include #include -#include - +#include "../Gvas.h" +#include "../../BinaryIo/BinaryIo.h" #include "../Types/UnrealPropertyBase.h" using namespace Corrade; -using namespace Magnum; -class BinaryReader; -class BinaryWriter; +namespace Gvas::Serialisers { -class AbstractUnrealStructSerialiser { +class AbstractUnrealStruct { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; - virtual ~AbstractUnrealStructSerialiser() = default; + virtual ~AbstractUnrealStruct() = default; - virtual auto supportsType(Containers::StringView type) -> bool = 0; + virtual bool supportsType(Containers::StringView type) = 0; - virtual auto deserialise(BinaryReader& reader) -> UnrealPropertyBase::ptr = 0; + virtual auto deserialise(BinaryIo::Reader& reader) -> Types::UnrealPropertyBase::ptr = 0; - virtual auto serialise(UnrealPropertyBase::ptr& structProp, BinaryWriter& writer, - UnsignedLong& bytes_written) -> bool = 0; + virtual bool serialise(Types::UnrealPropertyBase::ptr& structProp, BinaryIo::Writer& writer, + std::size_t& bytes_written) = 0; }; + +} diff --git a/src/UESaveFile/Serialisers/ArrayPropertySerialiser.cpp b/src/Gvas/Serialisers/ArrayProperty.cpp similarity index 61% rename from src/UESaveFile/Serialisers/ArrayPropertySerialiser.cpp rename to src/Gvas/Serialisers/ArrayProperty.cpp index d2efbb8..d6c4254 100644 --- a/src/UESaveFile/Serialisers/ArrayPropertySerialiser.cpp +++ b/src/Gvas/Serialisers/ArrayProperty.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,16 +16,18 @@ #include -#include "../BinaryReader.h" -#include "../BinaryWriter.h" +#include "../../BinaryIo/Reader.h" +#include "../../BinaryIo/Writer.h" #include "../PropertySerialiser.h" #include "../../Logger/Logger.h" -#include "ArrayPropertySerialiser.h" +#include "ArrayProperty.h" -auto ArrayPropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, BinaryReader& reader, - PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +namespace Gvas::Serialisers { + +Types::UnrealPropertyBase::ptr +ArrayProperty::deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) { Containers::String item_type; if(!reader.readUEString(item_type)) { @@ -39,23 +41,24 @@ auto ArrayPropertySerialiser::deserialiseProperty(Containers::StringView name, C return nullptr; } - UnsignedInt item_count; - if(!reader.readUnsignedInt(item_count)) { + std::uint32_t item_count; + if(!reader.readUint32(item_count)) { LOG_ERROR_FORMAT("Couldn't read array property {}'s item count.", name); return nullptr; } - auto prop = Containers::pointer(); - prop->itemType = std::move(item_type); + auto prop = Containers::pointer(); + prop->itemType = Utility::move(item_type); prop->items = serialiser.readSet(reader, prop->itemType, item_count); return prop; } -auto ArrayPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +bool +ArrayProperty::serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) { - auto array_prop = dynamic_cast(prop.get()); + auto array_prop = dynamic_cast(prop.get()); if(!array_prop) { LOG_ERROR("The property is not a valid array property."); return false; @@ -63,12 +66,14 @@ auto ArrayPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, U writer.writeUEStringToArray(array_prop->itemType); writer.writeValueToArray('\0'); - bytes_written += writer.writeValueToArray(UnsignedInt(array_prop->items.size())); + bytes_written += writer.writeValueToArray(std::uint32_t(array_prop->items.size())); - UnsignedLong start_pos = writer.arrayPosition(); - UnsignedLong dummy_bytes_written = 0; + std::size_t start_pos = writer.arrayPosition(); + std::size_t dummy_bytes_written = 0; bool ret = serialiser.writeSet(array_prop->items, array_prop->itemType, dummy_bytes_written, writer); bytes_written += writer.arrayPosition() - start_pos; return ret; } + +} diff --git a/src/UESaveFile/Serialisers/ArrayPropertySerialiser.h b/src/Gvas/Serialisers/ArrayProperty.h similarity index 57% rename from src/UESaveFile/Serialisers/ArrayPropertySerialiser.h rename to src/Gvas/Serialisers/ArrayProperty.h index f9b0334..9a0080a 100644 --- a/src/UESaveFile/Serialisers/ArrayPropertySerialiser.h +++ b/src/Gvas/Serialisers/ArrayProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -18,22 +18,24 @@ #include -#include - -#include "UnrealPropertySerialiser.h" - +#include "../Gvas.h" +#include "UnrealProperty.h" #include "../Types/ArrayProperty.h" using namespace Corrade; -using namespace Magnum; -class ArrayPropertySerialiser : public UnrealPropertySerialiser { +namespace Gvas::Serialisers { + +class ArrayProperty : public UnrealProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; private: - auto deserialiseProperty(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; - auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool override; + auto deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override; + bool serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) override; }; + +} diff --git a/src/UESaveFile/Serialisers/BoolPropertySerialiser.cpp b/src/Gvas/Serialisers/BoolProperty.cpp similarity index 61% rename from src/UESaveFile/Serialisers/BoolPropertySerialiser.cpp rename to src/Gvas/Serialisers/BoolProperty.cpp index 7f9e6cc..80de98a 100644 --- a/src/UESaveFile/Serialisers/BoolPropertySerialiser.cpp +++ b/src/Gvas/Serialisers/BoolProperty.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -14,29 +14,32 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "../BinaryReader.h" -#include "../BinaryWriter.h" +#include "../../BinaryIo/Reader.h" +#include "../../BinaryIo/Writer.h" #include "../../Logger/Logger.h" -#include "BoolPropertySerialiser.h" +#include "BoolProperty.h" -auto BoolPropertySerialiser::types() -> Containers::ArrayView { +namespace Gvas::Serialisers { + +StringArrayView +BoolProperty::types() { using namespace Containers::Literals; static const Containers::Array types{InPlaceInit, {"BoolProperty"_s}}; return types; } -auto BoolPropertySerialiser::deserialise(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, BinaryReader& reader, - PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +Types::UnrealPropertyBase::ptr +BoolProperty::deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) { 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)) { + std::int16_t value; + if(!reader.readInt16(value)) { LOG_ERROR_FORMAT("Couldn't read bool property {}'s value.", name); return nullptr; } @@ -46,22 +49,25 @@ auto BoolPropertySerialiser::deserialise(Containers::StringView name, Containers return nullptr; } - auto prop = Containers::pointer(); + auto prop = Containers::pointer(); prop->value = value; return prop; } -auto BoolPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +bool +BoolProperty::serialise(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, BinaryIo::Writer& writer, + PropertySerialiser& serialiser) { - auto bool_prop = dynamic_cast(prop.get()); + auto bool_prop = dynamic_cast(prop.get()); if(!bool_prop) { LOG_ERROR("The property is not a valid bool property."); return false; } - writer.writeValueToArray(Short(bool_prop->value)); + writer.writeValueToArray(std::int16_t(bool_prop->value)); return true; } + +} diff --git a/src/UESaveFile/Serialisers/BoolPropertySerialiser.h b/src/Gvas/Serialisers/BoolProperty.h similarity index 58% rename from src/UESaveFile/Serialisers/BoolPropertySerialiser.h rename to src/Gvas/Serialisers/BoolProperty.h index ddbbf85..a80b2e2 100644 --- a/src/UESaveFile/Serialisers/BoolPropertySerialiser.h +++ b/src/Gvas/Serialisers/BoolProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -19,21 +19,26 @@ #include #include -#include "AbstractUnrealPropertySerialiser.h" +#include "AbstractUnrealProperty.h" #include "../Types/BoolProperty.h" using namespace Corrade; -class BoolPropertySerialiser : public AbstractUnrealPropertySerialiser { +namespace Gvas::Serialisers { + +class BoolProperty : public AbstractUnrealProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; - auto types() -> Containers::ArrayView override; + auto types() -> StringArrayView override; - auto deserialise(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + auto deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override; - auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool override; + bool serialise(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, BinaryIo::Writer& writer, + PropertySerialiser& serialiser) override; }; + +} diff --git a/src/UESaveFile/Serialisers/BytePropertySerialiser.cpp b/src/Gvas/Serialisers/ByteProperty.cpp similarity index 64% rename from src/UESaveFile/Serialisers/BytePropertySerialiser.cpp rename to src/Gvas/Serialisers/ByteProperty.cpp index eb24db1..fba23ea 100644 --- a/src/UESaveFile/Serialisers/BytePropertySerialiser.cpp +++ b/src/Gvas/Serialisers/ByteProperty.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -14,25 +14,28 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "../BinaryReader.h" -#include "../BinaryWriter.h" +#include "../../BinaryIo/Reader.h" +#include "../../BinaryIo/Writer.h" #include "../../Logger/Logger.h" -#include "BytePropertySerialiser.h" +#include "ByteProperty.h" -auto BytePropertySerialiser::types() -> Containers::ArrayView { +namespace Gvas::Serialisers { + +StringArrayView +ByteProperty::types() { using namespace Containers::Literals; static const Containers::Array types{InPlaceInit, {"ByteProperty"_s}}; return types; } -auto BytePropertySerialiser::deserialise(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, BinaryReader& reader, - PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +Types::UnrealPropertyBase::ptr +ByteProperty::deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) { - auto prop = Containers::pointer(); + auto prop = Containers::pointer(); - if(value_length != UnsignedLong(-1)) { + if(value_length != std::size_t(-1)) { if(!reader.readUEString(prop->enumType)) { LOG_ERROR_FORMAT("Couldn't read byte property {}'s enum type.", name); return nullptr; @@ -52,8 +55,8 @@ auto BytePropertySerialiser::deserialise(Containers::StringView name, Containers prop->valueLength = value_length; - //UnsignedInt count = 0; - //if(!reader.readUnsignedInt(count)) { + //std::uint32_t count = 0; + //if(!reader.readstd::uint32_t(count)) { // return nullptr; //} @@ -64,20 +67,21 @@ auto BytePropertySerialiser::deserialise(Containers::StringView name, Containers return prop; } -auto BytePropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +bool +ByteProperty::serialise(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, BinaryIo::Writer& writer, + PropertySerialiser& serialiser) { - auto byte_prop = dynamic_cast(prop.get()); + auto byte_prop = dynamic_cast(prop.get()); if(!byte_prop) { LOG_ERROR("The property is not a valid byte property."); return false; } //writer.writeValueToArray('\0'); - //bytes_written += writer.writeValueToArray(byte_prop->value.size()); + //bytes_written += writer.writeValueToArray(byte_prop->value.size()); //bytes_written += writer.writeDataToArray(byte_prop->value); - if(byte_prop->valueLength != UnsignedLong(-1)) { + if(byte_prop->valueLength != std::size_t(-1)) { writer.writeUEStringToArray(byte_prop->enumType); writer.writeValueToArray('\0'); } @@ -86,3 +90,5 @@ auto BytePropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLo return true; } + +} diff --git a/src/UESaveFile/Serialisers/BytePropertySerialiser.h b/src/Gvas/Serialisers/ByteProperty.h similarity index 57% rename from src/UESaveFile/Serialisers/BytePropertySerialiser.h rename to src/Gvas/Serialisers/ByteProperty.h index 74b7205..4a80cba 100644 --- a/src/UESaveFile/Serialisers/BytePropertySerialiser.h +++ b/src/Gvas/Serialisers/ByteProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -19,19 +19,24 @@ #include #include -#include "AbstractUnrealPropertySerialiser.h" +#include "AbstractUnrealProperty.h" #include "../Types/ByteProperty.h" -class BytePropertySerialiser : public AbstractUnrealPropertySerialiser { +namespace Gvas::Serialisers { + +class ByteProperty : public AbstractUnrealProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; - auto types() -> Containers::ArrayView override; + auto types() -> StringArrayView override; - auto deserialise(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + auto deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override; - auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool override; + bool serialise(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, BinaryIo::Writer& writer, + PropertySerialiser& serialiser) override; }; + +} diff --git a/src/UESaveFile/Serialisers/ColourPropertySerialiser.cpp b/src/Gvas/Serialisers/ColourProperty.cpp similarity index 51% rename from src/UESaveFile/Serialisers/ColourPropertySerialiser.cpp rename to src/Gvas/Serialisers/ColourProperty.cpp index d90035f..d2923af 100644 --- a/src/UESaveFile/Serialisers/ColourPropertySerialiser.cpp +++ b/src/Gvas/Serialisers/ColourProperty.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -14,17 +14,19 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "../BinaryReader.h" -#include "../BinaryWriter.h" +#include "../../BinaryIo/Reader.h" +#include "../../BinaryIo/Writer.h" #include "../../Logger/Logger.h" -#include "ColourPropertySerialiser.h" +#include "ColourProperty.h" -auto ColourPropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, BinaryReader& reader, - PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +namespace Gvas::Serialisers { + +Types::UnrealPropertyBase::ptr +ColourProperty::deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) { - auto prop = Containers::pointer(); + auto prop = Containers::pointer(); if(!reader.readFloat(prop->r) || !reader.readFloat(prop->g) || !reader.readFloat(prop->b) || !reader.readFloat(prop->a)) @@ -36,17 +38,22 @@ auto ColourPropertySerialiser::deserialiseProperty(Containers::StringView name, return prop; } -auto ColourPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +bool +ColourProperty::serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) { - auto colour_prop = dynamic_cast(prop.get()); + auto colour_prop = dynamic_cast(prop.get()); if(!colour_prop) { LOG_ERROR("The property is not a valid colour property."); return false; } - bytes_written += writer.writeValueToArray(colour_prop->r) + writer.writeValueToArray(colour_prop->g) + - writer.writeValueToArray(colour_prop->b) + writer.writeValueToArray(colour_prop->a); + bytes_written += writer.writeValueToArray(colour_prop->r) + + writer.writeValueToArray(colour_prop->g) + + writer.writeValueToArray(colour_prop->b) + + writer.writeValueToArray(colour_prop->a); return true; } + +} diff --git a/src/UESaveFile/Serialisers/ColourPropertySerialiser.h b/src/Gvas/Serialisers/ColourProperty.h similarity index 57% rename from src/UESaveFile/Serialisers/ColourPropertySerialiser.h rename to src/Gvas/Serialisers/ColourProperty.h index d14dd1f..69ac66d 100644 --- a/src/UESaveFile/Serialisers/ColourPropertySerialiser.h +++ b/src/Gvas/Serialisers/ColourProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -18,19 +18,24 @@ #include -#include "UnrealPropertySerialiser.h" +#include "UnrealProperty.h" #include "../Types/ColourStructProperty.h" using namespace Corrade; -class ColourPropertySerialiser : public UnrealPropertySerialiser { +namespace Gvas::Serialisers { + +class ColourProperty : public UnrealProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; private: - auto deserialiseProperty(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; - auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool override; + auto deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override; + bool serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) override; }; + +} diff --git a/src/UESaveFile/Serialisers/DateTimePropertySerialiser.cpp b/src/Gvas/Serialisers/DateTimeProperty.cpp similarity index 51% rename from src/UESaveFile/Serialisers/DateTimePropertySerialiser.cpp rename to src/Gvas/Serialisers/DateTimeProperty.cpp index 6e32666..b4b5d9c 100644 --- a/src/UESaveFile/Serialisers/DateTimePropertySerialiser.cpp +++ b/src/Gvas/Serialisers/DateTimeProperty.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -14,19 +14,21 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "../BinaryReader.h" -#include "../BinaryWriter.h" +#include "../../BinaryIo/Reader.h" +#include "../../BinaryIo/Writer.h" #include "../../Logger/Logger.h" -#include "DateTimePropertySerialiser.h" +#include "DateTimeProperty.h" -auto DateTimePropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, BinaryReader& reader, - PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +namespace Gvas::Serialisers { + +Types::UnrealPropertyBase::ptr +DateTimeProperty::deserialiseProperty(Containers::StringView name, Containers::StringView type, + std::size_t value_length, BinaryIo::Reader& reader, PropertySerialiser& serialiser) { - auto prop = Containers::pointer(); + auto prop = Containers::pointer(); - if(!reader.readUnsignedLong(prop->timestamp)) { + if(!reader.readInt64(prop->timestamp)) { LOG_ERROR_FORMAT("Couldn't read date/time property {}'s value.", name); return nullptr; } @@ -34,16 +36,19 @@ auto DateTimePropertySerialiser::deserialiseProperty(Containers::StringView name return prop; } -auto DateTimePropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +bool +DateTimeProperty::serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) { - auto dt_prop = dynamic_cast(prop.get()); + auto dt_prop = dynamic_cast(prop.get()); if(!dt_prop) { LOG_ERROR("The property is not a valid date/time property."); return false; } - bytes_written += writer.writeValueToArray(dt_prop->timestamp); + bytes_written += writer.writeValueToArray(dt_prop->timestamp); return true; } + +} diff --git a/src/UESaveFile/Serialisers/DateTimePropertySerialiser.h b/src/Gvas/Serialisers/DateTimeProperty.h similarity index 56% rename from src/UESaveFile/Serialisers/DateTimePropertySerialiser.h rename to src/Gvas/Serialisers/DateTimeProperty.h index 51db914..8b7dfa0 100644 --- a/src/UESaveFile/Serialisers/DateTimePropertySerialiser.h +++ b/src/Gvas/Serialisers/DateTimeProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -18,17 +18,22 @@ #include -#include "UnrealPropertySerialiser.h" +#include "UnrealProperty.h" #include "../Types/DateTimeStructProperty.h" -class DateTimePropertySerialiser : public UnrealPropertySerialiser { +namespace Gvas::Serialisers { + +class DateTimeProperty : public UnrealProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; private: - auto deserialiseProperty(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; - auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool override; + auto deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override; + bool serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) override; }; + +} diff --git a/src/UESaveFile/Serialisers/EnumPropertySerialiser.cpp b/src/Gvas/Serialisers/EnumProperty.cpp similarity index 67% rename from src/UESaveFile/Serialisers/EnumPropertySerialiser.cpp rename to src/Gvas/Serialisers/EnumProperty.cpp index 4cb5835..04feb7d 100644 --- a/src/UESaveFile/Serialisers/EnumPropertySerialiser.cpp +++ b/src/Gvas/Serialisers/EnumProperty.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -14,23 +14,26 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "../BinaryReader.h" -#include "../BinaryWriter.h" +#include "../../BinaryIo/Reader.h" +#include "../../BinaryIo/Writer.h" #include "../../Logger/Logger.h" -#include "EnumPropertySerialiser.h" +#include "EnumProperty.h" -auto EnumPropertySerialiser::types() -> Containers::ArrayView { +namespace Gvas::Serialisers { + +StringArrayView +EnumProperty::types() { using namespace Containers::Literals; static const Containers::Array types{InPlaceInit, {"EnumProperty"_s}}; return types; } -auto EnumPropertySerialiser::deserialise(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, BinaryReader& reader, - PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +Types::UnrealPropertyBase::ptr +EnumProperty::deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) { - auto prop = Containers::pointer(); + auto prop = Containers::pointer(); if(!reader.readUEString(prop->enumType)) { LOG_ERROR_FORMAT("Couldn't read enum property {}'s enum type.", name); @@ -51,10 +54,11 @@ auto EnumPropertySerialiser::deserialise(Containers::StringView name, Containers return prop; } -auto EnumPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +bool +EnumProperty::serialise(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, BinaryIo::Writer& writer, + PropertySerialiser& serialiser) { - auto enum_prop = dynamic_cast(prop.get()); + auto enum_prop = dynamic_cast(prop.get()); if(!enum_prop) { LOG_ERROR("The property is not a valid enum property."); return false; @@ -66,3 +70,5 @@ auto EnumPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLo return true; } + +} diff --git a/src/UESaveFile/Serialisers/EnumPropertySerialiser.h b/src/Gvas/Serialisers/EnumProperty.h similarity index 57% rename from src/UESaveFile/Serialisers/EnumPropertySerialiser.h rename to src/Gvas/Serialisers/EnumProperty.h index 57b821b..97929f3 100644 --- a/src/UESaveFile/Serialisers/EnumPropertySerialiser.h +++ b/src/Gvas/Serialisers/EnumProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -19,19 +19,24 @@ #include #include -#include "AbstractUnrealPropertySerialiser.h" +#include "AbstractUnrealProperty.h" #include "../Types/EnumProperty.h" -class EnumPropertySerialiser : public AbstractUnrealPropertySerialiser { +namespace Gvas::Serialisers { + +class EnumProperty : public AbstractUnrealProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; - auto types() -> Containers::ArrayView override; + auto types() -> StringArrayView override; - auto deserialise(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + auto deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override; - auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool override; + bool serialise(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, BinaryIo::Writer& writer, + PropertySerialiser& serialiser) override; }; + +} diff --git a/src/UESaveFile/Serialisers/FloatPropertySerialiser.cpp b/src/Gvas/Serialisers/FloatProperty.cpp similarity index 61% rename from src/UESaveFile/Serialisers/FloatPropertySerialiser.cpp rename to src/Gvas/Serialisers/FloatProperty.cpp index 4134f94..11be552 100644 --- a/src/UESaveFile/Serialisers/FloatPropertySerialiser.cpp +++ b/src/Gvas/Serialisers/FloatProperty.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -14,23 +14,26 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "../BinaryReader.h" -#include "../BinaryWriter.h" +#include "../../BinaryIo/Reader.h" +#include "../../BinaryIo/Writer.h" #include "../../Logger/Logger.h" -#include "FloatPropertySerialiser.h" +#include "FloatProperty.h" -auto FloatPropertySerialiser::types() -> Containers::ArrayView { +namespace Gvas::Serialisers { + +StringArrayView +FloatProperty::types() { using namespace Containers::Literals; static const Containers::Array types{InPlaceInit, {"FloatProperty"_s}}; return types; } -auto FloatPropertySerialiser::deserialise(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, BinaryReader& reader, - PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +Types::UnrealPropertyBase::ptr +FloatProperty::deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) { - auto prop = Containers::pointer(); + auto prop = Containers::pointer(); char terminator; if(!reader.readChar(terminator) || terminator != '\0') { @@ -46,17 +49,20 @@ auto FloatPropertySerialiser::deserialise(Containers::StringView name, Container return prop; } -auto FloatPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +bool +FloatProperty::serialise(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, BinaryIo::Writer& writer, + PropertySerialiser& serialiser) { - auto float_prop = dynamic_cast(prop.get()); + auto float_prop = dynamic_cast(prop.get()); if(!float_prop) { LOG_ERROR("The property is not a valid float property."); return false; } writer.writeValueToArray('\0'); - bytes_written += writer.writeValueToArray(float_prop->value); + bytes_written += writer.writeValueToArray(float_prop->value); return true; } + +} diff --git a/src/UESaveFile/Serialisers/FloatPropertySerialiser.h b/src/Gvas/Serialisers/FloatProperty.h similarity index 57% rename from src/UESaveFile/Serialisers/FloatPropertySerialiser.h rename to src/Gvas/Serialisers/FloatProperty.h index d09d31b..5547a7b 100644 --- a/src/UESaveFile/Serialisers/FloatPropertySerialiser.h +++ b/src/Gvas/Serialisers/FloatProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -19,19 +19,24 @@ #include #include -#include "AbstractUnrealPropertySerialiser.h" +#include "AbstractUnrealProperty.h" #include "../Types/FloatProperty.h" -class FloatPropertySerialiser : public AbstractUnrealPropertySerialiser { +namespace Gvas::Serialisers { + +class FloatProperty : public AbstractUnrealProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; - auto types() -> Containers::ArrayView override; + auto types() -> StringArrayView override; - auto deserialise(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + auto deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override; - auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool override; + bool serialise(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, BinaryIo::Writer& writer, + PropertySerialiser& serialiser) override; }; + +} diff --git a/src/UESaveFile/Serialisers/GuidPropertySerialiser.cpp b/src/Gvas/Serialisers/GuidProperty.cpp similarity index 56% rename from src/UESaveFile/Serialisers/GuidPropertySerialiser.cpp rename to src/Gvas/Serialisers/GuidProperty.cpp index dd0036b..5626346 100644 --- a/src/UESaveFile/Serialisers/GuidPropertySerialiser.cpp +++ b/src/Gvas/Serialisers/GuidProperty.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -14,19 +14,21 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "../BinaryReader.h" -#include "../BinaryWriter.h" +#include "../../BinaryIo/Reader.h" +#include "../../BinaryIo/Writer.h" #include "../../Logger/Logger.h" -#include "GuidPropertySerialiser.h" +#include "GuidProperty.h" using namespace Containers::Literals; -auto GuidPropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, BinaryReader& reader, - PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +namespace Gvas::Serialisers { + +Types::UnrealPropertyBase::ptr +GuidProperty::deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) { - auto prop = Containers::pointer(); + auto prop = Containers::pointer(); if(!reader.readStaticArray(prop->guid)) { LOG_ERROR_FORMAT("Couldn't read GUID property {}'s value.", name); @@ -36,12 +38,13 @@ auto GuidPropertySerialiser::deserialiseProperty(Containers::StringView name, Co return prop; } -auto GuidPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +bool +GuidProperty::serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) { - auto guid_prop = dynamic_cast(prop.get()); + auto guid_prop = dynamic_cast(prop.get()); if(!guid_prop) { - LOG_ERROR("The property is not a valid byte property."); + LOG_ERROR("The property is not a valid GUID property."); return false; } @@ -49,3 +52,5 @@ auto GuidPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, Un return true; } + +} diff --git a/src/UESaveFile/Serialisers/GuidPropertySerialiser.h b/src/Gvas/Serialisers/GuidProperty.h similarity index 57% rename from src/UESaveFile/Serialisers/GuidPropertySerialiser.h rename to src/Gvas/Serialisers/GuidProperty.h index 33f7d8c..5942be5 100644 --- a/src/UESaveFile/Serialisers/GuidPropertySerialiser.h +++ b/src/Gvas/Serialisers/GuidProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -18,17 +18,22 @@ #include -#include "UnrealPropertySerialiser.h" +#include "UnrealProperty.h" #include "../Types/GuidStructProperty.h" -class GuidPropertySerialiser : public UnrealPropertySerialiser { +namespace Gvas::Serialisers { + +class GuidProperty : public UnrealProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; private: - auto deserialiseProperty(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; - auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool override; + auto deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override; + bool serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) override; }; + +} diff --git a/src/UESaveFile/Serialisers/IntPropertySerialiser.cpp b/src/Gvas/Serialisers/IntProperty.cpp similarity index 56% rename from src/UESaveFile/Serialisers/IntPropertySerialiser.cpp rename to src/Gvas/Serialisers/IntProperty.cpp index 3c2e50f..c08b1ec 100644 --- a/src/UESaveFile/Serialisers/IntPropertySerialiser.cpp +++ b/src/Gvas/Serialisers/IntProperty.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -14,25 +14,27 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "../BinaryReader.h" -#include "../BinaryWriter.h" +#include "../../BinaryIo/Reader.h" +#include "../../BinaryIo/Writer.h" #include "../../Logger/Logger.h" -#include "IntPropertySerialiser.h" +#include "IntProperty.h" -auto IntPropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, BinaryReader& reader, - PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +namespace Gvas::Serialisers { + +Types::UnrealPropertyBase::ptr +IntProperty::deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) { - auto prop = Containers::pointer(); + auto prop = Containers::pointer(); - if(value_length == UnsignedLong(-1)) { - if(!reader.readInt(prop->value)) { + if(value_length == std::size_t(-1)) { + if(!reader.readInt32(prop->value)) { LOG_ERROR("Couldn't read int property's value."); return nullptr; } - prop->valueLength = UnsignedLong(-1); + prop->valueLength = std::size_t(-1); return prop; } @@ -42,7 +44,7 @@ auto IntPropertySerialiser::deserialiseProperty(Containers::StringView name, Con return nullptr; } - if(!reader.readInt(prop->value)) { + if(!reader.readInt32(prop->value)) { LOG_ERROR_FORMAT("Couldn't read int property {}'s value.", name); return nullptr; } @@ -52,20 +54,23 @@ auto IntPropertySerialiser::deserialiseProperty(Containers::StringView name, Con return prop; } -auto IntPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +bool +IntProperty::serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) { - auto int_prop = dynamic_cast(prop.get()); + auto int_prop = dynamic_cast(prop.get()); if(!int_prop) { LOG_ERROR("The property is not a valid int property."); return false; } - if(prop->valueLength != UnsignedLong(-1)) { + if(prop->valueLength != std::size_t(-1)) { writer.writeValueToArray('\0'); } - bytes_written += writer.writeValueToArray(int_prop->value); + bytes_written += writer.writeValueToArray(int_prop->value); return true; } + +} diff --git a/src/UESaveFile/Serialisers/IntPropertySerialiser.h b/src/Gvas/Serialisers/IntProperty.h similarity index 57% rename from src/UESaveFile/Serialisers/IntPropertySerialiser.h rename to src/Gvas/Serialisers/IntProperty.h index 4dc9f88..59e0ca6 100644 --- a/src/UESaveFile/Serialisers/IntPropertySerialiser.h +++ b/src/Gvas/Serialisers/IntProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -18,17 +18,22 @@ #include -#include "UnrealPropertySerialiser.h" +#include "UnrealProperty.h" #include "../Types/IntProperty.h" -class IntPropertySerialiser : public UnrealPropertySerialiser { +namespace Gvas::Serialisers { + +class IntProperty : public UnrealProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; private: - auto deserialiseProperty(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; - auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool override; + auto deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override; + bool serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) override; }; + +} diff --git a/src/UESaveFile/Serialisers/MapPropertySerialiser.cpp b/src/Gvas/Serialisers/MapProperty.cpp similarity index 72% rename from src/UESaveFile/Serialisers/MapPropertySerialiser.cpp rename to src/Gvas/Serialisers/MapProperty.cpp index 35a9055..041f0f9 100644 --- a/src/UESaveFile/Serialisers/MapPropertySerialiser.cpp +++ b/src/Gvas/Serialisers/MapProperty.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -14,21 +14,23 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "../BinaryReader.h" -#include "../BinaryWriter.h" +#include "../../BinaryIo/Reader.h" +#include "../../BinaryIo/Writer.h" #include "../PropertySerialiser.h" #include "../Types/NoneProperty.h" #include "../../Logger/Logger.h" -#include "MapPropertySerialiser.h" +#include "MapProperty.h" using namespace Containers::Literals; -auto MapPropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, BinaryReader& reader, - PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +namespace Gvas::Serialisers { + +Types::UnrealPropertyBase::ptr +MapProperty::deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) { - auto prop = Containers::pointer(); + auto prop = Containers::pointer(); if(!reader.readUEString(prop->keyType)) { LOG_ERROR_FORMAT("Couldn't read map property {}'s key type.", name); @@ -46,14 +48,14 @@ auto MapPropertySerialiser::deserialiseProperty(Containers::StringView name, Con return nullptr; } - UnsignedInt null; - if(!reader.readUnsignedInt(null) || null != 0u) { + std::uint32_t null; + if(!reader.readUint32(null) || null != 0u) { LOG_ERROR_FORMAT("Couldn't read a null int in map property {}.", name); return nullptr; } - UnsignedInt count; - if(!reader.readUnsignedInt(count)) { + std::uint32_t count; + if(!reader.readUint32(count)) { LOG_ERROR_FORMAT("Couldn't read map property {}'s item count.", name); return nullptr; } @@ -63,8 +65,8 @@ auto MapPropertySerialiser::deserialiseProperty(Containers::StringView name, Con arrayReserve(prop->map, count); - for(UnsignedInt i = 0; i < count; i++) { - MapProperty::KeyValuePair pair; + for(std::uint32_t i = 0; i < count; i++) { + Types::MapProperty::KeyValuePair pair; if(prop->keyType == "IntProperty"_s || prop->keyType == "StrProperty"_s) { pair.key = serialiser.readItem(reader, prop->keyType, -1, name); @@ -78,14 +80,14 @@ auto MapPropertySerialiser::deserialiseProperty(Containers::StringView name, Con return nullptr; } - UnrealPropertyBase::ptr value_item; + Types::UnrealPropertyBase::ptr value_item; if(prop->valueType == "StructProperty"_s) { while((value_item = serialiser.read(reader)) != nullptr) { - arrayAppend(pair.values, std::move(value_item)); + arrayAppend(pair.values, Utility::move(value_item)); if(pair.values.back()->name == "None"_s && pair.values.back()->propertyType == "NoneProperty"_s && - dynamic_cast(pair.values.back().get()) != nullptr) + dynamic_cast(pair.values.back().get()) != nullptr) { break; } @@ -96,10 +98,10 @@ auto MapPropertySerialiser::deserialiseProperty(Containers::StringView name, Con return nullptr; } - arrayAppend(pair.values, std::move(value_item)); + arrayAppend(pair.values, Utility::move(value_item)); } - arrayAppend(prop->map, std::move(pair)); + arrayAppend(prop->map, Utility::move(pair)); } // End dirty code @@ -107,10 +109,11 @@ auto MapPropertySerialiser::deserialiseProperty(Containers::StringView name, Con return prop; } -auto MapPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +bool +MapProperty::serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) { - auto map_prop = dynamic_cast(prop.get()); + auto map_prop = dynamic_cast(prop.get()); if(!map_prop) { LOG_ERROR("The property is not a valid map property."); return false; @@ -120,12 +123,12 @@ auto MapPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, Uns writer.writeUEStringToArray(map_prop->valueType); writer.writeValueToArray('\0'); - UnsignedLong value_start = writer.arrayPosition(); - writer.writeValueToArray(0u); + std::size_t value_start = writer.arrayPosition(); + writer.writeValueToArray(0u); - writer.writeValueToArray(UnsignedInt(map_prop->map.size())); + writer.writeValueToArray(std::uint32_t(map_prop->map.size())); - UnsignedLong dummy_bytes_written = 0; + std::size_t 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."); @@ -152,3 +155,5 @@ auto MapPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, Uns return true; } + +} diff --git a/src/UESaveFile/Serialisers/MapPropertySerialiser.h b/src/Gvas/Serialisers/MapProperty.h similarity index 57% rename from src/UESaveFile/Serialisers/MapPropertySerialiser.h rename to src/Gvas/Serialisers/MapProperty.h index e8ebaba..044e06e 100644 --- a/src/UESaveFile/Serialisers/MapPropertySerialiser.h +++ b/src/Gvas/Serialisers/MapProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -18,17 +18,22 @@ #include -#include "UnrealPropertySerialiser.h" +#include "UnrealProperty.h" #include "../Types/MapProperty.h" -class MapPropertySerialiser : public UnrealPropertySerialiser { +namespace Gvas::Serialisers { + +class MapProperty : public UnrealProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; private: - auto deserialiseProperty(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; - auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool override; + auto deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override; + bool serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) override; }; + +} diff --git a/src/UESaveFile/Serialisers/ResourcePropertySerialiser.cpp b/src/Gvas/Serialisers/ResourceProperty.cpp similarity index 64% rename from src/UESaveFile/Serialisers/ResourcePropertySerialiser.cpp rename to src/Gvas/Serialisers/ResourceProperty.cpp index 6113c11..4fd36e4 100644 --- a/src/UESaveFile/Serialisers/ResourcePropertySerialiser.cpp +++ b/src/Gvas/Serialisers/ResourceProperty.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -14,22 +14,25 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "../BinaryReader.h" -#include "../BinaryWriter.h" +#include "../../BinaryIo/Reader.h" +#include "../../BinaryIo/Writer.h" #include "../PropertySerialiser.h" #include "../Types/IntProperty.h" #include "../Types/NoneProperty.h" #include "../../Logger/Logger.h" -#include "ResourcePropertySerialiser.h" +#include "ResourceProperty.h" using namespace Containers::Literals; -auto ResourcePropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, BinaryReader& reader, - PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +namespace Gvas::Serialisers { + +Types::UnrealPropertyBase::ptr +ResourceProperty::deserialiseProperty(Containers::StringView name, Containers::StringView type, + std::size_t value_length, BinaryIo::Reader& reader, + PropertySerialiser& serialiser) { - auto prop = Containers::pointer(); + auto prop = Containers::pointer(); auto id_prop = serialiser.read(reader); if(!id_prop) { @@ -39,13 +42,13 @@ auto ResourcePropertySerialiser::deserialiseProperty(Containers::StringView name if((*id_prop->name) != "ID_4_AAE08F17428E229EC7A2209F51081A21"_s || id_prop->propertyType != "IntProperty"_s || - dynamic_cast(id_prop.get()) == nullptr) + dynamic_cast(id_prop.get()) == nullptr) { LOG_ERROR("The ID property is invalid."_s); return nullptr; } - prop->id = dynamic_cast(id_prop.get())->value; + prop->id = dynamic_cast(id_prop.get())->value; auto value_prop = serialiser.read(reader); if(!value_prop) { @@ -55,20 +58,20 @@ auto ResourcePropertySerialiser::deserialiseProperty(Containers::StringView name if((*value_prop->name) != "Quantity_3_560F09B5485C365D3041888910019CE3"_s || value_prop->propertyType != "IntProperty"_s || - dynamic_cast(value_prop.get()) == nullptr) + dynamic_cast(value_prop.get()) == nullptr) { LOG_ERROR("The value property is invalid."_s); return nullptr; } - prop->quantity = dynamic_cast(value_prop.get())->value; + prop->quantity = dynamic_cast(value_prop.get())->value; auto none_prop = serialiser.read(reader); if(!none_prop || (*none_prop->name) != "None"_s || none_prop->propertyType != "NoneProperty"_s || - !dynamic_cast(none_prop.get())) + !dynamic_cast(none_prop.get())) { LOG_ERROR("Couldn't find a terminating NoneProperty."_s); return nullptr; @@ -77,10 +80,11 @@ auto ResourcePropertySerialiser::deserialiseProperty(Containers::StringView name return prop; } -auto ResourcePropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +bool +ResourceProperty::serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) { - auto res_prop = dynamic_cast(prop.get()); + auto res_prop = dynamic_cast(prop.get()); if(!res_prop) { LOG_ERROR("The property is not a valid ResourceItemValue property."); return false; @@ -88,17 +92,19 @@ auto ResourcePropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop bytes_written += writer.writeUEStringToArray("ID_4_AAE08F17428E229EC7A2209F51081A21"_s) + writer.writeUEStringToArray("IntProperty"_s) + - writer.writeValueToArray(4ull) + + writer.writeValueToArray(4ull) + writer.writeValueToArray('\0') + - writer.writeValueToArray(res_prop->id); + writer.writeValueToArray(res_prop->id); bytes_written += writer.writeUEStringToArray("Quantity_3_560F09B5485C365D3041888910019CE3"_s) + writer.writeUEStringToArray("IntProperty"_s) + - writer.writeValueToArray(4ull) + + writer.writeValueToArray(4ull) + writer.writeValueToArray('\0') + - writer.writeValueToArray(res_prop->quantity); + writer.writeValueToArray(res_prop->quantity); bytes_written += writer.writeUEStringToArray("None"_s); return true; } + +} diff --git a/src/UESaveFile/Serialisers/ResourcePropertySerialiser.h b/src/Gvas/Serialisers/ResourceProperty.h similarity index 56% rename from src/UESaveFile/Serialisers/ResourcePropertySerialiser.h rename to src/Gvas/Serialisers/ResourceProperty.h index bccd012..d9ef0b3 100644 --- a/src/UESaveFile/Serialisers/ResourcePropertySerialiser.h +++ b/src/Gvas/Serialisers/ResourceProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -18,17 +18,22 @@ #include -#include "UnrealPropertySerialiser.h" +#include "UnrealProperty.h" #include "../Types/ResourceItemValue.h" -class ResourcePropertySerialiser : public UnrealPropertySerialiser { +namespace Gvas::Serialisers { + +class ResourceProperty : public UnrealProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; private: - auto deserialiseProperty(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; - auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool override; + auto deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override; + bool serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) override; }; + +} diff --git a/src/Gvas/Serialisers/RotatorProperty.cpp b/src/Gvas/Serialisers/RotatorProperty.cpp new file mode 100644 index 0000000..380a618 --- /dev/null +++ b/src/Gvas/Serialisers/RotatorProperty.cpp @@ -0,0 +1,84 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include "../../BinaryIo/Reader.h" +#include "../../BinaryIo/Writer.h" +#include "../../Logger/Logger.h" + +#include "RotatorProperty.h" + +namespace Gvas::Serialisers { + +Types::UnrealPropertyBase::ptr +RotatorProperty::deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) +{ + auto prop = Containers::pointer(); + + if(value_length == 12) { // UE4 + float x, y, z; + + if(!reader.readFloat(x) || !reader.readFloat(y) || !reader.readFloat(z)) { + LOG_ERROR_FORMAT("Couldn't read rotator property {}'s value.", name); + return nullptr; + } + + prop->vector = Vector3{x, y, z}; + } + else if(value_length == 24) { // UE5 + double x, y, z; + + if(!reader.readDouble(x) || !reader.readDouble(y) || !reader.readDouble(z)) { + LOG_ERROR_FORMAT("Couldn't read rotator property {}'s value.", name); + return nullptr; + } + + prop->vector = Vector3d{x, y, z}; + } + + return prop; +} + +bool +RotatorProperty::serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) +{ + auto rotator = dynamic_cast(prop.get()); + if(!rotator) { + LOG_ERROR("The property is not a valid rotator property."); + return false; + } + + std::visit( + [&bytes_written, &writer](auto& arg){ + using T = std::decay_t; + if constexpr (std::is_same_v) { + bytes_written += writer.writeValueToArray(arg.x()) + + writer.writeValueToArray(arg.y()) + + writer.writeValueToArray(arg.z()); + } + else if constexpr (std::is_same_v) { + bytes_written += writer.writeValueToArray(arg.x()) + + writer.writeValueToArray(arg.y()) + + writer.writeValueToArray(arg.z()); + } + }, rotator->vector + ); + + return true; +} + +} diff --git a/src/UESaveFile/Serialisers/RotatorPropertySerialiser.h b/src/Gvas/Serialisers/RotatorProperty.h similarity index 56% rename from src/UESaveFile/Serialisers/RotatorPropertySerialiser.h rename to src/Gvas/Serialisers/RotatorProperty.h index eec73c9..355f1b3 100644 --- a/src/UESaveFile/Serialisers/RotatorPropertySerialiser.h +++ b/src/Gvas/Serialisers/RotatorProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -18,17 +18,22 @@ #include -#include "UnrealPropertySerialiser.h" +#include "UnrealProperty.h" #include "../Types/RotatorStructProperty.h" -class RotatorPropertySerialiser : public UnrealPropertySerialiser { +namespace Gvas::Serialisers { + +class RotatorProperty : public UnrealProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; private: - auto deserialiseProperty(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; - auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool override; + auto deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override; + bool serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) override; }; + +} diff --git a/src/Gvas/Serialisers/Serialisers.h b/src/Gvas/Serialisers/Serialisers.h new file mode 100644 index 0000000..d1bf748 --- /dev/null +++ b/src/Gvas/Serialisers/Serialisers.h @@ -0,0 +1,44 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +namespace Gvas::Serialisers { + +class AbstractUnrealCollectionProperty; +class AbstractUnrealProperty; +class AbstractUnrealStruct; +class ArrayProperty; +class BoolProperty; +class ByteProperty; +class ColourProperty; +class DateTimeProperty; +class EnumProperty; +class FloatProperty; +class GuidProperty; +class IntProperty; +class MapProperty; +class ResourceProperty; +class RotatorProperty; +class SetProperty; +class StringProperty; +class Struct; +class TextProperty; +class UnrealProperty; +class Vector2DProperty; +class VectorProperty; + +} diff --git a/src/UESaveFile/Serialisers/SetPropertySerialiser.cpp b/src/Gvas/Serialisers/SetProperty.cpp similarity index 59% rename from src/UESaveFile/Serialisers/SetPropertySerialiser.cpp rename to src/Gvas/Serialisers/SetProperty.cpp index cfa9bd0..e010996 100644 --- a/src/UESaveFile/Serialisers/SetPropertySerialiser.cpp +++ b/src/Gvas/Serialisers/SetProperty.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -14,16 +14,18 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "../BinaryReader.h" -#include "../BinaryWriter.h" +#include "../../BinaryIo/Reader.h" +#include "../../BinaryIo/Writer.h" #include "../PropertySerialiser.h" #include "../../Logger/Logger.h" -#include "SetPropertySerialiser.h" +#include "SetProperty.h" -auto SetPropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, BinaryReader& reader, - PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +namespace Gvas::Serialisers { + +Types::UnrealPropertyBase::ptr +SetProperty::deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) { Containers::String item_type; if(!reader.readUEString(item_type)) { @@ -37,29 +39,30 @@ auto SetPropertySerialiser::deserialiseProperty(Containers::StringView name, Con return nullptr; } - UnsignedInt four_bytes; - if(!reader.readUnsignedInt(four_bytes) || four_bytes != 0u) { + std::uint32_t four_bytes; + if(!reader.readUint32(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)) { + std::uint32_t item_count; + if(!reader.readUint32(item_count)) { LOG_ERROR_FORMAT("Couldn't read set property {}'s item count.", name); return nullptr; } - auto prop = Containers::pointer(); - prop->itemType = std::move(item_type); + auto prop = Containers::pointer(); + prop->itemType = Utility::move(item_type); prop->items = serialiser.readSet(reader, prop->itemType, item_count); return prop; } -auto SetPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +bool +SetProperty::serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) { - auto set_prop = dynamic_cast(prop.get()); + auto set_prop = dynamic_cast(prop.get()); if(!set_prop) { LOG_ERROR("The property is not a valid set property."); return false; @@ -68,13 +71,15 @@ auto SetPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, Uns writer.writeUEStringToArray(set_prop->itemType); writer.writeValueToArray('\0'); - bytes_written += writer.writeValueToArray(0u); - bytes_written += writer.writeValueToArray(UnsignedInt(set_prop->items.size())); + bytes_written += writer.writeValueToArray(0u); + bytes_written += writer.writeValueToArray(std::uint32_t(set_prop->items.size())); - UnsignedLong start_pos = writer.arrayPosition(); - UnsignedLong dummy_bytes_written = 0; + std::size_t start_pos = writer.arrayPosition(); + std::size_t dummy_bytes_written = 0; serialiser.writeSet(set_prop->items, set_prop->itemType, dummy_bytes_written, writer); bytes_written += writer.arrayPosition() - start_pos; return true; } + +} diff --git a/src/UESaveFile/Serialisers/SetPropertySerialiser.h b/src/Gvas/Serialisers/SetProperty.h similarity index 57% rename from src/UESaveFile/Serialisers/SetPropertySerialiser.h rename to src/Gvas/Serialisers/SetProperty.h index 8bfddee..7fa5276 100644 --- a/src/UESaveFile/Serialisers/SetPropertySerialiser.h +++ b/src/Gvas/Serialisers/SetProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -18,17 +18,22 @@ #include -#include "UnrealPropertySerialiser.h" +#include "UnrealProperty.h" #include "../Types/SetProperty.h" -class SetPropertySerialiser : public UnrealPropertySerialiser { +namespace Gvas::Serialisers { + +class SetProperty : public UnrealProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; private: - auto deserialiseProperty(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; - auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool override; + auto deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override; + bool serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) override; }; + +} diff --git a/src/UESaveFile/Serialisers/StringPropertySerialiser.cpp b/src/Gvas/Serialisers/StringProperty.cpp similarity index 56% rename from src/UESaveFile/Serialisers/StringPropertySerialiser.cpp rename to src/Gvas/Serialisers/StringProperty.cpp index 3cb4208..c9e36f0 100644 --- a/src/UESaveFile/Serialisers/StringPropertySerialiser.cpp +++ b/src/Gvas/Serialisers/StringProperty.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -14,27 +14,30 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include "../BinaryReader.h" -#include "../BinaryWriter.h" +#include "../../BinaryIo/Reader.h" +#include "../../BinaryIo/Writer.h" #include "../../Logger/Logger.h" -#include "StringPropertySerialiser.h" +#include "StringProperty.h" -auto StringPropertySerialiser::types() -> Containers::ArrayView { +namespace Gvas::Serialisers { + +StringArrayView +StringProperty::types() { using namespace Containers::Literals; - static const Containers::Array types{InPlaceInit, - {"NameProperty"_s, "StrProperty"_s, - "SoftObjectProperty"_s, "ObjectProperty"_s}}; + static const Containers::Array types{InPlaceInit, { + "NameProperty"_s, "StrProperty"_s, "SoftObjectProperty"_s, "ObjectProperty"_s + }}; return types; } -auto StringPropertySerialiser::deserialise(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, BinaryReader& reader, - PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +Types::UnrealPropertyBase::ptr +StringProperty::deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) { - auto prop = Containers::pointer(type); + auto prop = Containers::pointer(type); - if(value_length != UnsignedLong(-1)) { + if(value_length != std::size_t(-1)) { char terminator; if(!reader.readChar(terminator) || terminator != '\0') { LOG_ERROR_FORMAT("Couldn't read a null byte in string property {}.", name); @@ -52,16 +55,17 @@ auto StringPropertySerialiser::deserialise(Containers::StringView name, Containe return prop; } -auto StringPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +bool +StringProperty::serialise(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, BinaryIo::Writer& writer, + PropertySerialiser& serialiser) { - auto str_prop = dynamic_cast(prop.get()); + auto str_prop = dynamic_cast(prop.get()); if(!str_prop) { LOG_ERROR("The property is not a valid string property."); return false; } - if(str_prop->valueLength != UnsignedLong(-1)) { + if(str_prop->valueLength != std::size_t(-1)) { writer.writeValueToArray('\0'); } @@ -69,3 +73,5 @@ auto StringPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, Unsigned return true; } + +} diff --git a/src/UESaveFile/Serialisers/StringPropertySerialiser.h b/src/Gvas/Serialisers/StringProperty.h similarity index 57% rename from src/UESaveFile/Serialisers/StringPropertySerialiser.h rename to src/Gvas/Serialisers/StringProperty.h index 5965bcf..75d868e 100644 --- a/src/UESaveFile/Serialisers/StringPropertySerialiser.h +++ b/src/Gvas/Serialisers/StringProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -19,19 +19,24 @@ #include #include -#include "AbstractUnrealPropertySerialiser.h" +#include "AbstractUnrealProperty.h" #include "../Types/StringProperty.h" -class StringPropertySerialiser : public AbstractUnrealPropertySerialiser { +namespace Gvas::Serialisers { + +class StringProperty : public AbstractUnrealProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; - auto types() -> Containers::ArrayView override; + auto types() -> StringArrayView override; - auto deserialise(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + auto deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override; - auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool override; + bool serialise(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, BinaryIo::Writer& writer, + PropertySerialiser& serialiser) override; }; + +} diff --git a/src/UESaveFile/Serialisers/StructSerialiser.cpp b/src/Gvas/Serialisers/Struct.cpp similarity index 61% rename from src/UESaveFile/Serialisers/StructSerialiser.cpp rename to src/Gvas/Serialisers/Struct.cpp index 95001b7..ee73e54 100644 --- a/src/UESaveFile/Serialisers/StructSerialiser.cpp +++ b/src/Gvas/Serialisers/Struct.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,23 +16,27 @@ #include -#include "../BinaryReader.h" -#include "../BinaryWriter.h" +#include "../../BinaryIo/Reader.h" +#include "../../BinaryIo/Writer.h" #include "../PropertySerialiser.h" #include "../Types/GenericStructProperty.h" #include "../Types/NoneProperty.h" #include "../../Logger/Logger.h" -#include "StructSerialiser.h" +#include "Struct.h" -auto StructSerialiser::types() -> Containers::ArrayView { +namespace Gvas::Serialisers { + +StringArrayView +Struct::types() { using namespace Containers::Literals; static const Containers::Array types{InPlaceInit, {"StructProperty"_s}}; return types; } -auto StructSerialiser::deserialise(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - UnsignedInt count, BinaryReader& reader, PropertySerialiser& serialiser) -> Containers::Array +PropertyArray +Struct::deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length, + std::uint32_t count, BinaryIo::Reader& reader, PropertySerialiser& serialiser) { Containers::String item_type; if(!reader.readUEString(item_type)) { @@ -52,18 +56,18 @@ auto StructSerialiser::deserialise(Containers::StringView name, Containers::Stri return nullptr; } - Containers::Array array; + Containers::Array array; if(count == 0) { - auto prop = Containers::pointer(); - prop->structType = std::move(item_type); - prop->structGuid = std::move(guid); + auto prop = Containers::pointer(); + prop->structType = Utility::move(item_type); + prop->structGuid = guid; } else { - for(UnsignedInt i = 0; i < count; i++) { - auto prop = Containers::pointer(); + for(std::uint32_t i = 0; i < count; i++) { + auto prop = Containers::pointer(); - prop = serialiser.readItem(reader, item_type, UnsignedLong(-1), name); + prop = serialiser.readItem(reader, item_type, std::size_t(-1), name); if(!prop) { prop = readStructValue(name, item_type, value_length, reader, serialiser); @@ -74,17 +78,18 @@ auto StructSerialiser::deserialise(Containers::StringView name, Containers::Stri return nullptr; } - dynamic_cast(prop.get())->structGuid = guid; + dynamic_cast(prop.get())->structGuid = guid; - arrayAppend(array, std::move(prop)); + arrayAppend(array, Utility::move(prop)); } } return array; } -auto StructSerialiser::deserialise(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +Types::UnrealPropertyBase::ptr +Struct::deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) { Containers::String item_type; if(!reader.readUEString(item_type)) { @@ -93,7 +98,7 @@ auto StructSerialiser::deserialise(Containers::StringView name, Containers::Stri } if(item_type == "None") { - return NoneProperty::ptr{}; + return Containers::pointer(); } Containers::StaticArray<16, char> guid{ValueInit}; @@ -108,27 +113,28 @@ auto StructSerialiser::deserialise(Containers::StringView name, Containers::Stri return nullptr; } - UnrealPropertyBase::ptr prop = serialiser.readItem(reader, item_type, value_length, name); + auto prop = serialiser.readItem(reader, item_type, value_length, name); if(!prop) { prop = readStructValue(name, item_type, value_length, reader, serialiser); if(prop) { - dynamic_cast(prop.get())->structGuid = std::move(guid); + dynamic_cast(prop.get())->structGuid = guid; } } return prop; } -auto StructSerialiser::serialise(Containers::ArrayView props, Containers::StringView item_type, - UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +bool +Struct::serialise(PropertyArrayView props, Containers::StringView item_type, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) { bytes_written += writer.writeUEStringToArray(*(props.front()->name)); bytes_written += writer.writeUEStringToArray(item_type); - UnsignedLong vl_pos = writer.arrayPosition(); - bytes_written += writer.writeValueToArray(0ull); + std::size_t vl_pos = writer.arrayPosition(); + bytes_written += writer.writeValueToArray(0ull); - auto struct_prop = dynamic_cast(props.front().get()); + auto struct_prop = dynamic_cast(props.front().get()); if(!struct_prop) { LOG_ERROR("The property is not a valid struct property."); return false; @@ -138,11 +144,11 @@ auto StructSerialiser::serialise(Containers::ArrayView bytes_written += writer.writeDataToArray(arrayView(struct_prop->structGuid)); bytes_written += writer.writeValueToArray('\0'); - UnsignedLong vl_start = writer.arrayPosition(); + std::size_t vl_start = writer.arrayPosition(); - UnsignedLong bytes_written_here = 0; + std::size_t bytes_written_here = 0; for(auto& prop : props) { - struct_prop = dynamic_cast(prop.get()); + struct_prop = dynamic_cast(prop.get()); if(!struct_prop) { LOG_ERROR("The property is not a valid struct property."); return false; @@ -156,17 +162,18 @@ auto StructSerialiser::serialise(Containers::ArrayView } } - UnsignedLong vl_stop = writer.arrayPosition() - vl_start; + std::size_t vl_stop = writer.arrayPosition() - vl_start; writer.writeValueToArrayAt(vl_stop, vl_pos); bytes_written += vl_stop; return true; } -auto StructSerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +bool +Struct::serialise(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, BinaryIo::Writer& writer, + PropertySerialiser& serialiser) { - auto struct_prop = dynamic_cast(prop.get()); + auto struct_prop = dynamic_cast(prop.get()); if(!struct_prop) { LOG_ERROR("The property is not a valid struct property."); return false; @@ -177,8 +184,8 @@ auto StructSerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& by writer.writeValueToArray('\0'); if(!serialiser.writeItem(prop, struct_prop->structType, bytes_written, writer)) { - UnsignedLong dummy_bytes_written = 0; - UnsignedLong vl_start = writer.arrayPosition(); + std::size_t dummy_bytes_written = 0; + std::size_t vl_start = writer.arrayPosition(); if(!writeStructValue(struct_prop, dummy_bytes_written, writer, serialiser)) { LOG_ERROR("Couldn't write the struct value."); return false; @@ -189,19 +196,22 @@ auto StructSerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& by return true; } -auto StructSerialiser::readStructValue(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> StructProperty::ptr +Types::StructProperty::ptr +Struct::readStructValue(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) { - auto st_prop = Containers::pointer(); + static_cast(value_length); + + auto st_prop = Containers::pointer(); st_prop->structType = type; - UnrealPropertyBase::ptr prop; + Types::UnrealPropertyBase::ptr prop; while((prop = serialiser.read(reader)) != nullptr) { - arrayAppend(st_prop->properties, std::move(prop)); + arrayAppend(st_prop->properties, Utility::move(prop)); if(st_prop->properties.back()->name == "None" && st_prop->properties.back()->propertyType == "NoneProperty" && - dynamic_cast(st_prop->properties.back().get()) != nullptr) + dynamic_cast(st_prop->properties.back().get()) != nullptr) { break; } @@ -212,10 +222,11 @@ auto StructSerialiser::readStructValue(Containers::StringView name, Containers:: return st_prop; } -auto StructSerialiser::writeStructValue(StructProperty* prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +bool +Struct::writeStructValue(Types::StructProperty* prop, std::size_t& bytes_written, BinaryIo::Writer& writer, + PropertySerialiser& serialiser) { - auto struct_prop = dynamic_cast(prop); + auto struct_prop = dynamic_cast(prop); if(!struct_prop) { LOG_ERROR("The property is not a valid struct property."); return false; @@ -230,3 +241,5 @@ auto StructSerialiser::writeStructValue(StructProperty* prop, UnsignedLong& byte return true; } + +} diff --git a/src/Gvas/Serialisers/Struct.h b/src/Gvas/Serialisers/Struct.h new file mode 100644 index 0000000..339dd4e --- /dev/null +++ b/src/Gvas/Serialisers/Struct.h @@ -0,0 +1,55 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include +#include + +#include "AbstractUnrealCollectionProperty.h" +#include "AbstractUnrealProperty.h" +#include "AbstractUnrealStruct.h" + +#include "../Types/StructProperty.h" + +namespace Gvas::Serialisers { + +class Struct : public AbstractUnrealProperty, public AbstractUnrealCollectionProperty { + public: + using ptr = Containers::Pointer; + + auto types() -> StringArrayView override; + + auto deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length, + std::uint32_t count, BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> PropertyArray override; + auto deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override; + + bool serialise(PropertyArrayView props, Containers::StringView item_type, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) override; + bool serialise(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, BinaryIo::Writer& writer, + PropertySerialiser& serialiser) override; + + private: + auto readStructValue(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) -> Types::StructProperty::ptr; + bool writeStructValue(Types::StructProperty* prop, std::size_t& bytes_written, BinaryIo::Writer& writer, + PropertySerialiser& serialiser); +}; + +} diff --git a/src/UESaveFile/Serialisers/TextPropertySerialiser.cpp b/src/Gvas/Serialisers/TextProperty.cpp similarity index 62% rename from src/UESaveFile/Serialisers/TextPropertySerialiser.cpp rename to src/Gvas/Serialisers/TextProperty.cpp index 89d9af0..cd7724a 100644 --- a/src/UESaveFile/Serialisers/TextPropertySerialiser.cpp +++ b/src/Gvas/Serialisers/TextProperty.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,18 +16,22 @@ #include -#include "../BinaryReader.h" -#include "../BinaryWriter.h" +#include "../../Logger/Logger.h" -#include "TextPropertySerialiser.h" +#include "../../BinaryIo/Reader.h" +#include "../../BinaryIo/Writer.h" -auto TextPropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, BinaryReader& reader, - PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +#include "TextProperty.h" + +namespace Gvas::Serialisers { + +Types::UnrealPropertyBase::ptr +TextProperty::deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) { - auto prop = Containers::pointer(); + auto prop = Containers::pointer(); - Long start_position = reader.position(); + auto start_position = reader.position(); char terminator; if(!reader.readChar(terminator) || terminator != '\0') { @@ -49,7 +53,7 @@ auto TextPropertySerialiser::deserialiseProperty(Containers::StringView name, Co return nullptr; } - auto interval = reader.position() - start_position; + std::int64_t interval; do { Containers::String str; @@ -58,22 +62,23 @@ auto TextPropertySerialiser::deserialiseProperty(Containers::StringView name, Co return nullptr; } - arrayAppend(prop->data, std::move(str)); + arrayAppend(prop->data, Utility::move(str)); interval = reader.position() - start_position; - } while(UnsignedLong(interval) < value_length); + } while(std::size_t(interval) < value_length); prop->value = prop->data.back(); return prop; } -auto TextPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +bool +TextProperty::serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) { - auto text_prop = dynamic_cast(prop.get()); - + auto text_prop = dynamic_cast(prop.get()); if(!text_prop) { + LOG_ERROR("The property is not a valid text property."); return false; } @@ -85,3 +90,5 @@ auto TextPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, Un return true; } + +} diff --git a/src/UESaveFile/Serialisers/TextPropertySerialiser.h b/src/Gvas/Serialisers/TextProperty.h similarity index 57% rename from src/UESaveFile/Serialisers/TextPropertySerialiser.h rename to src/Gvas/Serialisers/TextProperty.h index 0938936..2b6d2fe 100644 --- a/src/UESaveFile/Serialisers/TextPropertySerialiser.h +++ b/src/Gvas/Serialisers/TextProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -18,17 +18,22 @@ #include -#include "UnrealPropertySerialiser.h" +#include "UnrealProperty.h" #include "../Types/TextProperty.h" -class TextPropertySerialiser : public UnrealPropertySerialiser { +namespace Gvas::Serialisers { + +class TextProperty : public UnrealProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; private: - auto deserialiseProperty(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; - auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool override; + auto deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override; + bool serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) override; }; + +} diff --git a/src/UESaveFile/Serialisers/UnrealPropertySerialiser.h b/src/Gvas/Serialisers/UnrealProperty.h similarity index 58% rename from src/UESaveFile/Serialisers/UnrealPropertySerialiser.h rename to src/Gvas/Serialisers/UnrealProperty.h index e3fcdb6..3b96f15 100644 --- a/src/UESaveFile/Serialisers/UnrealPropertySerialiser.h +++ b/src/Gvas/Serialisers/UnrealProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -22,22 +22,26 @@ #include #include -#include "AbstractUnrealPropertySerialiser.h" +#include "AbstractUnrealProperty.h" #include "../Types/StructProperty.h" +namespace Gvas::Serialisers { + template -class UnrealPropertySerialiser : public AbstractUnrealPropertySerialiser { - static_assert(std::is_base_of::value, "T must be derived from UnrealPropertyBase."); +class UnrealProperty : public AbstractUnrealProperty { + static_assert(std::is_base_of::value, "T must be derived from UnrealPropertyBase."); public: - using ptr = Containers::Pointer>; + using ptr = Containers::Pointer>; - auto types() -> Containers::ArrayView override { + auto types() -> StringArrayView override { static const Containers::Array types = []{ Containers::Array array; Containers::Pointer p(new T); - if(std::is_base_of::value) { - array = Containers::Array{InPlaceInit, {dynamic_cast(p.get())->structType}}; + if(std::is_base_of::value) { + array = Containers::Array{InPlaceInit, { + dynamic_cast(p.get())->structType + }}; } else { array = Containers::Array{InPlaceInit, {p->propertyType}}; @@ -47,23 +51,26 @@ class UnrealPropertySerialiser : public AbstractUnrealPropertySerialiser { return types; } - auto deserialise(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override + auto deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override { return deserialiseProperty(name, type, value_length, reader, serialiser); } - auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool override + bool serialise(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, BinaryIo::Writer& writer, + PropertySerialiser& serialiser) override { return serialiseProperty(prop, bytes_written, writer, serialiser); } private: virtual auto deserialiseProperty(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, BinaryReader& reader, - PropertySerialiser& serialiser) -> typename UnrealPropertyBase::ptr = 0; + std::size_t value_length, BinaryIo::Reader& reader, + PropertySerialiser& serialiser) -> Types::UnrealPropertyBase::ptr = 0; - virtual auto serialiseProperty(typename UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool = 0; + virtual bool serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) = 0; }; + +} diff --git a/src/Gvas/Serialisers/Vector2DProperty.cpp b/src/Gvas/Serialisers/Vector2DProperty.cpp new file mode 100644 index 0000000..48adf80 --- /dev/null +++ b/src/Gvas/Serialisers/Vector2DProperty.cpp @@ -0,0 +1,83 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include "../../BinaryIo/Reader.h" +#include "../../BinaryIo/Writer.h" +#include "../../Logger/Logger.h" + +#include "Vector2DProperty.h" + +namespace Gvas::Serialisers { + +Types::UnrealPropertyBase::ptr +Vector2DProperty::deserialiseProperty(Containers::StringView name, Containers::StringView type, + std::size_t value_length, BinaryIo::Reader& reader, + PropertySerialiser& serialiser) +{ + auto prop = Containers::pointer(); + + if(value_length == 8) { // UE4 + float x, y; + + if(!reader.readFloat(x) || !reader.readFloat(y)) { + LOG_ERROR_FORMAT("Couldn't read 2D vector property {}'s value.", name); + return nullptr; + } + + prop->vector = Vector2{x, y}; + } + else if(value_length == 16) { // UE5 + double x, y; + + if(!reader.readDouble(x) || !reader.readDouble(y)) { + LOG_ERROR_FORMAT("Couldn't read 2D vector property {}'s value.", name); + return nullptr; + } + + prop->vector = Vector2d{x, y}; + } + + return prop; +} + +bool +Vector2DProperty::serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) +{ + auto vector = dynamic_cast(prop.get()); + if(!vector) { + LOG_ERROR("The property is not a valid 2D vector property."); + return false; + } + + std::visit( + [&bytes_written, &writer](auto& arg){ + using T = std::decay_t; + if constexpr (std::is_same_v) { + bytes_written += writer.writeValueToArray(arg.x()) + + writer.writeValueToArray(arg.y()); + } + else if constexpr (std::is_same_v) { + bytes_written += writer.writeValueToArray(arg.x()) + + writer.writeValueToArray(arg.y()); + } + }, vector->vector + ); + + return true; +} + +} diff --git a/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.h b/src/Gvas/Serialisers/Vector2DProperty.h similarity index 56% rename from src/UESaveFile/Serialisers/Vector2DPropertySerialiser.h rename to src/Gvas/Serialisers/Vector2DProperty.h index faa1682..def656b 100644 --- a/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.h +++ b/src/Gvas/Serialisers/Vector2DProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -18,17 +18,22 @@ #include -#include "UnrealPropertySerialiser.h" +#include "UnrealProperty.h" #include "../Types/Vector2DStructProperty.h" -class Vector2DPropertySerialiser : public UnrealPropertySerialiser { +namespace Gvas::Serialisers { + +class Vector2DProperty : public UnrealProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; private: - auto deserialiseProperty(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; - auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool override; + auto deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override; + bool serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) override; }; + +} diff --git a/src/Gvas/Serialisers/VectorProperty.cpp b/src/Gvas/Serialisers/VectorProperty.cpp new file mode 100644 index 0000000..b66a64f --- /dev/null +++ b/src/Gvas/Serialisers/VectorProperty.cpp @@ -0,0 +1,85 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include "../../Logger/Logger.h" + +#include "../../BinaryIo/Reader.h" +#include "../../BinaryIo/Writer.h" + +#include "VectorProperty.h" + +namespace Gvas::Serialisers { + +Types::UnrealPropertyBase::ptr +VectorProperty::deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) +{ + auto prop = Containers::pointer(); + + if(value_length == 12) { // UE4 + float x, y, z; + + if(!reader.readFloat(x) || !reader.readFloat(y) || !reader.readFloat(z)) { + LOG_ERROR_FORMAT("Couldn't read vector property {}'s value.", name); + return nullptr; + } + + prop->vector = Vector3{x, y, z}; + } + else if(value_length == 24) { // UE5 + double x, y, z; + + if(!reader.readDouble(x) || !reader.readDouble(y) || !reader.readDouble(z)) { + LOG_ERROR_FORMAT("Couldn't read vector property {}'s value.", name); + return nullptr; + } + + prop->vector = Vector3d{x, y, z}; + } + + return prop; +} + +bool +VectorProperty::serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) +{ + auto vector = dynamic_cast(prop.get()); + if(!vector) { + LOG_ERROR("The property is not a valid vector property."); + return false; + } + + std::visit( + [&bytes_written, &writer](auto& arg){ + using T = std::decay_t; + if constexpr (std::is_same_v) { + bytes_written += writer.writeValueToArray(arg.x()) + + writer.writeValueToArray(arg.y()) + + writer.writeValueToArray(arg.z()); + } + else if constexpr (std::is_same_v) { + bytes_written += writer.writeValueToArray(arg.x()) + + writer.writeValueToArray(arg.y()) + + writer.writeValueToArray(arg.z()); + } + }, vector->vector + ); + + return true; +} + +} diff --git a/src/UESaveFile/Serialisers/VectorPropertySerialiser.h b/src/Gvas/Serialisers/VectorProperty.h similarity index 56% rename from src/UESaveFile/Serialisers/VectorPropertySerialiser.h rename to src/Gvas/Serialisers/VectorProperty.h index 8a8cc7e..72cea55 100644 --- a/src/UESaveFile/Serialisers/VectorPropertySerialiser.h +++ b/src/Gvas/Serialisers/VectorProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -18,17 +18,22 @@ #include -#include "UnrealPropertySerialiser.h" +#include "UnrealProperty.h" #include "../Types/VectorStructProperty.h" -class VectorPropertySerialiser : public UnrealPropertySerialiser { +namespace Gvas::Serialisers { + +class VectorProperty : public UnrealProperty { public: - using ptr = Containers::Pointer; + using ptr = Containers::Pointer; private: - auto deserialiseProperty(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; - auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, - PropertySerialiser& serialiser) -> bool override; + auto deserialiseProperty(Containers::StringView name, Containers::StringView type, std::size_t value_length, + BinaryIo::Reader& reader, PropertySerialiser& serialiser) + -> Types::UnrealPropertyBase::ptr override; + bool serialiseProperty(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, + BinaryIo::Writer& writer, PropertySerialiser& serialiser) override; }; + +} diff --git a/src/UESaveFile/Types/ArrayProperty.h b/src/Gvas/Types/ArrayProperty.h similarity index 95% rename from src/UESaveFile/Types/ArrayProperty.h rename to src/Gvas/Types/ArrayProperty.h index ab6bbec..d5a9f1d 100644 --- a/src/UESaveFile/Types/ArrayProperty.h +++ b/src/Gvas/Types/ArrayProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -23,6 +23,8 @@ #include "UnrealPropertyBase.h" +namespace Gvas::Types { + struct ArrayProperty : public UnrealPropertyBase { using ptr = Containers::Pointer; @@ -44,3 +46,5 @@ struct ArrayProperty : public UnrealPropertyBase { Containers::String itemType; Containers::Array items; }; + +} diff --git a/src/UESaveFile/Types/BoolProperty.h b/src/Gvas/Types/BoolProperty.h similarity index 93% rename from src/UESaveFile/Types/BoolProperty.h rename to src/Gvas/Types/BoolProperty.h index ccb6630..9c19ea7 100644 --- a/src/UESaveFile/Types/BoolProperty.h +++ b/src/Gvas/Types/BoolProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -23,6 +23,8 @@ using namespace Corrade; +namespace Gvas::Types { + struct BoolProperty : public UnrealProperty { using ptr = Containers::Pointer; @@ -31,3 +33,5 @@ struct BoolProperty : public UnrealProperty { propertyType = "BoolProperty"_s; } }; + +} diff --git a/src/UESaveFile/Types/ByteProperty.h b/src/Gvas/Types/ByteProperty.h similarity index 84% rename from src/UESaveFile/Types/ByteProperty.h rename to src/Gvas/Types/ByteProperty.h index fc1bb05..4257261 100644 --- a/src/UESaveFile/Types/ByteProperty.h +++ b/src/Gvas/Types/ByteProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -25,6 +25,8 @@ using namespace Corrade; +namespace Gvas::Types { + struct ByteProperty : public UnrealProperty> { using ptr = Containers::Pointer; @@ -33,7 +35,10 @@ struct ByteProperty : public UnrealProperty> { propertyType = "ByteProperty"_s; } - // For some reason, M.A.S.S. Builder stores EnumProperties as ByteProperties. Ugh... + // M.A.S.S. Builder stores EnumProperties as ByteProperties. + // I wonder if it's a general UE thing and agc93/unsave was just wrong. Containers::String enumType; Containers::String enumValue; }; + +} diff --git a/src/UESaveFile/Types/ColourStructProperty.h b/src/Gvas/Types/ColourStructProperty.h similarity index 89% rename from src/UESaveFile/Types/ColourStructProperty.h rename to src/Gvas/Types/ColourStructProperty.h index f9dd15f..92a174c 100644 --- a/src/UESaveFile/Types/ColourStructProperty.h +++ b/src/Gvas/Types/ColourStructProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -23,11 +23,15 @@ using namespace Corrade; +namespace Gvas::Types { + struct ColourStructProperty : public StructProperty { using ptr = Containers::Pointer; ColourStructProperty() { using namespace Containers::Literals; structType = "LinearColor"_s; } - Float r = 0.0f, g = 0.0f, b = 0.0f, a = 0.0f; + float r = 0.0f, g = 0.0f, b = 0.0f, a = 0.0f; }; + +} diff --git a/src/UESaveFile/Types/DateTimeStructProperty.h b/src/Gvas/Types/DateTimeStructProperty.h similarity index 89% rename from src/UESaveFile/Types/DateTimeStructProperty.h rename to src/Gvas/Types/DateTimeStructProperty.h index 2a75b1e..0f05a4b 100644 --- a/src/UESaveFile/Types/DateTimeStructProperty.h +++ b/src/Gvas/Types/DateTimeStructProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -19,12 +19,11 @@ #include #include -#include - #include "StructProperty.h" using namespace Corrade; -using namespace Magnum; + +namespace Gvas::Types { struct DateTimeStructProperty : public StructProperty { using ptr = Containers::Pointer; @@ -34,5 +33,7 @@ struct DateTimeStructProperty : public StructProperty { structType = "DateTime"_s; } - UnsignedLong timestamp = 0; + std::int64_t timestamp = 0; }; + +} diff --git a/src/UESaveFile/Types/EnumProperty.h b/src/Gvas/Types/EnumProperty.h similarity index 93% rename from src/UESaveFile/Types/EnumProperty.h rename to src/Gvas/Types/EnumProperty.h index b6c9730..50a9ac3 100644 --- a/src/UESaveFile/Types/EnumProperty.h +++ b/src/Gvas/Types/EnumProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,8 @@ using namespace Corrade; +namespace Gvas::Types { + struct EnumProperty : public UnrealProperty { using ptr = Containers::Pointer; @@ -34,3 +36,5 @@ struct EnumProperty : public UnrealProperty { Containers::String enumType; }; + +} diff --git a/src/UESaveFile/Types/FloatProperty.h b/src/Gvas/Types/FloatProperty.h similarity index 86% rename from src/UESaveFile/Types/FloatProperty.h rename to src/Gvas/Types/FloatProperty.h index 6f23ca9..6a99b6d 100644 --- a/src/UESaveFile/Types/FloatProperty.h +++ b/src/Gvas/Types/FloatProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -19,14 +19,13 @@ #include #include -#include - #include "UnrealProperty.h" using namespace Corrade; -using namespace Magnum; -struct FloatProperty : public UnrealProperty { +namespace Gvas::Types { + +struct FloatProperty : public UnrealProperty { using ptr = Containers::Pointer; FloatProperty() { @@ -34,3 +33,5 @@ struct FloatProperty : public UnrealProperty { propertyType = "FloatProperty"_s; } }; + +} diff --git a/src/UESaveFile/Types/GenericStructProperty.h b/src/Gvas/Types/GenericStructProperty.h similarity index 91% rename from src/UESaveFile/Types/GenericStructProperty.h rename to src/Gvas/Types/GenericStructProperty.h index 9c76828..c46e8c4 100644 --- a/src/UESaveFile/Types/GenericStructProperty.h +++ b/src/Gvas/Types/GenericStructProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -26,6 +26,8 @@ using namespace Corrade; +namespace Gvas::Types { + struct GenericStructProperty : public StructProperty { using ptr = Containers::Pointer; @@ -45,7 +47,7 @@ struct GenericStructProperty : public StructProperty { atMove(Containers::StringView name) { for(auto& item : properties) { if(item && item->name == name) { - return Containers::Pointer{static_cast(item.release())}; + return Containers::pointerCast(Utility::move(item)); } } return nullptr; @@ -53,3 +55,5 @@ struct GenericStructProperty : public StructProperty { Containers::Array properties; }; + +} diff --git a/src/UESaveFile/Types/GuidStructProperty.h b/src/Gvas/Types/GuidStructProperty.h similarity index 93% rename from src/UESaveFile/Types/GuidStructProperty.h rename to src/Gvas/Types/GuidStructProperty.h index 34619a6..85f83f6 100644 --- a/src/UESaveFile/Types/GuidStructProperty.h +++ b/src/Gvas/Types/GuidStructProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -24,6 +24,8 @@ using namespace Corrade; +namespace Gvas::Types { + struct GuidStructProperty : public StructProperty { using ptr = Containers::Pointer; @@ -34,3 +36,5 @@ struct GuidStructProperty : public StructProperty { Containers::StaticArray<16, char> guid{ValueInit}; }; + +} diff --git a/src/UESaveFile/Types/IntProperty.h b/src/Gvas/Types/IntProperty.h similarity index 88% rename from src/UESaveFile/Types/IntProperty.h rename to src/Gvas/Types/IntProperty.h index 8b231cf..947fc02 100644 --- a/src/UESaveFile/Types/IntProperty.h +++ b/src/Gvas/Types/IntProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -23,7 +23,9 @@ using namespace Corrade; -struct IntProperty : public UnrealProperty { +namespace Gvas::Types { + +struct IntProperty : public UnrealProperty { using ptr = Containers::Pointer; IntProperty() { @@ -31,3 +33,5 @@ struct IntProperty : public UnrealProperty { propertyType = "IntProperty"_s; } }; + +} diff --git a/src/UESaveFile/Types/MapProperty.h b/src/Gvas/Types/MapProperty.h similarity index 92% rename from src/UESaveFile/Types/MapProperty.h rename to src/Gvas/Types/MapProperty.h index 62a26b3..3ba902c 100644 --- a/src/UESaveFile/Types/MapProperty.h +++ b/src/Gvas/Types/MapProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,6 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include #include #include #include @@ -24,6 +25,8 @@ using namespace Corrade; +namespace Gvas::Types { + struct MapProperty : public UnrealPropertyBase { using ptr = Containers::Pointer; @@ -42,3 +45,5 @@ struct MapProperty : public UnrealPropertyBase { Containers::Array map; }; + +} diff --git a/src/UESaveFile/Types/NoneProperty.h b/src/Gvas/Types/NoneProperty.h similarity index 93% rename from src/UESaveFile/Types/NoneProperty.h rename to src/Gvas/Types/NoneProperty.h index 4985642..26727c1 100644 --- a/src/UESaveFile/Types/NoneProperty.h +++ b/src/Gvas/Types/NoneProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -23,6 +23,8 @@ using namespace Corrade; +namespace Gvas::Types { + struct NoneProperty : UnrealPropertyBase { using ptr = Containers::Pointer; @@ -32,3 +34,5 @@ struct NoneProperty : UnrealPropertyBase { propertyType = "NoneProperty"_s; } }; + +} diff --git a/src/UESaveFile/Types/ResourceItemValue.h b/src/Gvas/Types/ResourceItemValue.h similarity index 75% rename from src/UESaveFile/Types/ResourceItemValue.h rename to src/Gvas/Types/ResourceItemValue.h index e4b7c17..f196e14 100644 --- a/src/UESaveFile/Types/ResourceItemValue.h +++ b/src/Gvas/Types/ResourceItemValue.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -19,12 +19,11 @@ #include #include -#include - #include "StructProperty.h" using namespace Corrade; -using namespace Magnum; + +namespace Gvas::Types { struct ResourceItemValue : public StructProperty { using ptr = Containers::Pointer; @@ -32,8 +31,14 @@ struct ResourceItemValue : public StructProperty { ResourceItemValue() { using namespace Containers::Literals; structType = "sttResourceItemValue"_s; - structGuid = Containers::StaticArray<16, char>{'\xB7', '\xA7', '\x77', '\xAB', '\xD3', '\x1B', '\xA6', '\x43', '\xAF', '\x42', '\xE5', '\x9E', '\xBF', '\xFD', '\x37', '\x55'}; + structGuid = Containers::StaticArray<16, char>{ + '\xB7', '\xA7', '\x77', '\xAB', '\xD3', '\x1B', '\xA6', '\x43', + '\xAF', '\x42', '\xE5', '\x9E', '\xBF', '\xFD', '\x37', '\x55' + }; } - Int id = 0, quantity = 0; + std::int32_t id = 0; + std::int32_t quantity = 0; }; + +} diff --git a/src/UESaveFile/Types/RotatorStructProperty.h b/src/Gvas/Types/RotatorStructProperty.h similarity index 83% rename from src/UESaveFile/Types/RotatorStructProperty.h rename to src/Gvas/Types/RotatorStructProperty.h index eb001c8..7260930 100644 --- a/src/UESaveFile/Types/RotatorStructProperty.h +++ b/src/Gvas/Types/RotatorStructProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,16 +16,22 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include + #include #include -#include +#include #include "StructProperty.h" using namespace Corrade; using namespace Magnum; +typedef std::variant Vector3Variant; + +namespace Gvas::Types { + struct RotatorStructProperty : public StructProperty { using ptr = Containers::Pointer; @@ -34,5 +40,7 @@ struct RotatorStructProperty : public StructProperty { structType = "Rotator"_s; } - Float x = 0.0f, y = 0.0f, z = 0.0f; + Vector3Variant vector; }; + +} diff --git a/src/UESaveFile/Types/SetProperty.h b/src/Gvas/Types/SetProperty.h similarity index 94% rename from src/UESaveFile/Types/SetProperty.h rename to src/Gvas/Types/SetProperty.h index fa6389c..63599dc 100644 --- a/src/UESaveFile/Types/SetProperty.h +++ b/src/Gvas/Types/SetProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -25,6 +25,8 @@ using namespace Corrade; +namespace Gvas::Types { + struct SetProperty : public UnrealPropertyBase { using ptr = Containers::Pointer; @@ -44,3 +46,5 @@ struct SetProperty : public UnrealPropertyBase { Containers::String itemType; Containers::Array items; }; + +} diff --git a/src/UESaveFile/Types/StringProperty.h b/src/Gvas/Types/StringProperty.h similarity index 93% rename from src/UESaveFile/Types/StringProperty.h rename to src/Gvas/Types/StringProperty.h index bc28a92..79b452b 100644 --- a/src/UESaveFile/Types/StringProperty.h +++ b/src/Gvas/Types/StringProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -25,6 +25,8 @@ using namespace Corrade; using namespace Containers::Literals; +namespace Gvas::Types { + struct StringProperty : public UnrealProperty { using ptr = Containers::Pointer; @@ -32,3 +34,5 @@ struct StringProperty : public UnrealProperty { propertyType = type; } }; + +} diff --git a/src/UESaveFile/Types/StructProperty.h b/src/Gvas/Types/StructProperty.h similarity index 94% rename from src/UESaveFile/Types/StructProperty.h rename to src/Gvas/Types/StructProperty.h index a0ddb16..0ddbb3e 100644 --- a/src/UESaveFile/Types/StructProperty.h +++ b/src/Gvas/Types/StructProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -25,6 +25,8 @@ using namespace Corrade; +namespace Gvas::Types { + struct StructProperty : public UnrealPropertyBase { using ptr = Containers::Pointer; @@ -36,3 +38,5 @@ struct StructProperty : public UnrealPropertyBase { Containers::StaticArray<16, char> structGuid{ValueInit}; Containers::String structType; }; + +} diff --git a/src/UESaveFile/Types/TextProperty.h b/src/Gvas/Types/TextProperty.h similarity index 94% rename from src/UESaveFile/Types/TextProperty.h rename to src/Gvas/Types/TextProperty.h index 409b393..75420e3 100644 --- a/src/UESaveFile/Types/TextProperty.h +++ b/src/Gvas/Types/TextProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -25,6 +25,8 @@ using namespace Corrade; +namespace Gvas::Types { + struct TextProperty : public UnrealProperty { using ptr = Containers::Pointer; @@ -32,8 +34,10 @@ struct TextProperty : public UnrealProperty { using namespace Containers::Literals; propertyType = "TextProperty"_s; } - + Containers::Array flags; char id = 0; Containers::Array data; }; + +} diff --git a/src/UESaveFile/Debug.h b/src/Gvas/Types/Types.h similarity index 61% rename from src/UESaveFile/Debug.h rename to src/Gvas/Types/Types.h index 4a85ca1..628e565 100644 --- a/src/UESaveFile/Debug.h +++ b/src/Gvas/Types/Types.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,18 +16,30 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include +namespace Gvas::Types { -struct ArrayProperty; -struct SetProperty; -struct GenericStructProperty; -struct StructProperty; struct UnrealPropertyBase; +template +struct UnrealProperty; +struct ArrayProperty; +struct BoolProperty; +struct ByteProperty; +struct ColourStructProperty; +struct DateTimeStructProperty; +struct EnumProperty; +struct FloatProperty; +struct GenericStructProperty; +struct GuidStructProperty; +struct IntProperty; +struct MapProperty; +struct NoneProperty; +struct ResourceItemValue; +struct RotatorStructProperty; +struct SetProperty; +struct StringProperty; +struct StructProperty; +struct TextProperty; +struct Vector2DStructProperty; +struct VectorStructProperty; -using namespace Corrade; - -Utility::Debug& operator<<(Utility::Debug& debug, const ArrayProperty* prop); -Utility::Debug& operator<<(Utility::Debug& debug, const SetProperty* prop); -Utility::Debug& operator<<(Utility::Debug& debug, const GenericStructProperty* prop); -Utility::Debug& operator<<(Utility::Debug& debug, const StructProperty* prop); -Utility::Debug& operator<<(Utility::Debug& debug, const UnrealPropertyBase* prop); +} diff --git a/src/UESaveFile/Types/UnrealProperty.h b/src/Gvas/Types/UnrealProperty.h similarity index 92% rename from src/UESaveFile/Types/UnrealProperty.h rename to src/Gvas/Types/UnrealProperty.h index adccf4c..f812cf5 100644 --- a/src/UESaveFile/Types/UnrealProperty.h +++ b/src/Gvas/Types/UnrealProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -22,9 +22,13 @@ using namespace Corrade; +namespace Gvas::Types { + template struct UnrealProperty : public UnrealPropertyBase { using ptr = Containers::Pointer>; T value; }; + +} diff --git a/src/UESaveFile/Types/UnrealPropertyBase.h b/src/Gvas/Types/UnrealPropertyBase.h similarity index 89% rename from src/UESaveFile/Types/UnrealPropertyBase.h rename to src/Gvas/Types/UnrealPropertyBase.h index 47d8e52..31fb8f6 100644 --- a/src/UESaveFile/Types/UnrealPropertyBase.h +++ b/src/Gvas/Types/UnrealPropertyBase.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,14 +16,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include + #include #include #include -#include - using namespace Corrade; -using namespace Magnum; + +namespace Gvas::Types { struct UnrealPropertyBase { using ptr = Containers::Pointer; @@ -32,5 +33,7 @@ struct UnrealPropertyBase { Containers::Optional name = Containers::NullOpt; Containers::String propertyType; - UnsignedLong valueLength = 0; + std::size_t valueLength = 0; }; + +} diff --git a/src/UESaveFile/Types/Vector2DStructProperty.h b/src/Gvas/Types/Vector2DStructProperty.h similarity index 83% rename from src/UESaveFile/Types/Vector2DStructProperty.h rename to src/Gvas/Types/Vector2DStructProperty.h index 057bfd9..6a2afeb 100644 --- a/src/UESaveFile/Types/Vector2DStructProperty.h +++ b/src/Gvas/Types/Vector2DStructProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,16 +16,22 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include + #include #include -#include +#include #include "StructProperty.h" using namespace Corrade; using namespace Magnum; +typedef std::variant Vector2Variant; + +namespace Gvas::Types { + struct Vector2DStructProperty : public StructProperty { using ptr = Containers::Pointer; @@ -34,5 +40,7 @@ struct Vector2DStructProperty : public StructProperty { structType = "Vector2D"_s; } - Float x = 0.0f, y = 0.0f; + Vector2Variant vector; }; + +} diff --git a/src/UESaveFile/Types/VectorStructProperty.h b/src/Gvas/Types/VectorStructProperty.h similarity index 82% rename from src/UESaveFile/Types/VectorStructProperty.h rename to src/Gvas/Types/VectorStructProperty.h index b753c11..e9bda3e 100644 --- a/src/UESaveFile/Types/VectorStructProperty.h +++ b/src/Gvas/Types/VectorStructProperty.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,16 +16,22 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include + #include #include -#include +#include #include "StructProperty.h" using namespace Corrade; using namespace Magnum; +typedef std::variant Vector3Variant; + +namespace Gvas::Types { + struct VectorStructProperty : public StructProperty { using ptr = Containers::Pointer; @@ -34,5 +40,7 @@ struct VectorStructProperty : public StructProperty { structType = "Vector"_s; } - Float x = 0.0f, y = 0.0f, z = 0.0f; + Vector3Variant vector{Vector3{}}; }; + +} diff --git a/src/ImportExport/Export.cpp b/src/ImportExport/Export.cpp new file mode 100644 index 0000000..30ca77a --- /dev/null +++ b/src/ImportExport/Export.cpp @@ -0,0 +1,127 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include +#include + +#include "../Logger/Logger.h" +#include "../BinaryIo/Writer.h" +#include "../Configuration/Configuration.h" +#include "../Utilities/Crc32.h" + +#include "Keys.h" + +#include "Export.h" + +namespace mbst::ImportExport { + +static Containers::String last_export_error; + +Containers::StringView +lastExportError() { + return last_export_error; +} + +bool +exportStyle(Containers::StringView mass_name, mbst::GameObjects::CustomStyle& style) { + Containers::String style_type_str; + switch(style.type) { + case GameObjects::CustomStyle::Type::Unknown: + style_type_str = "Style"; + break; + case GameObjects::CustomStyle::Type::Frame: + style_type_str = "FrameStyle"; + break; + case GameObjects::CustomStyle::Type::Armour: + style_type_str = "ArmourStyle"; + break; + case GameObjects::CustomStyle::Type::Weapon: + style_type_str = "WeaponStyle"; + break; + case GameObjects::CustomStyle::Type::Global: + style_type_str = "GlobalStyle"; + break; + } + + auto filename = Utility::format("{}_{}_{}.style.mbst", mass_name, style_type_str, style.name); + for(auto& c : filename) { + if(c == ' ') { + c = '_'; + } + } + + auto path = Utility::Path::join(conf().directories().styles, filename); + BinaryIo::Writer writer{path}; + + if(!writer.open()) { + last_export_error = path + " couldn't be opened."; + return false; + } + + if(!writer.writeString("MBSTSTYLE")) { + LOG_ERROR(last_export_error = "Couldn't write magic bytes into " + filename); + return false; + } + + writer.writeValueToArray(Keys::CustomStyle::Name); + writer.writeUEStringToArray(style.name); + + writer.writeValueToArray(Keys::CustomStyle::Colour); + writer.writeValueToArray(style.colour); + writer.writeValueToArray(Keys::CustomStyle::Metallic); + writer.writeValueToArray(style.metallic); + writer.writeValueToArray(Keys::CustomStyle::Gloss); + writer.writeValueToArray(style.gloss); + writer.writeValueToArray(Keys::CustomStyle::Glow); + writer.writeValueToArray(style.glow); + + writer.writeValueToArray(Keys::CustomStyle::PatternId); + writer.writeValueToArray(style.patternId); + writer.writeValueToArray(Keys::CustomStyle::PatternOpacity); + writer.writeValueToArray(style.opacity); + writer.writeValueToArray(Keys::CustomStyle::PatternOffset); + writer.writeValueToArray(style.offset); + writer.writeValueToArray(Keys::CustomStyle::PatternRotation); + writer.writeValueToArray(style.rotation); + writer.writeValueToArray(Keys::CustomStyle::PatternScale); + writer.writeValueToArray(style.scale); + + if(!writer.writeUint64(writer.array().size())) { + LOG_ERROR(last_export_error = "Couldn't write data size into " + filename); + writer.closeFile(); + Utility::Path::remove(path); + return false; + } + + auto crc = Utilities::crc32(0, writer.array()); + if(!writer.writeUint32(crc)) { + LOG_ERROR(last_export_error = "Couldn't write CRC32 checksum into " + filename); + writer.closeFile(); + Utility::Path::remove(path); + return false; + } + + if(!writer.flushToFile()) { + LOG_ERROR(last_export_error = "Couldn't write data into " + filename); + writer.closeFile(); + Utility::Path::remove(path); + return false; + } + + return true; +} + +} diff --git a/src/ImportExport/Export.h b/src/ImportExport/Export.h new file mode 100644 index 0000000..40eda62 --- /dev/null +++ b/src/ImportExport/Export.h @@ -0,0 +1,31 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include "../GameObjects/CustomStyle.h" + +using namespace Corrade; + +namespace mbst::ImportExport { + +auto lastExportError() -> Containers::StringView; + +bool exportStyle(Containers::StringView mass_name, GameObjects::CustomStyle& style); + +} diff --git a/src/ImportExport/Import.cpp b/src/ImportExport/Import.cpp new file mode 100644 index 0000000..decdf35 --- /dev/null +++ b/src/ImportExport/Import.cpp @@ -0,0 +1,186 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include +#include +#include +#include + +#include "../BinaryIo/Reader.h" +#include "../Configuration/Configuration.h" +#include "../Logger/Logger.h" +#include "../Utilities/Crc32.h" + +#include "Keys.h" + +#include "Import.h" + +namespace mbst::ImportExport { + +static Containers::String last_import_error; + +Containers::StringView +lastImportError() { + return last_import_error; +} + +Containers::Optional +importStyle(Containers::StringView filename) { + auto path = Utility::Path::join(conf().directories().styles, filename); + if(!Utility::Path::exists(path)) { + LOG_ERROR(last_import_error = path + " doesn't exist."); + return Containers::NullOpt; + } + + BinaryIo::Reader reader{path}; + if(!reader.open()) { + last_import_error = path + " couldn't be opened."; + return Containers::NullOpt; + } + + Containers::Array magic_bytes; + if(!reader.readArray(magic_bytes, 9)) { + LOG_ERROR(last_import_error = "Couldn't read the magic bytes."); + return Containers::NullOpt; + } + + Containers::StringView magic_bytes_view = magic_bytes; + static const auto expected_magic_bytes = "MBSTSTYLE"_s; + if(magic_bytes_view != expected_magic_bytes) { + LOG_ERROR(last_import_error = "Magic bytes are mismatched."); + return Containers::NullOpt; + } + + std::size_t data_size; + if(!reader.readUint64(data_size)) { + LOG_ERROR(last_import_error = "Couldn't read data size."); + return Containers::NullOpt; + } + + std::uint32_t crc; + if(!reader.readUint32(crc)) { + LOG_ERROR(last_import_error = "Couldn't read CRC-32 checksum."); + return Containers::NullOpt; + } + + auto position = reader.position(); + + { + Containers::Array data; + if(!reader.readArray(data, data_size)) { + LOG_ERROR(last_import_error = "Couldn't read data."); + return Containers::NullOpt; + } + + auto computed_crc = Utilities::crc32(0, data); + if(computed_crc != crc) { + LOG_ERROR(last_import_error = Utility::format("CRC-32 checksum doesn't match. " + "Expected {}, got {} instead.", + crc, computed_crc)); + return Containers::NullOpt; + } + } + + if(!reader.seek(position)) { + LOG_ERROR(last_import_error = Utility::format("Couldn't seek to position {}.", position)); + return Containers::NullOpt; + } + + GameObjects::CustomStyle style{}; + + while(!reader.eof()) { + Keys::CustomStyle key; + if(!reader.readValue(key)) { + if(reader.eof()) { + break; + } + LOG_ERROR(last_import_error = "Couldn't read key."); + return Containers::NullOpt; + } + + switch(key) { + case Keys::Name: + if(!reader.readUEString(style.name)) { + LOG_ERROR(last_import_error = "Couldn't read style name."); + return Containers::NullOpt; + } + break; + case Keys::Colour: + if(!reader.readValue(style.colour)) { + LOG_ERROR(last_import_error = "Couldn't read style colour."); + return Containers::NullOpt; + } + break; + case Keys::Metallic: + if(!reader.readFloat(style.metallic)) { + LOG_ERROR(last_import_error = "Couldn't read style metallic."); + return Containers::NullOpt; + } + break; + case Keys::Gloss: + if(!reader.readFloat(style.gloss)) { + LOG_ERROR(last_import_error = "Couldn't read style gloss."); + return Containers::NullOpt; + } + break; + case Keys::Glow: + if(!reader.readValue(style.glow)) { + LOG_ERROR(last_import_error = "Couldn't read style glow."); + return Containers::NullOpt; + } + break; + case Keys::PatternId: + if(!reader.readInt32(style.patternId)) { + LOG_ERROR(last_import_error = "Couldn't read style pattern ID."); + return Containers::NullOpt; + } + break; + case Keys::PatternOpacity: + if(!reader.readFloat(style.opacity)) { + LOG_ERROR(last_import_error = "Couldn't read style pattern opacity."); + return Containers::NullOpt; + } + break; + case Keys::PatternOffset: + if(!reader.readValue(style.offset)) { + LOG_ERROR(last_import_error = "Couldn't read style pattern offset."); + return Containers::NullOpt; + } + break; + case Keys::PatternRotation: + if(!reader.readFloat(style.rotation)) { + LOG_ERROR(last_import_error = "Couldn't read style pattern rotation."); + return Containers::NullOpt; + } + break; + case Keys::PatternScale: + if(!reader.readFloat(style.scale)) { + LOG_ERROR(last_import_error = "Couldn't read style pattern scale."); + return Containers::NullOpt; + } + break; + default: + LOG_ERROR(last_import_error = Utility::format("Unknown key {}.", key)); + return Containers::NullOpt; + } + } + + return Utility::move(style); +} + +} diff --git a/src/ImportExport/Import.h b/src/ImportExport/Import.h new file mode 100644 index 0000000..61d0b77 --- /dev/null +++ b/src/ImportExport/Import.h @@ -0,0 +1,31 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include "../GameObjects/CustomStyle.h" + +using namespace Corrade; + +namespace mbst::ImportExport { + +auto lastImportError() -> Containers::StringView; + +auto importStyle(Containers::StringView filename) -> Containers::Optional; + +} diff --git a/src/ImportExport/Keys.h b/src/ImportExport/Keys.h new file mode 100644 index 0000000..eefc2a4 --- /dev/null +++ b/src/ImportExport/Keys.h @@ -0,0 +1,36 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +namespace mbst::ImportExport::Keys { + +enum CustomStyle: std::uint8_t { + Name = 0, // type: string + Colour = 1, // type: Magnum::Color4 + Metallic = 2, // type: float + Gloss = 3, // type: float + Glow = 4, // type: bool + PatternId = 5, // type: std::int32_t + PatternOpacity = 6, // type: float + PatternOffset = 7, // type: Magnum::Vector2 + PatternRotation = 8, // type: float + PatternScale = 9, // type: float +}; + +} diff --git a/src/Logger/EntryType.h b/src/Logger/EntryType.h index ac34d5c..1a8af03 100644 --- a/src/Logger/EntryType.h +++ b/src/Logger/EntryType.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 diff --git a/src/Logger/Logger.cpp b/src/Logger/Logger.cpp index b8f5cd4..4d738f0 100644 --- a/src/Logger/Logger.cpp +++ b/src/Logger/Logger.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,6 +16,11 @@ #include +#ifndef SAVETOOL_DEBUG_BUILD +#include +#include +#include +#endif #include #include "Logger.h" @@ -25,8 +30,6 @@ using Utility::Debug; using Utility::Warning; using Utility::Error; -using namespace Magnum; - Logger& Logger::instance() { static Logger logger; @@ -35,16 +38,17 @@ Logger::instance() { void Logger::initialise() { -#ifndef SAVETOOL_DEBUG_BUILD - _logFile.open("SaveToolLog.txt", std::ios::trunc); + #ifndef SAVETOOL_DEBUG_BUILD + auto exe_path = Utility::Path::split(*Utility::Path::executableLocation()).first(); + _logFile.open(Utility::Path::join(exe_path, "SaveToolLog.txt").cbegin(), 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" + "3. Mention me (@williamjcm) 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 + #endif } void @@ -62,38 +66,36 @@ Logger::unindent() { void Logger::log(EntryType type, StringView location, StringView message) { Debug d{ - #ifndef SAVETOOL_DEBUG_BUILD - &_logFile - #else + #ifdef SAVETOOL_DEBUG_BUILD &std::cout + #else + &_logFile #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); + d << "[ INFO]"_s; break; case EntryType::Warning: - d << COLOURED_TEXT(Yellow, "[WARNING]"_s); + d << "[WARNING]"_s; break; case EntryType::Error: - d << COLOURED_TEXT(Red, "[ ERROR]"_s); + d << "[ ERROR]"_s; break; } - #undef COLOURED_TEXT - d << "["_s << Debug::nospace << location << Debug::nospace << "]"; + d << "["_s << Debug::nospace << location << Debug::nospace << "]"_s; - for(UnsignedInt i = 0; i < _indentLevel; i++) { + for(auto i = 0u; i < _indentLevel; i++) { d << Debug::nospace << " "_s << Debug::nospace; } d << ((message.back() == '\n') ? message.exceptSuffix(1) : message); + + #ifndef SAVETOOL_DEBUG_BUILD + _logFile.flush(); + #endif } void diff --git a/src/Logger/Logger.h b/src/Logger/Logger.h index 7450754..f395c2d 100644 --- a/src/Logger/Logger.h +++ b/src/Logger/Logger.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,19 +16,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include - -#include #ifndef SAVETOOL_DEBUG_BUILD #include #endif +#include #include #include #include -#include - #include "EntryType.h" using namespace Corrade; @@ -37,8 +33,6 @@ using Containers::ArrayView; using Containers::String; using Containers::StringView; -using namespace Magnum; - using namespace Containers::Literals; class Logger { @@ -68,7 +62,7 @@ class Logger { std::ofstream _logFile; #endif - UnsignedInt _indentLevel = 0; + std::uint32_t _indentLevel = 0; std::mutex _logMutex{}; }; diff --git a/src/Logger/MagnumLogBuffer.cpp b/src/Logger/MagnumLogBuffer.cpp index e2e3df9..7f2f0e8 100644 --- a/src/Logger/MagnumLogBuffer.cpp +++ b/src/Logger/MagnumLogBuffer.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,7 +16,12 @@ #include "MagnumLogBuffer.h" -MagnumLogBuffer::MagnumLogBuffer(EntryType type): std::stringbuf(std::ios_base::out), _type{type} {} +MagnumLogBuffer::MagnumLogBuffer(EntryType type): + std::stringbuf(std::ios_base::out), + _type{type} +{ + //ctor +} MagnumLogBuffer::~MagnumLogBuffer() = default; diff --git a/src/Logger/MagnumLogBuffer.h b/src/Logger/MagnumLogBuffer.h index ea7888c..60872d7 100644 --- a/src/Logger/MagnumLogBuffer.h +++ b/src/Logger/MagnumLogBuffer.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -25,10 +25,10 @@ class MagnumLogBuffer : public std::stringbuf { public: explicit MagnumLogBuffer(EntryType type); - ~MagnumLogBuffer(); + ~MagnumLogBuffer() override; private: - int sync() override; + auto sync() -> int override; EntryType _type; }; diff --git a/src/Managers/Backup.h b/src/Managers/Backup.h new file mode 100644 index 0000000..c644610 --- /dev/null +++ b/src/Managers/Backup.h @@ -0,0 +1,43 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include +#include + +using namespace Corrade; + +namespace mbst::Managers { + +struct Backup { + Containers::String filename; + Containers::String company; + bool demo; + struct { + std::int32_t year; + std::int32_t month; + std::int32_t day; + std::int32_t hour; + std::int32_t minute; + std::int32_t second; + } timestamp; + Containers::Array includedFiles; +}; + +} diff --git a/src/Managers/BackupManager.cpp b/src/Managers/BackupManager.cpp new file mode 100644 index 0000000..be5941a --- /dev/null +++ b/src/Managers/BackupManager.cpp @@ -0,0 +1,338 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "../Configuration/Configuration.h" +#include "../Logger/Logger.h" +#include "../Utilities/Temp.h" + +#include "BackupManager.h" + +namespace mbst::Managers { + +BackupManager::BackupManager(): + _root{""} +{ + refresh(); +} + +Containers::StringView +BackupManager::lastError() { + return _lastError; +} + +void +BackupManager::refresh() { + _backups = Containers::Array{}; + + scanSubdir(""_s); + + _root.clear(); + _root.build(_backups); +} + +Containers::ArrayView +BackupManager::backups() const { + return _backups; +} + +const Vfs::Directory& +BackupManager::vfs() const +{ + return _root; +} + +bool +BackupManager::create(const GameObjects::Profile& profile) { + if(!profile.valid()) { + LOG_ERROR(_lastError = "Profile is not valid."); + return false; + } + + const auto timestamp = []{ + std::time_t timestamp = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); + return *std::localtime(×tamp); + }(); + + auto filename = Utility::format("{}_{}{:.2d}{:.2d}_{:.2d}{:.2d}{:.2d}.backup.mbst", + Utility::String::replaceAll(profile.companyName(), ' ', '_').data(), + timestamp.tm_year + 1900, timestamp.tm_mon + 1, timestamp.tm_mday, + timestamp.tm_hour, timestamp.tm_min, timestamp.tm_sec); + auto temp_path = Utilities::getTempPath(filename); + + int error_code = 0; + auto zip = zip_open(temp_path.data(), ZIP_CREATE|ZIP_TRUNCATE, &error_code); + if(zip == nullptr) { + zip_error_t error; + zip_error_init_with_code(&error, error_code); + LOG_ERROR(_lastError = zip_error_strerror(&error)); + zip_error_fini(&error); + return false; + } + + Containers::ScopeGuard guard{&filename, [](Containers::String* str){ + Utilities::deleteTempFile(*str); + }}; + + Containers::StringView save_dir = conf().directories().gameSaves; + + auto profile_source = zip_source_file(zip, Utility::Path::join(save_dir, profile.filename()).data(), 0, 0); + if(!profile_source) { + LOG_ERROR(_lastError = zip_strerror(zip)); + zip_source_free(profile_source); + return false; + } + + if(zip_file_add(zip, profile.filename().data(), profile_source, ZIP_FL_ENC_UTF_8) == -1) { + LOG_ERROR(_lastError = zip_strerror(zip)); + zip_source_free(profile_source); + return false; + } + + auto comment = Utility::format("{}|{}|{}-{:.2d}-{:.2d}-{:.2d}-{:.2d}-{:.2d}", + profile.companyName(), profile.isDemo() ? "demo"_s : "full"_s, + timestamp.tm_year + 1900, timestamp.tm_mon + 1, timestamp.tm_mday, + timestamp.tm_hour, timestamp.tm_min, timestamp.tm_sec); + zip_set_archive_comment(zip, comment.data(), comment.size()); + + for(std::uint8_t i = 0; i < 32; ++i) { + auto build_filename = Utility::format("{}Unit{:.2d}{}.sav", + profile.isDemo() ? "Demo"_s : ""_s, i, + profile.account()); + + if(!Utility::Path::exists(Utility::Path::join(save_dir, build_filename))) { + continue; + } + + auto build_source = zip_source_file(zip, Utility::Path::join(save_dir, build_filename).data(), 0, 0); + if(!build_source) { + LOG_ERROR(_lastError = zip_strerror(zip)); + zip_source_free(build_source); + return false; + } + + if(zip_file_add(zip, build_filename.data(), build_source, ZIP_FL_ENC_UTF_8) == -1) { + LOG_ERROR(_lastError = zip_strerror(zip)); + zip_source_free(build_source); + return false; + } + } + + if(zip_close(zip) == -1) { + LOG_ERROR(_lastError = zip_strerror(zip)); + return false; + } + + if(!Utilities::moveFromTemp(filename, conf().directories().backups)) { + _lastError = Utility::format("Couldn't move {} to {}.", filename, conf().directories().backups); + return false; + } + + guard.release(); + + return true; +} + +bool +BackupManager::remove(std::size_t index) { + CORRADE_INTERNAL_ASSERT(index < _backups.size()); + + if(!Utility::Path::remove(Utility::Path::join(conf().directories().backups, _backups[index].filename))) { + LOG_ERROR(_lastError = "Couldn't delete " + _backups[index].filename); + return false; + } + + return true; +} + +bool +BackupManager::restore(std::size_t index) { + CORRADE_INTERNAL_ASSERT(index < _backups.size()); + + const auto& backup = _backups[index]; + + int error_code = 0; + auto zip = zip_open(Utility::Path::join(conf().directories().backups, backup.filename).data(), ZIP_RDONLY, + &error_code); + if(zip == nullptr) { + zip_error_t error; + zip_error_init_with_code(&error, error_code); + LOG_ERROR(_lastError = zip_error_strerror(&error)); + zip_error_fini(&error); + return false; + } + + Containers::ScopeGuard zip_guard{zip, zip_close}; + + auto error_format = "Extraction of file {} failed: {}"_s; + + for(Containers::StringView file : backup.includedFiles) { + auto temp_file = Utilities::getTempPath(file); + auto out = std::fopen(temp_file.cbegin(), "wb"); + if(out == nullptr) { + LOG_ERROR(_lastError = Utility::format(error_format.data(), file, std::strerror(errno))); + return false; + } + + Containers::ScopeGuard out_guard{out, std::fclose}; + + auto zf = zip_fopen(zip, file.data(), ZIP_FL_ENC_UTF_8); + if(zf == nullptr) { + LOG_ERROR(_lastError = Utility::format(error_format.data(), file, zip_strerror(zip))); + return false; + } + + Containers::ScopeGuard zf_guard{zf, zip_fclose}; + + Containers::StaticArray<8192, char> buf{ValueInit}; + + std::int64_t bytes_read; + while((bytes_read = zip_fread(zf, buf.data(), buf.size())) > 0ll) { + if(std::fwrite(buf.data(), sizeof(char), bytes_read, out) < static_cast(bytes_read)) { + LOG_ERROR(_lastError = Utility::format(error_format.data(), file, "not enough bytes written.")); + return false; + } + } + + if(bytes_read == -1) { + LOG_ERROR(_lastError = Utility::format(error_format.data(), file, "couldn't read bytes from archive.")); + return false; + } + + if(!Utilities::moveFromTemp(file, conf().directories().gameSaves)) { + _lastError = Utility::format("Couldn't move {} to {}.", file, conf().directories().gameSaves); + return false; + } + } + + return true; +} + +void +BackupManager::scanSubdir(Containers::StringView subdir) { + static std::uint8_t depth = 0; + + auto full_subdir = Utility::Path::join(conf().directories().backups, subdir); + + using Flag = Utility::Path::ListFlag; + auto files = Utility::Path::list(full_subdir, Flag::SkipDirectories|Flag::SkipSpecial); + if(!files) { + LOG_ERROR_FORMAT("Couldn't list contents of {}.", full_subdir); + } + + auto predicate = [](Containers::StringView file)->bool{ + return !(file.hasSuffix(".mbprofbackup"_s) || file.hasSuffix(".backup.mbst"_s)); + }; + + auto files_view = files->exceptSuffix(files->end() - std::remove_if(files->begin(), files->end(), predicate)); + + int error_code = 0; + zip_t* zip; + for(Containers::StringView file : files_view) { + Backup backup; + backup.filename = Utility::Path::join(subdir, file); + + auto full_path = Utility::Path::join(full_subdir, file); + + zip = zip_open(full_path.cbegin(), ZIP_RDONLY, &error_code); + if(zip == nullptr) { + continue; + } + + Containers::ScopeGuard guard{zip, zip_close}; + + auto num_entries = zip_get_num_entries(zip, ZIP_FL_UNCHANGED); + + if(num_entries == 0) { + continue; + } + + int comment_length; + Containers::StringView comment = zip_get_archive_comment(zip, &comment_length, ZIP_FL_UNCHANGED); + if(comment == nullptr) { + continue; + } + + auto info = comment.split('|'); + + if(info.size() != 3) { + continue; + } + + backup.company = info[0]; + + if(info[1].hasPrefix("full")) { + backup.demo = false; + } + else if(info[1].hasPrefix("demo")) { + backup.demo = true; + } + else { + continue; + } + + auto ts = info[2].split('-'); + if(ts.size() != 6) { + continue; + } + + backup.timestamp.year = std::strtol(ts[0].data(), nullptr, 10); + backup.timestamp.month = std::strtol(ts[1].data(), nullptr, 10); + backup.timestamp.day = std::strtol(ts[2].data(), nullptr, 10); + backup.timestamp.hour = std::strtol(ts[3].data(), nullptr, 10); + backup.timestamp.minute = std::strtol(ts[4].data(), nullptr, 10); + backup.timestamp.second = std::strtol(ts[5].data(), nullptr, 10); + + arrayReserve(backup.includedFiles, num_entries); + + for(auto i = 0; i < num_entries; i++) { + arrayAppend(backup.includedFiles, InPlaceInit, zip_get_name(zip, i, ZIP_FL_UNCHANGED)); + } + + arrayAppend(_backups, Utility::move(backup)); + } + + if(depth == 5) { + return; + } + + auto subdirs = Utility::Path::list(full_subdir, Flag::SkipFiles|Flag::SkipSpecial|Flag::SkipDotAndDotDot); + if(!subdirs) { + LOG_ERROR_FORMAT("Couldn't list contents of {}.", full_subdir); + } + + depth++; + + for(auto& dir : *subdirs) { + scanSubdir(Utility::Path::join(subdir, dir)); + } + + depth--; +} + +} diff --git a/src/Managers/BackupManager.h b/src/Managers/BackupManager.h new file mode 100644 index 0000000..2f48e89 --- /dev/null +++ b/src/Managers/BackupManager.h @@ -0,0 +1,63 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include +#include +#include +#include + +#include + +#include "Backup.h" +#include "../GameObjects/Profile.h" +#include "Vfs/Directory.h" + +using namespace Corrade; + +namespace mbst::Managers { + +class BackupManager { + public: + explicit BackupManager(); + + auto lastError() -> Containers::StringView; + + void refresh(); + + auto backups() const -> Containers::ArrayView; + + auto vfs() const -> const Vfs::Directory&; + + bool create(const GameObjects::Profile& profile); + + bool remove(std::size_t index); + bool remove(Containers::StringView filename); + + bool restore(std::size_t index); + + private: + void scanSubdir(Containers::StringView subdir); + + Containers::String _lastError; + + Containers::Array _backups; + + Vfs::Directory _root; +}; + +} diff --git a/src/Managers/MassManager.cpp b/src/Managers/MassManager.cpp new file mode 100644 index 0000000..80c5f4a --- /dev/null +++ b/src/Managers/MassManager.cpp @@ -0,0 +1,150 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include +#include + +#include "../Configuration/Configuration.h" +#include "../Logger/Logger.h" + +#include "MassManager.h" + +using namespace Containers::Literals; + +namespace mbst::Managers { + +MassManager::MassManager(Containers::StringView account, bool demo): + _account{account}, _demo{demo} +{ + Containers::String mass_filename = ""; + for(std::uint32_t i = 0; i < _hangars.size(); i++) { + mass_filename = Utility::Path::join(conf().directories().gameSaves, + Utility::format("{}Unit{:.2d}{}.sav", demo ? "Demo"_s : ""_s, i, _account)); + new(&_hangars[i]) GameObjects::Mass{mass_filename}; + } +} + +Containers::StringView +MassManager::lastError() { + return _lastError; +} + +GameObjects::Mass& +MassManager::hangar(std::int32_t hangar) { + return _hangars[hangar]; +} + +void +MassManager::refreshHangar(std::int32_t hangar) { + if(hangar < 0 || hangar >= 32) { + _lastError = "Hangar index out of range."; + LOG_ERROR(_lastError); + return; + } + + Containers::String mass_filename = + Utility::Path::join(conf().directories().gameSaves, + Utility::format("{}Unit{:.2d}{}.sav", _demo ? "Demo" : "", hangar, _account)); + _hangars[hangar] = GameObjects::Mass{mass_filename}; +} + +bool +MassManager::exportMass(std::int32_t hangar) { + if(hangar < 0 || hangar >= 32) { + _lastError = "Hangar index out of range."_s; + LOG_ERROR(_lastError); + return false; + } + + if(_hangars[hangar].state() != GameObjects::Mass::State::Valid) { + _lastError = Utility::format("There is no valid data to export in hangar {:.2d}", hangar + 1); + LOG_ERROR(_lastError); + return false; + } + + Containers::String source = Utility::Path::join(conf().directories().gameSaves, _hangars[hangar].filename()); + Containers::String dest = Utility::Path::join(conf().directories().staging, + Utility::format("{}_{}.sav", _hangars[hangar].name(), _account)); + + if(!Utility::Path::copy(source, dest)) { + _lastError = Utility::format("Couldn't export data from hangar {:.2d} to {}", hangar, dest); + LOG_ERROR(_lastError); + return false; + } + + return true; +} + +bool +MassManager::moveMass(std::int32_t source, std::int32_t destination) { + if(source < 0 || source >= 32) { + _lastError = "Source hangar index out of range."_s; + LOG_ERROR(_lastError); + return false; + } + + if(destination < 0 || destination >= 32) { + _lastError = "Destination hangar index out of range."_s; + LOG_ERROR(_lastError); + return false; + } + + Containers::String source_file = Utility::Path::join(conf().directories().gameSaves, + _hangars[source].filename()); + Containers::String dest_file = Utility::Path::join(conf().directories().gameSaves, + _hangars[destination].filename()); + GameObjects::Mass::State dest_state = _hangars[destination].state(); + + switch(dest_state) { + case GameObjects::Mass::State::Empty: + break; + case GameObjects::Mass::State::Invalid: + Utility::Path::remove(dest_file); + break; + case GameObjects::Mass::State::Valid: + Utility::Path::move(dest_file, dest_file + ".tmp"_s); + break; + } + + Utility::Path::move(source_file, dest_file); + + if(dest_state == GameObjects::Mass::State::Valid) { + Utility::Path::move(dest_file + ".tmp"_s, source_file); + } + + return true; +} + +bool +MassManager::deleteMass(std::int32_t hangar) { + if(hangar < 0 || hangar >= 32) { + _lastError = "Hangar index out of range."_s; + LOG_ERROR(_lastError); + return false; + } + + if(!Utility::Path::remove(Utility::Path::join(conf().directories().gameSaves, _hangars[hangar].filename()))) { + _lastError = Utility::format("Deletion failed: {}", std::strerror(errno)); + LOG_ERROR(_lastError); + return false; + } + + return true; +} + +} diff --git a/src/Managers/MassManager.h b/src/Managers/MassManager.h new file mode 100644 index 0000000..eda9a9a --- /dev/null +++ b/src/Managers/MassManager.h @@ -0,0 +1,53 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include +#include +#include + +#include "../GameObjects/Mass.h" + +using namespace Corrade; + +namespace mbst::Managers { + +class MassManager { + public: + MassManager(Containers::StringView account, bool demo); + + auto lastError() -> Containers::StringView; + + auto hangar(int hangar) -> GameObjects::Mass&; + + void refreshHangar(int hangar); + + bool exportMass(int hangar); + + bool moveMass(int source, int destination); + bool deleteMass(int hangar); + + private: + Containers::StringView _account; + bool _demo; + + Containers::String _lastError; + + Containers::StaticArray<32, GameObjects::Mass> _hangars{NoInit}; +}; + +} diff --git a/src/Managers/ProfileManager.cpp b/src/Managers/ProfileManager.cpp new file mode 100644 index 0000000..6dc724e --- /dev/null +++ b/src/Managers/ProfileManager.cpp @@ -0,0 +1,132 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include +#include +#include +#include +#include + +#include "../Configuration/Configuration.h" +#include "../Logger/Logger.h" + +#include "ProfileManager.h" + +using namespace Containers::Literals; + +namespace mbst::Managers { + +ProfileManager::ProfileManager() { + _ready = refreshProfiles(); +} + +bool +ProfileManager::ready() const { + return _ready; +} + +Containers::StringView +ProfileManager::lastError() { + return _lastError; +} + +Containers::ArrayView +ProfileManager::profiles() { + return _profiles; +} + +bool +ProfileManager::refreshProfiles() { + LOG_INFO("Refreshing profiles."); + + _profiles = Containers::Array{}; + + using Utility::Path::ListFlag; + auto files = Utility::Path::list(conf().directories().gameSaves, + ListFlag::SkipSpecial|ListFlag::SkipDirectories|ListFlag::SkipDotAndDotDot); + + if(!files) { + LOG_ERROR(_lastError = conf().directories().gameSaves + " can't be opened."); + return false; + } + + auto predicate = [](Containers::StringView file)->bool{ + return !((file.hasPrefix("DemoProfile") || file.hasPrefix("Profile")) && file.hasSuffix(".sav")); + }; + + auto files_view = files->exceptSuffix(files->end() - std::remove_if(files->begin(), files->end(), predicate)); + + for(const auto& file : files_view) { + GameObjects::Profile profile{Utility::Path::join(conf().directories().gameSaves, file)}; + + if(!profile.valid()) { + LOG_WARNING_FORMAT("Profile {} is invalid: {}", file, profile.lastError()); + continue; + } + + arrayAppend(_profiles, Utility::move(profile)); + } + + if(_profiles.isEmpty()) { + _lastError = "No valid profiles were found."_s; + LOG_ERROR(_lastError); + return false; + } + + return true; +} + +GameObjects::Profile* +ProfileManager::getProfile(std::size_t index) { + return index <= _profiles.size() ? &(_profiles[index]) : nullptr; +} + +bool +ProfileManager::deleteProfile(std::size_t index, bool delete_builds) { + CORRADE_INTERNAL_ASSERT(index < _profiles.size()); + + if(!Utility::Path::remove(Utility::Path::join(conf().directories().gameSaves, _profiles[index].filename()))) { + _lastError = Utility::format("Couldn't delete {} (filename: {}).", + _profiles[index].companyName(), + _profiles[index].filename()); + LOG_ERROR(_lastError); + refreshProfiles(); + return false; + } + + if(delete_builds) { + for(std::uint8_t i = 0; i < 32; ++i) { + auto filename = Utility::format("{}Unit{:.2d}{}.sav", + _profiles[index].type() == GameObjects::Profile::Type::Demo ? "Demo": "", + i, _profiles[index].account()); + Utility::Path::remove(Utility::Path::join(conf().directories().gameSaves, filename)); + } + } + + auto file = _profiles[index].filename(); + auto it = std::remove_if(_profiles.begin(), _profiles.end(), + [&file](GameObjects::Profile& profile){ return profile.filename() == file; }); + + if(it != _profiles.end()) { + arrayRemoveSuffix(_profiles, 1); + } + + return true; +} + +} diff --git a/src/Managers/ProfileManager.h b/src/Managers/ProfileManager.h new file mode 100644 index 0000000..8418ddf --- /dev/null +++ b/src/Managers/ProfileManager.h @@ -0,0 +1,48 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include +#include + +#include "../GameObjects/Profile.h" + +using namespace Corrade; + +namespace mbst::Managers { + +class ProfileManager { + public: + explicit ProfileManager(); + + auto ready() const -> bool; + auto lastError() -> Containers::StringView; + + auto profiles() -> Containers::ArrayView; + bool refreshProfiles(); + + auto getProfile(std::size_t index) -> GameObjects::Profile*; + bool deleteProfile(std::size_t index, bool delete_builds); + + private: + bool _ready = false; + Containers::String _lastError; + + Containers::Array _profiles; +}; + +} diff --git a/src/Managers/StagedMass.h b/src/Managers/StagedMass.h new file mode 100644 index 0000000..0d48fa9 --- /dev/null +++ b/src/Managers/StagedMass.h @@ -0,0 +1,30 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +using namespace Corrade; + +namespace mbst::Managers { + +struct StagedMass { + Containers::String filename; + Containers::String name; +}; + +} diff --git a/src/Managers/StagedMassManager.cpp b/src/Managers/StagedMassManager.cpp new file mode 100644 index 0000000..20bd5fa --- /dev/null +++ b/src/Managers/StagedMassManager.cpp @@ -0,0 +1,215 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include + +#include +#include + +#include "../Configuration/Configuration.h" +#include "../GameObjects/Mass.h" +#include "../Logger/Logger.h" +#include "../Utilities/Temp.h" + +#include "StagedMassManager.h" + + +namespace mbst::Managers { + +StagedMassManager::StagedMassManager() { + refresh(); +} + +Containers::StringView +StagedMassManager::lastError() { + return _lastError; +} + +Containers::ArrayView +StagedMassManager::stagedMasses() const { + return _stagedMasses; +} + +const StagedMass& +StagedMassManager::at(Containers::StringView filename) const { + for(const auto& mass : _stagedMasses) { + if(mass.filename == filename) { + return mass; + } + } + + CORRADE_ASSERT_UNREACHABLE("Invalid staged M.A.S.S.!", StagedMass{}); +} + +void +StagedMassManager::refresh() { + _stagedMasses = Containers::Array{}; + + LOG_INFO("Scanning for staged M.A.S.S.es..."); + + scanSubdir(""_s); +} + +void +StagedMassManager::refreshMass(Containers::StringView filename) { + LOG_INFO_FORMAT("Refreshing staged unit with filename {}.", filename); + + bool file_exists = Utility::Path::exists(Utility::Path::join(conf().directories().staging, filename)); + auto index = _stagedMasses.size(); + for(std::size_t i = 0; i < _stagedMasses.size(); ++i) { + if(_stagedMasses[i].filename == filename) { + index = i; + break; + } + } + + if(file_exists) { + if(auto name = GameObjects::Mass::getNameFromFile(Utility::Path::join(conf().directories().staging, filename))) { + arrayAppend(_stagedMasses, StagedMass{filename, *name}); + std::sort(_stagedMasses.begin(), _stagedMasses.end(), + [](const StagedMass& a, const StagedMass& b)->bool{ + if(a.filename.contains('/') && !b.filename.contains('/')) { + return true; + } + return a.filename < b.filename; + } + ); + } + else if(index != _stagedMasses.size()) { + arrayRemove(_stagedMasses, index); + } + } + else if(index != _stagedMasses.size()) { + arrayRemove(_stagedMasses, index); + } +} + +bool +StagedMassManager::import(Containers::StringView filename, int index, Containers::StringView new_account, bool demo) { + if(index < 0 || index >= 32) { + LOG_ERROR(_lastError = "Hangar index out of range."_s); + return false; + } + + bool found = false; + for(auto& mass : _stagedMasses) { + if(mass.filename == filename) { + found = true; + break; + } + } + + if(!found) { + LOG_ERROR(_lastError = "Couldn't find "_s + filename + " in the staged M.A.S.S.es."_s); + return false; + } + + auto source = Utility::Path::join(conf().directories().staging, filename); + auto temp_path = Utilities::getTempPath(Utility::Path::split(filename).second()); + Utility::Path::copy(source, temp_path); + + { + GameObjects::Mass mass{temp_path}; + if(!mass.updateAccount(new_account)) { + LOG_ERROR(_lastError = mass.lastError()); + Utility::Path::remove(temp_path); + return false; + } + } + + auto dest = Utility::Path::join(conf().directories().gameSaves, + Utility::format("{}Unit{}{}.sav", demo ? "Demo" : "", index, new_account)); + + if(Utility::Path::exists(dest)) { + Utility::Path::remove(dest); + } + + if(!Utility::Path::move(temp_path, dest)) { + _lastError = Utility::format("Couldn't move {} to hangar {:.2d}", filename, index + 1); + LOG_ERROR(_lastError); + return false; + } + + return true; +} + +bool +StagedMassManager::remove(Containers::StringView filename) { + bool found = false; + for(auto& mass : _stagedMasses) { + if(mass.filename == filename) { + found = true; + break; + } + } + + if(!found || !Utility::Path::remove(Utility::Path::join(conf().directories().staging, filename))) { + LOG_ERROR(_lastError = Utility::format("{} couldn't be found.", filename)); + return false; + } + + return true; +} + +void +StagedMassManager::scanSubdir(Containers::StringView subdir) { + static std::uint8_t depth = 0; + + auto full_subdir = Utility::Path::join(conf().directories().staging, subdir); + + using Flag = Utility::Path::ListFlag; + auto files = Utility::Path::list(full_subdir, Flag::SkipSpecial|Flag::SkipDirectories); + + if(!files) { + LOG_ERROR_FORMAT("{} couldn't be opened.", full_subdir); + return; + } + + auto iter = std::remove_if(files->begin(), files->end(), [](Containers::StringView file){ + return !file.hasSuffix(".sav"_s); + }); + + auto files_view = files->exceptSuffix(files->end() - iter); + + for(Containers::StringView file : files_view) { + if(auto name = GameObjects::Mass::getNameFromFile(Utility::Path::join(full_subdir, file))) { + LOG_INFO_FORMAT("Found staged M.A.S.S.: {}", *name); + arrayAppend(_stagedMasses, InPlaceInit, file, *name); + } + else { + LOG_WARNING_FORMAT("Skipped {}.", file); + } + } + + if(depth == 5) { + return; + } + + auto subdirs = Utility::Path::list(full_subdir, Flag::SkipFiles|Flag::SkipSpecial|Flag::SkipDotAndDotDot); + if(!subdirs) { + LOG_ERROR_FORMAT("Couldn't list contents of {}.", full_subdir); + } + + depth++; + + for(auto& dir : *subdirs) { + scanSubdir(Utility::Path::join(subdir, dir)); + } + + depth--; +} + +} diff --git a/src/Managers/StagedMassManager.h b/src/Managers/StagedMassManager.h new file mode 100644 index 0000000..87b1630 --- /dev/null +++ b/src/Managers/StagedMassManager.h @@ -0,0 +1,52 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include +#include +#include +#include + +#include "StagedMass.h" + +using namespace Corrade; + +namespace mbst::Managers { + +class StagedMassManager { + public: + explicit StagedMassManager(); + + auto lastError() -> Containers::StringView; + + auto stagedMasses() const -> Containers::ArrayView; + auto at(Containers::StringView filename) const -> const StagedMass&; + + void refresh(); + void refreshMass(Containers::StringView filename); + bool import(Containers::StringView filename, int index, Containers::StringView new_account, bool demo); + bool remove(Containers::StringView filename); + + private: + void scanSubdir(Containers::StringView subdir); + + Containers::String _lastError; + + Containers::Array _stagedMasses; +}; + +} diff --git a/src/Managers/Vfs/Directory.h b/src/Managers/Vfs/Directory.h new file mode 100644 index 0000000..afc8fd7 --- /dev/null +++ b/src/Managers/Vfs/Directory.h @@ -0,0 +1,102 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include +#include +#include + +using namespace Corrade; + +namespace mbst::Managers::Vfs { + +CORRADE_HAS_TYPE(HasFilename, decltype(T::filename)); + +template +class Directory { + public: + static_assert(HasFilename::value && (std::is_same::value || std::is_same::value), + "FileType has no string-like member named filename."); + + using DirType = Directory; + + explicit Directory(Containers::StringView name): _name{name} { + // ctor + } + + Directory(const Directory& other) = delete; + Directory& operator=(const Directory& other) = delete; + + Directory(Directory&& other) = default; + Directory& operator=(Directory&& other) = default; + + auto name() const -> Containers::StringView { + return _name; + } + + auto subdirs() const -> Containers::ArrayView { + return _subdirs; + } + + auto files() const -> Containers::ArrayView { + return _files; + } + + void clear() { + _subdirs = Containers::Array{}; + _files = Containers::Array{}; + } + + void build(Containers::ArrayView files) { + for(auto& file : files) { + auto components = file.filename.split('/'); + CORRADE_INTERNAL_ASSERT(components.size() != 0); + + buildNestedPath(*this, components, file); + } + } + + private: + void buildNestedPath(Directory& root, Containers::ArrayView components, + FileType& file) + { + if(components.size() > 1) { + bool found = false; + for(auto& subdir : root._subdirs) { + if(subdir._name == components[0]) { + found = true; + buildNestedPath(subdir, components.exceptPrefix(1), file); + break; + } + } + + if(!found) { + arrayAppend(root._subdirs, InPlaceInit, components[0]); + buildNestedPath(root._subdirs.back(), components.exceptPrefix(1), file); + } + } + else if(components.size() == 1) { + arrayAppend(root._files, &file); + } + } + + Containers::String _name; + Containers::Array _subdirs; + Containers::Array _files; +}; + +} diff --git a/src/Maps/Accessories.h b/src/Maps/Accessories.h deleted file mode 100644 index 5b3ebbb..0000000 --- a/src/Maps/Accessories.h +++ /dev/null @@ -1,684 +0,0 @@ -#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 . - -#include - -#include - -#include - -using namespace Corrade; -using namespace Containers::Literals; -using namespace Magnum; - -enum AccessorySize { - S, - M, - L, - XL -}; - -struct AccessoryData{ - Containers::StringView name; - AccessorySize size = AccessorySize::S; -}; - -static const std::map accessories { - // region Primitives - {1, {"Cube"_s, AccessorySize::S}}, - {2, {"Pentagon"_s, AccessorySize::S}}, - {3, {"Hexagon"_s, AccessorySize::S}}, - {4, {"Cylinder"_s, AccessorySize::S}}, - {5, {"Sphere"_s, AccessorySize::S}}, - {6, {"TriPyramid"_s, AccessorySize::S}}, - {7, {"SquPyramid"_s, AccessorySize::S}}, - {8, {"PenPyramid"_s, AccessorySize::S}}, - {9, {"HexPyramid"_s, AccessorySize::S}}, - {10, {"Cone"_s, AccessorySize::S}}, - {11, {"SquStick"_s, AccessorySize::S}}, - {12, {"PenStick"_s, AccessorySize::S}}, - {13, {"HexStick"_s, AccessorySize::S}}, - {14, {"CycStick"_s, AccessorySize::S}}, - {15, {"Capsule"_s, AccessorySize::S}}, - {16, {"Decal Pad 01"_s, AccessorySize::S}}, - {17, {"Decal Pad 02"_s, AccessorySize::S}}, - {18, {"Decal Pad 03"_s, AccessorySize::S}}, - {19, {"Decal Pad 04"_s, AccessorySize::S}}, - {20, {"Decal Pad 05"_s, AccessorySize::S}}, - {21, {"Triangle"_s, AccessorySize::S}}, - {22, {"ThinStar"_s, AccessorySize::S}}, - {23, {"Star"_s, AccessorySize::S}}, - {24, {"SixSideStar"_s, AccessorySize::S}}, - {25, {"Asterisk"_s, AccessorySize::S}}, - {26, {"Ring"_s, AccessorySize::S}}, - {27, {"SawedRing"_s, AccessorySize::S}}, - {28, {"HalfRing"_s, AccessorySize::S}}, - {29, {"Cresent"_s, AccessorySize::S}}, - {30, {"Donut"_s, AccessorySize::S}}, - {31, {"FiveCogWheel"_s, AccessorySize::S}}, - {32, {"SixCogWheel"_s, AccessorySize::S}}, - {33, {"SevenCogWheel"_s, AccessorySize::S}}, - {34, {"EightCogWheel"_s, AccessorySize::S}}, - {35, {"TwelveCogWheel"_s, AccessorySize::S}}, - - {51, {"SquBevel"_s, AccessorySize::S}}, - {52, {"TriBevel"_s, AccessorySize::S}}, - {53, {"PenBevel"_s, AccessorySize::S}}, - {54, {"HexBevel"_s, AccessorySize::S}}, - {55, {"CycBevel"_s, AccessorySize::S}}, - {56, {"RecBevel"_s, AccessorySize::S}}, - {57, {"DaiBevel"_s, AccessorySize::S}}, - {58, {"MonBevel"_s, AccessorySize::S}}, - {59, {"CofBevel"_s, AccessorySize::S}}, - {60, {"JevBevel"_s, AccessorySize::S}}, - {61, {"SquEmboss"_s, AccessorySize::S}}, - {62, {"TriEmboss"_s, AccessorySize::S}}, - {63, {"PenEmboss"_s, AccessorySize::S}}, - {64, {"HexEmboss"_s, AccessorySize::S}}, - {65, {"CycEmboss"_s, AccessorySize::S}}, - {66, {"RecEmboss"_s, AccessorySize::S}}, - {67, {"DaiEmboss"_s, AccessorySize::S}}, - {68, {"MonEmboss"_s, AccessorySize::S}}, - {69, {"CofEmboss"_s, AccessorySize::S}}, - {70, {"JevEmboss"_s, AccessorySize::S}}, - - {101, {"Flat Hex Pin"_s, AccessorySize::S}}, - {102, {"Cross Circle Pin"_s, AccessorySize::S}}, - {103, {"Flat Circle Pin"_s, AccessorySize::S}}, - {104, {"Hex Circle Pin"_s, AccessorySize::S}}, - {105, {"Circle Button Pin"_s, AccessorySize::S}}, - {106, {"Hexagon Pin"_s, AccessorySize::S}}, - {107, {"Cross Square Pin"_s, AccessorySize::S}}, - {108, {"Flat Square Pin"_s, AccessorySize::S}}, - {109, {"Quad Corner Pin"_s, AccessorySize::S}}, - {110, {"Bi Corner Pin"_s, AccessorySize::S}}, - {111, {"Circle Pin"_s, AccessorySize::S}}, - {112, {"Flat End Pin"_s, AccessorySize::S}}, - {113, {"Flat Cut Pin"_s, AccessorySize::S}}, - {114, {"Radial Pin"_s, AccessorySize::S}}, - {115, {"Diamiter Pin"_s, AccessorySize::S}}, - - {151, {"TriPoint"_s, AccessorySize::S}}, - {152, {"SquPoint"_s, AccessorySize::S}}, - {153, {"PenPoint"_s, AccessorySize::S}}, - {154, {"HexPoint"_s, AccessorySize::S}}, - {155, {"CycPoint"_s, AccessorySize::S}}, - {156, {"Bevel SquCutPoint"_s, AccessorySize::S}}, - {157, {"Bevel HexCutPoint"_s, AccessorySize::S}}, - {158, {"Bevel HexPoint"_s, AccessorySize::S}}, - {159, {"Bevel CycCutPoint"_s, AccessorySize::S}}, - {160, {"Bevel CycPoint"_s, AccessorySize::S}}, - - {201, {"Shaped Edge 01"_s, AccessorySize::M}}, - {202, {"Shaped Edge 02"_s, AccessorySize::M}}, - {203, {"Shaped Edge 03"_s, AccessorySize::M}}, - {204, {"Shaped Edge 04"_s, AccessorySize::M}}, - {205, {"Shaped Edge 05"_s, AccessorySize::M}}, - {206, {"Shaped Edge 06"_s, AccessorySize::M}}, - {207, {"Shaped Edge 07"_s, AccessorySize::M}}, - {208, {"Shaped Edge 08"_s, AccessorySize::M}}, - {209, {"Shaped Edge 09"_s, AccessorySize::M}}, - {210, {"Shaped Edge 10"_s, AccessorySize::M}}, - {211, {"Shaped Edge 11"_s, AccessorySize::M}}, - {212, {"Shaped Edge 12"_s, AccessorySize::M}}, - {213, {"Shaped Edge 13"_s, AccessorySize::M}}, - {214, {"Shaped Edge 14"_s, AccessorySize::M}}, - {215, {"Shaped Edge 15"_s, AccessorySize::M}}, - {216, {"Shaped Edge 16"_s, AccessorySize::M}}, - {217, {"Shaped Edge 17"_s, AccessorySize::M}}, - {218, {"Shaped Edge 18"_s, AccessorySize::M}}, - {219, {"Shaped Edge 19"_s, AccessorySize::M}}, - {220, {"Shaped Edge 20"_s, AccessorySize::M}}, - - {251, {"Fish Tail 01"_s, AccessorySize::M}}, - {252, {"Fish Tail 02"_s, AccessorySize::M}}, - {253, {"Fish Tail 03"_s, AccessorySize::M}}, - {254, {"Fish Tail 04"_s, AccessorySize::M}}, - {255, {"Fish Tail 05"_s, AccessorySize::M}}, - {256, {"Based Separator 01"_s, AccessorySize::M}}, - {257, {"Based Separator 02"_s, AccessorySize::M}}, - {258, {"Based Separator 03"_s, AccessorySize::M}}, - {259, {"Based Separator 04"_s, AccessorySize::M}}, - {260, {"Based Separator 05"_s, AccessorySize::M}}, - {261, {"Based Separator 06"_s, AccessorySize::M}}, - {262, {"Based Separator 07"_s, AccessorySize::M}}, - {263, {"Based Separator 08"_s, AccessorySize::M}}, - {264, {"Based Separator 09"_s, AccessorySize::M}}, - {265, {"Based Separator 10"_s, AccessorySize::M}}, - - {301, {"Rectangular Box 01"_s, AccessorySize::M}}, - {302, {"Rectangular Box 02"_s, AccessorySize::M}}, - {303, {"Rectangular Box 03"_s, AccessorySize::M}}, - {304, {"Rectangular Box 04"_s, AccessorySize::M}}, - {305, {"Rectangular Box 05"_s, AccessorySize::M}}, - {306, {"CofBox 01"_s, AccessorySize::M}}, - {307, {"CofBox 02"_s, AccessorySize::M}}, - {308, {"CofBox 03"_s, AccessorySize::M}}, - {309, {"CofBox 04"_s, AccessorySize::M}}, - {310, {"CofBox 05"_s, AccessorySize::M}}, - {311, {"Triangular Box 01"_s, AccessorySize::M}}, - {312, {"Triangular Box 02"_s, AccessorySize::M}}, - {313, {"Triangular Box 03"_s, AccessorySize::M}}, - {314, {"Triangular Box 04"_s, AccessorySize::M}}, - {315, {"Triangular Box 05"_s, AccessorySize::M}}, - {316, {"Diagonal Box A01"_s, AccessorySize::M}}, - {317, {"Diagonal Box A02"_s, AccessorySize::M}}, - {318, {"Diagonal Box A03"_s, AccessorySize::M}}, - {319, {"Diagonal Box A04"_s, AccessorySize::M}}, - {320, {"Diagonal Box A05"_s, AccessorySize::M}}, - {321, {"Diagonal Box B01"_s, AccessorySize::M}}, - {322, {"Diagonal Box B02"_s, AccessorySize::M}}, - {323, {"Diagonal Box B03"_s, AccessorySize::M}}, - {324, {"Diagonal Box B04"_s, AccessorySize::M}}, - {325, {"Diagonal Box B05"_s, AccessorySize::M}}, - // endregion - - // region Armours - {1001, {"Short Layer 01"_s, AccessorySize::M}}, - {1002, {"Short Layer 02"_s, AccessorySize::M}}, - {1003, {"Short Layer 03"_s, AccessorySize::M}}, - {1004, {"Short Layer 04"_s, AccessorySize::M}}, - {1005, {"Short Layer 05"_s, AccessorySize::M}}, - {1006, {"Long Layer 01"_s, AccessorySize::M}}, - {1007, {"Long Layer 02"_s, AccessorySize::M}}, - {1008, {"Long Layer 03"_s, AccessorySize::M}}, - {1009, {"Long Layer 04"_s, AccessorySize::M}}, - {1010, {"Long Layer 05"_s, AccessorySize::M}}, - {1011, {"Diagonal Long Layer 01"_s, AccessorySize::M}}, - {1012, {"Diagonal Long Layer 02"_s, AccessorySize::M}}, - {1013, {"Diagonal Long Layer 03"_s, AccessorySize::M}}, - {1014, {"Diagonal Long Layer 04"_s, AccessorySize::M}}, - {1015, {"Diagonal Long Layer 05"_s, AccessorySize::M}}, - - {1051, {"Sloped Layer 01"_s, AccessorySize::M}}, - {1052, {"Sloped Layer 02"_s, AccessorySize::M}}, - {1053, {"Sloped Layer 03"_s, AccessorySize::M}}, - {1054, {"Sloped Layer 04"_s, AccessorySize::M}}, - {1055, {"Sloped Layer 05"_s, AccessorySize::M}}, - {1056, {"Sloped Layer 06"_s, AccessorySize::M}}, - {1057, {"Sloped Layer 07"_s, AccessorySize::M}}, - {1058, {"Sloped Layer 08"_s, AccessorySize::M}}, - {1059, {"Sloped Layer 09"_s, AccessorySize::M}}, - {1060, {"Sloped Layer 10"_s, AccessorySize::M}}, - {1061, {"Sloped Layer 11"_s, AccessorySize::M}}, - {1062, {"Sloped Layer 12"_s, AccessorySize::M}}, - {1063, {"Sloped Layer 13"_s, AccessorySize::M}}, - {1064, {"Sloped Layer 14"_s, AccessorySize::M}}, - {1065, {"Sloped Layer 15"_s, AccessorySize::M}}, - - {1101, {"Raised Center 01"_s, AccessorySize::M}}, - {1102, {"Raised Center 02"_s, AccessorySize::M}}, - {1103, {"Raised Center 03"_s, AccessorySize::M}}, - {1104, {"Raised Center 04"_s, AccessorySize::M}}, - {1105, {"Raised Center 05"_s, AccessorySize::M}}, - {1106, {"Raised Block 01"_s, AccessorySize::M}}, - {1107, {"Raised Block 02"_s, AccessorySize::M}}, - {1108, {"Raised Block 03"_s, AccessorySize::M}}, - {1109, {"Raised Pointed"_s, AccessorySize::M}}, - {1110, {"Raised Cover"_s, AccessorySize::M}}, - {1111, {"Raised Slant 01"_s, AccessorySize::M}}, - {1112, {"Raised Slant 02"_s, AccessorySize::M}}, - {1113, {"Raised Slant 03"_s, AccessorySize::M}}, - {1114, {"Raised Slant 04"_s, AccessorySize::M}}, - {1115, {"Raised Slant 05"_s, AccessorySize::M}}, - - {1151, {"Wide Patch 01"_s, AccessorySize::L}}, - {1152, {"Wide Patch 02"_s, AccessorySize::L}}, - {1153, {"Wide Patch 03"_s, AccessorySize::L}}, - {1154, {"Wide Patch 04"_s, AccessorySize::L}}, - {1155, {"Wide Patch 05"_s, AccessorySize::L}}, - - {1201, {"Pointed Armour 01"_s, AccessorySize::L}}, - {1202, {"Pointed Armour 02"_s, AccessorySize::L}}, - {1203, {"Pointed Armour 03"_s, AccessorySize::L}}, - {1204, {"Pointed Armour 04"_s, AccessorySize::L}}, - {1205, {"Pointed Armour 05"_s, AccessorySize::L}}, - {1206, {"Pointed Armour 06"_s, AccessorySize::L}}, - {1207, {"Pointed Armour 07"_s, AccessorySize::L}}, - {1208, {"Pointed Armour 08"_s, AccessorySize::L}}, - {1209, {"Pointed Armour 09"_s, AccessorySize::L}}, - {1210, {"Pointed Armour 10"_s, AccessorySize::L}}, - {1211, {"Pointed Armour 11"_s, AccessorySize::L}}, - {1212, {"Pointed Armour 12"_s, AccessorySize::L}}, - {1213, {"Pointed Armour 13"_s, AccessorySize::L}}, - {1214, {"Pointed Armour 14"_s, AccessorySize::L}}, - {1215, {"Pointed Armour 15"_s, AccessorySize::L}}, - - {1251, {"E Limb Cover 01"_s, AccessorySize::L}}, - {1252, {"E Limb Cover 02"_s, AccessorySize::L}}, - {1253, {"E Limb Cover 03"_s, AccessorySize::L}}, - {1254, {"E Limb Cover 04"_s, AccessorySize::L}}, - {1255, {"E Limb Cover 05"_s, AccessorySize::L}}, - {1256, {"E Limb Cover 06"_s, AccessorySize::L}}, - {1257, {"E Limb Cover 07"_s, AccessorySize::L}}, - {1258, {"E Limb Cover 08"_s, AccessorySize::L}}, - {1259, {"E Limb Cover 09"_s, AccessorySize::L}}, - {1260, {"E Limb Cover 10"_s, AccessorySize::L}}, - - {1301, {"C Limb Cover 01"_s, AccessorySize::L}}, - {1302, {"C Limb Cover 02"_s, AccessorySize::L}}, - {1303, {"C Limb Cover 03"_s, AccessorySize::L}}, - {1304, {"C Limb Cover 04"_s, AccessorySize::L}}, - {1305, {"C Limb Cover 05"_s, AccessorySize::L}}, - {1306, {"C Limb Cover 06"_s, AccessorySize::L}}, - {1307, {"C Limb Cover 07"_s, AccessorySize::L}}, - {1308, {"C Limb Cover 08"_s, AccessorySize::L}}, - {1309, {"C Limb Cover 09"_s, AccessorySize::L}}, - {1310, {"C Limb Cover 10"_s, AccessorySize::L}}, - {1311, {"C Limb Cover 11"_s, AccessorySize::L}}, - {1312, {"C Limb Cover 12"_s, AccessorySize::L}}, - {1313, {"C Limb Cover 13"_s, AccessorySize::L}}, - {1314, {"C Limb Cover 14"_s, AccessorySize::L}}, - {1315, {"C Limb Cover 15"_s, AccessorySize::L}}, - {1316, {"C Limb Cover 16"_s, AccessorySize::L}}, - {1317, {"C Limb Cover 17"_s, AccessorySize::L}}, - {1318, {"C Limb Cover 18"_s, AccessorySize::L}}, - {1319, {"C Limb Cover 19"_s, AccessorySize::L}}, - {1320, {"C Limb Cover 20"_s, AccessorySize::L}}, - - {1351, {"P Limb Cover 01"_s, AccessorySize::XL}}, - {1352, {"P Limb Cover 02"_s, AccessorySize::XL}}, - {1353, {"P Limb Cover 03"_s, AccessorySize::XL}}, - {1354, {"P Limb Cover 04"_s, AccessorySize::XL}}, - {1355, {"P Limb Cover 05"_s, AccessorySize::XL}}, - - {1401, {"Flat Cover 01"_s, AccessorySize::XL}}, - {1402, {"Flat Cover 02"_s, AccessorySize::XL}}, - {1403, {"Flat Cover 03"_s, AccessorySize::XL}}, - {1404, {"Flat Cover 04"_s, AccessorySize::XL}}, - {1405, {"Flat Cover 05"_s, AccessorySize::XL}}, - {1406, {"Flat Cover 06"_s, AccessorySize::XL}}, - {1407, {"Flat Cover 07"_s, AccessorySize::XL}}, - {1408, {"Flat Cover 08"_s, AccessorySize::XL}}, - {1409, {"Flat Cover 09"_s, AccessorySize::XL}}, - {1410, {"Flat Cover 10"_s, AccessorySize::XL}}, - - {1451, {"L Side Opening 01"_s, AccessorySize::XL}}, - {1452, {"L Side Opening 02"_s, AccessorySize::XL}}, - {1453, {"L Side Opening 03"_s, AccessorySize::XL}}, - {1454, {"L Side Opening 04"_s, AccessorySize::XL}}, - {1455, {"L Side Opening 05"_s, AccessorySize::XL}}, - {1456, {"L Side Opening 06"_s, AccessorySize::XL}}, - {1457, {"L Side Opening 07"_s, AccessorySize::XL}}, - {1458, {"L Side Opening 08"_s, AccessorySize::XL}}, - {1459, {"L Side Opening 09"_s, AccessorySize::XL}}, - {1460, {"L Side Opening 10"_s, AccessorySize::XL}}, - // endregion - - // region Components - {2001, {"Disc Padding 01"_s, AccessorySize::M}}, - {2002, {"Disc Padding 02"_s, AccessorySize::M}}, - {2003, {"Disc Padding 03"_s, AccessorySize::M}}, - {2004, {"Disc Padding 04"_s, AccessorySize::M}}, - {2005, {"Disc Padding 05"_s, AccessorySize::M}}, - {2006, {"Thin Padding 01"_s, AccessorySize::M}}, - {2007, {"Thin Padding 02"_s, AccessorySize::M}}, - {2008, {"Thin Padding 03"_s, AccessorySize::M}}, - {2009, {"Thin Padding 04"_s, AccessorySize::M}}, - {2010, {"Thin Padding 05"_s, AccessorySize::M}}, - {2011, {"Thick Padding 01"_s, AccessorySize::M}}, - {2012, {"Thick Padding 02"_s, AccessorySize::M}}, - {2013, {"Thick Padding 03"_s, AccessorySize::M}}, - {2014, {"Thick Padding 04"_s, AccessorySize::M}}, - {2015, {"Thick Padding 05"_s, AccessorySize::M}}, - {2016, {"Thick Padding 06"_s, AccessorySize::M}}, - {2017, {"Thick Padding 07"_s, AccessorySize::M}}, - {2018, {"Thick Padding 08"_s, AccessorySize::M}}, - {2019, {"Thick Padding 09"_s, AccessorySize::M}}, - {2020, {"Thick Padding 10"_s, AccessorySize::M}}, - {2021, {"CSide Padding 01"_s, AccessorySize::M}}, - {2022, {"CSide Padding 02"_s, AccessorySize::M}}, - {2023, {"CSide Padding 03"_s, AccessorySize::M}}, - {2024, {"CSide Padding 04"_s, AccessorySize::M}}, - {2025, {"CSide Padding 05"_s, AccessorySize::M}}, - - {2051, {"Container 01"_s, AccessorySize::L}}, - {2052, {"Container 02"_s, AccessorySize::L}}, - {2053, {"Container 03"_s, AccessorySize::L}}, - {2054, {"Container 04"_s, AccessorySize::L}}, - {2055, {"Container 05"_s, AccessorySize::L}}, - - {2101, {"Plating 01"_s, AccessorySize::L}}, - {2102, {"Plating 02"_s, AccessorySize::L}}, - {2103, {"Plating 03"_s, AccessorySize::L}}, - {2104, {"Plating 04"_s, AccessorySize::L}}, - {2105, {"Plating 05"_s, AccessorySize::L}}, - - {2151, {"Complex Base 01"_s, AccessorySize::L}}, - {2152, {"Complex Base 02"_s, AccessorySize::L}}, - {2153, {"Complex Base 03"_s, AccessorySize::L}}, - {2154, {"Complex Base 04"_s, AccessorySize::L}}, - {2155, {"Complex Base 05"_s, AccessorySize::L}}, - {2156, {"Complex Base 06"_s, AccessorySize::L}}, - {2157, {"Complex Base 07"_s, AccessorySize::L}}, - {2158, {"Complex Base 08"_s, AccessorySize::L}}, - {2159, {"Complex Base 09"_s, AccessorySize::L}}, - {2160, {"Complex Base 10"_s, AccessorySize::L}}, - - {2201, {"Long Base 01"_s, AccessorySize::XL}}, - {2202, {"Long Base 02"_s, AccessorySize::XL}}, - {2203, {"Long Base 03"_s, AccessorySize::XL}}, - {2204, {"Long Base 04"_s, AccessorySize::XL}}, - {2205, {"Long Base 05"_s, AccessorySize::XL}}, - - {2251, {"Straight Wing 01"_s, AccessorySize::XL}}, - {2252, {"Straight Wing 02"_s, AccessorySize::XL}}, - {2253, {"Straight Wing 03"_s, AccessorySize::XL}}, - {2254, {"Straight Wing 04"_s, AccessorySize::XL}}, - {2255, {"Straight Wing 05"_s, AccessorySize::XL}}, - {2256, {"Straight Wing 06"_s, AccessorySize::XL}}, - {2257, {"Straight Wing 07"_s, AccessorySize::XL}}, - {2258, {"Straight Wing 08"_s, AccessorySize::XL}}, - {2259, {"Straight Wing 09"_s, AccessorySize::XL}}, - {2260, {"Straight Wing 10"_s, AccessorySize::XL}}, - - {2301, {"Triangular Wing 01"_s, AccessorySize::XL}}, - {2302, {"Triangular Wing 02"_s, AccessorySize::XL}}, - {2303, {"Triangular Wing 03"_s, AccessorySize::XL}}, - {2304, {"Triangular Wing 04"_s, AccessorySize::XL}}, - {2305, {"Triangular Wing 05"_s, AccessorySize::XL}}, - {2306, {"Triangular Wing 06"_s, AccessorySize::XL}}, - {2307, {"Triangular Wing 07"_s, AccessorySize::XL}}, - {2308, {"Triangular Wing 08"_s, AccessorySize::XL}}, - {2309, {"Triangular Wing 09"_s, AccessorySize::XL}}, - {2310, {"Triangular Wing 10"_s, AccessorySize::XL}}, - {2311, {"Triangular Wing 11"_s, AccessorySize::L}}, - {2312, {"Triangular Wing 12"_s, AccessorySize::L}}, - {2313, {"Triangular Wing 13"_s, AccessorySize::L}}, - {2314, {"Triangular Wing 14"_s, AccessorySize::L}}, - {2315, {"Triangular Wing 15"_s, AccessorySize::L}}, - - {2351, {"Complex Wing 01"_s, AccessorySize::XL}}, - {2352, {"Complex Wing 02"_s, AccessorySize::XL}}, - {2353, {"Complex Wing 03"_s, AccessorySize::XL}}, - {2354, {"Complex Wing 04"_s, AccessorySize::XL}}, - {2355, {"Complex Wing 05"_s, AccessorySize::XL}}, - {2356, {"Complex Wing 06"_s, AccessorySize::L}}, - {2357, {"Complex Wing 07"_s, AccessorySize::L}}, - {2358, {"Complex Wing 08"_s, AccessorySize::L}}, - {2359, {"Complex Wing 09"_s, AccessorySize::L}}, - {2360, {"Complex Wing 10"_s, AccessorySize::L}}, - - {2401, {"Blade 01"_s, AccessorySize::XL}}, - {2402, {"Blade 02"_s, AccessorySize::XL}}, - {2403, {"Blade 03"_s, AccessorySize::XL}}, - {2404, {"Blade 04"_s, AccessorySize::XL}}, - {2405, {"Blade 05"_s, AccessorySize::XL}}, - {2406, {"Blade 06"_s, AccessorySize::XL}}, - {2407, {"Blade 07"_s, AccessorySize::XL}}, - {2408, {"Blade 08"_s, AccessorySize::XL}}, - {2409, {"Blade 09"_s, AccessorySize::XL}}, - {2410, {"Blade 10"_s, AccessorySize::XL}}, - {2411, {"Blade 11"_s, AccessorySize::XL}}, - {2412, {"Blade 12"_s, AccessorySize::XL}}, - {2413, {"Blade 13"_s, AccessorySize::XL}}, - {2414, {"Blade 14"_s, AccessorySize::XL}}, - {2415, {"Blade 15"_s, AccessorySize::XL}}, - - {2426, {"Curved Blade 01"_s, AccessorySize::XL}}, - {2427, {"Curved Blade 02"_s, AccessorySize::XL}}, - {2428, {"Curved Blade 03"_s, AccessorySize::XL}}, - {2429, {"Curved Blade 04"_s, AccessorySize::XL}}, - {2430, {"Curved Blade 05"_s, AccessorySize::XL}}, - {2431, {"Axe Head 01"_s, AccessorySize::XL}}, - {2432, {"Axe Head 02"_s, AccessorySize::XL}}, - {2433, {"Axe Head 03"_s, AccessorySize::XL}}, - {2434, {"Axe Head 04"_s, AccessorySize::XL}}, - {2435, {"Axe Head 05"_s, AccessorySize::XL}}, - - {2451, {"Horn 01"_s, AccessorySize::M}}, - {2452, {"Horn 02"_s, AccessorySize::M}}, - {2453, {"Horn 03"_s, AccessorySize::M}}, - {2454, {"Horn 04"_s, AccessorySize::M}}, - {2455, {"Horn 05"_s, AccessorySize::M}}, - {2456, {"Horn 06"_s, AccessorySize::M}}, - {2457, {"Horn 07"_s, AccessorySize::M}}, - {2458, {"Horn 08"_s, AccessorySize::M}}, - {2459, {"Horn 09"_s, AccessorySize::M}}, - {2460, {"Horn 10"_s, AccessorySize::M}}, - {2461, {"Horn 11"_s, AccessorySize::M}}, - {2462, {"Horn 12"_s, AccessorySize::M}}, - {2463, {"Horn 13"_s, AccessorySize::M}}, - {2464, {"Horn 14"_s, AccessorySize::M}}, - {2465, {"Horn 15"_s, AccessorySize::M}}, - - {2471, {"Mask"_s, AccessorySize::M}}, - {2472, {"Droplet"_s, AccessorySize::M}}, - {2473, {"Thigh"_s, AccessorySize::M}}, - {2474, {"LegS"_s, AccessorySize::M}}, - {2475, {"LegTH"_s, AccessorySize::M}}, - {2476, {"Plume 01"_s, AccessorySize::M}}, - {2477, {"Plume 02"_s, AccessorySize::M}}, - {2478, {"Plume 03"_s, AccessorySize::M}}, - {2479, {"Plume 04"_s, AccessorySize::M}}, - {2480, {"Plume 05"_s, AccessorySize::M}}, - - {2491, {"Tail 01"_s, AccessorySize::XL}}, - {2492, {"Tail 02"_s, AccessorySize::XL}}, - {2493, {"Tail 03"_s, AccessorySize::XL}}, - {2494, {"Tail 04"_s, AccessorySize::XL}}, - {2495, {"Tail 05"_s, AccessorySize::XL}}, - - {2501, {"Finger 01"_s, AccessorySize::M}}, - {2502, {"Finger 02"_s, AccessorySize::M}}, - {2503, {"Finger 03"_s, AccessorySize::M}}, - {2504, {"Finger 04"_s, AccessorySize::M}}, - {2505, {"Finger 05"_s, AccessorySize::M}}, - - {2510, {"Clamp 01"_s, AccessorySize::M}}, - {2511, {"Clamp 02"_s, AccessorySize::M}}, - {2512, {"Clamp 03"_s, AccessorySize::M}}, - {2513, {"Clamp 04"_s, AccessorySize::M}}, - {2514, {"Clamp 05"_s, AccessorySize::M}}, - - {2521, {"Fabric 01"_s, AccessorySize::XL}}, - {2522, {"Fabric 02"_s, AccessorySize::XL}}, - {2523, {"Fabric 03"_s, AccessorySize::XL}}, - {2524, {"Fabric 04"_s, AccessorySize::XL}}, - {2525, {"Fabric 05"_s, AccessorySize::XL}}, - - {2551, {"Energy Barrel 01"_s, AccessorySize::XL}}, - {2552, {"Energy Barrel 02"_s, AccessorySize::XL}}, - {2553, {"Energy Barrel 03"_s, AccessorySize::XL}}, - {2554, {"Energy Barrel 04"_s, AccessorySize::XL}}, - {2555, {"Energy Barrel 05"_s, AccessorySize::XL}}, - - {2601, {"L Bullet Barrel 01"_s, AccessorySize::XL}}, - {2602, {"L Bullet Barrel 02"_s, AccessorySize::XL}}, - {2603, {"L Bullet Barrel 03"_s, AccessorySize::XL}}, - {2604, {"L Bullet Barrel 04"_s, AccessorySize::XL}}, - {2605, {"L Bullet Barrel 05"_s, AccessorySize::XL}}, - {2606, {"S Bullet Barrel 01"_s, AccessorySize::XL}}, - {2607, {"S Bullet Barrel 02"_s, AccessorySize::XL}}, - {2608, {"S Bullet Barrel 03"_s, AccessorySize::XL}}, - {2609, {"S Bullet Barrel 04"_s, AccessorySize::XL}}, - {2610, {"S Bullet Barrel 05"_s, AccessorySize::XL}}, - {2611, {"B Bullet Barrel 01"_s, AccessorySize::XL}}, - {2612, {"B Bullet Barrel 02"_s, AccessorySize::XL}}, - {2613, {"B Bullet Barrel 03"_s, AccessorySize::XL}}, - {2614, {"B Bullet Barrel 04"_s, AccessorySize::XL}}, - {2615, {"B Bullet Barrel 05"_s, AccessorySize::XL}}, - {2616, {"B Bullet Barrel 06"_s, AccessorySize::XL}}, - {2617, {"B Bullet Barrel 07"_s, AccessorySize::XL}}, - {2618, {"B Bullet Barrel 08"_s, AccessorySize::XL}}, - {2619, {"B Bullet Barrel 09"_s, AccessorySize::XL}}, - {2620, {"B Bullet Barrel 10"_s, AccessorySize::XL}}, - - {2651, {"Cylinder Scope 01"_s, AccessorySize::M}}, - {2652, {"Cylinder Scope 02"_s, AccessorySize::M}}, - {2653, {"Cylinder Scope 03"_s, AccessorySize::M}}, - {2654, {"Cylinder Scope 04"_s, AccessorySize::M}}, - {2655, {"Cylinder Scope 05"_s, AccessorySize::M}}, - {2656, {"Elec Scope 01"_s, AccessorySize::M}}, - {2657, {"Elec Scope 02"_s, AccessorySize::M}}, - {2658, {"Elec Scope 03"_s, AccessorySize::M}}, - {2659, {"Elec Scope 04"_s, AccessorySize::M}}, - {2660, {"Elec Scope 05"_s, AccessorySize::M}}, - {2661, {"Mark Scope 01"_s, AccessorySize::S}}, - {2662, {"Mark Scope 02"_s, AccessorySize::S}}, - {2663, {"Mark Scope 03"_s, AccessorySize::S}}, - {2664, {"Mark Scope 04"_s, AccessorySize::S}}, - {2665, {"Mark Scope 05"_s, AccessorySize::S}}, - - {2701, {"S Single Weaponry"_s, AccessorySize::M}}, - {2702, {"S Packed Weaponry 01"_s, AccessorySize::M}}, - {2703, {"S Packed Weaponry 02"_s, AccessorySize::M}}, - {2704, {"S Packed Weaponry 03"_s, AccessorySize::M}}, - {2705, {"S Packed Weaponry 04"_s, AccessorySize::M}}, - {2706, {"L Single Weaponry"_s, AccessorySize::XL}}, - {2707, {"L Packed Weaponry 01"_s, AccessorySize::XL}}, - {2708, {"L Packed Weaponry 02"_s, AccessorySize::XL}}, - {2709, {"L Packed Weaponry 03"_s, AccessorySize::XL}}, - {2710, {"L Packed Weaponry 04"_s, AccessorySize::XL}}, - {2711, {"Atk Single Weaponry"_s, AccessorySize::XL}}, - {2712, {"Atk Packed Weaponry 01"_s, AccessorySize::XL}}, - {2713, {"Atk Packed Weaponry 02"_s, AccessorySize::XL}}, - {2714, {"Atk Packed Weaponry 03"_s, AccessorySize::XL}}, - {2715, {"Atk Packed Weaponry 04"_s, AccessorySize::XL}}, - - {2751, {"Vent 01"_s, AccessorySize::M}}, - {2752, {"Vent 02"_s, AccessorySize::M}}, - {2753, {"Vent 03"_s, AccessorySize::M}}, - {2754, {"Vent 04"_s, AccessorySize::M}}, - {2755, {"Vent 05"_s, AccessorySize::M}}, - {2756, {"Vent 06"_s, AccessorySize::M}}, - {2757, {"Vent 07"_s, AccessorySize::M}}, - {2758, {"Vent 08"_s, AccessorySize::M}}, - {2759, {"Vent 09"_s, AccessorySize::M}}, - {2760, {"Vent 10"_s, AccessorySize::M}}, - - {2901, {"Complex Construct 01"_s, AccessorySize::L}}, - {2902, {"Complex Construct 02"_s, AccessorySize::L}}, - {2903, {"Complex Construct 03"_s, AccessorySize::L}}, - {2904, {"Complex Construct 04"_s, AccessorySize::L}}, - {2905, {"Complex Construct 05"_s, AccessorySize::L}}, - - {2950, {"Q Mask 01"_s, AccessorySize::M}}, - {2951, {"Q Mask 02"_s, AccessorySize::M}}, - {2952, {"Q Mask 03"_s, AccessorySize::M}}, - {2953, {"Q Mask 04"_s, AccessorySize::M}}, - {2954, {"Q Mask 05"_s, AccessorySize::M}}, - // endregion - - // region Connectors - {3001, {"Circular Vent 01"_s, AccessorySize::M}}, - {3002, {"Circular Vent 02"_s, AccessorySize::M}}, - {3003, {"Circular Vent 03"_s, AccessorySize::M}}, - {3004, {"Circular Vent 04"_s, AccessorySize::M}}, - {3005, {"Circular Vent 05"_s, AccessorySize::M}}, - {3006, {"Circular Vent 06"_s, AccessorySize::M}}, - {3007, {"Circular Vent 07"_s, AccessorySize::M}}, - {3008, {"Circular Vent 08"_s, AccessorySize::M}}, - {3009, {"Circular Vent 09"_s, AccessorySize::M}}, - {3010, {"Circular Vent 10"_s, AccessorySize::M}}, - {3011, {"Circular Vent 11"_s, AccessorySize::M}}, - {3012, {"Circular Vent 12"_s, AccessorySize::M}}, - {3013, {"Circular Vent 13"_s, AccessorySize::M}}, - {3014, {"Circular Vent 14"_s, AccessorySize::M}}, - {3015, {"Circular Vent 15"_s, AccessorySize::M}}, - - {3051, {"Reactor 01"_s, AccessorySize::L}}, - {3052, {"Reactor 02"_s, AccessorySize::L}}, - {3053, {"Reactor 03"_s, AccessorySize::L}}, - {3054, {"Reactor 04"_s, AccessorySize::L}}, - {3055, {"Reactor 05"_s, AccessorySize::L}}, - - {3101, {"Connecting Tube 01"_s, AccessorySize::XL}}, - {3102, {"Connecting Tube 02"_s, AccessorySize::XL}}, - {3103, {"Connecting Tube 03"_s, AccessorySize::XL}}, - {3104, {"Connecting Tube 04"_s, AccessorySize::XL}}, - {3105, {"Connecting Tube 05"_s, AccessorySize::XL}}, - - {3151, {"Latch 01"_s, AccessorySize::M}}, - {3152, {"Latch 02"_s, AccessorySize::M}}, - {3153, {"Latch 03"_s, AccessorySize::M}}, - {3154, {"Latch 04"_s, AccessorySize::M}}, - {3155, {"Latch 05"_s, AccessorySize::M}}, - {3156, {"Latch 06"_s, AccessorySize::M}}, - {3157, {"Latch 07"_s, AccessorySize::M}}, - {3158, {"Latch 08"_s, AccessorySize::M}}, - {3159, {"Latch 09"_s, AccessorySize::M}}, - {3160, {"Latch 10"_s, AccessorySize::M}}, - {3161, {"Latch 11"_s, AccessorySize::M}}, - {3162, {"Latch 12"_s, AccessorySize::M}}, - {3163, {"Latch 13"_s, AccessorySize::M}}, - {3164, {"Latch 14"_s, AccessorySize::M}}, - {3165, {"Latch 15"_s, AccessorySize::M}}, - - {3201, {"Short Connector 01"_s, AccessorySize::M}}, - {3202, {"Short Connector 02"_s, AccessorySize::M}}, - {3203, {"Short Connector 03"_s, AccessorySize::M}}, - {3204, {"Short Connector 04"_s, AccessorySize::M}}, - {3205, {"Short Connector 05"_s, AccessorySize::M}}, - {3206, {"Antenna 01"_s, AccessorySize::S}}, - {3207, {"Antenna 02"_s, AccessorySize::S}}, - {3208, {"Antenna 03"_s, AccessorySize::S}}, - {3209, {"Antenna 04"_s, AccessorySize::S}}, - {3210, {"Antenna 05"_s, AccessorySize::S}}, - - {3226, {"Long Connector 01"_s, AccessorySize::XL}}, - {3227, {"Long Connector 02"_s, AccessorySize::XL}}, - {3228, {"Long Connector 03"_s, AccessorySize::XL}}, - {3229, {"Long Connector 04"_s, AccessorySize::XL}}, - {3230, {"Long Connector 05"_s, AccessorySize::XL}}, - {3231, {"Long Connector 06"_s, AccessorySize::XL}}, - {3232, {"Long Connector 07"_s, AccessorySize::XL}}, - {3233, {"Long Connector 08"_s, AccessorySize::XL}}, - {3234, {"Long Connector 09"_s, AccessorySize::XL}}, - {3235, {"Long Connector 10"_s, AccessorySize::XL}}, - - {3251, {"Complex Connector 01"_s, AccessorySize::XL}}, - {3252, {"Complex Connector 02"_s, AccessorySize::XL}}, - {3253, {"Complex Connector 03"_s, AccessorySize::XL}}, - {3254, {"Complex Connector 04"_s, AccessorySize::XL}}, - {3255, {"Complex Connector 05"_s, AccessorySize::XL}}, - - {3301, {"Tube Line 01"_s, AccessorySize::L}}, - {3302, {"Tube Line 02"_s, AccessorySize::L}}, - {3303, {"Tube Line 03"_s, AccessorySize::L}}, - {3304, {"Tube Line 04"_s, AccessorySize::XL}}, - {3305, {"Tube Line 05"_s, AccessorySize::XL}}, - {3306, {"Tube Line 06"_s, AccessorySize::M}}, - {3307, {"Tube Line 07"_s, AccessorySize::M}}, - {3308, {"Tube Line 08"_s, AccessorySize::M}}, - {3309, {"Tube Line 09"_s, AccessorySize::L}}, - {3310, {"Tube Line 10"_s, AccessorySize::L}}, - - {3351, {"Radar Plate 01"_s, AccessorySize::M}}, - {3352, {"Radar Plate 02"_s, AccessorySize::M}}, - {3353, {"Radar Plate 03"_s, AccessorySize::M}}, - {3354, {"Radar Plate 04"_s, AccessorySize::M}}, - {3355, {"Radar Plate 05"_s, AccessorySize::M}}, - {3356, {"Radar Pod 01"_s, AccessorySize::M}}, - {3357, {"Radar Pod 02"_s, AccessorySize::M}}, - {3358, {"Radar Pod 03"_s, AccessorySize::M}}, - {3359, {"Radar Pod 04"_s, AccessorySize::M}}, - {3360, {"Radar Pod 05"_s, AccessorySize::M}}, - - {3401, {"Tri Pod 01"_s, AccessorySize::M}}, - {3402, {"Tri Pod 02"_s, AccessorySize::M}}, - {3403, {"Tri Pod 03"_s, AccessorySize::M}}, - {3404, {"Tri Pod 04"_s, AccessorySize::M}}, - {3405, {"Tri Pod 05"_s, AccessorySize::M}}, - {3406, {"Signal Pod 01"_s, AccessorySize::M}}, - {3407, {"Signal Pod 02"_s, AccessorySize::M}}, - {3408, {"Signal Pod 03"_s, AccessorySize::M}}, - {3409, {"Signal Pod 04"_s, AccessorySize::M}}, - {3410, {"Signal Pod 05"_s, AccessorySize::M}}, - // endregion -}; diff --git a/src/Maps/ArmourSlots.hpp b/src/Maps/ArmourSlots.hpp index 73f7393..cb51f0b 100644 --- a/src/Maps/ArmourSlots.hpp +++ b/src/Maps/ArmourSlots.hpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -22,6 +22,7 @@ c(Neck, "enuArmorSlots::NewEnumerator3"_s, "Neck"_s) c(UpperBody, "enuArmorSlots::NewEnumerator4"_s, "Upper body"_s) c(MiddleBody, "enuArmorSlots::NewEnumerator5"_s, "Middle body"_s) c(LowerBody, "enuArmorSlots::NewEnumerator6"_s, "Lower body"_s) +c(Backpack, "enuArmorSlots::NewEnumerator23"_s, "Backpack"_s) c(FrontWaist, "enuArmorSlots::NewEnumerator7"_s, "Front waist"_s) c(LeftFrontSkirt, "enuArmorSlots::NewEnumerator8"_s, "Left front skirt"_s) c(RightFrontSkirt, "enuArmorSlots::NewEnumerator9"_s, "Right front skirt"_s) @@ -38,7 +39,6 @@ c(LeftElbow, "enuArmorSlots::NewEnumerator19"_s, "Left elbow"_s) c(RightElbow, "enuArmorSlots::NewEnumerator20"_s, "Right elbow"_s) c(LeftLowerArm, "enuArmorSlots::NewEnumerator21"_s, "Left lower arm"_s) c(RightLowerArm, "enuArmorSlots::NewEnumerator22"_s, "Right lower arm"_s) -c(Backpack, "enuArmorSlots::NewEnumerator23"_s, "Backpack"_s) c(LeftHand, "enuArmorSlots::NewEnumerator24"_s, "Left hand"_s) c(RightHand, "enuArmorSlots::NewEnumerator25"_s, "Right hand"_s) c(LeftUpperLeg, "enuArmorSlots::NewEnumerator26"_s, "Left upper leg"_s) diff --git a/src/Maps/BulletLauncherAttachmentStyles.hpp b/src/Maps/BulletLauncherAttachmentStyles.hpp index 53dd9db..95d9d44 100644 --- a/src/Maps/BulletLauncherAttachmentStyles.hpp +++ b/src/Maps/BulletLauncherAttachmentStyles.hpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 diff --git a/src/Maps/BulletLauncherSockets.hpp b/src/Maps/BulletLauncherSockets.hpp index 3c50a60..c6819f6 100644 --- a/src/Maps/BulletLauncherSockets.hpp +++ b/src/Maps/BulletLauncherSockets.hpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 diff --git a/src/Maps/DamageTypes.hpp b/src/Maps/DamageTypes.hpp index edfdaad..1022a5d 100644 --- a/src/Maps/DamageTypes.hpp +++ b/src/Maps/DamageTypes.hpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 diff --git a/src/Maps/EffectColourModes.hpp b/src/Maps/EffectColourModes.hpp index 9e18866..13b37e6 100644 --- a/src/Maps/EffectColourModes.hpp +++ b/src/Maps/EffectColourModes.hpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 diff --git a/src/Maps/LastMissionId.h b/src/Maps/LastMissionId.h deleted file mode 100644 index 4cbdd23..0000000 --- a/src/Maps/LastMissionId.h +++ /dev/null @@ -1,61 +0,0 @@ -#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 . - -#include - -#include - -#include - -using namespace Corrade; -using namespace Containers::Literals; -using namespace Magnum; - -static const std::map mission_id_map {{ - // Story missions - {0x0064, "Mission 1 - Training"_s}, - {0x0065, "Mission 2 - Patrol Operation"_s}, - {0x0066, "Mission 3 - Fusion Cells in the Snow"_s}, - {0x0067, "Mission 4 - Earning Changes"_s}, - {0x0068, "Mission 5 - Unexpected Coordination"_s}, - {0x0069, "Mission 6 - Empowering Void"_s}, - {0x006A, "Mission 7 - Logisitics Obstacles"_s}, - {0x006B, "Mission 8 - Wrath of the Wastelands"_s}, - {0x006C, "Mission 9 - Suspicious Originator"_s}, - {0x006D, "Mission 10 - Researchers Data Recovery"_s}, - {0x006E, "Mission 11 - Tempestuous Sector"_s}, - {0x006F, "Mission 12 - Clashes of Metal"_s}, - {0x0070, "Mission 13 - The Sandstorm Glutton"_s}, - {0x0071, "Mission 14 - An Icy Investigation"_s}, - {0x0072, "Mission 15 - Outposts Line of Defense"_s}, - {0x0073, "Mission 16 - Hidden in the Pass"_s}, - {0x0074, "Mission 17 - Homebase Security"_s}, - - // Hunting grounds - {0x00C8, "Hunt 1 - Desert Pathway Safety"_s}, - {0x00C9, "Hunt 2 - Snowfield Custodian"_s}, - {0x00CA, "Hunt 3 - Abandoned Valley Raid"_s}, - {0x00CB, "Hunt 4 - Depths of the Machineries"_s}, - {0x00CC, "Hunt 5 - Crater Crashers"_s}, - {0x00CD, "Hunt 6 - Prototype Performance Tests"_s}, - - // Challenges - {0x012C, "Challenge 1 - Redline Battlefront"_s}, - {0x0140, "Challenge 2 - Void Convergence"_s}, - {0x0190, "Challenge 3 - Gates of Ascension"_s} -}}; diff --git a/src/Maps/WeaponTypes.hpp b/src/Maps/WeaponTypes.hpp index 9ef7858..9a4e048 100644 --- a/src/Maps/WeaponTypes.hpp +++ b/src/Maps/WeaponTypes.hpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 diff --git a/src/Mass/Mass_DecalsAccessories.cpp b/src/Mass/Mass_DecalsAccessories.cpp deleted file mode 100644 index acb987f..0000000 --- a/src/Mass/Mass_DecalsAccessories.cpp +++ /dev/null @@ -1,148 +0,0 @@ -// 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 . - -#include "PropertyNames.h" -#include "../UESaveFile/Types/ArrayProperty.h" -#include "../UESaveFile/Types/BoolProperty.h" -#include "../UESaveFile/Types/ColourStructProperty.h" -#include "../UESaveFile/Types/FloatProperty.h" -#include "../UESaveFile/Types/GenericStructProperty.h" -#include "../UESaveFile/Types/RotatorStructProperty.h" -#include "../UESaveFile/Types/VectorStructProperty.h" -#include "../UESaveFile/Types/Vector2DStructProperty.h" -#include "../UESaveFile/Types/IntProperty.h" - -#include "Mass.h" - -using namespace Containers::Literals; - -void Mass::getDecals(Containers::ArrayView decals, ArrayProperty* decal_array) { - for(UnsignedInt i = 0; i < decal_array->items.size(); i++) { - auto decal_prop = decal_array->at(i); - CORRADE_INTERNAL_ASSERT(decal_prop); - auto& decal = decals[i]; - - decal.id = decal_prop->at(MASS_DECAL_ID)->value; - auto colour_prop = decal_prop->at(MASS_DECAL_COLOUR); - decal.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; - auto pos_prop = decal_prop->at(MASS_DECAL_POSITION); - decal.position = Vector3{pos_prop->x, pos_prop->y, pos_prop->z}; - auto u_prop = decal_prop->at(MASS_DECAL_UAXIS); - decal.uAxis = Vector3{u_prop->x, u_prop->y, u_prop->z}; - auto v_prop = decal_prop->at(MASS_DECAL_VAXIS); - decal.vAxis = Vector3{v_prop->x, v_prop->y, v_prop->z}; - auto offset_prop = decal_prop->at(MASS_DECAL_OFFSET); - decal.offset = Vector2{offset_prop->x, offset_prop->y}; - decal.scale = decal_prop->at(MASS_DECAL_SCALE)->value; - decal.rotation = decal_prop->at(MASS_DECAL_ROTATION)->value; - decal.flip = decal_prop->at(MASS_DECAL_FLIP)->value; - decal.wrap = decal_prop->at(MASS_DECAL_WRAP)->value; - } -} - -void Mass::writeDecals(Containers::ArrayView decals, ArrayProperty* decal_array) { - for(UnsignedInt i = 0; i < decal_array->items.size(); i++) { - auto decal_prop = decal_array->at(i); - CORRADE_INTERNAL_ASSERT(decal_prop); - auto& decal = decals[i]; - - decal_prop->at(MASS_DECAL_ID)->value = decal.id; - auto colour_prop = decal_prop->at(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(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(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(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(MASS_DECAL_OFFSET); - offset_prop->x = decal.offset.x(); - offset_prop->y = decal.offset.y(); - decal_prop->at(MASS_DECAL_SCALE)->value = decal.scale; - decal_prop->at(MASS_DECAL_ROTATION)->value = decal.rotation; - decal_prop->at(MASS_DECAL_FLIP)->value = decal.flip; - decal_prop->at(MASS_DECAL_WRAP)->value = decal.wrap; - } -} - -void Mass::getAccessories(Containers::ArrayView accessories, ArrayProperty* accessory_array) { - for(UnsignedInt i = 0; i < accessory_array->items.size(); i++) { - auto acc_prop = accessory_array->at(i); - CORRADE_INTERNAL_ASSERT(acc_prop); - auto& accessory = accessories[i]; - - accessory.attachIndex = acc_prop->at(MASS_ACCESSORY_ATTACH_INDEX)->value; - accessory.id = acc_prop->at(MASS_ACCESSORY_ID)->value; - auto acc_styles = acc_prop->at(MASS_ACCESSORY_STYLES); - for(UnsignedInt j = 0; j < acc_styles->items.size(); j++) { - accessory.styles[j] = acc_styles->at(j)->value; - } - auto rel_pos_prop = acc_prop->at(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(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(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(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(MASS_ACCESSORY_SCALE); - accessory.localScale = Vector3{local_scale_prop->x, local_scale_prop->y, local_scale_prop->z}; - } -} - -void Mass::writeAccessories(Containers::ArrayView accessories, ArrayProperty* accs_array) { - for(UnsignedInt i = 0; i < accs_array->items.size(); i++) { - auto acc_prop = accs_array->at(i); - CORRADE_INTERNAL_ASSERT(acc_prop); - auto& accessory = accessories[i]; - - acc_prop->at(MASS_ACCESSORY_ATTACH_INDEX)->value = accessory.attachIndex; - acc_prop->at(MASS_ACCESSORY_ID)->value = accessory.id; - auto acc_styles = acc_prop->at(MASS_ACCESSORY_STYLES); - for(UnsignedInt j = 0; j < acc_styles->items.size(); j++) { - acc_styles->at(j)->value = accessory.styles[j]; - } - auto rel_pos_prop = acc_prop->at(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(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(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(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(MASS_ACCESSORY_SCALE); - local_scale_prop->x = accessory.localScale.x(); - local_scale_prop->y = accessory.localScale.y(); - local_scale_prop->z = accessory.localScale.z(); - } -} diff --git a/src/Mass/Mass_Styles.cpp b/src/Mass/Mass_Styles.cpp deleted file mode 100644 index 362a850..0000000 --- a/src/Mass/Mass_Styles.cpp +++ /dev/null @@ -1,145 +0,0 @@ -// 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 . - -#include "PropertyNames.h" -#include "../Logger/Logger.h" -#include "../UESaveFile/Types/ArrayProperty.h" -#include "../UESaveFile/Types/ColourStructProperty.h" -#include "../UESaveFile/Types/FloatProperty.h" -#include "../UESaveFile/Types/GenericStructProperty.h" -#include "../UESaveFile/Types/IntProperty.h" -#include "../UESaveFile/Types/StringProperty.h" - -#include "Mass.h" - -using namespace Containers::Literals; - -auto Mass::globalStyles() -> Containers::ArrayView { - return _globalStyles; -} - -void Mass::getGlobalStyles() { - LOG_INFO("Getting global styles."); - - auto unit_data = _mass->at(MASS_UNIT_DATA); - if(!unit_data) { - LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename); - _state = State::Invalid; - return; - } - - auto global_styles = unit_data->at(MASS_GLOBAL_STYLES); - if(!global_styles) { - LOG_WARNING_FORMAT("Couldn't find global styles in {}.", _filename); - _globalStyles = Containers::Array{0}; - return; - } - - if(global_styles->items.size() != _globalStyles.size()) { - _globalStyles = Containers::Array{global_styles->items.size()}; - } - - getCustomStyles(_globalStyles, global_styles); -} - -auto Mass::writeGlobalStyle(UnsignedLong index) -> bool { - LOG_INFO_FORMAT("Writing global style number {}.", index); - - if(index > _globalStyles.size()) { - _lastError = "Global style index out of range"_s; - LOG_ERROR(_lastError); - return false; - } - - auto unit_data = _mass->at(MASS_UNIT_DATA); - if(!unit_data) { - _lastError = "No unit data found in "_s + _filename; - LOG_ERROR(_lastError); - _state = State::Invalid; - return false; - } - - auto global_styles = unit_data->at(MASS_GLOBAL_STYLES); - if(!global_styles) { - _lastError = "No global styles found in "_s + _filename; - LOG_ERROR(_lastError); - _state = State::Invalid; - return false; - } - - return writeCustomStyle(_globalStyles[index], index, global_styles); -} - -void Mass::getCustomStyles(Containers::ArrayView styles, ArrayProperty* style_array) { - for(UnsignedInt i = 0; i < style_array->items.size(); i++) { - auto style_prop = style_array->at(i); - auto& style = styles[i]; - - style.name = style_prop->at(MASS_STYLE_NAME)->value; - auto colour_prop = style_prop->at(MASS_STYLE_COLOUR); - style.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; - style.metallic = style_prop->at(MASS_STYLE_METALLIC)->value; - style.gloss = style_prop->at(MASS_STYLE_GLOSS)->value; - style.glow = colour_prop->a != 0.0f; - - style.patternId = style_prop->at(MASS_STYLE_PATTERN_ID)->value; - style.opacity = style_prop->at(MASS_STYLE_PATTERN_OPACITY)->value; - style.offset = Vector2{ - style_prop->at(MASS_STYLE_PATTERN_OFFSETX)->value, - style_prop->at(MASS_STYLE_PATTERN_OFFSETY)->value - }; - style.rotation = style_prop->at(MASS_STYLE_PATTERN_ROTATION)->value; - style.scale = style_prop->at(MASS_STYLE_PATTERN_SCALE)->value; - } -} - -auto Mass::writeCustomStyle(const CustomStyle& style, UnsignedLong index, ArrayProperty* style_array) -> bool { - if(!style_array) { - _lastError = "style_array is null."_s; - LOG_ERROR(_lastError); - return false; - } - - auto style_prop = style_array->at(index); - if(!style_prop) { - _lastError = "Style index is out of range in "_s + _filename; - LOG_ERROR(_lastError); - return false; - } - - style_prop->at(MASS_STYLE_NAME)->value = style.name; - auto colour_prop = style_prop->at(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(MASS_STYLE_METALLIC)->value = style.metallic; - style_prop->at(MASS_STYLE_GLOSS)->value = style.gloss; - - style_prop->at(MASS_STYLE_PATTERN_ID)->value = style.patternId; - style_prop->at(MASS_STYLE_PATTERN_OPACITY)->value = style.opacity; - style_prop->at(MASS_STYLE_PATTERN_OFFSETX)->value = style.offset.x(); - style_prop->at(MASS_STYLE_PATTERN_OFFSETY)->value = style.offset.y(); - style_prop->at(MASS_STYLE_PATTERN_ROTATION)->value = style.rotation; - style_prop->at(MASS_STYLE_PATTERN_SCALE)->value = style.scale; - - if(!_mass->saveToFile()) { - _lastError = _mass->lastError(); - return false; - } - - return true; -} diff --git a/src/MassManager/MassManager.cpp b/src/MassManager/MassManager.cpp deleted file mode 100644 index 788a4ef..0000000 --- a/src/MassManager/MassManager.cpp +++ /dev/null @@ -1,254 +0,0 @@ -// 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 . - -#include - -#include -#include - -#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): - _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)); - new(&_hangars[i]) Mass{mass_filename}; - } - - refreshStagedMasses(); -} - -auto MassManager::lastError() -> Containers::StringView { - return _lastError; -} - -auto MassManager::hangar(Int hangar) -> Mass& { - return _hangars[hangar]; -} - -void MassManager::refreshHangar(Int hangar) { - if(hangar < 0 || hangar >= 32) { - _lastError = "Hangar index out of range."; - LOG_ERROR(_lastError); - return; - } - - Containers::String mass_filename = - Utility::Path::join(_saveDirectory, - Utility::format("{}Unit{:.2d}{}.sav", _demo ? "Demo" : "", hangar, _account)); - _hangars[hangar] = Mass{mass_filename}; -} - -auto MassManager::importMass(Containers::StringView staged_fn, Int hangar) -> bool { - if(hangar < 0 || hangar >= 32) { - _lastError = "Hangar index out of range."; - LOG_ERROR(_lastError); - return false; - } - - auto it = _stagedMasses.find(Containers::String::nullTerminatedView(staged_fn)); - - 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; - } - - Containers::String source = Utility::Path::join(_stagingAreaDirectory, staged_fn); - Utility::Path::copy(source, source + ".tmp"_s); - - { - Mass mass{source + ".tmp"_s}; - if(!mass.updateAccount(_account)) { - _lastError = mass.lastError(); - Utility::Path::remove(source + ".tmp"_s); - return false; - } - } - - Containers::String dest = Utility::Path::join(_saveDirectory, _hangars[hangar].filename()); - - if(Utility::Path::exists(dest)) { - Utility::Path::remove(dest); - } - - if(!Utility::Path::move(source + ".tmp"_s, dest)) { - _lastError = Utility::format("Couldn't move {} to hangar {:.2d}", staged_fn, hangar + 1); - LOG_ERROR(_lastError); - return false; - } - - return true; -} - -auto MassManager::exportMass(Int hangar) -> bool { - if(hangar < 0 || hangar >= 32) { - _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; - } - - Containers::String source = Utility::Path::join(_saveDirectory, _hangars[hangar].filename()); - Containers::String dest = Utility::Path::join(_stagingAreaDirectory, - Utility::format("{}_{}.sav", _hangars[hangar].name(), _account)); - - if(!Utility::Path::copy(source, dest)) { - _lastError = Utility::format("Couldn't export data from hangar {:.2d} to {}", hangar, dest); - LOG_ERROR(_lastError); - return false; - } - - return true; -} - -auto MassManager::moveMass(Int source, Int destination) -> bool { - if(source < 0 || source >= 32) { - _lastError = "Source hangar index out of range."_s; - LOG_ERROR(_lastError); - return false; - } - - if(destination < 0 || destination >= 32) { - _lastError = "Destination hangar index out of range."_s; - LOG_ERROR(_lastError); - return false; - } - - Containers::String source_file = Utility::Path::join(_saveDirectory, _hangars[source].filename()); - Containers::String dest_file = Utility::Path::join(_saveDirectory, _hangars[destination].filename()); - Mass::State dest_state = _hangars[destination].state(); - - switch(dest_state) { - case Mass::State::Empty: - break; - case Mass::State::Invalid: - Utility::Path::remove(dest_file); - break; - case Mass::State::Valid: - Utility::Path::move(dest_file, dest_file + ".tmp"_s); - break; - } - - Utility::Path::move(source_file, dest_file); - - if(dest_state == Mass::State::Valid) { - Utility::Path::move(dest_file + ".tmp"_s, source_file); - } - - return true; -} - -auto MassManager::deleteMass(Int hangar) -> bool { - if(hangar < 0 || hangar >= 32) { - _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; - } - - return true; -} - -auto MassManager::stagedMasses() -> std::map const& { - return _stagedMasses; -} - -void MassManager::refreshStagedMasses() { - _stagedMasses.clear(); - - using Utility::Path::ListFlag; - auto file_list = Utility::Path::list(_stagingAreaDirectory, - ListFlag::SkipSpecial|ListFlag::SkipDirectories|ListFlag::SkipDotAndDotDot); - - if(!file_list) { - LOG_ERROR_FORMAT("{} couldn't be opened.", _stagingAreaDirectory); - return; - } - - auto iter = std::remove_if(file_list->begin(), file_list->end(), [](Containers::StringView file){ - return !file.hasSuffix(".sav"_s); - }); - - auto list_view = file_list->exceptSuffix(file_list->end() - iter); - - 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) { - LOG_INFO_FORMAT("Found staged M.A.S.S.: {}", *name); - _stagedMasses[file] = *name; - } - else { - LOG_WARNING_FORMAT("Skipped {}.", file); - } - } -} - -void MassManager::refreshStagedMass(Containers::StringView filename) { - LOG_INFO_FORMAT("Refreshing staged unit with filename {}.", filename); - - bool file_exists = Utility::Path::exists(Utility::Path::join(_stagingAreaDirectory, filename)); - auto it = _stagedMasses.find(filename); - - if(file_exists) { - auto name = Mass::getNameFromFile(Utility::Path::join(_stagingAreaDirectory, filename)); - if(name) { - _stagedMasses[filename] = *name; - } - else if(it != _stagedMasses.cend()) { - _stagedMasses.erase(it); - } - } - else if(it != _stagedMasses.cend()) { - _stagedMasses.erase(it); - } -} - -auto MassManager::deleteStagedMass(Containers::StringView filename) -> bool { - if(_stagedMasses.find(filename) == _stagedMasses.cend()) { - _lastError = "The file "_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; - } - - return true; -} diff --git a/src/MassManager/MassManager.h b/src/MassManager/MassManager.h deleted file mode 100644 index 330d98d..0000000 --- a/src/MassManager/MassManager.h +++ /dev/null @@ -1,62 +0,0 @@ -#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 . - -#include - -#include -#include -#include - -#include "../Mass/Mass.h" - -using namespace Corrade; - -class MassManager { - public: - MassManager(Containers::StringView save_path, Containers::StringView account, bool demo, Containers::StringView staging_dir); - - auto lastError() -> Containers::StringView; - - auto hangar(int hangar) -> Mass&; - - void refreshHangar(int hangar); - - auto importMass(Containers::StringView staged_fn, int hangar) -> bool; - auto exportMass(int hangar) -> bool; - - auto moveMass(int source, int destination) -> bool; - auto deleteMass(int hangar) -> bool; - - auto stagedMasses() -> std::map const&; - void refreshStagedMasses(); - void refreshStagedMass(Containers::StringView filename); - auto deleteStagedMass(Containers::StringView filename) -> bool; - - private: - Containers::StringView _saveDirectory; - Containers::StringView _account; - bool _demo; - - Containers::String _lastError; - - Containers::StaticArray<32, Mass> _hangars{NoInit}; - - Containers::StringView _stagingAreaDirectory; - - std::map _stagedMasses; -}; diff --git a/src/Profile/Profile.cpp b/src/Profile/Profile.cpp deleted file mode 100644 index 706a88f..0000000 --- a/src/Profile/Profile.cpp +++ /dev/null @@ -1,494 +0,0 @@ -// 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 . - -#include - -#include -#include - -#include "PropertyNames.h" -#include "../Logger/Logger.h" -#include "../UESaveFile/Types/ArrayProperty.h" -#include "../UESaveFile/Types/ResourceItemValue.h" -#include "../UESaveFile/Types/IntProperty.h" -#include "../UESaveFile/Types/StringProperty.h" - -#include "Profile.h" - -using namespace Corrade; -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; - } - - _filename = Utility::Path::split(path).second(); - - if(_filename.hasPrefix("Demo"_s)) { - _type = ProfileType::Demo; - } - else { - _type = ProfileType::FullGame; - } - - auto account_prop = _profile.at(PROFILE_ACCOUNT); - if(!account_prop) { - _lastError = "Couldn't find an account ID in "_s + _filename; - _valid = false; - return; - } - _account = account_prop->value; - - refreshValues(); -} - -auto Profile::valid() const -> bool { - return _valid; -} - -auto Profile::lastError() const -> Containers::StringView { - return _lastError; -} - -auto Profile::filename() const -> Containers::StringView { - return _filename; -} - -auto Profile::type() const -> ProfileType { - return _type; -} - -auto Profile::isDemo() const -> bool { - return _type == ProfileType::Demo; -} - -auto Profile::account() const -> Containers::StringView { - return _account; -} - -void Profile::refreshValues() { - if(!_profile.reloadData()) { - LOG_ERROR(_profile.lastError()); - _valid = false; - return; - } - - if(_profile.saveType() != "/Game/Core/Save/bpSaveGameProfile.bpSaveGameProfile_C"_s) { - LOG_ERROR_FORMAT("{} is not a valid profile save.", _filename); - _valid = false; - return; - } - - LOG_INFO("Getting the company name."); - auto name_prop = _profile.at(PROFILE_NAME); - if(!name_prop) { - _lastError = "No company name in "_s + _filename; - LOG_ERROR(_lastError); - _valid = false; - return; - } - _name = name_prop->value; - - LOG_INFO("Getting the active frame slot."); - auto prop = _profile.at(PROFILE_ACTIVE_FRAME_SLOT); - _activeFrameSlot = prop ? prop->value : 0; - - LOG_INFO("Getting the credits."); - prop = _profile.at(PROFILE_CREDITS); - _credits = prop ? prop->value : 0; - - LOG_INFO("Getting the story progress."); - prop = _profile.at(PROFILE_STORY_PROGRESS); - _storyProgress = prop ? prop->value : 0; - - LOG_INFO("Getting the last mission ID."); - prop = _profile.at(PROFILE_LAST_MISSION_ID); - _lastMissionId = prop ? prop->value : 0; - - 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(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(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(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; -} - -auto Profile::companyName() const -> Containers::StringView { - return _name; -} - -auto Profile::renameCompany(Containers::StringView new_name) -> bool { - auto name_prop = _profile.at(PROFILE_NAME); - if(!name_prop) { - _lastError = "No company name in "_s + _filename; - LOG_ERROR(_lastError); - _valid = false; - return false; - } - - name_prop->value = new_name; - - if(!_profile.saveToFile()) { - _lastError = _profile.lastError(); - return false; - } - - return true; -} - -auto Profile::activeFrameSlot() const -> Int { - return _activeFrameSlot; -} - -auto Profile::credits() const -> Int { - return _credits; -} - -auto Profile::setCredits(Int amount) -> bool { - auto credits_prop = _profile.at(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}); - } - - credits_prop->value = amount; - - if(!_profile.saveToFile()) { - _lastError = _profile.lastError(); - return false; - } - - return true; -} - -auto Profile::storyProgress() const -> Int { - return _storyProgress; -} - -auto Profile::setStoryProgress(Int progress) -> bool { - auto story_progress_prop = _profile.at("StoryProgress"_s); - - 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}); - } - - story_progress_prop->value = progress; - - if(!_profile.saveToFile()) { - _lastError = _profile.lastError(); - return false; - } - - return true; -} - -auto Profile::lastMissionId() const -> Int { - return _lastMissionId; -} - -auto Profile::verseSteel() const -> Int { - return _verseSteel; -} - -auto Profile::setVerseSteel(Int amount) -> bool { - return setResource(PROFILE_MATERIAL, VerseSteel, amount); -} - -auto Profile::undinium() const -> Int { - return _undinium; -} - -auto Profile::setUndinium(Int amount) -> bool { - return setResource(PROFILE_MATERIAL, Undinium, amount); -} - -auto Profile::necriumAlloy() const -> Int { - return _necriumAlloy; -} - -auto Profile::setNecriumAlloy(Int amount) -> bool { - return setResource(PROFILE_MATERIAL, NecriumAlloy, amount); -} - -auto Profile::lunarite() const -> Int { - return _lunarite; -} - -auto Profile::setLunarite(Int amount) -> bool { - return setResource(PROFILE_MATERIAL, Lunarite, amount); -} - -auto Profile::asterite() const -> Int { - return _asterite; -} - -auto Profile::setAsterite(Int amount) -> bool { - 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 { - return _ednil; -} - -auto Profile::setEdnil(Int amount) -> bool { - return setResource(PROFILE_MATERIAL, Ednil, amount); -} - -auto Profile::nuflalt() const -> Int { - return _nuflalt; -} - -auto Profile::setNuflalt(Int amount) -> bool { - return setResource(PROFILE_MATERIAL, Nuflalt, amount); -} - -auto Profile::aurelene() const -> Int { - return _aurelene; -} - -auto Profile::setAurelene(Int amount) -> bool { - return setResource(PROFILE_MATERIAL, Aurelene, amount); -} - -auto Profile::soldus() const -> Int { - return _soldus; -} - -auto Profile::setSoldus(Int amount) -> bool { - return setResource(PROFILE_MATERIAL, Soldus, amount); -} - -auto Profile::synthesisedN() const -> Int { - return _synthesisedN; -} - -auto Profile::setSynthesisedN(Int amount) -> bool { - 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 { - return _alcarbonite; -} - -auto Profile::setAlcarbonite(Int amount) -> bool { - return setResource(PROFILE_MATERIAL, Alcarbonite, amount); -} - -auto Profile::keriphene() const -> Int { - return _keriphene; -} - -auto Profile::setKeriphene(Int amount) -> bool { - return setResource(PROFILE_MATERIAL, Keriphene, amount); -} - -auto Profile::nitinolCM() const -> Int { - return _nitinolCM; -} - -auto Profile::setNitinolCM(Int amount) -> bool { - return setResource(PROFILE_MATERIAL, NitinolCM, amount); -} - -auto Profile::quarkium() const -> Int { - return _quarkium; -} - -auto Profile::setQuarkium(Int amount) -> bool { - return setResource(PROFILE_MATERIAL, Quarkium, amount); -} - -auto Profile::alterene() const -> Int { - return _alterene; -} - -auto Profile::setAlterene(Int amount) -> bool { - 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 { - return _mixedComposition; -} - -auto Profile::setMixedComposition(Int amount) -> bool { - return setResource(PROFILE_QUARK_DATA, MixedComposition, amount); -} - -auto Profile::voidResidue() const -> Int { - return _voidResidue; -} - -auto Profile::setVoidResidue(Int amount) -> bool { - return setResource(PROFILE_QUARK_DATA, VoidResidue, amount); -} - -auto Profile::muscularConstruction() const -> Int { - return _muscularConstruction; -} - -auto Profile::setMuscularConstruction(Int amount) -> bool { - return setResource(PROFILE_QUARK_DATA, MuscularConstruction, amount); -} - -auto Profile::mineralExoskeletology() const -> Int { - return _mineralExoskeletology; -} - -auto Profile::setMineralExoskeletology(Int amount) -> bool { - return setResource(PROFILE_QUARK_DATA, MineralExoskeletology, amount); -} - -auto Profile::carbonisedSkin() const -> Int { - return _carbonisedSkin; -} - -auto Profile::setCarbonisedSkin(Int amount) -> bool { - 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 { - auto mats_prop = _profile.at(container); - - if(!mats_prop) { - return 0; - } - - auto predicate = [&id](UnrealPropertyBase::ptr& prop){ - auto res_prop = static_cast(prop.get()); - return res_prop->id == id; - }; - - auto it = std::find_if(mats_prop->items.begin(), mats_prop->items.end(), predicate); - return it != mats_prop->items.end() ? static_cast(it->get())->quantity : 0; -} - -auto Profile::setResource(Containers::StringView container, MaterialID id, Int amount) -> bool { - auto mats_prop = _profile.at(container); - - if(!mats_prop) { - 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){ - auto res_prop = static_cast(prop.get()); - return res_prop->id == id; - }; - - auto it = std::find_if(mats_prop->items.begin(), mats_prop->items.end(), predicate); - - 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)); - } - else { - res_prop = static_cast(it->get()); - } - - res_prop->quantity = amount; - - if(!_profile.saveToFile()) { - _lastError = _profile.lastError(); - return false; - } - - return true; -} diff --git a/src/Profile/Profile.h b/src/Profile/Profile.h deleted file mode 100644 index eede0fd..0000000 --- a/src/Profile/Profile.h +++ /dev/null @@ -1,186 +0,0 @@ -#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 . - -#include -#include - -#include - -#include "../UESaveFile/UESaveFile.h" - -#include "ResourceIDs.h" - -using namespace Corrade; -using namespace Magnum; - -enum class ProfileType : UnsignedByte { - Demo, - FullGame -}; - -class Profile { - public: - explicit Profile(Containers::StringView path); - - auto valid() const -> bool; - - auto lastError() const -> Containers::StringView; - - auto filename() const -> Containers::StringView; - - auto type() const -> ProfileType; - auto isDemo() const -> bool; - - auto account() const -> Containers::StringView; - - void refreshValues(); - - auto companyName() const -> Containers::StringView; - auto renameCompany(Containers::StringView new_name) -> bool; - - auto activeFrameSlot() const -> Int; - - auto credits() const -> Int; - auto setCredits(Int credits) -> bool; - - auto storyProgress() const -> Int; - auto setStoryProgress(Int progress) -> bool; - - auto lastMissionId() const -> Int; - - auto verseSteel() const -> Int; - auto setVerseSteel(Int amount) -> bool; - - auto undinium() const -> Int; - auto setUndinium(Int amount) -> bool; - - auto necriumAlloy() const -> Int; - auto setNecriumAlloy(Int amount) -> bool; - - auto lunarite() const -> Int; - auto setLunarite(Int amount) -> bool; - - 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; - - auto nuflalt() const -> Int; - auto setNuflalt(Int amount) -> bool; - - auto aurelene() const -> Int; - auto setAurelene(Int amount) -> bool; - - auto soldus() const -> Int; - auto setSoldus(Int amount) -> bool; - - 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; - - auto keriphene() const -> Int; - auto setKeriphene(Int amount) -> bool; - - auto nitinolCM() const -> Int; - auto setNitinolCM(Int amount) -> bool; - - auto quarkium() const -> Int; - auto setQuarkium(Int amount) -> bool; - - 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; - - auto voidResidue() const -> Int; - auto setVoidResidue(Int amount) -> bool; - - auto muscularConstruction() const -> Int; - auto setMuscularConstruction(Int amount) -> bool; - - auto mineralExoskeletology() const -> Int; - auto setMineralExoskeletology(Int amount) -> bool; - - 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; - - Containers::String _filename; - - ProfileType _type; - - UESaveFile _profile; - - Containers::String _name; - Int _activeFrameSlot = 0; - Int _credits = 0; - Int _storyProgress = 0; - Int _lastMissionId = 0; - - Int _verseSteel = 0; - Int _undinium = 0; - Int _necriumAlloy = 0; - Int _lunarite = 0; - Int _asterite = 0; - Int _halliteFragma = 0; - - Int _ednil = 0; - Int _nuflalt = 0; - Int _aurelene = 0; - Int _soldus = 0; - Int _synthesisedN = 0; - Int _nanoc = 0; - - Int _alcarbonite = 0; - Int _keriphene = 0; - Int _nitinolCM = 0; - Int _quarkium = 0; - Int _alterene = 0; - Int _cosmium = 0; - - Int _mixedComposition = 0; - Int _voidResidue = 0; - Int _muscularConstruction = 0; - Int _mineralExoskeletology = 0; - Int _carbonisedSkin = 0; - Int _isolatedVoidParticle = 0; - - Containers::String _account; - - bool _valid = false; - Containers::String _lastError; -}; diff --git a/src/Profile/PropertyNames.h b/src/Profile/PropertyNames.h deleted file mode 100644 index 73ebbe3..0000000 --- a/src/Profile/PropertyNames.h +++ /dev/null @@ -1,10 +0,0 @@ -#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" diff --git a/src/ProfileManager/ProfileManager.cpp b/src/ProfileManager/ProfileManager.cpp deleted file mode 100644 index 1b358a2..0000000 --- a/src/ProfileManager/ProfileManager.cpp +++ /dev/null @@ -1,367 +0,0 @@ -// 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 . - -#include - -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include "../Logger/Logger.h" - -#include "ProfileManager.h" - -using namespace Containers::Literals; - -ProfileManager::ProfileManager(Containers::StringView save_dir, Containers::StringView backup_dir): - _saveDirectory{save_dir}, - _backupsDirectory{backup_dir} -{ - _ready = refreshProfiles(); -} - -auto ProfileManager::ready() const -> bool { - return _ready; -} - -auto ProfileManager::lastError() -> Containers::StringView { - return _lastError; -} - -auto ProfileManager::profiles() -> Containers::ArrayView { - return _profiles; -} - -auto ProfileManager::refreshProfiles() -> bool { - LOG_INFO("Refreshing profiles."); - - _profiles = Containers::Array{}; - - using Utility::Path::ListFlag; - auto files = Utility::Path::list(_saveDirectory, - ListFlag::SkipSpecial|ListFlag::SkipDirectories|ListFlag::SkipDotAndDotDot); - - if(!files) { - _lastError = _saveDirectory + " can't be opened."; - LOG_ERROR(_lastError); - return false; - } - - auto predicate = [](Containers::StringView file)->bool{ - return !((file.hasPrefix("DemoProfile") || file.hasPrefix("Profile")) && file.hasSuffix(".sav")); - }; - - 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()) { - LOG_WARNING_FORMAT("Profile {} is invalid: {}", file, profile.lastError()); - continue; - } - - arrayAppend(_profiles, std::move(profile)); - } - - if(_profiles.isEmpty()) { - _lastError = "No valid profiles were found."_s; - LOG_ERROR(_lastError); - return false; - } - - return true; -} - -auto ProfileManager::getProfile(std::size_t index) -> Profile* { - return index <= _profiles.size() ? &(_profiles[index]) : nullptr; -} - -auto ProfileManager::deleteProfile(std::size_t index, bool delete_builds) -> bool { - if(!Utility::Path::remove(Utility::Path::join(_saveDirectory, _profiles[index].filename()))) { - _lastError = Utility::format("Couldn't delete {} (filename: {}).", - _profiles[index].companyName(), - _profiles[index].filename()); - LOG_ERROR(_lastError); - refreshProfiles(); - return false; - } - - if(delete_builds) { - for(UnsignedByte i = 0; i < 32; ++i) { - auto filename = Utility::format("{}Unit{:.2d}{}.sav", - _profiles[index].type() == ProfileType::Demo ? "Demo": "", - i, _profiles[index].account()); - Utility::Path::remove(Utility::Path::join(_saveDirectory, filename)); - } - } - - auto file = _profiles[index].filename(); - auto it = std::remove_if(_profiles.begin(), _profiles.end(), [&file](Profile& profile){ return profile.filename() == file; }); - - if(it != _profiles.end()) { - arrayRemoveSuffix(_profiles, 1); - } - - return true; -} - -auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> bool { - std::time_t timestamp = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); - std::tm* time = std::localtime(×tamp); - auto& profile = _profiles[index]; - - auto filename = Utility::format("{}_{}{:.2d}{:.2d}_{:.2d}{:.2d}{:.2d}.backup.mbst", - Utility::String::replaceAll(profile.companyName().data(), " ", "_").c_str(), - time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, - time->tm_hour, time->tm_min, time->tm_sec); - - int error_code = 0; - zip_error_t error; - zip_t* zip = zip_open(Utility::Path::join(_backupsDirectory, filename).data(), ZIP_CREATE|ZIP_TRUNCATE, &error_code); - if(zip == nullptr) { - zip_error_init_with_code(&error, error_code); - _lastError = zip_error_strerror(&error); - LOG_ERROR(_lastError); - return false; - } - - zip_source_t* profile_source = zip_source_file(zip, Utility::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}", - profile.companyName(), - profile.isDemo() ? "demo"_s : "full"_s, - time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, - time->tm_hour, time->tm_min, time->tm_sec); - zip_set_archive_comment(zip, comment.data(), comment.size()); - - if(backup_builds) { - for(UnsignedByte i = 0; i < 32; ++i) { - auto build_filename = Utility::format("{}Unit{:.2d}{}.sav", - profile.isDemo() ? "Demo"_s : ""_s, i, - profile.account()); - - if(!Utility::Path::exists(Utility::Path::join(_saveDirectory, build_filename))) { - continue; - } - - zip_source_t* build_source = zip_source_file(zip, Utility::Path::toNativeSeparators(Utility::Path::join(_saveDirectory, build_filename)).data(), 0, 0); - if(build_source == nullptr) { - zip_source_free(build_source); - continue; - } - - if(zip_file_add(zip, build_filename.data(), build_source, ZIP_FL_ENC_UTF_8) == -1) { - zip_source_free(build_source); - continue; - } - } - } - - if(zip_close(zip) == -1) { - _lastError = zip_strerror(zip); - LOG_ERROR(_lastError); - return false; - } - - refreshBackups(); - - return true; -} - -auto ProfileManager::backups() -> Containers::ArrayView { - return _backups; -} - -void ProfileManager::refreshBackups() { - _backups = Containers::Array{}; - - using Utility::Path::ListFlag; - auto files = Utility::Path::list(_backupsDirectory, - ListFlag::SkipSpecial|ListFlag::SkipDirectories|ListFlag::SkipDotAndDotDot); - - if(!files) { - _lastError = _backupsDirectory + " can't be opened."; - LOG_ERROR(_lastError); - return; - } - - auto predicate = [](Containers::StringView file)->bool{ - return !(file.hasSuffix(".mbprofbackup"_s) || file.hasSuffix(".backup.mbst")); - }; - - auto files_view = files->exceptSuffix(files->end() - std::remove_if(files->begin(), files->end(), predicate)); - - int error_code = 0; - zip_t* zip = nullptr; - for(Containers::StringView file : files_view) { - Backup backup; - backup.filename = file; - - zip = zip_open(Utility::Path::join(_backupsDirectory, file).data(), ZIP_RDONLY, &error_code); - if(zip == nullptr) { - continue; - } - - Containers::ScopeGuard guard{zip, zip_close}; - - Long num_entries = zip_get_num_entries(zip, ZIP_FL_UNCHANGED); - - if(num_entries == 0) { - continue; - } - - int comment_length; - Containers::StringView comment = zip_get_archive_comment(zip, &comment_length, ZIP_FL_UNCHANGED); - if(comment == nullptr) { - continue; - } - - auto info = comment.split('|'); - - if(info.size() != 3) { - continue; - } - - backup.company = info[0]; - - if(info[1].hasPrefix("full")) { - backup.type = ProfileType::FullGame; - } - else if(info[1].hasPrefix("demo")) { - backup.type = ProfileType::Demo; - } - else { - continue; - } - - auto ts = info[2].split('-'); - if(ts.size() != 6) { - continue; - } - - backup.timestamp.year = std::strtol(ts[0].data(), nullptr, 10); - backup.timestamp.month = std::strtol(ts[1].data(), nullptr, 10); - backup.timestamp.day = std::strtol(ts[2].data(), nullptr, 10); - backup.timestamp.hour = std::strtol(ts[3].data(), nullptr, 10); - backup.timestamp.minute = std::strtol(ts[4].data(), nullptr, 10); - backup.timestamp.second = std::strtol(ts[5].data(), nullptr, 10); - - arrayReserve(backup.includedFiles, num_entries); - - for(Long i = 0; i < num_entries; i++) { - arrayAppend(backup.includedFiles, InPlaceInit, zip_get_name(zip, i, ZIP_FL_UNCHANGED)); - } - - arrayAppend(_backups, std::move(backup)); - } -} - -auto ProfileManager::deleteBackup(std::size_t index) -> bool { - if(!Utility::Path::remove(Utility::Path::join(_backupsDirectory, _backups[index].filename))) { - _lastError = "Couldn't delete " + _backups[index].filename; - LOG_ERROR(_lastError); - return false; - } - - auto file = _backups[index].filename; - auto it = std::remove_if(_backups.begin(), _backups.end(), [&file](Backup& backup){return backup.filename == file;}); - - if(it != _backups.end()) { - arrayRemoveSuffix(_backups, 1); - } - - return true; -} - -auto ProfileManager::restoreBackup(std::size_t index) -> bool { - const Backup& backup = _backups[index]; - - auto error_format = "Extraction of file {} failed: {}"_s; - - int error_code = 0; - zip_t* zip = nullptr; - - zip = zip_open(Utility::Path::join(_backupsDirectory, backup.filename).data(), ZIP_RDONLY, &error_code); - if(zip == nullptr) { - zip_error_t error; - zip_error_init_with_code(&error, error_code); - _lastError = zip_error_strerror(&error); - LOG_ERROR(_lastError); - return false; - } - - Containers::ScopeGuard zip_guard{zip, zip_close}; - - for(Containers::StringView file : backup.includedFiles) { - 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; - } - - Containers::ScopeGuard out_guard{out, std::fclose}; - - 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; - } - - Containers::ScopeGuard zf_guard{zf, zip_fclose}; - - Containers::StaticArray<8192, char> buf{ValueInit}; - - Long bytes_read = 0; - while((bytes_read = zip_fread(zf, buf.data(), buf.size())) > 0) { - if(std::fwrite(buf.data(), sizeof(char), bytes_read, out) < static_cast(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; - } - } - - return true; -} diff --git a/src/ProfileManager/ProfileManager.h b/src/ProfileManager/ProfileManager.h deleted file mode 100644 index 0fe9e11..0000000 --- a/src/ProfileManager/ProfileManager.h +++ /dev/null @@ -1,72 +0,0 @@ -#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 . - -#include - -#include -#include - -#include "../Profile/Profile.h" - -using namespace Corrade; - -struct Backup { - Containers::String filename; - Containers::String company; - ProfileType type; - struct { - int year; - int month; - int day; - int hour; - int minute; - int second; - } timestamp; - Containers::Array includedFiles; -}; - -class ProfileManager { - public: - explicit ProfileManager(Containers::StringView save_dir, Containers::StringView backup_dir); - - auto ready() const -> bool; - auto lastError() -> Containers::StringView; - - auto profiles() -> Containers::ArrayView; - auto refreshProfiles() -> bool; - - auto getProfile(std::size_t index) -> Profile*; - auto deleteProfile(std::size_t index, bool delete_builds) -> bool; - auto backupProfile(std::size_t index, bool backup_builds) -> bool; - - auto backups() -> Containers::ArrayView; - void refreshBackups(); - - auto deleteBackup(std::size_t index) -> bool; - auto restoreBackup(std::size_t index) -> bool; - - private: - bool _ready = false; - Containers::String _lastError; - - Containers::StringView _saveDirectory; - Containers::StringView _backupsDirectory; - - Containers::Array _profiles; - Containers::Array _backups; -}; diff --git a/src/SaveTool/SaveTool_Initialisation.cpp b/src/SaveTool/SaveTool_Initialisation.cpp deleted file mode 100644 index 5f5ed49..0000000 --- a/src/SaveTool/SaveTool_Initialisation.cpp +++ /dev/null @@ -1,280 +0,0 @@ -// 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 . - -#include -#include -#include -#include - -#include -#include - -#include - -#include "../FontAwesome/IconsFontAwesome5.h" -#include "../FontAwesome/IconsFontAwesome5Brands.h" -#include "../Logger/Logger.h" - -#include "SaveTool.h" - -void SaveTool::initEvent(SDL_Event& event) { - _initThread.join(); - - switch(event.user.code) { - case InitSuccess: - _uiState = UiState::ProfileManager; - ImGui::CloseCurrentPopup(); - break; - case ProfileManagerFailure: - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error ", - _profileManager->lastError().data(), window()); - exit(EXIT_FAILURE); - break; - default: - break; - } -} - -void SaveTool::initialiseConfiguration() { - LOG_INFO("Reading configuration file."); - - if(_conf.hasValue("cheat_mode"_s)) { - _cheatMode = _conf.value("cheat_mode"_s); - } - else { - _conf.setValue("cheat_mode"_s, _cheatMode); - } - - if(_conf.hasValue("advanced_mode"_s)) { - _advancedMode = _conf.value("advanced_mode"_s); - } - else { - _conf.setValue("advanced_mode"_s, _advancedMode); - } - - if(_conf.hasValue("startup_update_check"_s)) { - _checkUpdatesOnStartup = _conf.value("startup_update_check"_s); - } - else { - _conf.setValue("startup_update_check"_s, _checkUpdatesOnStartup); - } - - if(_conf.hasValue("skip_disclaimer"_s)) { - _skipDisclaimer = _conf.value("skip_disclaimer"_s); - } - else { - _conf.setValue("skip_disclaimer"_s, _skipDisclaimer); - } - - if(_conf.hasValue("swap_interval"_s)) { - _swapInterval = _conf.value("swap_interval"_s); - } - else { - _conf.setValue("swap_interval"_s, 1); - } - - if(_conf.hasValue("fps_cap"_s)) { - _fpsCap = _conf.value("fps_cap"); - } - else { - _conf.setValue("fps_cap", 60.0f); - } - - if(_conf.hasValue("frame_limit"_s)) { - std::string frame_limit = _conf.value("frame_limit"_s); - if(frame_limit == "half_vsync"_s) { - _swapInterval = 2; - } - _conf.removeValue("frame_limit"_s); - } - - setSwapInterval(_swapInterval); - if(_swapInterval == 0) { - setMinimalLoopPeriod(0); - } - - _conf.save(); -} - -void SaveTool::initialiseGui() { - LOG_INFO("Initialising Dear ImGui."); - - ImGui::CreateContext(); - - ImGuiIO& io = ImGui::GetIO(); - - const auto size = Vector2{windowSize()}/dpiScaling(); - - auto reg_font = _rs.getRaw("SourceSansPro-Regular.ttf"_s); - ImFontConfig font_config; - font_config.FontDataOwnedByAtlas = false; - std::strcpy(font_config.Name, "Source Sans Pro"); - io.Fonts->AddFontFromMemoryTTF(const_cast(reg_font.data()), int(reg_font.size()), - 20.0f * Float(framebufferSize().x()) / size.x(), &font_config); - - auto icon_font = _rs.getRaw(FONT_ICON_FILE_NAME_FAS); - static const ImWchar icon_range[] = { ICON_MIN_FA, ICON_MAX_FA, 0 }; - ImFontConfig icon_config; - icon_config.FontDataOwnedByAtlas = false; - icon_config.MergeMode = true; - icon_config.PixelSnapH = true; - icon_config.OversampleH = icon_config.OversampleV = 1; - icon_config.GlyphMinAdvanceX = 18.0f; - io.Fonts->AddFontFromMemoryTTF(const_cast(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(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 range; - ImFontGlyphRangesBuilder builder; - builder.AddRanges(io.Fonts->GetGlyphRangesDefault()); - builder.AddChar(u'š'); // This allows displaying Vladimír Vondruš' name in Corrade's and Magnum's licences. - builder.BuildRanges(&range); - io.Fonts->AddFontFromMemoryTTF(const_cast(mono_font.data()), int(mono_font.size()), - 18.0f * Float(framebufferSize().x()) / size.x(), &font_config, range.Data); - - _imgui = ImGuiIntegration::Context(*ImGui::GetCurrentContext(), windowSize()); - - io.IniFilename = nullptr; - - ImGuiStyle& style = ImGui::GetStyle(); - - style.WindowTitleAlign = {0.5f, 0.5f}; - style.FrameRounding = 3.2f; - style.Colors[ImGuiCol_WindowBg] = ImColor(0xff1f1f1f); -} - -void SaveTool::initialiseManager() { - LOG_INFO("Initialising the profile manager."); - - SDL_Event event; - SDL_zero(event); - event.type = _initEventId; - - _profileManager.emplace(_saveDir, _backupsDir); - if(!_profileManager->ready()) { - event.user.code = ProfileManagerFailure; - SDL_PushEvent(&event); - return; - } - - event.user.code = InitSuccess; - SDL_PushEvent(&event); -} - -auto SaveTool::initialiseToolDirectories() -> bool { - LOG_INFO("Initialising Save Tool directories."); - - _backupsDir = Utility::Path::join(Utility::Path::split(*Utility::Path::executableLocation()).first(), "backups"); - _stagingDir = Utility::Path::join(Utility::Path::split(*Utility::Path::executableLocation()).first(), "staging"); - //_armouryDir = Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "armoury"); - //_armoursDir = Utility::Directory::join(_armouryDir, "armours"); - //_weaponsDir = Utility::Directory::join(_armouryDir, "weapons"); - //_stylesDir = Utility::Directory::join(_armouryDir, "styles"); - - if(!Utility::Path::exists(_backupsDir)) { - LOG_WARNING("Backups directory not found, creating..."); - if(!Utility::Path::make(_backupsDir)) { - LOG_ERROR(_lastError = "Couldn't create the backups directory."); - return false; - } - } - - if(!Utility::Path::exists(_stagingDir)) { - LOG_WARNING("Staging directory not found, creating..."); - if(!Utility::Path::make(_stagingDir)) { - LOG_ERROR(_lastError = "Couldn't create the staging directory."); - return false; - } - } - - //if(!Utility::Directory::exists(_armouryDir)) { - // Utility::Debug{} << "Armoury directory not found, creating..."; - // if(!Utility::Path::make(_armouryDir)) { - // Utility::Error{} << (_lastError = "Couldn't create the armoury directory."); - // return false; - // } - //} - - //if(!Utility::Directory::exists(_armoursDir)) { - // Utility::Debug{} << "Armours directory not found, creating..."; - // if(!Utility::Path::make(_armoursDir)) { - // Utility::Error{} << (_lastError = "Couldn't create the armours directory."); - // return false; - // } - //} - - //if(!Utility::Directory::exists(_weaponsDir)) { - // Utility::Debug{} << "Weapons directory not found, creating..."; - // if(!Utility::Path::make(_weaponsDir)) { - // Utility::Error{} << (_lastError = "Couldn't create the weapons directory."); - // return false; - // } - //} - - //if(!Utility::Directory::exists(_stylesDir)) { - // Utility::Debug{} << "Styles directory not found, creating..."; - // if(!Utility::Path::make(_stylesDir)) { - // Utility::Error{} << (_lastError = "Couldn't create the styles directory."); - // return false; - // } - //} - - return true; -} - -auto SaveTool::findGameDataDirectory() -> bool { - LOG_INFO("Searching for the game's save directory."); - - wchar_t* localappdata_path = nullptr; - Containers::ScopeGuard guard{localappdata_path, CoTaskMemFree}; - if(SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_NO_APPCONTAINER_REDIRECTION, nullptr, &localappdata_path) != S_OK) - { - _lastError = Utility::format("SHGetKnownFolderPath() failed with error code {}.", GetLastError()); - LOG_ERROR(_lastError); - return false; - } - - _gameDataDir = Utility::Path::join(Utility::Path::fromNativeSeparators(Utility::Unicode::narrow(localappdata_path)), "MASS_Builder"_s); - - if(!Utility::Path::exists(_gameDataDir)) { - LOG_ERROR(_lastError = _gameDataDir + " wasn't found. Make sure to play the game at least once."_s); - return false; - } - - _configDir = Utility::Path::join(_gameDataDir, "Saved/Config/WindowsNoEditor"_s); - _saveDir = Utility::Path::join(_gameDataDir, "Saved/SaveGames"_s); - _screenshotsDir = Utility::Path::join(_gameDataDir, "Saved/Screenshots/WindowsNoEditor"_s); - - return true; -} - -void SaveTool::initialiseMassManager() { - LOG_INFO("Initialising the M.A.S.S. manager."); - _massManager.emplace(_saveDir, _currentProfile->account(), _currentProfile->isDemo(), _stagingDir); -} - -void SaveTool::initialiseFileWatcher() { - LOG_INFO("Initialising the file watcher."); - _fileWatcher.emplace(); - _watchIDs[SaveDir] = _fileWatcher->addWatch(_saveDir, this, false); - _watchIDs[StagingDir] = _fileWatcher->addWatch(_stagingDir, this, false); - _fileWatcher->watch(); -} diff --git a/src/SaveTool/SaveTool_MassViewer_Armour.cpp b/src/SaveTool/SaveTool_MassViewer_Armour.cpp deleted file mode 100644 index 6f5aeb4..0000000 --- a/src/SaveTool/SaveTool_MassViewer_Armour.cpp +++ /dev/null @@ -1,325 +0,0 @@ -// 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 . - -#include "../FontAwesome/IconsFontAwesome5.h" - -#include "../Maps/ArmourSets.h" -#include "../Maps/StyleNames.h" - -#include "SaveTool.h" - -void SaveTool::drawArmour() { - if(!_currentMass || _currentMass->state() != Mass::State::Valid) { - return; - } - - if(ImGui::Button(ICON_FA_UNDO_ALT " Reset all")) { - _currentMass->getArmourParts(); - _currentMass->getBulletLauncherAttachments(); - } - - if(!ImGui::BeginChild("##ArmourParts", {0.0f, 0.0f}, true)) { - ImGui::EndChild(); - return; - } - - static Containers::StringView slot_labels[] = { - #define c(enumerator, strenum, name) name, - #include "../Maps/ArmourSlots.hpp" - #undef c - }; - - for(UnsignedInt i = 0; i < _currentMass->armourParts().size(); i++) { - ImGui::PushID(int(i)); - - auto& part = _currentMass->armourParts()[i]; - - static char header[129] = {'\0'}; - - std::memset(header, '\0', 129); - - if(armour_sets.find(part.id) != armour_sets.cend()) { - std::snprintf(header, 128, "%s: %s###%u", slot_labels[UnsignedInt(part.slot)].data(), armour_sets.at(part.id).name.data(), UnsignedInt(part.slot)); - } - else { - std::snprintf(header, 128, "%s: %i###%u", slot_labels[UnsignedInt(part.slot)].data(), part.id, UnsignedInt(part.slot)); - } - - if(ImGui::CollapsingHeader(header)) { - ImGui::BeginGroup(); - - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x * 0.491f); - if(ImGui::BeginListBox("##ChangePart")) { - if(std::strncmp("Neck", slot_labels[UnsignedInt(part.slot)].data(), 4) != 0) { - for(auto& set : armour_sets) { - if(ImGui::Selectable(set.second.name.data(), set.first == part.id, ImGuiSelectableFlags_SpanAvailWidth)) { - part.id = set.first; - } - } - } - else { - for(auto& set : armour_sets) { - if(!set.second.neck_compatible) { - continue; - } - - if(ImGui::Selectable(set.second.name.data(), set.first == part.id, ImGuiSelectableFlags_SpanAvailWidth)) { - part.id = set.first; - } - } - } - ImGui::EndListBox(); - } - - ImGui::EndGroup(); - - ImGui::SameLine(); - - ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical); - - ImGui::SameLine(); - - ImGui::BeginGroup(); - - ImGui::TextUnformatted("Styles:"); - - for(Int j = 0; j < 4; j++) { - drawAlignedText("Slot %d:", j + 1); - - ImGui::SameLine(); - - ImGui::PushID(j); - - ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - 2.0f); - if(ImGui::BeginCombo("##Style", getStyleName(part.styles[j], _currentMass->armourCustomStyles()).data())) { - for(const auto& style : style_names) { - if(ImGui::Selectable(getStyleName(style.first, _currentMass->armourCustomStyles()).data(), part.styles[j] == style.first)) { - part.styles[j] = style.first; - } - } - - ImGui::EndCombo(); - } - - ImGui::PopID(); - } - - ImGui::EndGroup(); - - ImGui::Separator(); - - ImGui::PushID("Decal"); - - drawAlignedText("Showing/editing decal"); - for(UnsignedInt j = 0; j < part.decals.size(); j++) { - ImGui::SameLine(); - ImGui::RadioButton(std::to_string(j + 1).c_str(), &_selectedArmourDecals[i], int(j)); - } - - drawDecalEditor(part.decals[_selectedArmourDecals[i]]); - - ImGui::PopID(); - - if(part.accessories.size() != 0) { - ImGui::Separator(); - - ImGui::PushID("Accessory"); - - drawAlignedText("Showing/editing accessory"); - for(UnsignedInt j = 0; j < part.accessories.size(); j++) { - ImGui::SameLine(); - ImGui::RadioButton(std::string{char(65 + j)}.c_str(), &_selectedArmourAccessories[i], int(j)); - } - - drawAccessoryEditor(part.accessories[_selectedArmourAccessories[i]], _currentMass->armourCustomStyles()); - - ImGui::PopID(); - } - - ImGui::Separator(); - - if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { - _modifiedBySaveTool = true; - if(!_currentMass->writeArmourPart(part.slot)) { - _modifiedBySaveTool = false; - _queue.addToast(Toast::Type::Error, _currentMass->lastError()); - } - } - } - - ImGui::PopID(); - } - - if(_currentMass->bulletLauncherAttachmentStyle() != BulletLauncherAttachmentStyle::NotFound && - ImGui::CollapsingHeader("Bullet launcher placement")) - { - drawAlignedText("Attachment style:"_s); - ImGui::SameLine(); - ImGui::RadioButton("Active one", - _currentMass->bulletLauncherAttachmentStyle() == BulletLauncherAttachmentStyle::ActiveOne); - ImGui::SameLine(); - ImGui::RadioButton("Active one per slot", - _currentMass->bulletLauncherAttachmentStyle() == BulletLauncherAttachmentStyle::ActiveOnePerSlot); - ImGui::SameLine(); - ImGui::RadioButton("All equipped", - _currentMass->bulletLauncherAttachmentStyle() == BulletLauncherAttachmentStyle::AllEquipped); - - ImGui::Separator(); - - drawAlignedText("Launcher slot:"); - ImGui::SameLine(); - ImGui::RadioButton("1", &_selectedBLPlacement, 0); - ImGui::SameLine(); - ImGui::RadioButton("2", &_selectedBLPlacement, 1); - ImGui::SameLine(); - ImGui::RadioButton("3", &_selectedBLPlacement, 2); - ImGui::SameLine(); - ImGui::RadioButton("4", &_selectedBLPlacement, 3); - - auto& placement = _currentMass->bulletLauncherAttachments()[_selectedBLPlacement]; - - static const Containers::StringView socket_labels[] = { - #define c(enumerator, enumstr, name) name, - #include "../Maps/BulletLauncherSockets.hpp" - #undef c - }; - - drawAlignedText("Socket:"); - ImGui::SameLine(); - if(ImGui::BeginCombo("##Socket", socket_labels[UnsignedInt(placement.socket)].data())) { - for(UnsignedInt i = 0; i < (sizeof(socket_labels) / sizeof(socket_labels[0])); i++) { - if(ImGui::Selectable(socket_labels[i].data(), i == UnsignedInt(placement.socket), ImGuiSelectableFlags_SpanAvailWidth)) { - placement.socket = static_cast(i); - } - } - ImGui::EndCombo(); - } - - if(placement.socket != BulletLauncherSocket::Auto) { - ImGui::BeginGroup(); - drawAlignedText("Relative position:"); - drawAlignedText("Offset position:"); - drawAlignedText("Relative rotation:"); - drawAlignedText("Offset rotation:"); - drawAlignedText("Scale:"); - ImGui::EndGroup(); - - ImGui::SameLine(); - - ImGui::BeginGroup(); - ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); - ImGui::DragFloat("##RelPosX", &placement.relativeLocation.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::DragFloat("##RelPosY", &placement.relativeLocation.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::DragFloat("##RelPosZ", &placement.relativeLocation.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f"); - ImGui::PopItemWidth(); - - ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); - ImGui::SliderFloat("##OffPosX", &placement.offsetLocation.x(), -500.0f, +500.0f, "X: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##OffPosY", &placement.offsetLocation.y(), -500.0f, +500.0f, "Y: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##OffPosZ", &placement.offsetLocation.z(), -500.0f, +500.0f, "Z: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(); - drawHelpMarker("+/-500.0 = +/-250 in-game"); - - ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); - ImGui::DragFloat("##RotX", &placement.relativeRotation.x(), 1.0f, -FLT_MAX, +FLT_MAX, "Roll: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::DragFloat("##RotY", &placement.relativeRotation.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Yaw: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::DragFloat("##RotZ", &placement.relativeRotation.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Pitch: %.3f"); - ImGui::PopItemWidth(); - - ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); - ImGui::SliderFloat("##RotOffsetZ", &placement.offsetRotation.z(), -180.0f, +180.0f, "Roll: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##RotOffsetX", &placement.offsetRotation.x(), -30.0f, +30.0f, "Pitch: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##RotOffsetY", &placement.offsetRotation.y(), -30.0f, +30.0f, "Yaw: %.3f"); - ImGui::PopItemWidth(); - - ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); - ImGui::SliderFloat("##ScaleX", &placement.relativeScale.x(), 0.5f, 1.5f, "X: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##ScaleY", &placement.relativeScale.y(), 0.5f, 1.5f, "Y: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::SliderFloat("##ScaleZ", &placement.relativeScale.z(), 0.5f, 1.5f, "Z: %.3f"); - ImGui::PopItemWidth(); - ImGui::SameLine(); - drawHelpMarker("0.5 = 50 in-game\n1.5 = 150 in-game"); - ImGui::EndGroup(); - } - - _modifiedBySaveTool = true; - if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); }) && - !_currentMass->writeBulletLauncherAttachments()) - { - _modifiedBySaveTool = false; - _queue.addToast(Toast::Type::Error, _currentMass->lastError()); - } - } - - ImGui::EndChild(); -} - -void SaveTool::drawCustomArmourStyles() { - if(!_currentMass || _currentMass->state() != Mass::State::Valid) { - return; - } - - if(!ImGui::BeginChild("##ArmourStyles")) { - ImGui::EndChild(); - return; - } - - ImGui::TextWrapped("In-game values are multiplied by 100. For example, 0.500 here is equal to 50 in-game."); - - for(UnsignedInt i = 0; i < _currentMass->armourCustomStyles().size(); i++) { - ImGui::PushID(int(i)); - DCSResult result; - result = drawCustomStyle(_currentMass->armourCustomStyles()[i]); - switch(result) { - case DCS_ResetStyle: - _currentMass->getArmourCustomStyles(); - break; - case DCS_Save: - _modifiedBySaveTool = true; - if(!_currentMass->writeArmourCustomStyle(i)) { - _modifiedBySaveTool = false; - _queue.addToast(Toast::Type::Error, _currentMass->lastError()); - } - break; - default: - break; - } - ImGui::PopID(); - } - - ImGui::EndChild(); -} diff --git a/src/SaveTool/SaveTool_MassViewer_Frame.cpp b/src/SaveTool/SaveTool_MassViewer_Frame.cpp deleted file mode 100644 index a648c90..0000000 --- a/src/SaveTool/SaveTool_MassViewer_Frame.cpp +++ /dev/null @@ -1,315 +0,0 @@ -// 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 . - -#include "../FontAwesome/IconsFontAwesome5.h" - -#include "../Maps/StyleNames.h" - -#include "SaveTool.h" - -void SaveTool::drawFrameInfo() { - if(!_currentMass || _currentMass->state() != Mass::State::Valid) { - return; - } - - if(!ImGui::BeginChild("##FrameInfo")) { - ImGui::EndChild(); - return; - } - - ImGui::BeginGroup(); - - if(ImGui::BeginChild("##JointSliders", {(ImGui::GetContentRegionAvail().x / 2.0f) - (ImGui::GetStyle().WindowPadding.x / 2.0f), 300.0f}, true, ImGuiWindowFlags_MenuBar)) { - if(ImGui::BeginMenuBar()) { - ImGui::TextUnformatted("Joint sliders"); - - ImGui::EndMenuBar(); - } - - drawJointSliders(); - } - ImGui::EndChild(); - - if(ImGui::BeginChild("##FrameStyles", {(ImGui::GetContentRegionAvail().x / 2.0f) - (ImGui::GetStyle().WindowPadding.x / 2.0f), 0.0f}, true, ImGuiWindowFlags_MenuBar)) { - if(ImGui::BeginMenuBar()) { - ImGui::TextUnformatted("Frame styles"); - - ImGui::EndMenuBar(); - } - - drawFrameStyles(); - } - ImGui::EndChild(); - - ImGui::EndGroup(); - - ImGui::SameLine(); - - if(ImGui::BeginChild("##EyeFlare", {0.0f, 0.0f}, true, ImGuiWindowFlags_MenuBar)) { - if(ImGui::BeginMenuBar()) { - ImGui::TextUnformatted("Eye flare colour"); - drawHelpMarker("Right-click the picker for more options.", 250.0f); - - ImGui::EndMenuBar(); - } - - drawEyeColourPicker(); - } - ImGui::EndChild(); - - ImGui::EndChild(); -} - -void SaveTool::drawJointSliders() { - if(!_currentMass || _currentMass->state() != Mass::State::Valid) { - return; - } - - ImGui::TextWrapped("In-game values are multiplied by 100.\nFor example, 0.500 here is equal to 50 in-game."); - - if(ImGui::BeginTable("##JointSliderTable", 2, ImGuiTableFlags_Borders)) { - ImGui::TableSetupColumn("##SliderLabel", ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("##Sliders", ImGuiTableColumnFlags_WidthStretch); - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - drawAlignedText("Neck"); - ImGui::TableSetColumnIndex(1); - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##NeckSlider", &_currentMass->jointSliders().neck, 0.0f, 1.0f)) { - _jointsDirty = true; - } - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - drawAlignedText("Body"); - ImGui::TableSetColumnIndex(1); - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##BodySlider", &_currentMass->jointSliders().body, 0.0f, 1.0f)) { - _jointsDirty = true; - } - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - drawAlignedText("Shoulders"); - ImGui::TableSetColumnIndex(1); - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##ShouldersSlider", &_currentMass->jointSliders().shoulders, 0.0f, 1.0f)) { - _jointsDirty = true; - } - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - drawAlignedText("Hips"); - ImGui::TableSetColumnIndex(1); - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##HipsSlider", &_currentMass->jointSliders().hips, 0.0f, 1.0f)) { - _jointsDirty = true; - } - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - drawAlignedText("Arms"); - ImGui::TableSetColumnIndex(1); - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2{2.0f, 1.0f}); - if(ImGui::BeginTable("##UpperLowerArmsLayoutTable", 2)) { - ImGui::TableSetupColumn("##UpperArms", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("##LowerArms", ImGuiTableColumnFlags_WidthStretch); - - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##UpperArmsSlider", &_currentMass->jointSliders().upperArms, 0.0f, 1.0f, "Upper: %.3f")) { - _jointsDirty = true; - } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##LowerArmsSlider", &_currentMass->jointSliders().lowerArms, 0.0f, 1.0f, "Lower: %.3f")) { - _jointsDirty = true; - } - - ImGui::EndTable(); - } - ImGui::PopStyleVar(); - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - drawAlignedText("Legs"); - ImGui::TableSetColumnIndex(1); - ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2{2.0f, 1.0f}); - if(ImGui::BeginTable("##UpperLowerLegsLayoutTable", 2)) { - ImGui::TableSetupColumn("##UpperLegs", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("##LowerLegs", ImGuiTableColumnFlags_WidthStretch); - - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##UpperLegsSlider", &_currentMass->jointSliders().upperLegs, 0.0f, 1.0f, "Upper: %.3f")) { - _jointsDirty = true; - } - ImGui::TableNextColumn(); - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##LowerLegsSlider", &_currentMass->jointSliders().lowerLegs, 0.0f, 1.0f, "Lower: %.3f")) { - _jointsDirty = true; - } - - ImGui::EndTable(); - } - ImGui::PopStyleVar(); - - ImGui::EndTable(); - } - - if(!_jointsDirty) { - ImGui::BeginDisabled(); - ImGui::Button(ICON_FA_SAVE " Save"); - ImGui::SameLine(); - ImGui::Button(ICON_FA_UNDO " Reset"); - ImGui::EndDisabled(); - } - else { - if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { - _modifiedBySaveTool = true; - if(!_currentMass->writeJointSliders()) { - _modifiedBySaveTool = false; - _queue.addToast(Toast::Type::Error, _currentMass->lastError()); - } - _jointsDirty = false; - } - ImGui::SameLine(); - if(ImGui::Button(ICON_FA_UNDO " Reset")) { - _currentMass->getJointSliders(); - _jointsDirty = false; - } - } -} - -void SaveTool::drawFrameStyles() { - if(!_currentMass || _currentMass->state() != Mass::State::Valid) { - return; - } - - for(Int i = 0; i < 4; i++) { - drawAlignedText("Slot %d:", i + 1); - - ImGui::SameLine(); - - ImGui::PushID(i); - - if(ImGui::BeginCombo("##Style", getStyleName(_currentMass->frameStyles()[i], _currentMass->frameCustomStyles()).data())) { - for(const auto& style : style_names) { - if(ImGui::Selectable(getStyleName(style.first, _currentMass->frameCustomStyles()).data(), _currentMass->frameStyles()[i] == style.first)) { - _currentMass->frameStyles()[i] = style.first; - _stylesDirty = true; - } - } - - ImGui::EndCombo(); - } - - ImGui::PopID(); - } - - if(!_stylesDirty) { - ImGui::BeginDisabled(); - ImGui::Button(ICON_FA_SAVE " Save"); - ImGui::SameLine(); - ImGui::Button(ICON_FA_UNDO " Reset"); - ImGui::EndDisabled(); - } - else { - if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { - _modifiedBySaveTool = true; - if(!_currentMass->writeFrameStyles()) { - _modifiedBySaveTool = false; - _queue.addToast(Toast::Type::Error, _currentMass->lastError()); - } - _stylesDirty = false; - } - ImGui::SameLine(); - if(ImGui::Button(ICON_FA_UNDO " Reset")) { - _currentMass->getFrameStyles(); - _stylesDirty = false; - } - } -} - -void SaveTool::drawEyeColourPicker() { - if(!_currentMass || _currentMass->state() != Mass::State::Valid) { - return; - } - - if(ImGui::ColorPicker3("##EyeFlarePicker", &_currentMass->eyeFlareColour().x())) { - _eyeFlareDirty = true; - } - - if(!_eyeFlareDirty) { - ImGui::BeginDisabled(); - ImGui::Button(ICON_FA_SAVE " Save"); - ImGui::SameLine(); - ImGui::Button(ICON_FA_UNDO " Reset"); - ImGui::EndDisabled(); - } - else { - if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { - _modifiedBySaveTool = true; - if(!_currentMass->writeEyeFlareColour()) { - _modifiedBySaveTool = false; - _queue.addToast(Toast::Type::Error, _currentMass->lastError()); - } - _eyeFlareDirty = false; - } - ImGui::SameLine(); - if(ImGui::Button(ICON_FA_UNDO " Reset")) { - _currentMass->getEyeFlareColour(); - _eyeFlareDirty = false; - } - } -} - -void SaveTool::drawCustomFrameStyles() { - if(!_currentMass || _currentMass->state() != Mass::State::Valid) { - return; - } - - if(!ImGui::BeginChild("##FrameStyles")) { - ImGui::EndChild(); - return; - } - - ImGui::TextWrapped("In-game values are multiplied by 100. For example, 0.500 here is equal to 50 in-game."); - - for(UnsignedInt i = 0; i < _currentMass->frameCustomStyles().size(); i++) { - ImGui::PushID(int(i)); - DCSResult result; - result = drawCustomStyle(_currentMass->frameCustomStyles()[i]); - switch(result) { - case DCS_ResetStyle: - _currentMass->getFrameCustomStyles(); - break; - case DCS_Save: - _modifiedBySaveTool = true; - if(!_currentMass->writeFrameCustomStyle(i)) { - _modifiedBySaveTool = false; - _queue.addToast(Toast::Type::Error, _currentMass->lastError()); - } - break; - default: - break; - } - ImGui::PopID(); - } - - ImGui::EndChild(); -} diff --git a/src/SaveTool/SaveTool_UpdateChecker.cpp b/src/SaveTool/SaveTool_UpdateChecker.cpp deleted file mode 100644 index 768b330..0000000 --- a/src/SaveTool/SaveTool_UpdateChecker.cpp +++ /dev/null @@ -1,176 +0,0 @@ -// 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 . - -#include - -#include - -#include - -#include "../Logger/Logger.h" - -#include "SaveTool.h" - -void SaveTool::updateCheckEvent(SDL_Event& event) { - _updateThread.join(); - - if(event.user.code == CurlInitFailed) { - _queue.addToast(Toast::Type::Error, "Couldn't initialise libcurl. Update check aborted."_s); - LOG_ERROR("Couldn't initialise libcurl. Update check aborted."); - return; - } - else if(event.user.code == CurlError) { - Containers::String error{static_cast(event.user.data2), CURL_ERROR_SIZE, nullptr}; - _queue.addToast(Toast::Type::Error, error, std::chrono::milliseconds{5000}); - _queue.addToast(Toast::Type::Error, static_cast(event.user.data1), - std::chrono::milliseconds{5000}); - LOG_ERROR_FORMAT("{}: {}", static_cast(event.user.data1), static_cast(event.user.data2)); - return; - } - else if(event.user.code == CurlTimeout) { - _queue.addToast(Toast::Type::Error, "The request timed out."_s); - LOG_ERROR("The request timed out."); - return; - } - else if(event.user.code != 200) { - _queue.addToast(Toast::Type::Error, - Utility::format("The request failed with error code {}.", event.user.code)); - LOG_ERROR_FORMAT("The request failed with error code {}.", event.user.code); - return; - } - - struct Version { - explicit Version(Containers::StringView str) { - std::size_t start_point = 0; - - if(str[0] == 'v') { - start_point++; - } - - auto components = Containers::StringView{str.data() + start_point}.split('.'); - - major = std::strtol(components[0].data(), nullptr, 10); - minor = std::strtol(components[1].data(), nullptr, 10); - patch = std::strtol(components[2].data(), nullptr, 10); - - fullVersion = major * 10000 + minor * 100 + patch; - - if(str.hasSuffix("-pre")) { - prerelease = true; - } - } - Int fullVersion; - Int major = 0; - Int minor = 0; - Int patch = 0; - bool prerelease = false; - - bool operator==(const Version& other) const { - return fullVersion == other.fullVersion && prerelease == other.prerelease; - } - bool operator>(const Version& other) const { - if((fullVersion > other.fullVersion) || - (fullVersion == other.fullVersion && !prerelease && other.prerelease)) - { - return true; - } - else { - return false; - } - } - explicit operator Containers::String() const { - return Utility::format("{}.{}.{}{}", major, minor, patch, prerelease ? "-pre" : ""); - } - }; - - static const Version current_ver{SAVETOOL_VERSION}; - - auto str = static_cast(event.user.data1); - Containers::String response{str, strlen(str), nullptr}; - auto components = response.splitOnAnyWithoutEmptyParts("\r\n"); - - Version latest_ver{components.front()}; - - if(latest_ver > current_ver) { - _queue.addToast(Toast::Type::Warning, - "Your version is out of date.\nCheck the settings for more information."_s, - std::chrono::milliseconds{5000}); - _updateAvailable = true; - _latestVersion = Containers::String{latest_ver}; - _releaseLink = Utility::format("https://williamjcm.ovh/git/williamjcm/MassBuilderSaveTool/releases/tag/v{}", - components.front()); - _downloadLink = components.back(); - } - else if(latest_ver == current_ver || (current_ver > latest_ver && current_ver.prerelease)) { - _queue.addToast(Toast::Type::Success, "The application is already up to date."_s); - } - else if(current_ver > latest_ver && !current_ver.prerelease) { - _queue.addToast(Toast::Type::Warning, - "Your version is more recent than the latest one in the repo. How???"_s); - } -} - -inline auto writeData(char* ptr, std::size_t size, std::size_t nmemb, Containers::String* buf)-> std::size_t { - if(!ptr || !buf) return 0; - (*buf) = Utility::format("{}{}", *buf, Containers::StringView{ptr, size * nmemb}); - return size * nmemb; -} - -void SaveTool::checkForUpdates() { - SDL_Event event; - SDL_zero(event); - event.type = _updateEventId; - - auto curl = curl_easy_init(); - if(!curl) { - event.user.code = CurlInitFailed; - } - - if(curl) { - Containers::String response_body{Containers::AllocatedInit, ""}; - Containers::String error_buffer{ValueInit, CURL_ERROR_SIZE * 2}; - - curl_easy_setopt(curl, CURLOPT_URL, "https://williamjcm.ovh/mbst/version"); - curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_body); - curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer.data()); - curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 0L); - curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 10000L); - - auto code = curl_easy_perform(curl); - - if(code == CURLE_OK) { - long status = 0; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); - event.user.code = Int(status); - event.user.data1 = response_body.release(); - } - else if(code == CURLE_OPERATION_TIMEDOUT) { - event.user.code = CurlTimeout; - } - else { - event.user.code = CurlError; - event.user.data1 = const_cast(curl_easy_strerror(code)); - event.user.data2 = Containers::String{error_buffer}.release(); - } - - curl_easy_cleanup(curl); - } - - SDL_PushEvent(&event); -} diff --git a/src/SaveTool/SaveTool_drawMainMenu.cpp b/src/SaveTool/SaveTool_drawMainMenu.cpp deleted file mode 100644 index 26b1573..0000000 --- a/src/SaveTool/SaveTool_drawMainMenu.cpp +++ /dev/null @@ -1,217 +0,0 @@ -// 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 . - -#include - -#include "../FontAwesome/IconsFontAwesome5.h" -#include "../FontAwesome/IconsFontAwesome5Brands.h" - -#include "SaveTool.h" - -void SaveTool::drawMainMenu() { - if(ImGui::BeginMainMenuBar()) { - if(ImGui::BeginMenu("Save Tool##SaveToolMenu")) { - if(ImGui::BeginMenu(ICON_FA_FOLDER_OPEN " Open game data directory", Utility::Path::exists(_gameDataDir))) { - if(ImGui::MenuItem(ICON_FA_COG " Configuration", nullptr, false, Utility::Path::exists(_configDir))) { - openUri(Utility::Path::toNativeSeparators(_configDir)); - } - - if(ImGui::MenuItem(ICON_FA_SAVE " Saves", nullptr, false, Utility::Path::exists(_saveDir))) { - openUri(Utility::Path::toNativeSeparators(_saveDir)); - } - - if(ImGui::MenuItem(ICON_FA_IMAGE " Screenshots", nullptr, false, Utility::Path::exists(_screenshotsDir))) { - openUri(Utility::Path::toNativeSeparators(_screenshotsDir)); - } - - ImGui::EndMenu(); - } - - if(ImGui::BeginMenu(ICON_FA_FOLDER_OPEN " Open manager directory")) { - if(ImGui::MenuItem(ICON_FA_FILE_ARCHIVE " Profile backups", nullptr, false, Utility::Path::exists(_backupsDir))) { - openUri(Utility::Path::toNativeSeparators(_backupsDir)); - } - - if(ImGui::MenuItem(ICON_FA_EXCHANGE_ALT " Staging area", nullptr, false, Utility::Path::exists(_stagingDir))) { - openUri(Utility::Path::toNativeSeparators(_stagingDir)); - } - - ImGui::EndMenu(); - } - - ImGui::Separator(); - - if(ImGui::BeginMenu(ICON_FA_COG " Settings")) { - ImGui::BeginGroup(); - drawAlignedText("Vertical sync:"); - if(_swapInterval == 0) { - drawAlignedText("FPS cap:"); - } - ImGui::EndGroup(); - - ImGui::SameLine(); - - ImGui::BeginGroup(); - - static const char* framelimit_labels[] = { - "Off", - "Every VBLANK", - "Every second VBLANK", - "Every third VBLANK", - }; - - ImGui::PushItemWidth(300.0f); - - if(ImGui::BeginCombo("##FrameLimit", framelimit_labels[_swapInterval])) { - for(int i = 0; i <= 3; i++) { - if(ImGui::Selectable(framelimit_labels[i], _swapInterval == i)) { - _swapInterval = i; - setSwapInterval(i); - if(i == 0) { - setMinimalLoopPeriod(0); - } - } - } - - ImGui::EndCombo(); - } - - if(_swapInterval == 0) { - ImGui::SliderFloat("##FpsCapSlider", &_fpsCap, 15.0f, 301.0f, - _fpsCap != 301.0f ? "%.0f" : "Uncapped", ImGuiSliderFlags_AlwaysClamp); - } - - ImGui::PopItemWidth(); - - ImGui::EndGroup(); - - ImGui::Checkbox("Cheat mode", &_cheatMode); - ImGui::SameLine(); - ImGui::AlignTextToFramePadding(); - drawHelpMarker("This gives access to save edition features that can be considered cheats.", - Float(windowSize().x()) * 0.4f); - - ImGui::Checkbox("Advanced mode", &_advancedMode); - ImGui::SameLine(); - ImGui::AlignTextToFramePadding(); - drawHelpMarker("This gives access to editing values that have unknown purposes or are undocumented.", - Float(windowSize().x()) * 0.4f); - - ImGui::Checkbox("Check for updates on startup", &_checkUpdatesOnStartup); - ImGui::SameLine(); - if(ImGui::Button(ICON_FA_SYNC_ALT " Check now")) { - _queue.addToast(Toast::Type::Default, "Checking for updates..."); - _updateThread = std::thread{[this]{ checkForUpdates(); }}; - } - - if(_updateAvailable) { - drawAlignedText("Version %s is available.", _latestVersion.data()); - if(ImGui::Button(ICON_FA_FILE_SIGNATURE " Release notes")) { - openUri(_releaseLink); - } - ImGui::SameLine(); - if(ImGui::Button(ICON_FA_DOWNLOAD " Download now")) { - openUri(_downloadLink); - } - } - - ImGui::Checkbox("Skip disclaimer", &_skipDisclaimer); - - ImGui::EndMenu(); - } - - ImGui::Separator(); - - if(ImGui::MenuItem(ICON_FA_SIGN_OUT_ALT " Quit##QuitMenuItem")) { - exit(EXIT_SUCCESS); - } - - ImGui::EndMenu(); - } - - if(ImGui::BeginMenu("Game##GameMenu")) { - if(ImGui::MenuItem(ICON_FA_PLAY " Run demo##RunDemoMenuItem")) { - openUri("steam://run/1048390"); - } - drawTooltip("Will not work if you have the full game."); - - if(ImGui::MenuItem(ICON_FA_PLAY " Run full game##RunFullGameMenuItem")) { - openUri("steam://run/956680"); - } - - ImGui::Separator(); - - if(ImGui::BeginMenu(ICON_FA_DISCORD " Discord communities")) { - if(ImGui::MenuItem("Official server")) { - openUri("https://discord.gg/sekai-project"); - } - - if(ImGui::MenuItem("Community server")) { - openUri("https://discord.gg/massbuildercommunity"); - } - - ImGui::EndMenu(); - } - - ImGui::EndMenu(); - } - - #ifdef SAVETOOL_DEBUG_BUILD - if(ImGui::BeginMenu("Debug tools")) { - ImGui::MenuItem("ImGui demo window", nullptr, &_demoWindow); - ImGui::MenuItem("ImGui style editor", nullptr, &_styleEditor); - ImGui::MenuItem("ImGui metrics window", nullptr, &_metricsWindow); - - ImGui::EndMenu(); - } - #endif - - if(ImGui::BeginMenu("Help")) { - if(ImGui::BeginMenu(ICON_FA_KEYBOARD " Keyboard shortcuts")) { - ImGui::BulletText("CTRL+Click on a slider or drag box to input value as text."); - ImGui::BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields."); - ImGui::BulletText("While inputing text:\n"); - ImGui::Indent(); - ImGui::BulletText("CTRL+Left/Right to word jump."); - ImGui::BulletText("CTRL+A or double-click to select all."); - ImGui::BulletText("CTRL+X/C/V to use clipboard cut/copy/paste."); - ImGui::BulletText("CTRL+Z,CTRL+Y to undo/redo."); - ImGui::BulletText("ESCAPE to revert."); - ImGui::BulletText("You can apply arithmetic operators +,*,/ on numerical values.\nUse +- to subtract."); - ImGui::Unindent(); - ImGui::EndMenu(); - } - ImGui::MenuItem(ICON_FA_INFO_CIRCLE " About", nullptr, &_aboutPopup); - - ImGui::EndMenu(); - } - - if(_gameCheckTimerId != 0) { - if(ImGui::BeginTable("##MainMenuLayout", 2)) { - ImGui::TableSetupColumn("##Dummy", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("##GameState", ImGuiTableColumnFlags_WidthFixed); - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(1); - drawGameState(); - - ImGui::EndTable(); - } - } - - ImGui::EndMainMenuBar(); - } -} diff --git a/src/ToastQueue/ToastQueue.cpp b/src/ToastQueue/ToastQueue.cpp index d0902d7..bafc92b 100644 --- a/src/ToastQueue/ToastQueue.cpp +++ b/src/ToastQueue/ToastQueue.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -26,77 +26,92 @@ using namespace Containers::Literals; -constexpr UnsignedInt success_colour = 0xff67d23bu; -constexpr UnsignedInt info_colour = 0xffcc832fu; -constexpr UnsignedInt warning_colour = 0xff2fcfc7u; -constexpr UnsignedInt error_colour = 0xff3134cdu; +namespace mbst { -constexpr UnsignedInt fade_time = 150; -constexpr Float base_opacity = 1.0f; +constexpr std::uint32_t success_colour = 0xff67d23bu; +constexpr std::uint32_t info_colour = 0xffcc832fu; +constexpr std::uint32_t warning_colour = 0xff2fcfc7u; +constexpr std::uint32_t error_colour = 0xff3134cdu; + +constexpr std::uint32_t fade_time = 150; +constexpr float base_opacity = 1.0f; constexpr Vector2 padding{20.0f, 20.0f}; -constexpr Float toast_spacing = 10.0f; +constexpr float toast_spacing = 10.0f; Toast::Toast(Type type, Containers::StringView message, std::chrono::milliseconds timeout): - _type{type}, _message{message}, _timeout{timeout}, _creationTime{std::chrono::steady_clock::now()} + _type{type}, + _message{message}, + _timeout{timeout}, + _creationTime{std::chrono::steady_clock::now()} { - _phaseTrack = Animation::Track{{ - {0, Phase::FadeIn}, - {fade_time, Phase::Wait}, - {fade_time + timeout.count(), Phase::FadeOut}, + _phaseTrack = Animation::Track{{ + {0, Phase::FadeIn}, + {fade_time, Phase::Wait}, + {fade_time + timeout.count(), Phase::FadeOut}, {(fade_time * 2) + timeout.count(), Phase::TimedOut} }, Math::select, Animation::Extrapolation::Constant}; } -auto Toast::type() -> Type { +Toast::Type +Toast::type() { return _type; } -auto Toast::message() -> Containers::StringView { +Containers::StringView +Toast::message() { return _message; } -auto Toast::timeout() -> std::chrono::milliseconds { +std::chrono::milliseconds +Toast::timeout() { return _timeout; } -auto Toast::creationTime() -> std::chrono::steady_clock::time_point { +std::chrono::steady_clock::time_point +Toast::creationTime() { return _creationTime; } -auto Toast::elapsedTime() -> std::chrono::milliseconds { +std::chrono::milliseconds +Toast::elapsedTime() { return std::chrono::duration_cast(std::chrono::steady_clock::now() - _creationTime); } -auto Toast::phase() -> Phase { +Toast::Phase +Toast::phase() { return _phaseTrack.at(elapsedTime().count()); } -auto Toast::opacity() -> Float { +float +Toast::opacity() { Phase phase = this->phase(); - Long elapsed_time = elapsedTime().count(); + std::int64_t elapsed_time = elapsedTime().count(); if(phase == Phase::FadeIn) { - return Float(elapsed_time) / Float(fade_time); + return float(elapsed_time) / float(fade_time); } else if(phase == Phase::FadeOut) { - return 1.0f - ((Float(elapsed_time) - Float(fade_time) - Float(_timeout.count())) / Float(fade_time)); + return 1.0f - ((float(elapsed_time) - float(fade_time) - float(_timeout.count())) / float(fade_time)); } return 1.0f; } -void ToastQueue::addToast(Toast&& toast) { - _toasts.push_back(std::move(toast)); +void +ToastQueue::addToast(Toast&& toast) { + _toasts.push_back(Utility::move(toast)); } -void ToastQueue::addToast(Toast::Type type, Containers::StringView message, std::chrono::milliseconds timeout) { +void +ToastQueue::addToast(Toast::Type type, Containers::StringView message, std::chrono::milliseconds timeout) { _toasts.emplace_back(type, message, timeout); } -void ToastQueue::draw(Vector2i viewport_size) { - Float height = 0.0f; +void +ToastQueue::draw(Vector2i viewport_size) { + float height = 0.0f; - for(UnsignedInt i = 0; i < _toasts.size(); i++) { + for(std::uint32_t i = 0; i < _toasts.size(); i++) { Toast* current = &_toasts[i]; if(current->phase() == Toast::Phase::TimedOut) { @@ -106,11 +121,12 @@ void ToastQueue::draw(Vector2i viewport_size) { Containers::String win_id = Utility::format("##Toast{}", i); - Float opacity = base_opacity * current->opacity(); + float opacity = base_opacity * current->opacity(); ImGui::PushStyleVar(ImGuiStyleVar_Alpha, opacity); - ImGui::SetNextWindowPos({viewport_size.x() - padding.x(), viewport_size.y() - padding.y() - height}, ImGuiCond_Always, {1.0f, 1.0f}); + ImGui::SetNextWindowPos({float(viewport_size.x()) - padding.x(), float(viewport_size.y()) - padding.y() - height}, + ImGuiCond_Always, {1.0f, 1.0f}); if(ImGui::Begin(win_id.data(), nullptr, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoDecoration| ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoNav|ImGuiWindowFlags_NoFocusOnAppearing)) @@ -154,6 +170,9 @@ void ToastQueue::draw(Vector2i viewport_size) { } } -void ToastQueue::removeToast(Long index) { +void +ToastQueue::removeToast(std::int64_t index) { _toasts.erase(_toasts.begin() + index); } + +} diff --git a/src/ToastQueue/ToastQueue.h b/src/ToastQueue/ToastQueue.h index fe0fc42..0a0e222 100644 --- a/src/ToastQueue/ToastQueue.h +++ b/src/ToastQueue/ToastQueue.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -27,13 +27,15 @@ using namespace Corrade; using namespace Magnum; +namespace mbst { + class Toast { public: - enum class Type : UnsignedByte { + enum class Type: std::uint8_t { Default, Success, Info, Warning, Error }; - enum class Phase : UnsignedByte { + enum class Phase: std::uint8_t { FadeIn, Wait, FadeOut, TimedOut }; @@ -58,14 +60,14 @@ class Toast { auto phase() -> Phase; - auto opacity() -> Float; + auto opacity() -> float; private: - Type _type{Type::Default}; - Containers::String _message; - std::chrono::milliseconds _timeout; - std::chrono::steady_clock::time_point _creationTime; - Animation::Track _phaseTrack; + Type _type{Type::Default}; + Containers::String _message; + std::chrono::milliseconds _timeout; + std::chrono::steady_clock::time_point _creationTime; + Animation::Track _phaseTrack; }; class ToastQueue { @@ -78,7 +80,9 @@ class ToastQueue { void draw(Vector2i viewport_size); private: - void removeToast(Long index); + void removeToast(std::int64_t index); std::vector _toasts; }; + +} diff --git a/src/UESaveFile/BinaryReader.cpp b/src/UESaveFile/BinaryReader.cpp deleted file mode 100644 index f1d88a5..0000000 --- a/src/UESaveFile/BinaryReader.cpp +++ /dev/null @@ -1,127 +0,0 @@ -// 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 . - -#include - -#include -#include - -#include "../Logger/Logger.h" - -#include "BinaryReader.h" - -BinaryReader::BinaryReader(Containers::StringView filename) { - _file = std::fopen(filename.data(), "rb"); - - if(!_file) { - LOG_ERROR_FORMAT("Couldn't open {} for reading: {}", filename, std::strerror(errno)); - } -} - -BinaryReader::~BinaryReader() { - closeFile(); -} - -auto BinaryReader::open() -> bool { - return _file; -} - -auto BinaryReader::eof() -> bool { - return std::feof(_file) != 0; -} - -auto BinaryReader::position() -> Long { - return _ftelli64(_file); -} - -auto BinaryReader::seek(Long position) -> bool { - return _fseeki64(_file, position, SEEK_SET) == 0; -} - -void BinaryReader::closeFile() { - std::fclose(_file); - _file = nullptr; -} - -auto BinaryReader::readChar(char& value) -> bool { - return std::fread(&value, sizeof(char), 1, _file) == 1; -} - -auto BinaryReader::readByte(Byte& value) -> bool { - return std::fread(&value, sizeof(Byte), 1, _file) == 1; -} - -auto BinaryReader::readUnsignedByte(UnsignedByte& value) -> bool { - return std::fread(&value, sizeof(UnsignedByte), 1, _file) == 1; -} - -auto BinaryReader::readShort(Short& value) -> bool { - return std::fread(&value, sizeof(Short), 1, _file) == 1; -} - -auto BinaryReader::readUnsignedShort(UnsignedShort& value) -> bool { - return std::fread(&value, sizeof(UnsignedShort), 1, _file) == 1; -} - -auto BinaryReader::readInt(Int& value) -> bool { - return std::fread(&value, sizeof(Int), 1, _file) == 1; -} - -auto BinaryReader::readUnsignedInt(UnsignedInt& value) -> bool { - return std::fread(&value, sizeof(UnsignedInt), 1, _file) == 1; -} - -auto BinaryReader::readLong(Long& value) -> bool { - return std::fread(&value, sizeof(Long), 1, _file) == 1; -} - -auto BinaryReader::readUnsignedLong(UnsignedLong& value) -> bool { - return std::fread(&value, sizeof(UnsignedLong), 1, _file) == 1; -} - -auto BinaryReader::readFloat(Float& value) -> bool { - return std::fread(&value, sizeof(Float), 1, _file) == 1; -} - -auto BinaryReader::readDouble(Double& value) -> bool { - return std::fread(&value, sizeof(Double), 1, _file) == 1; -} - -auto BinaryReader::readArray(Containers::Array& array, std::size_t count) -> bool { - if(array.size() < count) { - array = Containers::Array{ValueInit, count}; - } - - return std::fread(array.data(), sizeof(char), count, _file) == count; -} - -auto BinaryReader::readUEString(Containers::String& str) -> bool { - UnsignedInt length = 0; - if(!readUnsignedInt(length) || length == 0) { - return false; - } - - str = Containers::String{ValueInit, length - 1}; - - return std::fread(str.data(), sizeof(char), length, _file) == length; -} - -auto BinaryReader::peekChar() -> Int { - Int c; - c = std::fgetc(_file); - std::ungetc(c, _file); - return c; -} diff --git a/src/UESaveFile/BinaryReader.h b/src/UESaveFile/BinaryReader.h deleted file mode 100644 index d43df8a..0000000 --- a/src/UESaveFile/BinaryReader.h +++ /dev/null @@ -1,72 +0,0 @@ -#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 . - -#include - -#include -#include -#include - -#include - -using namespace Corrade; -using namespace Magnum; - -class BinaryReader { - public: - explicit BinaryReader(Containers::StringView filename); - ~BinaryReader(); - - auto open() -> bool; - auto eof() -> bool; - auto position() -> Long; - - auto seek(Long position) -> bool; - - void closeFile(); - - auto readChar(char& value) -> bool; - auto readByte(Byte& value) -> bool; - auto readUnsignedByte(UnsignedByte& value) -> bool; - auto readShort(Short& value) -> bool; - auto readUnsignedShort(UnsignedShort& value) -> bool; - auto readInt(Int& value) -> bool; - auto readUnsignedInt(UnsignedInt& value) -> bool; - auto readLong(Long& value) -> bool; - auto readUnsignedLong(UnsignedLong& value) -> bool; - auto readFloat(Float& value) -> bool; - auto readDouble(Double& value) -> bool; - auto readArray(Containers::Array& array, std::size_t count) -> bool; - - template - auto readValue(T& value) -> bool { - return fread(&value, sizeof(T), 1, _file) == sizeof(T); - } - - template - auto readStaticArray(Containers::StaticArray& array) -> bool { - return std::fread(array.data(), sizeof(char), S, _file) == S; - } - - auto readUEString(Containers::String& str) -> bool; - - auto peekChar() -> Int; - - private: - std::FILE* _file = nullptr; -}; diff --git a/src/UESaveFile/BinaryWriter.cpp b/src/UESaveFile/BinaryWriter.cpp deleted file mode 100644 index 10fa76d..0000000 --- a/src/UESaveFile/BinaryWriter.cpp +++ /dev/null @@ -1,139 +0,0 @@ -// 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 . - -#include - -#include "../Logger/Logger.h" - -#include "BinaryWriter.h" - -using namespace Containers::Literals; - -BinaryWriter::BinaryWriter(Containers::StringView filename) { - _file = std::fopen(filename.data(), "wb"); - if(!_file) { - LOG_ERROR_FORMAT("Couldn't open {} for reading: {}", filename, std::strerror(errno)); - } -} - -BinaryWriter::~BinaryWriter() { - closeFile(); -} - -auto BinaryWriter::open() -> bool { - return _file; -} - -void BinaryWriter::closeFile() { - std::fflush(_file); - std::fclose(_file); - _file = nullptr; -} - -auto BinaryWriter::position() -> Long { - return _ftelli64(_file); -} - -auto BinaryWriter::array() const -> Containers::ArrayView { - return _data; -} - -auto BinaryWriter::arrayPosition() const -> UnsignedLong { - return _index; -} - -auto BinaryWriter::flushToFile() -> bool { - bool ret = writeArray(_data); - std::fflush(_file); - _data = Containers::Array{}; - _index = 0; - return ret; -} - -auto BinaryWriter::writeChar(char value) -> bool { - return std::fwrite(&value, sizeof(char), 1, _file) == 1; -} - -auto BinaryWriter::writeByte(Byte value) -> bool { - return std::fwrite(&value, sizeof(Byte), 1, _file) == 1; -} - -auto BinaryWriter::writeUnsignedByte(UnsignedByte value) -> bool { - return std::fwrite(&value, sizeof(UnsignedByte), 1, _file) == 1; -} - -auto BinaryWriter::writeShort(Short value) -> bool { - return std::fwrite(&value, sizeof(Short), 1, _file) == 1; -} - -auto BinaryWriter::writeUnsignedShort(UnsignedShort value) -> bool { - return std::fwrite(&value, sizeof(UnsignedShort), 1, _file) == 1; -} - -auto BinaryWriter::writeInt(Int value) -> bool { - return std::fwrite(&value, sizeof(Int), 1, _file) == 1; -} - -auto BinaryWriter::writeUnsignedInt(UnsignedInt value) -> bool { - return std::fwrite(&value, sizeof(UnsignedInt), 1, _file) == 1; -} - -auto BinaryWriter::writeLong(Long value) -> bool { - return std::fwrite(&value, sizeof(Long), 1, _file) == 1; -} - -auto BinaryWriter::writeUnsignedLong(UnsignedLong value) -> bool { - return std::fwrite(&value, sizeof(UnsignedLong), 1, _file) == 1; -} - -auto BinaryWriter::writeFloat(Float value) -> bool { - return std::fwrite(&value, sizeof(Float), 1, _file) == 1; -} - -auto BinaryWriter::writeDouble(Double value) -> bool { - return std::fwrite(&value, sizeof(Double), 1, _file) == 1; -} - -auto BinaryWriter::writeArray(Containers::ArrayView array) -> bool { - if(array.size() == 0) { - return false; - } - - return std::fwrite(array.data(), sizeof(char), array.size(), _file) == array.size(); -} - -auto BinaryWriter::writeUEString(Containers::StringView str) -> bool { - if(str.size() > UINT32_MAX) { - LOG_ERROR_FORMAT("String is too big. Expected size() < UINT32_MAX, got {} instead.", str.size()); - return false; - } - - writeUnsignedInt(static_cast(str.size()) + 1); - - if(str.size() > 0) { - std::size_t count = std::fwrite(str.data(), sizeof(char), str.size(), _file); - if(count != str.size()) { - return false; - } - } - return writeChar('\0'); -} - -auto BinaryWriter::writeUEStringToArray(Containers::StringView value) -> UnsignedLong { - return writeValueToArray(UnsignedInt(value.size()) + 1u) + - writeDataToArray(Containers::ArrayView{value}) + - writeValueToArray('\0'); -} diff --git a/src/UESaveFile/PropertySerialiser.cpp b/src/UESaveFile/PropertySerialiser.cpp deleted file mode 100644 index 77e8e4a..0000000 --- a/src/UESaveFile/PropertySerialiser.cpp +++ /dev/null @@ -1,264 +0,0 @@ -// 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 . - -#include - -#include "Serialisers/ArrayPropertySerialiser.h" -#include "Serialisers/BoolPropertySerialiser.h" -#include "Serialisers/BytePropertySerialiser.h" -#include "Serialisers/ColourPropertySerialiser.h" -#include "Serialisers/DateTimePropertySerialiser.h" -#include "Serialisers/EnumPropertySerialiser.h" -#include "Serialisers/FloatPropertySerialiser.h" -#include "Serialisers/GuidPropertySerialiser.h" -#include "Serialisers/IntPropertySerialiser.h" -#include "Serialisers/MapPropertySerialiser.h" -#include "Serialisers/ResourcePropertySerialiser.h" -#include "Serialisers/RotatorPropertySerialiser.h" -#include "Serialisers/StringPropertySerialiser.h" -#include "Serialisers/SetPropertySerialiser.h" -#include "Serialisers/StructSerialiser.h" -#include "Serialisers/TextPropertySerialiser.h" -#include "Serialisers/VectorPropertySerialiser.h" -#include "Serialisers/Vector2DPropertySerialiser.h" - -#include "Types/NoneProperty.h" - -#include "BinaryReader.h" -#include "BinaryWriter.h" - -#include "../Logger/Logger.h" - -#include "PropertySerialiser.h" - -PropertySerialiser::PropertySerialiser() { - arrayAppend(_serialisers, Containers::pointer()); - arrayAppend(_serialisers, Containers::pointer()); - arrayAppend(_serialisers, Containers::pointer()); - arrayAppend(_serialisers, Containers::pointer()); - arrayAppend(_serialisers, Containers::pointer()); - arrayAppend(_serialisers, Containers::pointer()); - arrayAppend(_serialisers, Containers::pointer()); - arrayAppend(_serialisers, Containers::pointer()); - arrayAppend(_serialisers, Containers::pointer()); - arrayAppend(_serialisers, Containers::pointer()); - arrayAppend(_serialisers, Containers::pointer()); - arrayAppend(_serialisers, Containers::pointer()); - arrayAppend(_serialisers, Containers::pointer()); - arrayAppend(_serialisers, Containers::pointer()); - arrayAppend(_serialisers, Containers::pointer()); - arrayAppend(_serialisers, Containers::pointer()); - arrayAppend(_serialisers, Containers::pointer()); - arrayAppend(_serialisers, Containers::pointer()); - - arrayAppend(_collectionSerialisers, Containers::pointer()); -} - -auto PropertySerialiser::instance() -> PropertySerialiser& { - static PropertySerialiser serialiser; - return serialiser; -} - -auto PropertySerialiser::read(BinaryReader& reader) -> UnrealPropertyBase::ptr { - if(reader.peekChar() < 0 || reader.eof()) { - return nullptr; - } - - Containers::String name; - if(!reader.readUEString(name)) { - return nullptr; - } - - if(name == "None") { - return Containers::pointer(); - } - - Containers::String type; - if(!reader.readUEString(type)) { - return nullptr; - } - - UnsignedLong value_length; - if(!reader.readUnsignedLong(value_length)) { - return nullptr; - } - - return deserialise(std::move(name), std::move(type), value_length, reader); -} - -auto PropertySerialiser::readItem(BinaryReader& reader, Containers::String type, UnsignedLong value_length, - Containers::String name) -> UnrealPropertyBase::ptr { - if(reader.peekChar() < 0 || reader.eof()) { - return nullptr; - } - - return deserialise(std::move(name), std::move(type), value_length, reader); -} - -auto PropertySerialiser::readSet(BinaryReader& reader, Containers::StringView item_type, - UnsignedInt count) -> Containers::Array -{ - if(reader.peekChar() < 0 || reader.eof()) { - return nullptr; - } - - auto serialiser = getCollectionSerialiser(item_type); - - Containers::Array array; - - if(serialiser) { - Containers::String name; - if(!reader.readUEString(name)) { - return nullptr; - } - - Containers::String type; - if(!reader.readUEString(type)) { - return nullptr; - } - - UnsignedLong value_length; - if(!reader.readUnsignedLong(value_length)) { - return nullptr; - } - - array = serialiser->deserialise(name, type, value_length, count, reader, *this); - - for(auto& item : array) { - if(item->name == Containers::NullOpt) { - item->name.emplace(name); - } - } - } - else { - for(UnsignedInt i = 0; i < count; i++) { - auto item = readItem(reader, item_type, UnsignedLong(-1), ""); - arrayAppend(array, std::move(item)); - } - } - - return array; -} - -auto PropertySerialiser::deserialise(Containers::String name, Containers::String type, UnsignedLong value_length, - BinaryReader& reader) -> UnrealPropertyBase::ptr -{ - UnrealPropertyBase::ptr prop; - auto serialiser = getSerialiser(type); - - if(serialiser == nullptr) { - return nullptr; - } - - prop = serialiser->deserialise(name, type, value_length, reader, *this); - - if(!prop) { - LOG_ERROR("No property."); - return nullptr; - } - - prop->name = std::move(name); - prop->propertyType = std::move(type); - - return prop; -} - -auto PropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, Containers::StringView item_type, - UnsignedLong& bytes_written, BinaryWriter& writer) -> bool -{ - auto serialiser = getSerialiser(item_type); - if(!serialiser) { - return false; - } - return serialiser->serialise(prop, bytes_written, writer, *this); -} - -auto PropertySerialiser::write(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer) -> bool { - if(prop->name == "None" && prop->propertyType == "NoneProperty" && dynamic_cast(prop.get())) { - bytes_written += writer.writeUEStringToArray(*prop->name); - return true; - } - - bytes_written += writer.writeUEStringToArray(*prop->name); - bytes_written += writer.writeUEStringToArray(prop->propertyType); - - UnsignedLong value_length = 0; - UnsignedLong vl_position = writer.arrayPosition(); - - bytes_written += writer.writeValueToArray(value_length); - - bool ret = serialise(prop, prop->propertyType, value_length, writer); - - writer.writeValueToArrayAt(value_length, vl_position); - - bytes_written += value_length; - - return ret; -} - -auto PropertySerialiser::writeItem(UnrealPropertyBase::ptr& prop, Containers::StringView item_type, - UnsignedLong& bytes_written, BinaryWriter& writer) -> bool -{ - if(prop->name == "None" && prop->propertyType == "NoneProperty" && dynamic_cast(prop.get())) { - bytes_written += writer.writeUEStringToArray(*prop->name); - return true; - } - - return serialise(prop, item_type, bytes_written, writer); -} - -auto PropertySerialiser::writeSet(Containers::ArrayView props, - Containers::StringView item_type, UnsignedLong& bytes_written, - BinaryWriter& writer) -> bool -{ - auto serialiser = getCollectionSerialiser(item_type); - if(serialiser) { - return serialiser->serialise(props, item_type, bytes_written, writer, *this); - } - else { - for(auto& prop : props) { - if(!writeItem(prop, item_type, bytes_written, writer)) { - return false; - } - } - - return true; - } -} - -auto PropertySerialiser::getSerialiser(Containers::StringView item_type) -> AbstractUnrealPropertySerialiser* { - for(auto& item : _serialisers) { - for(auto serialiser_type : item->types()) { - if(item_type == serialiser_type) { - return item.get(); - } - } - } - - return nullptr; -} - -auto PropertySerialiser::getCollectionSerialiser(Containers::StringView item_type) -> AbstractUnrealCollectionPropertySerialiser* { - for(auto& item : _collectionSerialisers) { - for(Containers::StringView serialiser_type : item->types()) { - if(item_type == serialiser_type) { - return item.get(); - } - } - } - - return nullptr; -} diff --git a/src/UESaveFile/PropertySerialiser.h b/src/UESaveFile/PropertySerialiser.h deleted file mode 100644 index 67a1aed..0000000 --- a/src/UESaveFile/PropertySerialiser.h +++ /dev/null @@ -1,60 +0,0 @@ -#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 . - -#include -#include -#include - -#include "Serialisers/AbstractUnrealPropertySerialiser.h" -#include "Serialisers/AbstractUnrealCollectionPropertySerialiser.h" - -#include "Types/UnrealPropertyBase.h" - -using namespace Corrade; - -class BinaryReader; -class BinaryWriter; - -class PropertySerialiser { - public: - static auto instance() -> PropertySerialiser&; - - auto read(BinaryReader& reader) -> UnrealPropertyBase::ptr; - auto readItem(BinaryReader& reader, Containers::String type, UnsignedLong value_length, - Containers::String name) -> UnrealPropertyBase::ptr; - auto readSet(BinaryReader& reader, Containers::StringView item_type, UnsignedInt count) -> Containers::Array; - auto deserialise(Containers::String name, Containers::String type, UnsignedLong value_length, - BinaryReader& reader) -> UnrealPropertyBase::ptr; - - auto serialise(UnrealPropertyBase::ptr& prop, Containers::StringView item_type, UnsignedLong& bytes_written, - BinaryWriter& writer) -> bool; - auto write(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer) -> bool; - auto writeItem(UnrealPropertyBase::ptr& prop, Containers::StringView item_type, UnsignedLong& bytes_written, - BinaryWriter& writer) -> bool; - auto writeSet(Containers::ArrayView props, Containers::StringView item_type, - UnsignedLong& bytes_written, BinaryWriter& writer) -> bool; - - private: - PropertySerialiser(); - - auto getSerialiser(Containers::StringView item_type) -> AbstractUnrealPropertySerialiser*; - auto getCollectionSerialiser(Containers::StringView item_type) -> AbstractUnrealCollectionPropertySerialiser*; - - Containers::Array _serialisers; - Containers::Array _collectionSerialisers; -}; diff --git a/src/UESaveFile/Serialisers/RotatorPropertySerialiser.cpp b/src/UESaveFile/Serialisers/RotatorPropertySerialiser.cpp deleted file mode 100644 index ecd125d..0000000 --- a/src/UESaveFile/Serialisers/RotatorPropertySerialiser.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// 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 . - -#include "../BinaryReader.h" -#include "../BinaryWriter.h" -#include "../../Logger/Logger.h" - -#include "RotatorPropertySerialiser.h" - -auto RotatorPropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, BinaryReader& reader, - PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr -{ - auto prop = Containers::pointer(); - - 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; - } - - return prop; -} - -auto RotatorPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool -{ - auto rotator = dynamic_cast(prop.get()); - if(!rotator) { - LOG_ERROR("The property is not a valid rotator property."); - return false; - } - - bytes_written += writer.writeValueToArray(rotator->x) + writer.writeValueToArray(rotator->y) + - writer.writeValueToArray(rotator->z); - - return true; -} diff --git a/src/UESaveFile/Serialisers/StructSerialiser.h b/src/UESaveFile/Serialisers/StructSerialiser.h deleted file mode 100644 index 81efcc0..0000000 --- a/src/UESaveFile/Serialisers/StructSerialiser.h +++ /dev/null @@ -1,48 +0,0 @@ -#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 . - -#include -#include - -#include "AbstractUnrealCollectionPropertySerialiser.h" -#include "AbstractUnrealPropertySerialiser.h" -#include "AbstractUnrealStructSerialiser.h" - -#include "../Types/StructProperty.h" - -class StructSerialiser : public AbstractUnrealPropertySerialiser, public AbstractUnrealCollectionPropertySerialiser { - public: - using ptr = Containers::Pointer; - - auto types() -> Containers::ArrayView override; - - auto deserialise(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - UnsignedInt count, BinaryReader& reader, PropertySerialiser& serialiser) -> Containers::Array override; - auto deserialise(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; - - auto serialise(Containers::ArrayView props, Containers::StringView item_type, - UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; - auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; - - private: - auto readStructValue(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, - BinaryReader& reader, PropertySerialiser& serialiser) -> StructProperty::ptr; - auto writeStructValue(StructProperty* prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool; -}; diff --git a/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.cpp b/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.cpp deleted file mode 100644 index ef002c7..0000000 --- a/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// 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 . - -#include "../BinaryReader.h" -#include "../BinaryWriter.h" -#include "../../Logger/Logger.h" - -#include "Vector2DPropertySerialiser.h" - -auto Vector2DPropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, BinaryReader& reader, - PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr -{ - auto prop = Containers::pointer(); - - if(!reader.readFloat(prop->x) || !reader.readFloat(prop->y)) { - LOG_ERROR_FORMAT("Couldn't read 2D vector property {}'s value.", name); - return nullptr; - } - - return prop; -} - -auto Vector2DPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool -{ - auto vector = dynamic_cast(prop.get()); - if(!vector) { - LOG_ERROR("The property is not a valid 2D vector property."); - return false; - } - - bytes_written += writer.writeValueToArray(vector->x) + writer.writeValueToArray(vector->y); - - return true; -} diff --git a/src/UESaveFile/Serialisers/VectorPropertySerialiser.cpp b/src/UESaveFile/Serialisers/VectorPropertySerialiser.cpp deleted file mode 100644 index 97bd0a8..0000000 --- a/src/UESaveFile/Serialisers/VectorPropertySerialiser.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// 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 . - -#include "../BinaryReader.h" -#include "../BinaryWriter.h" -#include "../../Logger/Logger.h" - -#include "VectorPropertySerialiser.h" - -auto VectorPropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type, - UnsignedLong value_length, BinaryReader& reader, - PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr -{ - auto prop = Containers::pointer(); - - 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; - } - - return prop; -} - -auto VectorPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, - BinaryWriter& writer, PropertySerialiser& serialiser) -> bool -{ - auto vector = dynamic_cast(prop.get()); - if(!vector) { - LOG_ERROR("The property is not a valid vector property."); - return false; - } - - bytes_written += writer.writeValueToArray(vector->x) + writer.writeValueToArray(vector->y) + - writer.writeValueToArray(vector->z); - - return true; -} diff --git a/src/UpdateChecker/UpdateChecker.cpp b/src/UpdateChecker/UpdateChecker.cpp new file mode 100644 index 0000000..f65d807 --- /dev/null +++ b/src/UpdateChecker/UpdateChecker.cpp @@ -0,0 +1,122 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include +#include +#include +#include + +#include + +#include "../Logger/Logger.h" + +#include "UpdateChecker.h" + +using namespace Corrade; + +namespace mbst { + +UpdateChecker::UpdateChecker() { + LOG_INFO("Initialising update checker."); + curl_global_init(CURL_GLOBAL_DEFAULT); +} + +UpdateChecker::~UpdateChecker() { + LOG_INFO("Shutting libcurl down."); + curl_global_cleanup(); +} + +inline +std::size_t +writeCallback(char* data, std::size_t size, std::size_t nmemb, void* user_data) { + if(!data || !user_data) return std::size_t{}; + + auto* s = static_cast(user_data); + (*s) = (*s) + Containers::StringView{data, size * nmemb}; + + return size * nmemb; +} + +UpdateChecker::Result +UpdateChecker::check() { + auto curl = curl_easy_init(); + if(!curl) { + return Result::CurlInitFailed; + } + + Containers::ScopeGuard guard(curl, curl_easy_cleanup); + + Containers::String response_body{Containers::AllocatedInit, ""}; + Containers::String error_buffer{ValueInit, CURL_ERROR_SIZE}; + + curl_easy_setopt(curl, CURLOPT_URL, "https://williamjcm.ovh/mbst/version"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_body); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer.data()); + curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 0L); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); + + auto code = curl_easy_perform(curl); + + if(code == CURLE_OK) { + long status = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); + + if(status != 200) { + _error = Utility::format("The request failed with error code {}.", status); + return Result::HttpError; + } + + auto parts = response_body.splitOnAnyWithoutEmptyParts("\r\n"); + + _foundVersion = Version{parts.front()}; + _downloadLink = parts.back(); + LOG_INFO_FORMAT("Found version: {}", Containers::String{_foundVersion}); + + return Result::Success; + } + else if(code == CURLE_OPERATION_TIMEDOUT) { + return Result::CurlTimeout; + } + else { + _error = Utility::format("{}: {}", curl_easy_strerror(code), error_buffer); + return Result::CurlError; + } +} + +Containers::StringView +UpdateChecker::error() const { + return _error; +} + +bool +UpdateChecker::updateAvailable() const { + return _foundVersion > current_version; +} + +const Version& +UpdateChecker::version() const { + return _foundVersion; +} + +Containers::StringView +UpdateChecker::downloadLink() const { + return _downloadLink; +} + +} diff --git a/src/UpdateChecker/UpdateChecker.h b/src/UpdateChecker/UpdateChecker.h new file mode 100644 index 0000000..5da13dc --- /dev/null +++ b/src/UpdateChecker/UpdateChecker.h @@ -0,0 +1,53 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#pragma once + +#include "../Version/Version.h" + +namespace mbst { + +class UpdateChecker { + public: + explicit UpdateChecker(); + ~UpdateChecker(); + + enum Result: std::int32_t { + Success, + HttpError, + CurlInitFailed, + CurlError, + CurlTimeout + }; + + auto check() -> Result; + + auto error() const -> Containers::StringView; + + bool updateAvailable() const; + + auto version() const -> const Version&; + + auto downloadLink() const -> Containers::StringView; + + private: + Containers::String _error; + + Version _foundVersion{}; + Containers::String _downloadLink; +}; + +} diff --git a/src/Utilities/Crc32.cpp b/src/Utilities/Crc32.cpp new file mode 100644 index 0000000..38378db --- /dev/null +++ b/src/Utilities/Crc32.cpp @@ -0,0 +1,56 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include "Crc32.h" + +namespace mbst::Utilities { + +std::uint32_t +crc32(std::uint32_t initial, Containers::ArrayView data) { + static const auto table = []{ + std::uint32_t polynomial = 0xEDB88320u; + Containers::StaticArray<256, std::uint32_t> temp{ValueInit}; + + for(std::uint32_t i = 0; i < 256; i++) { + std::uint32_t c = i; + + for(std::size_t j = 0; j < 8; j++) { + if(c & 1) { + c = polynomial ^ (c >> 1); + } + else { + c >>= 1; + } + } + + temp[i] = c; + } + + return temp; + }(); + + std::uint32_t c = initial ^ 0xFFFFFFFF; + + auto u = Containers::arrayCast(data); + + for(std::size_t i = 0; i < data.size(); ++i) { + c = table[(c ^ u[i]) & 0xFF] ^ (c >> 8); + } + + return c ^ 0xFFFFFFFF; +} + +} diff --git a/src/Utilities/Crc32.h b/src/Utilities/Crc32.h index e6babd6..a0d56e3 100644 --- a/src/Utilities/Crc32.h +++ b/src/Utilities/Crc32.h @@ -1,7 +1,7 @@ #pragma once // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -16,47 +16,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include + #include #include -#include - using namespace Corrade; -using namespace Magnum; -struct Crc32 { - static const Containers::StaticArray<256, UnsignedInt> table; +namespace mbst::Utilities { - static auto update(UnsignedInt initial, Containers::ArrayView data) -> UnsignedInt { - UnsignedInt c = initial ^ 0xFFFFFFFF; - auto u = Containers::arrayCast(data); +auto crc32(std::uint32_t initial, Containers::ArrayView data) -> std::uint32_t; - for(std::size_t i = 0; i < data.size(); ++i) { - c = table[(c ^ u[i]) & 0xFF] ^ (c >> 8); - } - - return c ^ 0xFFFFFFFF; - } -}; - -const Containers::StaticArray<256, UnsignedInt> Crc32::table = []{ - UnsignedInt polynomial = 0xEDB88320u; - Containers::StaticArray<256, UnsignedInt> temp{ValueInit}; - - for(UnsignedInt i = 0; i < 256; i++) { - UnsignedInt c = i; - - for(std::size_t j = 0; j < 8; j++) { - if(c & 1) { - c = polynomial ^ (c >> 1); - } - else { - c >>= 1; - } - } - - temp[i] = c; - } - - return temp; -}(); +} diff --git a/src/Utilities/Temp.cpp b/src/Utilities/Temp.cpp new file mode 100644 index 0000000..3e0cdbe --- /dev/null +++ b/src/Utilities/Temp.cpp @@ -0,0 +1,88 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include +#include +#include + +#include "../Configuration/Configuration.h" +#include "../Logger/Logger.h" + +#include "Temp.h" + +namespace mbst::Utilities { + +Containers::String +getTempPath(Containers::StringView filename) { + return Utility::Path::join(conf().directories().temp, filename); +} + +Containers::Optional +copyToTemp(Containers::StringView path) { + auto filename = Utility::Path::split(path).first(); + auto dest = Utility::Path::join(conf().directories().temp, filename); + + if(!Utility::Path::copy(path, dest)) { + LOG_ERROR_FORMAT("Couldn't copy {} to {}.", path, conf().directories().temp); + return Containers::NullOpt; + } + + return Utility::move(dest); +} + +Containers::Optional +moveToTemp(Containers::StringView path) { + auto filename = Utility::Path::split(path).first(); + auto dest = Utility::Path::join(conf().directories().temp, filename); + + if(!Utility::Path::move(path, dest)) { + LOG_ERROR_FORMAT("Couldn't move {} to {}.", path, conf().directories().temp); + return Containers::NullOpt; + } + + return Utility::move(dest); +} + +bool +moveFromTemp(Containers::StringView filename, Containers::StringView destination) { + auto source = Utility::Path::join(conf().directories().temp, filename); + auto dest = Utility::Path::join(destination, filename); + + if(!Utility::Path::move(source, dest)) { + LOG_ERROR_FORMAT("Couldn't move {} to {}.", filename, destination); + return false; + } + + return true; +} + +bool +deleteTempFile(Containers::StringView filename) { + return Utility::Path::remove(Utility::Path::join(conf().directories().temp, filename)); +} + +void +emptyTempDir() { + using Flag = Utility::Path::ListFlag; + auto files = Utility::Path::list(conf().directories().temp, Flag::SkipDirectories|Flag::SkipSpecial); + CORRADE_INTERNAL_ASSERT(files); + + for(auto& filename : *files) { + deleteTempFile(filename); + } +} + +} diff --git a/src/Utilities/Temp.h b/src/Utilities/Temp.h new file mode 100644 index 0000000..80ff65a --- /dev/null +++ b/src/Utilities/Temp.h @@ -0,0 +1,34 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include +#include +#include + +using namespace Corrade; + +namespace mbst::Utilities { + +auto getTempPath(Containers::StringView filename) -> Containers::String; +auto copyToTemp(Containers::StringView path) -> Containers::Optional; +auto moveToTemp(Containers::StringView path) -> Containers::Optional; +bool moveFromTemp(Containers::StringView filename, Containers::StringView destination); +bool deleteTempFile(Containers::StringView filename); +void emptyTempDir(); + +} diff --git a/src/Version/Version.cpp b/src/Version/Version.cpp new file mode 100644 index 0000000..9b9d0d0 --- /dev/null +++ b/src/Version/Version.cpp @@ -0,0 +1,41 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#include "Version.h" + +namespace mbst { + +Version::Version(Containers::StringView str) { + std::size_t start_point = 0; + + if(str[0] == 'v') { + start_point++; + } + + auto components = Containers::StringView{str.data() + start_point}.split('.'); + + major = std::strtol(components[0].data(), nullptr, 10); + minor = std::strtol(components[1].data(), nullptr, 10); + patch = std::strtol(components[2].data(), nullptr, 10); + + fullVersion = major * 10000 + minor * 100 + patch; + + if(str.hasSuffix("-pre")) { + prerelease = true; + } +} + +} diff --git a/src/Version/Version.h b/src/Version/Version.h new file mode 100644 index 0000000..e89af93 --- /dev/null +++ b/src/Version/Version.h @@ -0,0 +1,75 @@ +// MassBuilderSaveTool +// Copyright (C) 2021-2024 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 . + +#pragma once + +#include + +#include +#include +#include +#include + +using namespace Corrade; + +namespace mbst { + +struct Version { + explicit Version() = default; + + explicit Version(Containers::StringView str); + + constexpr explicit Version(long major_, long minor_, long patch_, bool prerelease_): + major{major_}, minor{minor_}, patch{patch_}, prerelease{prerelease_}, + fullVersion{major_ * 10000 + minor_ * 100 + patch_} + { + //ctor + } + + long major = 0; + long minor = 0; + long patch = 0; + bool prerelease = false; + long fullVersion = 0; + + inline bool operator==(const Version& other) const { + return fullVersion == other.fullVersion && prerelease == other.prerelease; + } + + inline bool operator>(const Version& other) const { + if((fullVersion > other.fullVersion) || + (fullVersion == other.fullVersion && !prerelease && other.prerelease)) + { + return true; + } + else { + return false; + } + } + + explicit operator Containers::String() const { + return Utility::format("{}.{}.{}{}", major, minor, patch, prerelease ? "-pre" : ""); + } +}; + +constexpr Version current_version{ + SAVETOOL_VERSION_MAJOR, + SAVETOOL_VERSION_MINOR, + SAVETOOL_VERSION_PATCH, + SAVETOOL_VERSION_PRERELEASE +}; + +} diff --git a/src/assets.conf b/src/assets.conf index 212724e..e0035cc 100644 --- a/src/assets.conf +++ b/src/assets.conf @@ -19,31 +19,3 @@ alias=fa-brands-400.ttf [file] filename=../COPYING alias=COPYING - -[file] -filename=../third-party/corrade/COPYING -alias=COPYING.Corrade - -[file] -filename=../third-party/magnum/COPYING -alias=COPYING.Magnum - -[file] -filename=../third-party/imgui/LICENSE.txt -alias=LICENSE.ImGui - -[file] -filename=../third-party/SDL/LICENSE.txt -alias=LICENSE.SDL - -[file] -filename=../third-party/libzip/LICENSE -alias=LICENSE.libzip - -[file] -filename=../third-party/efsw/LICENSE -alias=LICENSE.efsw - -[file] -filename=../third-party/curl/COPYING -alias=LICENSE.curl diff --git a/src/main.cpp b/src/main.cpp index 5af499f..89d3227 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 @@ -22,8 +22,10 @@ #include #include +#include "Application/Application.h" +#include "Configuration/Configuration.h" #include "Logger/MagnumLogBuffer.h" -#include "SaveTool/SaveTool.h" +#include "Utilities/Temp.h" int main(int argc, char** argv) { MagnumLogBuffer debug_intercept_buffer{EntryType::Info}; @@ -38,18 +40,32 @@ int main(int argc, char** argv) { logger().initialise(); - LOG_INFO("Initialising M.A.S.S. Builder Save Tool version " SAVETOOL_VERSION "."); + LOG_INFO("Initialising M.A.S.S. Builder Save Tool version " SAVETOOL_VERSION_STRING "."); + + if(!mbst::conf().valid()) { + LOG_ERROR_FORMAT("There was an error initialising the app: {}", mbst::conf().lastError()); + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", + mbst::conf().lastError().cbegin(), nullptr); + return EXIT_FAILURE; + } + + mbst::Utilities::emptyTempDir(); auto str = setlocale(LC_ALL, ".utf-8"); if(str) { Containers::StringView locale{str}; LOG_INFO_FORMAT("Current locale: {}", locale); - if(!locale.hasSuffix(".utf8")) { + if(!locale.hasSuffix(".utf8") && !locale.hasSuffix(".65001")) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", - "Your system doesn't support UTF-8.", nullptr); + "Your system doesn't support the UTF-8 codepage.", nullptr); return EXIT_FAILURE; } + + if(locale.hasSuffix(".65001")) { + LOG_INFO("Wine detected."); + mbst::conf().setRunningInWine(true); + } } auto mutex_handle = CreateMutexW(nullptr, 0, L"MassBuilderSaveTool"); @@ -80,8 +96,8 @@ int main(int argc, char** argv) { LOG_INFO_FORMAT("Processor: {}", Containers::arrayCast(brand).data()); } - SaveTool app({argc, argv}); - Int result = app.exec(); + mbst::Application app({argc, argv}); + int result = app.exec(); ReleaseMutex(mutex_handle); diff --git a/src/resource.rc b/src/resource.rc index df99b1e..1658ac8 100644 --- a/src/resource.rc +++ b/src/resource.rc @@ -1,5 +1,5 @@ // MassBuilderSaveTool -// Copyright (C) 2021-2022 Guillaume Jacquemin +// Copyright (C) 2021-2024 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 diff --git a/third-party/SDL b/third-party/SDL index 0bfeed0..1fa6142 160000 --- a/third-party/SDL +++ b/third-party/SDL @@ -1 +1 @@ -Subproject commit 0bfeed061b10ea7dd37c88d9bae1824bad760f3a +Subproject commit 1fa6142903b88007c7b77d324ee78fad9966871a diff --git a/third-party/corrade b/third-party/corrade index 41b2e25..f966f91 160000 --- a/third-party/corrade +++ b/third-party/corrade @@ -1 +1 @@ -Subproject commit 41b2e250b7e1e09c2ed057f079617e3d77393c47 +Subproject commit f966f918bd7dec95002921227b6bd68ccbdda113 diff --git a/third-party/curl b/third-party/curl index 280cbee..9287563 160000 --- a/third-party/curl +++ b/third-party/curl @@ -1 +1 @@ -Subproject commit 280cbeee2789749fa9b2267ce7a07813645897de +Subproject commit 9287563e86a5b2007fb67f68c075a87a93825861 diff --git a/third-party/efsw b/third-party/efsw index f3914e4..3419347 160000 --- a/third-party/efsw +++ b/third-party/efsw @@ -1 +1 @@ -Subproject commit f3914e475cc8cec461ac05060470c6510ee81246 +Subproject commit 341934765471e4074e90bb5205ff4a65c16499c6 diff --git a/third-party/imgui b/third-party/imgui index a8df192..a8e96ae 160000 --- a/third-party/imgui +++ b/third-party/imgui @@ -1 +1 @@ -Subproject commit a8df192df022ed6ac447e7b7ada718c4c4824b41 +Subproject commit a8e96ae21a4ec10e5f02b19dd865dfffe8a98e67 diff --git a/third-party/libzip b/third-party/libzip index bdc03ab..aa90b70 160000 --- a/third-party/libzip +++ b/third-party/libzip @@ -1 +1 @@ -Subproject commit bdc03ab23b703fcc516436d6ebcbfb6ac4484033 +Subproject commit aa90b70e552709316cd2144837c3dd13b5fa1ec3 diff --git a/third-party/magnum b/third-party/magnum index a496029..a40020f 160000 --- a/third-party/magnum +++ b/third-party/magnum @@ -1 +1 @@ -Subproject commit a49602987499379e4a2d155472961d99ddfc75ba +Subproject commit a40020f3fff91dc9c59148f52adb0d48203799eb diff --git a/third-party/magnum-integration b/third-party/magnum-integration index 883bd0c..bf09698 160000 --- a/third-party/magnum-integration +++ b/third-party/magnum-integration @@ -1 +1 @@ -Subproject commit 883bd0cb6e00172b6fca34ff8cc4bc70bc302637 +Subproject commit bf09698491f2061733dc263f375da1f02f41d8ec