Add initial files.

Feature parity is also reached.
This commit is contained in:
Guillaume Jacquemin 2021-06-11 19:24:52 +02:00
parent e4bbd9ac59
commit 61d1e3635f
47 changed files with 9166 additions and 0 deletions

28
.gitmodules vendored Normal file
View File

@ -0,0 +1,28 @@
[submodule "corrade"]
path = third-party/corrade
url = https://github.com/mosra/corrade
branch = master
[submodule "magnum"]
path = third-party/magnum
url = https://github.com/mosra/magnum
branch = master
[submodule "magnum-integration"]
path = third-party/magnum-integration
url = https://github.com/mosra/magnum-integration
branch = master
[submodule "imgui"]
path = third-party/imgui
url = https://github.com/ocornut/imgui
branch = master
[submodule "SDL2"]
path = third-party/SDL
url = https://github.com/libsdl-org/SDL
branch = main
[submodule "libzip"]
path = third-party/libzip
url = https://github.com/nih-at/libzip
branch = master
[submodule "efsw"]
path = third-party/efsw
url = https://github.com/SpartanJ/efsw
branch = master

91
CMakeLists.txt Normal file
View File

@ -0,0 +1,91 @@
# MassBuilderSaveTool
# Copyright (C) 2021 Guillaume Jacquemin
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
cmake_minimum_required(VERSION 3.5)
project(MassBuilderSaveTool)
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/modules/" ${CMAKE_MODULE_PATH})
SET(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
set(BUILD_STATIC ON CACHE BOOL "" FORCE)
set(BUILD_STATIC_PIC ON CACHE BOOL "" FORCE)
set(BUILD_STATIC_UNIQUE_GLOBALS OFF CACHE BOOL "" FORCE)
set(WITH_INTERCONNECT ON CACHE BOOL "" FORCE)
set(WITH_PLUGINMANAGER ON CACHE BOOL "" FORCE)
set(WITH_TESTSUITE OFF CACHE BOOL "" FORCE)
set(WITH_MAIN ON CACHE BOOL "" FORCE)
add_subdirectory(third-party/corrade EXCLUDE_FROM_ALL)
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)
set(TARGET_GL ON CACHE BOOL "" FORCE)
set(TARGET_GLES OFF CACHE BOOL "" FORCE)
set(TARGET_VK OFF CACHE BOOL "" FORCE)
set(WITH_AUDIO OFF CACHE BOOL "" FORCE)
set(WITH_DEBUGTOOLS OFF CACHE BOOL "" FORCE)
set(WITH_GL ON CACHE BOOL "" FORCE)
set(WITH_MESHTOOLS ON CACHE BOOL "" FORCE)
set(WITH_PRIMITIVES ON CACHE BOOL "" FORCE)
set(WITH_SCENEGRAPH ON CACHE BOOL "" FORCE)
set(WITH_SHADERS ON CACHE BOOL "" FORCE)
set(WITH_SHADERTOOLS OFF CACHE BOOL "" FORCE)
set(WITH_TEXT OFF CACHE BOOL "" FORCE)
set(WITH_TEXTURETOOLS OFF CACHE BOOL "" FORCE)
set(WITH_TRADE ON CACHE BOOL "" FORCE)
set(WITH_VK OFF CACHE BOOL "" FORCE)
set(WITH_SDL2APPLICATION ON CACHE BOOL "" FORCE)
add_subdirectory(third-party/magnum EXCLUDE_FROM_ALL)
set(IMGUI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third-party/imgui)
set(WITH_IMGUI ON CACHE BOOL "" FORCE)
add_subdirectory(third-party/magnum-integration 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)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
add_subdirectory(third-party/libzip EXCLUDE_FROM_ALL)
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)
add_subdirectory(src)

20
README.md Normal file
View File

@ -0,0 +1,20 @@
# M.A.S.S. Builder Save Tool
A save file manager and editor for M.A.S.S. Builder. Based on [wxMASSManager](https://github.com/williamjcm/wxMASSManager), this is a fork using Magnum and ImGui for the UI.
## Installing
Get the `MassBuilderSaveTool-<version>.zip` file from the [Releases](https://github.com/williamjcm/MassBuilderSaveTool/releases) page, and extract it somewhere. Then, launch `MassBuilderSaveTool.exe`.
## Building on MSYS2 - IGNORE IF YOU JUST WANT TO USE THE APP!
1. Install the 64-bit (`x86_64`) version of [MSYS2](https://www.msys2.org/) in its default path (`C:\msys64`), and update it fully.
2. Run `pacman -S git mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja`.
3. In a `MINGW64` shell, type `git clone https://github.com/williamjcm/MassBuilderSaveTool`.
4. Type `cd MassBuilderSaveTool && git submodule init && git submodule update && mkdir build && cd build`.
5. Type `cmake -GNinja -DCMAKE_BUILD_TYPE=Release ..`
6. Type `ninja`
7. ...
8. Profit!
You'll be able to find the executable in `build/Release/bin`.

623
modules/FindCorrade.cmake Normal file
View File

@ -0,0 +1,623 @@
#.rst:
# Find Corrade
# ------------
#
# Finds the Corrade library. Basic usage::
#
# find_package(Corrade REQUIRED)
#
# This module tries to find the base Corrade library and then defines the
# following:
#
# Corrade_FOUND - Whether the base library was found
# CORRADE_LIB_SUFFIX_MODULE - Path to CorradeLibSuffix.cmake module
#
# This command will try to find only the base library, not the optional
# components, which are:
#
# Containers - Containers library
# PluginManager - PluginManager library
# TestSuite - TestSuite library
# Utility - Utility library
# rc - corrade-rc executable
#
# Example usage with specifying additional components is::
#
# find_package(Corrade REQUIRED Utility TestSuite)
#
# For each component is then defined:
#
# Corrade_*_FOUND - Whether the component was found
# Corrade::* - Component imported target
#
# The package is found if either debug or release version of each library is
# found. If both debug and release libraries are found, proper version is
# chosen based on actual build configuration of the project (i.e. Debug build
# is linked to debug libraries, Release build to release libraries).
#
# Corrade conditionally defines ``CORRADE_IS_DEBUG_BUILD`` preprocessor
# variable in case build configuration is ``Debug`` (not Corrade itself, but
# build configuration of the project using it). Useful e.g. for selecting
# proper plugin directory.
#
# Corrade defines the following custom target properties:
#
# CORRADE_CXX_STANDARD - C++ standard to require when compiling given
# target. Does nothing if :variable:`CMAKE_CXX_FLAGS` already contains
# particular standard setting flag or if given target contains
# :prop_tgt:`CMAKE_CXX_STANDARD` property. Allowed value is 11, 14 or 17.
# INTERFACE_CORRADE_CXX_STANDARD - C++ standard to require when using given
# target. Does nothing if :variable:`CMAKE_CXX_FLAGS` already contains
# particular standard setting flag or if given target contains
# :prop_tgt:`CMAKE_CXX_STANDARD` property. Allowed value is 11, 14 or 17.
# CORRADE_USE_PEDANTIC_FLAGS - Enable additional compiler/linker flags.
# Boolean.
#
# These properties are inherited from directory properties, meaning that if you
# set them on directories, they get implicitly set on all targets in given
# directory (with a possibility to do target-specific overrides). All Corrade
# libraries have the :prop_tgt:`INTERFACE_CORRADE_CXX_STANDARD` property set to
# 11, meaning that you will always have at least C++11 enabled once you link to
# any Corrade library.
#
# Features of found Corrade library are exposed in these variables:
#
# CORRADE_MSVC2019_COMPATIBILITY - Defined if compiled with compatibility
# mode for MSVC 2019
# CORRADE_MSVC2017_COMPATIBILITY - Defined if compiled with compatibility
# 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
# included
# CORRADE_BUILD_STATIC - Defined if compiled as static libraries.
# Default are shared libraries.
# CORRADE_BUILD_STATIC_UNIQUE_GLOBALS - Defined if static libraries keep their
# globals unique even across different shared libraries. Enabled by default
# for static builds.
# CORRADE_BUILD_MULTITHREADED - Defined if compiled in a way that makes it
# possible to safely use certain Corrade features simultaneously in multiple
# threads
# CORRADE_TARGET_UNIX - Defined if compiled for some Unix flavor
# (Linux, BSD, macOS)
# CORRADE_TARGET_APPLE - Defined if compiled for Apple platforms
# CORRADE_TARGET_IOS - Defined if compiled for iOS (device or
# simulator)
# CORRADE_TARGET_IOS_SIMULATOR - Defined if compiled for iOS Simulator
# CORRADE_TARGET_WINDOWS - Defined if compiled for Windows
# CORRADE_TARGET_WINDOWS_RT - Defined if compiled for Windows RT
# CORRADE_TARGET_EMSCRIPTEN - Defined if compiled for Emscripten
# CORRADE_TARGET_ANDROID - Defined if compiled for Android
# CORRADE_TARGET_GCC - Defined if compiling with GCC or GCC-
# compatible Clang
# CORRADE_TARGET_CLANG - Defined if compiling with Clang or any of its
# variants
# CORRADE_TARGET_APPLE_CLANG - Defined if compiling with Apple's Clang
# CORRADE_TARGET_CLANG_CL - Defined if compiling with Clang-CL (Clang
# with a MSVC frontend)
# CORRADE_TARGET_MSVC - Defined if compiling with MSVC or Clang with
# a MSVC frontend
# CORRADE_TARGET_MINGW - Defined if compiling under MinGW
# CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT - Defined if PluginManager
# doesn't support dynamic plugin loading due to platform limitations
# CORRADE_TESTSUITE_TARGET_XCTEST - Defined if TestSuite is targetting Xcode
# XCTest
# CORRADE_UTILITY_USE_ANSI_COLORS - Defined if ANSI escape sequences are used
# for colored output with Utility::Debug on Windows
#
# Additionally these variables are defined for internal usage:
#
# CORRADE_INCLUDE_DIR - Root include dir
# CORRADE_*_LIBRARY_DEBUG - Debug version of given library, if found
# CORRADE_*_LIBRARY_RELEASE - Release version of given library, if found
# CORRADE_*_EXECUTABLE - Location of given executable, if found
# CORRADE_USE_MODULE - Path to UseCorrade.cmake module (included
# automatically)
# CORRADE_TESTSUITE_XCTEST_RUNNER - Path to XCTestRunner.mm.in file
# CORRADE_TESTSUITE_ADB_RUNNER - Path to AdbRunner.sh 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
# definitions used for targets with :prop_tgt:`CORRADE_USE_PEDANTIC_FLAGS`
# enabled
# CORRADE_CXX{11,14,17,20}_STANDARD_FLAG - Compiler flag to use for targeting
# C++11, 14, 17 or 20 in cases where it's not possible to use
# :prop_tgt:`CORRADE_CXX_STANDARD`. Not defined if a standard switch is
# already present in :variable:`CMAKE_CXX_FLAGS`.
#
# Corrade provides these macros and functions:
#
# .. command:: corrade_add_test
#
# Add unit test using Corrade's TestSuite::
#
# corrade_add_test(<test name>
# <sources>...
# [LIBRARIES <libraries>...]
# [FILES <files>...]
# [ARGUMENTS <arguments>...])
#
# Test name is also executable name. You can use ``LIBRARIES`` to specify
# libraries to link with instead of using :command:`target_link_libraries()`.
# The ``Corrade::TestSuite`` target is linked automatically to each test. Note
# that the :command:`enable_testing()` function must be called explicitly.
# Arguments passed after ``ARGUMENTS`` will be appended to the test
# command line. ``ARGUMENTS`` are supported everywhere except when
# ``CORRADE_TESTSUITE_TARGET_XCTEST`` is enabled.
#
# You can list files needed by the test in the ``FILES`` section. If given
# filename is relative, it is treated relatively to `CMAKE_CURRENT_SOURCE_DIR`.
# The files are added to the :prop_test:`REQUIRED_FILES` target property. On
# Emscripten they are bundled to the executable and available in the virtual
# filesystem root. On Android they are copied along the executable to the
# target. In case of Emscripten and Android, if the file is absolute or
# contains ``..``, only the leaf name is used. Alternatively you can have a
# filename formatted as ``<input>@<output>``, in which case the ``<input>`` is
# treated as local filesystem location and ``<output>`` as remote/virtual
# filesystem location. The remote location can't be absolute or contain ``..``
# / ``@`` characters.
#
# Unless :variable:`CORRADE_TESTSUITE_TARGET_XCTEST` is set, test cases on iOS
# targets are created as bundles with bundle identifier set to CMake project
# name by default. Use the cache variable :variable:`CORRADE_TESTSUITE_BUNDLE_IDENTIFIER_PREFIX`
# to change it to something else.
#
# .. command:: corrade_add_resource
#
# Compile data resources into application binary::
#
# corrade_add_resource(<name> <resources.conf>)
#
# Depends on ``Corrade::rc``, which is part of Corrade utilities. This command
# generates resource data using given configuration file in current build
# directory. Argument name is name under which the resources can be explicitly
# loaded. Variable ``<name>`` contains compiled resource filename, which is
# then used for compiling library / executable. On CMake >= 3.1 the
# `resources.conf` file can contain UTF-8-encoded filenames. Example usage::
#
# corrade_add_resource(app_resources resources.conf)
# add_executable(app source1 source2 ... ${app_resources})
#
# .. command:: corrade_add_plugin
#
# Add dynamic plugin::
#
# corrade_add_plugin(<plugin name>
# "<debug binary install dir>;<debug library install dir>"
# "<release binary install dir>;<release library install dir>"
# <metadata file>
# <sources>...)
#
# The macro adds a preprocessor directive ``CORRADE_DYNAMIC_PLUGIN`` when
# compiling ``<sources>``. Additional libraries can be linked in via
# :command:`target_link_libraries(plugin_name ...) <target_link_libraries>`.
# On DLL platforms, the plugin DLLs and metadata files are put into
# ``<debug binary install dir>`` / ``<release binary install dir>`` and the
# ``*.lib`` files into ``<debug library install dir>`` /
# ``<release library install dir>``. On non-DLL platforms everything is put
# into ``<debug library install dir>`` / ``<release library install dir>``.
#
# If the plugin interface disables plugin metadata files, the
# ``<metadata file>`` can be set to ``""``, in which case no metadata file is
# copied anywhere. Otherwise the metadata file is copied and renamed to
# ``<plugin name>``, retaining its original extension.
#
# corrade_add_plugin(<plugin name>
# <debug install dir>
# <release install dir>
# <metadata file>
# <sources>...)
#
# Unline the above version this puts everything into ``<debug install dir>`` on
# both DLL and non-DLL platforms. If ``<debug install dir>`` 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
# files are actually put into configuration-based subdirectory, i.e.
# ``${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}``. See documentation of
# :variable:`CMAKE_CFG_INTDIR` variable for more information.
#
# .. command:: corrade_add_static_plugin
#
# Add static plugin::
#
# corrade_add_static_plugin(<plugin name>
# "<binary install dir>;<library install dir>"
# <metadata file>
# <sources>...)
#
# The macro adds a preprocessor directive ``CORRADE_STATIC_PLUGIN`` when
# compiling ``<sources>``. Additional libraries can be linked in via
# :command:`target_link_libraries(plugin_name ...) <target_link_libraries>`.
# The ``<binary install dir>`` is ignored and included just for compatibility
# with the :command:`corrade_add_plugin` command, everything is installed into
# ``<library install dir>``. Note that plugins built in debug configuration
# (e.g. with :variable:`CMAKE_BUILD_TYPE` set to ``Debug``) have ``"-d"``
# suffix to make it possible to have both debug and release plugins installed
# alongside each other.
#
# If the plugin interface disables plugin metadata files, the
# ``<metadata file>`` can be set to ``""``, in which case no metadata file is
# used. Otherwise the metadata file is bundled and renamed to
# ``<plugin name>``, retaining its original extension.
#
# corrade_add_static_plugin(<plugin name>
# <install dir>
# <metadata file>
# <sources>...)
#
# Equivalent to the above with ``<library install dir>`` set to ``<install dir>``.
# If ``<install dir>`` is set to :variable:`CMAKE_CURRENT_BINARY_DIR` (e.g. for
# testing purposes), no installation rules are added.
#
# .. command:: corrade_find_dlls_for_libs
#
# Find corresponding DLLs for library files::
#
# corrade_find_dlls_for_libs(<output variable> <libs>...)
#
# Available only on Windows, for all ``*.lib`` files tries to find
# corresponding DLL file. Useful for bundling dependencies for e.g. WinRT
# packages.
#
#
# This file is part of Corrade.
#
# Copyright © 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016,
# 2017, 2018, 2019, 2020, 2021
# Vladimír Vondruš <mosra@centrum.cz>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
# Root include dir
find_path(CORRADE_INCLUDE_DIR
NAMES Corrade/Corrade.h)
mark_as_advanced(CORRADE_INCLUDE_DIR)
# Configuration file
find_file(_CORRADE_CONFIGURE_FILE configure.h
HINTS ${CORRADE_INCLUDE_DIR}/Corrade/)
mark_as_advanced(_CORRADE_CONFIGURE_FILE)
# We need to open configure.h file from CORRADE_INCLUDE_DIR before we check for
# the components. Bail out with proper error message if it wasn't found. The
# complete check with all components is further below.
if(NOT CORRADE_INCLUDE_DIR)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Corrade
REQUIRED_VARS CORRADE_INCLUDE_DIR _CORRADE_CONFIGURE_FILE)
endif()
# Read flags from configuration
file(READ ${_CORRADE_CONFIGURE_FILE} _corradeConfigure)
string(REGEX REPLACE ";" "\\\\;" _corradeConfigure "${_corradeConfigure}")
string(REGEX REPLACE "\n" ";" _corradeConfigure "${_corradeConfigure}")
set(_corradeFlags
MSVC2015_COMPATIBILITY
MSVC2017_COMPATIBILITY
MSVC2019_COMPATIBILITY
BUILD_DEPRECATED
BUILD_STATIC
BUILD_STATIC_UNIQUE_GLOBALS
BUILD_MULTITHREADED
TARGET_UNIX
TARGET_APPLE
TARGET_IOS
TARGET_IOS_SIMULATOR
TARGET_WINDOWS
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.
PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT
TESTSUITE_TARGET_XCTEST
UTILITY_USE_ANSI_COLORS)
foreach(_corradeFlag ${_corradeFlags})
list(FIND _corradeConfigure "#define CORRADE_${_corradeFlag}" _corrade_${_corradeFlag})
if(NOT _corrade_${_corradeFlag} EQUAL -1)
set(CORRADE_${_corradeFlag} 1)
endif()
endforeach()
# CMake module dir
find_path(_CORRADE_MODULE_DIR
NAMES UseCorrade.cmake CorradeLibSuffix.cmake
PATH_SUFFIXES share/cmake/Corrade)
mark_as_advanced(_CORRADE_MODULE_DIR)
set(CORRADE_USE_MODULE ${_CORRADE_MODULE_DIR}/UseCorrade.cmake)
set(CORRADE_LIB_SUFFIX_MODULE ${_CORRADE_MODULE_DIR}/CorradeLibSuffix.cmake)
# Component distinction (listing them explicitly to avoid mistakes with finding
# unknown components)
set(_CORRADE_LIBRARY_COMPONENTS
Containers Interconnect Main PluginManager TestSuite Utility)
set(_CORRADE_HEADER_ONLY_COMPONENTS Containers)
if(NOT CORRADE_TARGET_WINDOWS)
# CorradeMain is a real library only on windows, a dummy target elsewhere
list(APPEND _CORRADE_HEADER_ONLY_COMPONENTS Main)
endif()
set(_CORRADE_EXECUTABLE_COMPONENTS rc)
# Currently everything is enabled implicitly. Keep in sync with Corrade's root
# CMakeLists.txt.
set(_CORRADE_IMPLICITLY_ENABLED_COMPONENTS
Containers Interconnect Main PluginManager TestSuite Utility rc)
# Inter-component dependencies
set(_CORRADE_Containers_DEPENDENCIES Utility)
set(_CORRADE_Interconnect_DEPENDENCIES Containers Utility)
set(_CORRADE_PluginManager_DEPENDENCIES Containers Utility rc)
set(_CORRADE_TestSuite_DEPENDENCIES Containers Utility Main) # see below
set(_CORRADE_Utility_DEPENDENCIES Containers rc)
# Ensure that all inter-component dependencies are specified as well
foreach(_component ${Corrade_FIND_COMPONENTS})
# Mark the dependencies as required if the component is also required
if(Corrade_FIND_REQUIRED_${_component})
foreach(_dependency ${_CORRADE_${_component}_DEPENDENCIES})
set(Corrade_FIND_REQUIRED_${_dependency} TRUE)
endforeach()
endif()
list(APPEND _CORRADE_ADDITIONAL_COMPONENTS ${_CORRADE_${_component}_DEPENDENCIES})
endforeach()
# Main is linked only in corrade_add_test(), not to everything that depends on
# TestSuite, so remove it from the list again once we filled the above
# variables
set(_CORRADE_TestSuite_DEPENDENCIES Containers Utility)
# Join the lists, remove duplicate components
set(_CORRADE_ORIGINAL_FIND_COMPONENTS ${Corrade_FIND_COMPONENTS})
if(_CORRADE_ADDITIONAL_COMPONENTS)
list(INSERT Corrade_FIND_COMPONENTS 0 ${_CORRADE_ADDITIONAL_COMPONENTS})
endif()
if(Corrade_FIND_COMPONENTS)
list(REMOVE_DUPLICATES Corrade_FIND_COMPONENTS)
endif()
# Find all components
foreach(_component ${Corrade_FIND_COMPONENTS})
string(TOUPPER ${_component} _COMPONENT)
# Create imported target in case the library is found. If the project is
# added as subproject to CMake, the target already exists and all the
# required setup is already done from the build tree.
if(TARGET Corrade::${_component})
set(Corrade_${_component}_FOUND TRUE)
else()
# 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)
# Try to find both debug and release version
find_library(CORRADE_${_COMPONENT}_LIBRARY_DEBUG Corrade${_component}-d)
find_library(CORRADE_${_COMPONENT}_LIBRARY_RELEASE Corrade${_component})
mark_as_advanced(CORRADE_${_COMPONENT}_LIBRARY_DEBUG
CORRADE_${_COMPONENT}_LIBRARY_RELEASE)
if(CORRADE_${_COMPONENT}_LIBRARY_RELEASE)
set_property(TARGET Corrade::${_component} APPEND PROPERTY
IMPORTED_CONFIGURATIONS RELEASE)
set_property(TARGET Corrade::${_component} PROPERTY
IMPORTED_LOCATION_RELEASE ${CORRADE_${_COMPONENT}_LIBRARY_RELEASE})
endif()
if(CORRADE_${_COMPONENT}_LIBRARY_DEBUG)
set_property(TARGET Corrade::${_component} APPEND PROPERTY
IMPORTED_CONFIGURATIONS DEBUG)
set_property(TARGET Corrade::${_component} PROPERTY
IMPORTED_LOCATION_DEBUG ${CORRADE_${_COMPONENT}_LIBRARY_DEBUG})
endif()
endif()
# Header-only library components
if(_component IN_LIST _CORRADE_HEADER_ONLY_COMPONENTS)
add_library(Corrade::${_component} INTERFACE IMPORTED)
endif()
# Default include path names to look for for library / header-only
# components
if(_component IN_LIST _CORRADE_LIBRARY_COMPONENTS)
set(_CORRADE_${_COMPONENT}_INCLUDE_PATH_SUFFIX Corrade/${_component})
set(_CORRADE_${_COMPONENT}_INCLUDE_PATH_NAMES ${_component}.h)
endif()
# Executable components
if(_component IN_LIST _CORRADE_EXECUTABLE_COMPONENTS)
add_executable(Corrade::${_component} IMPORTED)
find_program(CORRADE_${_COMPONENT}_EXECUTABLE corrade-${_component})
mark_as_advanced(CORRADE_${_COMPONENT}_EXECUTABLE)
if(CORRADE_${_COMPONENT}_EXECUTABLE)
set_property(TARGET Corrade::${_component} PROPERTY
IMPORTED_LOCATION ${CORRADE_${_COMPONENT}_EXECUTABLE})
endif()
endif()
# No special setup for Containers library
# 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")
if(CMAKE_VERSION VERSION_LESS 3.13)
set_property(TARGET Corrade::${_component} PROPERTY
INTERFACE_LINK_LIBRARIES "-OPT:NOICF,REF")
else()
set_property(TARGET Corrade::${_component} PROPERTY
INTERFACE_LINK_OPTIONS "/OPT:NOICF,REF")
endif()
endif()
# Main library
elseif(_component STREQUAL Main)
set(_CORRADE_${_COMPONENT}_INCLUDE_PATH_SUFFIX Corrade)
set(_CORRADE_${_COMPONENT}_INCLUDE_PATH_NAMES Corrade.h)
if(CORRADE_TARGET_WINDOWS)
if(NOT MINGW)
# Abusing INTERFACE_LINK_LIBRARIES because
# INTERFACE_LINK_OPTIONS is only since 3.13. They treat
# things with `-` in front as linker flags and fortunately
# I can use `-ENTRY` instead of `/ENTRY`.
# https://gitlab.kitware.com/cmake/cmake/issues/16543
set_property(TARGET Corrade::${_component} APPEND PROPERTY
INTERFACE_LINK_LIBRARIES "-ENTRY:$<$<NOT:$<BOOL:$<TARGET_PROPERTY:WIN32_EXECUTABLE>>>:wmainCRTStartup>$<$<BOOL:$<TARGET_PROPERTY:WIN32_EXECUTABLE>>:wWinMainCRTStartup>")
else()
set_property(TARGET Corrade::${_component} APPEND PROPERTY
INTERFACE_LINK_LIBRARIES "-municode")
endif()
endif()
# PluginManager library
elseif(_component STREQUAL PluginManager)
# -ldl is handled by Utility now
# TestSuite library has some additional files
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)
# 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)
# 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)
endif()
# Utility library (contains all setup that is used by others)
elseif(_component STREQUAL Utility)
# Top-level include directory
set_property(TARGET Corrade::${_component} APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES ${CORRADE_INCLUDE_DIR})
# Require (at least) C++11 for users
set_property(TARGET Corrade::${_component} PROPERTY
INTERFACE_CORRADE_CXX_STANDARD 11)
set_property(TARGET Corrade::${_component} APPEND PROPERTY
COMPATIBLE_INTERFACE_NUMBER_MAX CORRADE_CXX_STANDARD)
# Directory::libraryLocation() needs this
if(CORRADE_TARGET_UNIX)
set_property(TARGET Corrade::${_component} APPEND PROPERTY
INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS})
endif()
# AndroidLogStreamBuffer class needs to be linked to log library
if(CORRADE_TARGET_ANDROID)
set_property(TARGET Corrade::${_component} APPEND PROPERTY
INTERFACE_LINK_LIBRARIES "log")
endif()
endif()
# Find library includes
if(_component IN_LIST _CORRADE_LIBRARY_COMPONENTS)
find_path(_CORRADE_${_COMPONENT}_INCLUDE_DIR
NAMES ${_CORRADE_${_COMPONENT}_INCLUDE_PATH_NAMES}
HINTS ${CORRADE_INCLUDE_DIR}/${_CORRADE_${_COMPONENT}_INCLUDE_PATH_SUFFIX})
mark_as_advanced(_CORRADE_${_COMPONENT}_INCLUDE_DIR)
endif()
# Add inter-library dependencies
if(_component IN_LIST _CORRADE_LIBRARY_COMPONENTS OR _component IN_LIST _CORRADE_HEADER_ONLY_COMPONENTS)
foreach(_dependency ${_CORRADE_${_component}_DEPENDENCIES})
if(_dependency IN_LIST _CORRADE_LIBRARY_COMPONENTS OR _dependency IN_LIST _CORRADE_HEADER_ONLY_COMPONENTS)
set_property(TARGET Corrade::${_component} APPEND PROPERTY
INTERFACE_LINK_LIBRARIES Corrade::${_dependency})
endif()
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)
endif()
endif()
endforeach()
# For CMake 3.16+ with REASON_FAILURE_MESSAGE, provide additional potentially
# useful info about the failed components.
if(NOT CMAKE_VERSION VERSION_LESS 3.16)
set(_CORRADE_REASON_FAILURE_MESSAGE )
# Go only through the originally specified find_package() components, not
# the dependencies added by us afterwards
foreach(_component ${_CORRADE_ORIGINAL_FIND_COMPONENTS})
if(Corrade_${_component}_FOUND)
continue()
endif()
# If it's not known at all, tell the user -- it might be a new library
# and an old Find module, or something platform-specific.
if(NOT _component IN_LIST _CORRADE_LIBRARY_COMPONENTS AND NOT _component IN_LIST _CORRADE_EXECUTABLE_COMPONENTS)
list(APPEND _CORRADE_REASON_FAILURE_MESSAGE "${_component} is not a known component on this platform.")
# Otherwise, if it's not among implicitly built components, hint that
# the user may need to enable it.
# TODO: currently, the _FOUND variable doesn't reflect if dependencies
# were found. When it will, this needs to be updated to avoid
# 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.")
# Otherwise we have no idea. Better be silent than to print something
# misleading.
else()
endif()
endforeach()
string(REPLACE ";" " " _CORRADE_REASON_FAILURE_MESSAGE "${_CORRADE_REASON_FAILURE_MESSAGE}")
set(_CORRADE_REASON_FAILURE_MESSAGE REASON_FAILURE_MESSAGE "${_CORRADE_REASON_FAILURE_MESSAGE}")
endif()
include(FindPackageHandleStandardArgs)
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})
# Finalize the finding process
include(${CORRADE_USE_MODULE})
set(CORRADE_INCLUDE_INSTALL_DIR include/Corrade)
if(CORRADE_BUILD_DEPRECATED AND CORRADE_INCLUDE_INSTALL_PREFIX AND NOT CORRADE_INCLUDE_INSTALL_PREFIX STREQUAL ".")
message(DEPRECATION "CORRADE_INCLUDE_INSTALL_PREFIX is obsolete as its primary use was for old Android NDK versions. Please switch to the NDK r19+ layout instead of using this variable and recreate your build directory to get rid of this warning.")
set(CORRADE_INCLUDE_INSTALL_DIR ${CORRADE_INCLUDE_INSTALL_PREFIX}/${CORRADE_INCLUDE_INSTALL_DIR})
endif()

226
modules/FindImGui.cmake Normal file
View File

@ -0,0 +1,226 @@
#.rst:
# Find ImGui
# -------------
#
# Finds the ImGui library. This module defines:
#
# ImGui_FOUND - True if ImGui is found
# ImGui::ImGui - ImGui interface target
# ImGui::Sources - ImGui source target for core functionality
# ImGui::SourcesMiscCpp - ImGui source target for misc/cpp
#
# Additionally these variables are defined for internal usage:
#
# ImGui_INCLUDE_DIR - Include dir
#
# The find module first tries to find ``imgui`` via a CMake config file (which
# is distributed this way via Vcpkg, for example). If that's found, the
# ``ImGui::ImGui`` target is an alias to it and the ``ImGui::Sources`` target
# is empty except for having ``ImGui::ImGui`` as a dependency.
#
# If ``imgui`` is not found, as a fallback it tries to find the C++ sources.
# You can supply their location via an ``IMGUI_DIR`` variable. Once found, the
# ``ImGui::ImGui`` target contains just the header file, while
# ``ImGui::Sources`` contains the source files in ``INTERFACE_SOURCES``.
#
# The ``ImGui::SourcesMiscCpp`` component, if requested, is always searched for
# in the form of C++ sources. Vcpkg doesn't distribute these.
#
# The desired usage that covers both cases is to link ``ImGui::Sources``
# ``PRIVATE``\ ly to a *single* target, which will then contain either the
# sources or be linked to the imgui library from Vcpkg; and linking
# ``ImGui::ImGui`` to this target ``PUBLIC``\ ly.
#
#
# This file is part of Magnum.
#
# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
# 2020, 2021 Vladimír Vondruš <mosra@centrum.cz>
# Copyright © 2018 Jonathan Hale <squareys@googlemail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
# In 1.71 ImGui depends on the ApplicationServices framework for macOS
# clipboard support. It's removed again in 1.72. TODO: remove once obsolete
if(CORRADE_TARGET_APPLE)
find_library(_IMGUI_ApplicationServices_LIBRARY ApplicationServices)
mark_as_advanced(_IMGUI_ApplicationServices_LIBRARY)
set(_IMGUI_EXTRA_LIBRARIES ${_IMGUI_ApplicationServices_LIBRARY})
endif()
# Vcpkg distributes imgui as a library with a config file, so try that first --
# but only if IMGUI_DIR wasn't explicitly passed, in which case we'll look
# there instead
if(NOT IMGUI_DIR AND NOT TARGET imgui::imgui)
find_package(imgui CONFIG QUIET)
endif()
if(NOT IMGUI_DIR AND TARGET imgui::imgui)
if(NOT TARGET ImGui::ImGui)
add_library(ImGui::ImGui INTERFACE IMPORTED)
# TODO: remove once 1.71 is obsolete
set_property(TARGET ImGui::ImGui APPEND PROPERTY
INTERFACE_LINK_LIBRARIES imgui::imgui ${_IMGUI_EXTRA_LIBRARIES})
# Retrieve include directory for FindPackageHandleStandardArgs later
get_target_property(ImGui_INCLUDE_DIR imgui::imgui
INTERFACE_INCLUDE_DIRECTORIES)
add_library(ImGui::Sources INTERFACE IMPORTED)
set_property(TARGET ImGui::Sources APPEND PROPERTY
INTERFACE_LINK_LIBRARIES ImGui::ImGui)
endif()
# Otherwise find the source files and compile them as part of the library they
# get linked to
else()
# Disable the find root path here, it overrides the
# CMAKE_FIND_ROOT_PATH_MODE_INCLUDE setting potentially set in
# toolchains.
find_path(ImGui_INCLUDE_DIR NAMES imgui.h
HINTS ${IMGUI_DIR}
PATH_SUFFIXES MagnumExternal/ImGui
NO_CMAKE_FIND_ROOT_PATH)
mark_as_advanced(ImGui_INCLUDE_DIR)
if(NOT TARGET ImGui::ImGui)
add_library(ImGui::ImGui INTERFACE IMPORTED)
set_property(TARGET ImGui::ImGui APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES ${ImGui_INCLUDE_DIR})
# TODO: remove once 1.71 is obsolete
if(_IMGUI_EXTRA_LIBRARIES)
set_property(TARGET ImGui::ImGui APPEND PROPERTY
INTERFACE_LINK_LIBRARIES ${_IMGUI_EXTRA_LIBRARIES})
endif()
# Handle export and import of imgui symbols via IMGUI_API definition
# in visibility.h of Magnum ImGuiIntegration.
set_property(TARGET ImGui::ImGui APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS
"IMGUI_USER_CONFIG=\"Magnum/ImGuiIntegration/visibility.h\"")
endif()
endif()
macro(_imgui_setup_source_file source_var)
# Handle export and import of imgui symbols via IMGUI_API
# definition in visibility.h of Magnum ImGuiIntegration.
set_property(SOURCE ${${source_var}} APPEND PROPERTY COMPILE_DEFINITIONS
"IMGUI_USER_CONFIG=\"Magnum/ImGuiIntegration/visibility.h\"")
# Hide warnings from imgui source files
# GCC- and Clang-specific flags
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR (CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?Clang"
AND NOT CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC") OR CORRADE_TARGET_EMSCRIPTEN)
set_property(SOURCE ${${source_var}} APPEND_STRING PROPERTY COMPILE_FLAGS
" -Wno-old-style-cast")
endif()
# GCC-specific flags
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set_property(SOURCE ${${source_var}} APPEND_STRING PROPERTY COMPILE_FLAGS
" -Wno-double-promotion -Wno-zero-as-null-pointer-constant")
endif()
mark_as_advanced(${source_var})
endmacro()
# Find components
foreach(_component IN LISTS ImGui_FIND_COMPONENTS)
if(_component STREQUAL "Sources")
if(NOT TARGET ImGui::Sources)
set(ImGui_Sources_FOUND TRUE)
set(ImGui_SOURCES )
foreach(_file imgui imgui_widgets imgui_draw imgui_demo)
# Disable the find root path here, it overrides the
# CMAKE_FIND_ROOT_PATH_MODE_INCLUDE setting potentially set in
# toolchains.
find_file(ImGui_${_file}_SOURCE NAMES ${_file}.cpp
HINTS ${IMGUI_DIR} NO_CMAKE_FIND_ROOT_PATH)
if(NOT ImGui_${_file}_SOURCE)
set(ImGui_Sources_FOUND FALSE)
break()
endif()
list(APPEND ImGui_SOURCES ${ImGui_${_file}_SOURCE})
_imgui_setup_source_file(ImGui_${_file}_SOURCE)
endforeach()
# Files not present in all ImGui versions, treat them as optional
# and do nothing if not found.
# - imgui_tables added in https://github.com/ocornut/imgui/commit/9874077fc0e364383ef997e3d4332172bfddc0b9
foreach(_file imgui_tables)
# Disable the find root path here, it overrides the
# CMAKE_FIND_ROOT_PATH_MODE_INCLUDE setting potentially set in
# toolchains.
find_file(ImGui_${_file}_SOURCE NAMES ${_file}.cpp
HINTS ${IMGUI_DIR} NO_CMAKE_FIND_ROOT_PATH)
if(NOT ImGui_${_file}_SOURCE)
mark_as_advanced(ImGui_${_file}_SOURCE)
continue()
endif()
list(APPEND ImGui_SOURCES ${ImGui_${_file}_SOURCE})
_imgui_setup_source_file(ImGui_${_file}_SOURCE)
endforeach()
add_library(ImGui::Sources INTERFACE IMPORTED)
set_property(TARGET ImGui::Sources APPEND PROPERTY
INTERFACE_SOURCES "${ImGui_SOURCES}")
set_property(TARGET ImGui::Sources APPEND PROPERTY
INTERFACE_LINK_LIBRARIES ImGui::ImGui)
else()
set(ImGui_Sources_FOUND TRUE)
endif()
elseif(_component STREQUAL "SourcesMiscCpp")
set(ImGui_SourcesMiscCpp_FOUND TRUE)
set(ImGui_MISC_CPP_SOURCES )
foreach(_file imgui_stdlib)
# Disable the find root path here, it overrides the
# CMAKE_FIND_ROOT_PATH_MODE_INCLUDE setting potentially set in
# toolchains.
find_file(ImGui_${_file}_MISC_CPP_SOURCE NAMES ${_file}.cpp
HINTS ${IMGUI_DIR}/misc/cpp NO_CMAKE_FIND_ROOT_PATH)
list(APPEND ImGui_MISC_CPP_SOURCES ${ImGui_${_file}_MISC_CPP_SOURCE})
if(NOT ImGui_${_file}_MISC_CPP_SOURCE)
set(ImGui_SourcesMiscCpp_FOUND FALSE)
break()
endif()
_imgui_setup_source_file(ImGui_${_file}_MISC_CPP_SOURCE)
endforeach()
if(NOT TARGET ImGui::SourcesMiscCpp)
add_library(ImGui::SourcesMiscCpp INTERFACE IMPORTED)
set_property(TARGET ImGui::SourcesMiscCpp APPEND PROPERTY
INTERFACE_SOURCES "${ImGui_MISC_CPP_SOURCES}")
set_property(TARGET ImGui::SourcesMiscCpp APPEND PROPERTY
INTERFACE_LINK_LIBRARIES ImGui::ImGui)
endif()
endif()
endforeach()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ImGui
REQUIRED_VARS ImGui_INCLUDE_DIR HANDLE_COMPONENTS)

1269
modules/FindMagnum.cmake Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,332 @@
#.rst:
# Find Magnum integration library
# -------------------------------
#
# Finds the Magnum integration library. Basic usage::
#
# find_package(MagnumIntegration REQUIRED)
#
# This command tries to find Magnum integration library and then defines the
# following:
#
# MagnumIntegration_FOUND - Whether the library was found
#
# This command alone is useless without specifying the components:
#
# Bullet - Bullet Physics integration library
# Dart - Dart Physics integration library
# Eigen - Eigen integration library
# Glm - GLM integration library
# ImGui - ImGui integration library
# Ovr - Oculus SDK integration library
#
# Example usage with specifying additional components is:
#
# find_package(MagnumIntegration REQUIRED Bullet)
#
# For each component is then defined:
#
# MagnumIntegration_*_FOUND - Whether the component was found
# MagnumIntegration::* - Component imported target
#
# The package is found if either debug or release version of each requested
# library is found. If both debug and release libraries are found, proper
# version is chosen based on actual build configuration of the project (i.e.
# Debug build is linked to debug libraries, Release build to release
# libraries).
#
# Additionally these variables are defined for internal usage:
#
# MAGNUMINTEGRATION_INCLUDE_DIR - Magnum integration include dir (w/o
# dependencies)
# MAGNUMINTEGRATION_*_LIBRARY_DEBUG - Debug version of given library, if found
# MAGNUMINTEGRATION_*_LIBRARY_RELEASE - Release version of given library, if
# found
#
#
# This file is part of Magnum.
#
# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
# 2020, 2021 Vladimír Vondruš <mosra@centrum.cz>
# Copyright © 2018 Konstantinos Chatzilygeroudis <costashatz@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
# Magnum library dependencies
set(_MAGNUMINTEGRATION_DEPENDENCIES )
foreach(_component ${MagnumIntegration_FIND_COMPONENTS})
if(_component STREQUAL Bullet)
set(_MAGNUMINTEGRATION_${_component}_MAGNUM_DEPENDENCIES SceneGraph Shaders GL)
elseif(_component STREQUAL Dart)
set(_MAGNUMINTEGRATION_${_component}_MAGNUM_DEPENDENCIES SceneGraph Primitives MeshTools GL)
elseif(_component STREQUAL ImGui)
set(_MAGNUMINTEGRATION_${_component}_MAGNUM_DEPENDENCIES GL Shaders)
endif()
list(APPEND _MAGNUMINTEGRATION_DEPENDENCIES ${_MAGNUMINTEGRATION_${_component}_MAGNUM_DEPENDENCIES})
list(APPEND _MAGNUMINTEGRATION_OPTIONAL_DEPENDENCIES ${_MAGNUMINTEGRATION_${_component}_MAGNUM_OPTIONAL_DEPENDENCIES})
endforeach()
find_package(Magnum REQUIRED ${_MAGNUMINTEGRATION_DEPENDENCIES})
if(_MAGNUMINTEGRATION_OPTIONAL_DEPENDENCIES)
find_package(Magnum OPTIONAL_COMPONENTS ${_MAGNUMINTEGRATION_OPTIONAL_DEPENDENCIES})
endif()
# Global integration include dir
find_path(MAGNUMINTEGRATION_INCLUDE_DIR Magnum
HINTS ${MAGNUM_INCLUDE_DIR})
mark_as_advanced(MAGNUMINTEGRATION_INCLUDE_DIR)
# Component distinction (listing them explicitly to avoid mistakes with finding
# components from other repositories)
set(_MAGNUMINTEGRATION_LIBRARY_COMPONENTS Bullet Dart Eigen ImGui Glm)
if(CORRADE_TARGET_WINDOWS)
list(APPEND _MAGNUMINTEGRATION_LIBRARY_COMPONENTS Ovr)
endif()
set(_MAGNUMINTEGRATION_HEADER_ONLY_COMPONENTS Eigen)
# Nothing is enabled by default right now
set(_MAGNUMINTEGRATION_IMPLICITLY_ENABLED_COMPONENTS )
# Inter-component dependencies (none yet)
# set(_MAGNUMINTEGRATION_Component_DEPENDENCIES Dependency)
# Ensure that all inter-component dependencies are specified as well
set(_MAGNUMINTEGRATION_ADDITIONAL_COMPONENTS )
foreach(_component ${MagnumIntegration_FIND_COMPONENTS})
# Mark the dependencies as required if the component is also required
if(MagnumIntegration_FIND_REQUIRED_${_component})
foreach(_dependency ${_MAGNUMINTEGRATION_${_component}_DEPENDENCIES})
set(MagnumIntegration_FIND_REQUIRED_${_dependency} TRUE)
endforeach()
endif()
list(APPEND _MAGNUMINTEGRATION_ADDITIONAL_COMPONENTS ${_MAGNUMINTEGRATION_${_component}_DEPENDENCIES})
endforeach()
# Join the lists, remove duplicate components
set(_MAGNUMINTEGRATION_ORIGINAL_FIND_COMPONENTS ${MagnumIntegration_FIND_COMPONENTS})
if(_MAGNUMINTEGRATION_ADDITIONAL_COMPONENTS)
list(INSERT MagnumIntegration_FIND_COMPONENTS 0 ${_MAGNUMINTEGRATION_ADDITIONAL_COMPONENTS})
endif()
if(MagnumIntegration_FIND_COMPONENTS)
list(REMOVE_DUPLICATES MagnumIntegration_FIND_COMPONENTS)
endif()
# Find all components
foreach(_component ${MagnumIntegration_FIND_COMPONENTS})
string(TOUPPER ${_component} _COMPONENT)
# Create imported target in case the library is found. If the project is
# added as subproject to CMake, the target already exists and all the
# required setup is already done from the build tree.
if(TARGET MagnumIntegration::${_component})
set(MagnumIntegration_${_component}_FOUND TRUE)
else()
# Library components
if(_component IN_LIST _MAGNUMINTEGRATION_LIBRARY_COMPONENTS AND NOT _component IN_LIST _MAGNUMINTEGRATION_HEADER_ONLY_COMPONENTS)
add_library(MagnumIntegration::${_component} UNKNOWN IMPORTED)
# Try to find both debug and release version
find_library(MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_DEBUG Magnum${_component}Integration-d)
find_library(MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_RELEASE Magnum${_component}Integration)
mark_as_advanced(MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_DEBUG
MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_RELEASE)
if(MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_RELEASE)
set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY
IMPORTED_CONFIGURATIONS RELEASE)
set_property(TARGET MagnumIntegration::${_component} PROPERTY
IMPORTED_LOCATION_RELEASE ${MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_RELEASE})
endif()
if(MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_DEBUG)
set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY
IMPORTED_CONFIGURATIONS DEBUG)
set_property(TARGET MagnumIntegration::${_component} PROPERTY
IMPORTED_LOCATION_DEBUG ${MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_DEBUG})
endif()
# Header-only library components
elseif(_component IN_LIST _MAGNUMINTEGRATION_HEADER_ONLY_COMPONENTS)
add_library(MagnumIntegration::${_component} INTERFACE IMPORTED)
# Something unknown, skip. FPHSA will take care of handling this below.
else()
continue()
endif()
# Bullet integration library
if(_component STREQUAL Bullet)
# On Emscripten, Bullet could be taken from ports. If that's the
# case, propagate proper compiler flag.
if(CORRADE_TARGET_EMSCRIPTEN)
find_file(_MAGNUMINTEGRATION_${_COMPONENT}_CONFIGURE_FILE configure.h
HINTS ${MAGNUMINTEGRATION_INCLUDE_DIR}/Magnum/${_component}Integration)
file(READ ${_MAGNUMINTEGRATION_${_COMPONENT}_CONFIGURE_FILE} _magnum${_component}IntegrationConfigure)
string(FIND "${_magnum${_component}IntegrationConfigure}" "#define MAGNUM_USE_EMSCRIPTEN_PORTS_BULLET" _magnum${_component}Integration_USE_EMSCRIPTEN_PORTS_BULLET)
if(NOT _magnum${_component}Integration_USE_EMSCRIPTEN_PORTS_BULLET EQUAL -1)
set(MAGNUM_USE_EMSCRIPTEN_PORTS_BULLET 1)
endif()
endif()
if(MAGNUM_USE_EMSCRIPTEN_PORTS_BULLET)
if(CMAKE_VERSION VERSION_LESS 3.13)
message(FATAL_ERROR "BulletIntegration was compiled against emscripten-ports version but linking to it requires CMake 3.13 at least")
endif()
set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY
INTERFACE_COMPILE_OPTIONS "SHELL:-s USE_BULLET=1")
set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY
INTERFACE_LINK_OPTIONS "SHELL:-s USE_BULLET=1")
else()
find_package(Bullet)
set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY
INTERFACE_LINK_LIBRARIES Bullet::LinearMath)
endif()
set(_MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_PATH_NAMES MotionState.h)
# Eigen integration library
elseif(_component STREQUAL Eigen)
find_package(Eigen3)
# We could drop this once we can use at least 3.3.1 (Ubuntu 16.04
# has only 3.3 beta, which doesn't have this target yet), however
# for Travis and AppVeyor we're using FindEigen3.cmake from the
# downloaded sources (because the Eigen3Config.cmake, which
# produces the actual targets, is not there -- only
# Eigen3Config.cmake.in). See the YML files for an extended rant.
# Also, FindEigen3 only defines EIGEN3_INCLUDE_DIR, not even
# EIGEN3_INCLUDE_DIRS, so be extra careful.
# http://eigen.tuxfamily.org/index.php?title=ChangeLog#Eigen_3.3.1
set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY
INTERFACE_INCLUDE_DIRECTORIES ${EIGEN3_INCLUDE_DIR})
set(_MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_PATH_NAMES Integration.h)
# ImGui integration library
elseif(_component STREQUAL ImGui)
find_package(ImGui)
set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY
INTERFACE_LINK_LIBRARIES ImGui::ImGui)
set(_MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_PATH_NAMES Integration.h)
# GLM integration library
elseif(_component STREQUAL Glm)
find_package(GLM)
set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY
INTERFACE_LINK_LIBRARIES GLM::GLM)
set(_MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_PATH_NAMES Integration.h)
# Dart integration library
elseif(_component STREQUAL Dart)
find_package(DART 6.0.0 CONFIG REQUIRED)
set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY
INTERFACE_LINK_LIBRARIES dart)
set(_MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_PATH_NAMES ConvertShapeNode.h)
# Oculus SDK integration library
elseif(_component STREQUAL Ovr)
find_package(OVR)
set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY
INTERFACE_LINK_LIBRARIES OVR::OVR)
set(_MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_PATH_NAMES OvrIntegration.h)
endif()
# Find library includes
if(_component IN_LIST _MAGNUMINTEGRATION_LIBRARY_COMPONENTS)
find_path(_MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_DIR
NAMES ${_MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_PATH_NAMES}
HINTS ${MAGNUMINTEGRATION_INCLUDE_DIR}/Magnum/${_component}Integration)
mark_as_advanced(_MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_DIR)
endif()
if(_component IN_LIST _MAGNUMINTEGRATION_LIBRARY_COMPONENTS)
# Link to core Magnum library, add other Magnum required and
# optional dependencies
set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY
INTERFACE_LINK_LIBRARIES Magnum::Magnum)
foreach(_dependency ${_MAGNUMINTEGRATION_${_component}_MAGNUM_DEPENDENCIES})
set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY
INTERFACE_LINK_LIBRARIES Magnum::${_dependency})
endforeach()
foreach(_dependency ${_MAGNUMINTEGRATION_${_component}_MAGNUM_OPTIONAL_DEPENDENCIES})
if(Magnum_${_dependency}_FOUND)
set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY
INTERFACE_LINK_LIBRARIES Magnum::${_dependency})
endif()
endforeach()
# Add inter-project dependencies
foreach(_dependency ${_MAGNUMINTEGRATION_${_component}_DEPENDENCIES})
set_property(TARGET MagnumIntegration::${_component} APPEND PROPERTY
INTERFACE_LINK_LIBRARIES MagnumIntegration::${_dependency})
endforeach()
endif()
# Decide if the library was found
if(_component IN_LIST _MAGNUMINTEGRATION_LIBRARY_COMPONENTS AND _MAGNUMINTEGRATION_${_COMPONENT}_INCLUDE_DIR AND (_component IN_LIST _MAGNUMINTEGRATION_HEADER_ONLY_COMPONENTS OR MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_DEBUG OR MAGNUMINTEGRATION_${_COMPONENT}_LIBRARY_RELEASE))
set(MagnumIntegration_${_component}_FOUND TRUE)
else()
set(MagnumIntegration_${_component}_FOUND FALSE)
endif()
endif()
endforeach()
# For CMake 3.16+ with REASON_FAILURE_MESSAGE, provide additional potentially
# useful info about the failed components.
if(NOT CMAKE_VERSION VERSION_LESS 3.16)
set(_MAGNUMINTEGRATION_REASON_FAILURE_MESSAGE )
# Go only through the originally specified find_package() components, not
# the dependencies added by us afterwards
foreach(_component ${_MAGNUMINTEGRATION_ORIGINAL_FIND_COMPONENTS})
if(MagnumIntegration_${_component}_FOUND)
continue()
endif()
# If it's not known at all, tell the user -- it might be a new library
# and an old Find module, or something platform-specific.
if(NOT _component IN_LIST _MAGNUMINTEGRATION_LIBRARY_COMPONENTS)
list(APPEND _MAGNUMINTEGRATION_REASON_FAILURE_MESSAGE "${_component} is not a known component on this platform.")
# Otherwise, if it's not among implicitly built components, hint that
# the user may need to enable it
# TODO: currently, the _FOUND variable doesn't reflect if dependencies
# were found. When it will, this needs to be updated to avoid
# 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.")
# Otherwise we have no idea. Better be silent than to print something
# misleading.
else()
endif()
endforeach()
string(REPLACE ";" " " _MAGNUMINTEGRATION_REASON_FAILURE_MESSAGE "${_MAGNUMINTEGRATION_REASON_FAILURE_MESSAGE}")
set(_MAGNUMINTEGRATION_REASON_FAILURE_MESSAGE REASON_FAILURE_MESSAGE "${_MAGNUMINTEGRATION_REASON_FAILURE_MESSAGE}")
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MagnumIntegration
REQUIRED_VARS MAGNUMINTEGRATION_INCLUDE_DIR
HANDLE_COMPONENTS
${_MAGNUMINTEGRATION_REASON_FAILURE_MESSAGE})

285
modules/FindSDL2.cmake Normal file
View File

@ -0,0 +1,285 @@
#.rst:
# Find SDL2
# ---------
#
# Finds the SDL2 library. This module defines:
#
# SDL2_FOUND - True if SDL2 library is found
# SDL2::SDL2 - SDL2 imported target
#
# Additionally these variables are defined for internal usage:
#
# SDL2_LIBRARY_DEBUG - SDL2 debug library, if found
# SDL2_LIBRARY_RELEASE - SDL2 release library, if found
# SDL2_DLL_DEBUG - SDL2 debug DLL on Windows, if found
# SDL2_DLL_RELEASE - SDL2 release DLL on Windows, if found
# SDL2_INCLUDE_DIR - Root include dir
#
#
# This file is part of Magnum.
#
# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
# 2020, 2021 Vladimír Vondruš <mosra@centrum.cz>
# Copyright © 2018 Jonathan Hale <squareys@googlemail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
# If we have a CMake subproject, use the targets directly. I'd prefer the
# static variant, however SDL2 defines its own SDL2::SDL2 alias for only the
# dynamic variant since https://github.com/libsdl-org/SDL/pull/4074 and so I'm
# forced to use that, if available.
if(TARGET SDL2)
# In case we don't have https://github.com/libsdl-org/SDL/pull/4074 yet,
# do the alias ourselves.
if(NOT TARGET SDL2::SDL2)
# Aliases of (global) targets are only supported in CMake 3.11, so we
# work around it by this. This is easier than fetching all possible
# properties (which are impossible to track of) and then attempting to
# rebuild them into a new target.
add_library(SDL2::SDL2 INTERFACE IMPORTED)
set_target_properties(SDL2::SDL2 PROPERTIES INTERFACE_LINK_LIBRARIES SDL2)
endif()
# Just to make FPHSA print some meaningful location, nothing else. Not
# using the INTERFACE_INCLUDE_DIRECTORIES as that contains
# $<BUILD_INTERFACE and looks ugly in the output. Funnily enough, the
# BUILD_INTERFACE thing works here without having to override it with
# custom-found paths like I do in FindAssimp and elsewhere. Needs further
# investigation.
get_target_property(_SDL2_SOURCE_DIR SDL2 SOURCE_DIR)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args("SDL2" DEFAULT_MSG _SDL2_SOURCE_DIR)
if(CORRADE_TARGET_WINDOWS)
# .dll is in LOCATION, .lib is in IMPLIB. Yay, useful!
get_target_property(SDL2_DLL_DEBUG SDL2 IMPORTED_LOCATION_DEBUG)
get_target_property(SDL2_DLL_RELEASE SDL2 IMPORTED_LOCATION_RELEASE)
endif()
return()
# The static build is a separate target for some reason. I wonder HOW that
# makes more sense than just having a build-time option for static/shared and
# use the same name for both. Are all depending projects supposed to branch on
# it like this?!
elseif(TARGET SDL2-static)
# The target should not be defined by SDL itself. If it is, that's from us.
if(NOT TARGET SDL2::SDL2)
# Aliases of (global) targets are only supported in CMake 3.11, so we
# work around it by this. This is easier than fetching all possible
# properties (which are impossible to track of) and then attempting to
# rebuild them into a new target.
add_library(SDL2::SDL2 INTERFACE IMPORTED)
set_target_properties(SDL2::SDL2 PROPERTIES INTERFACE_LINK_LIBRARIES SDL2-static)
endif()
# Just to make FPHSA print some meaningful location, nothing else
get_target_property(_SDL2_SOURCE_DIR SDL2-static SOURCE_DIR)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args("SDL2" DEFAULT_MSG _SDL2_SOURCE_DIR)
return()
endif()
# In Emscripten SDL is linked automatically, thus no need to find the library.
# Also the includes are in SDL subdirectory, not SDL2.
if(CORRADE_TARGET_EMSCRIPTEN)
set(_SDL2_PATH_SUFFIXES SDL)
else()
set(_SDL2_PATH_SUFFIXES SDL2)
if(WIN32)
# Precompiled libraries for MSVC are in x86/x64 subdirectories
if(MSVC)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(_SDL2_LIBRARY_PATH_SUFFIX lib/x64)
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(_SDL2_LIBRARY_PATH_SUFFIX lib/x86)
endif()
# Both includes and libraries for MinGW are in some directory deep
# inside. There's also a CMake config file but it has HARDCODED path
# to /opt/local/i686-w64-mingw32, which doesn't make ANY SENSE,
# especially on Windows.
elseif(MINGW)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(_SDL2_LIBRARY_PATH_SUFFIX x86_64-w64-mingw32/lib)
set(_SDL2_RUNTIME_PATH_SUFFIX x86_64-w64-mingw32/bin)
list(APPEND _SDL2_PATH_SUFFIXES x86_64-w64-mingw32/include/SDL2)
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(_SDL2_LIBRARY_PATH_SUFFIX i686-w64-mingw32/lib)
set(_SDL2_RUNTIME_PATH_SUFFIX i686-w64-mingw32/lib)
list(APPEND _SDL2_PATH_SUFFIXES i686-w64-mingw32/include/SDL2)
endif()
else()
message(FATAL_ERROR "Unsupported compiler")
endif()
endif()
find_library(SDL2_LIBRARY_RELEASE
# Compiling SDL2 from scratch on macOS creates dead libSDL2.so symlink
# 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
PATH_SUFFIXES ${_SDL2_LIBRARY_PATH_SUFFIX})
find_library(SDL2_LIBRARY_DEBUG
NAMES SDL2d
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
# select_library_configurations() below.
set(SDL2_LIBRARY_NEEDED SDL2_LIBRARY)
endif()
include(SelectLibraryConfigurations)
select_library_configurations(SDL2)
# Include dir
find_path(SDL2_INCLUDE_DIR
# We must search file which is present only in SDL2 and not in SDL1.
# Apparently when both SDL.h and SDL_scancode.h are specified, CMake is
# happy enough that it found SDL.h and doesn't bother about the other.
#
# On macOS, where the includes are not in SDL2/SDL.h form (which would
# solve this issue), but rather SDL2.framework/Headers/SDL.h, CMake might
# find SDL.framework/Headers/SDL.h if SDL1 is installed, which is wrong.
NAMES SDL_scancode.h
PATH_SUFFIXES ${_SDL2_PATH_SUFFIXES})
# DLL on Windows
if(CORRADE_TARGET_WINDOWS)
find_file(SDL2_DLL_RELEASE
NAMES SDL2.dll
PATH_SUFFIXES ${_SDL2_RUNTIME_PATH_SUFFIX} ${_SDL2_LIBRARY_PATH_SUFFIX})
find_file(SDL2_DLL_DEBUG
NAMES SDL2d.dll # not sure?
PATH_SUFFIXES ${_SDL2_RUNTIME_PATH_SUFFIX} ${_SDL2_LIBRARY_PATH_SUFFIX})
endif()
# (Static) macOS / iOS dependencies
if(CORRADE_TARGET_APPLE AND SDL2_LIBRARY MATCHES "${CMAKE_STATIC_LIBRARY_SUFFIX}$")
if(CORRADE_TARGET_IOS)
set(_SDL2_FRAMEWORKS
AudioToolbox
AVFoundation
CoreGraphics
CoreMotion
Foundation
GameController
Metal # needed since 2.0.8
QuartzCore
UIKit)
else()
# Those are needed when building SDL statically using its CMake project
set(_SDL2_FRAMEWORKS
iconv # should be in the system
AudioToolbox
AVFoundation
Carbon
Cocoa
CoreAudio
CoreVideo
ForceFeedback
Foundation
IOKit)
endif()
set(_SDL2_FRAMEWORK_LIBRARIES )
foreach(framework ${_SDL2_FRAMEWORKS})
find_library(_SDL2_${framework}_LIBRARY ${framework})
mark_as_advanced(_SDL2_${framework}_LIBRARY)
list(APPEND _SDL2_FRAMEWORK_LIBRARIES ${_SDL2_${framework}_LIBRARY})
list(APPEND _SDL2_FRAMEWORK_LIBRARY_NAMES _SDL2_${framework}_LIBRARY)
endforeach()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args("SDL2" DEFAULT_MSG
${SDL2_LIBRARY_NEEDED}
${_SDL2_FRAMEWORK_LIBRARY_NAMES}
SDL2_INCLUDE_DIR)
if(NOT TARGET SDL2::SDL2)
if(SDL2_LIBRARY_NEEDED)
add_library(SDL2::SDL2 UNKNOWN IMPORTED)
# Work around BUGGY framework support on macOS
# https://cmake.org/Bug/view.php?id=14105
if(CORRADE_TARGET_APPLE AND SDL2_LIBRARY_RELEASE MATCHES "\\.framework$")
set_property(TARGET SDL2::SDL2 APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
set_property(TARGET SDL2::SDL2 PROPERTY IMPORTED_LOCATION_RELEASE ${SDL2_LIBRARY_RELEASE}/SDL2)
else()
if(SDL2_LIBRARY_RELEASE)
set_property(TARGET SDL2::SDL2 APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
set_property(TARGET SDL2::SDL2 PROPERTY IMPORTED_LOCATION_RELEASE ${SDL2_LIBRARY_RELEASE})
endif()
if(SDL2_LIBRARY_DEBUG)
set_property(TARGET SDL2::SDL2 APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
set_property(TARGET SDL2::SDL2 PROPERTY IMPORTED_LOCATION_DEBUG ${SDL2_LIBRARY_DEBUG})
endif()
endif()
# Link additional `dl` and `pthread` libraries required by a static
# build of SDL on Unixy platforms (except Apple, where it is most
# probably some frameworks instead)
if(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE AND (SDL2_LIBRARY_DEBUG MATCHES "${CMAKE_STATIC_LIBRARY_SUFFIX}$" OR SDL2_LIBRARY_RELEASE MATCHES "${CMAKE_STATIC_LIBRARY_SUFFIX}$"))
find_package(Threads REQUIRED)
set_property(TARGET SDL2::SDL2 APPEND PROPERTY
INTERFACE_LINK_LIBRARIES Threads::Threads ${CMAKE_DL_LIBS})
endif()
# Link frameworks on macOS / iOS if we have a static SDL
if(CORRADE_TARGET_APPLE AND SDL2_LIBRARY MATCHES ".*libSDL2.a$")
set_property(TARGET SDL2::SDL2 APPEND PROPERTY
INTERFACE_LINK_LIBRARIES ${_SDL2_FRAMEWORK_LIBRARIES})
endif()
# Windows dependencies for a static library. Unfortunately there's no
# easy way to figure out if a *.lib is static or dynamic, so we're
# adding only if a DLL is not found.
if(CORRADE_TARGET_WINDOWS AND NOT CORRADE_TARGET_WINDOWS_RT AND NOT SDL2_DLL_RELEASE AND NOT SDL2_DLL_DEBUG)
set_property(TARGET SDL2::SDL2 APPEND PROPERTY INTERFACE_LINK_LIBRARIES
# https://github.com/SDL-mirror/SDL/blob/release-2.0.10/CMakeLists.txt#L1338
user32 gdi32 winmm imm32 ole32 oleaut32 version uuid advapi32 setupapi shell32
# https://github.com/SDL-mirror/SDL/blob/release-2.0.10/CMakeLists.txt#L1384
dinput8)
# https://github.com/SDL-mirror/SDL/blob/release-2.0.10/CMakeLists.txt#L1422
# additionally has dxerr for MSVC if DirectX SDK is not used, but
# according to https://walbourn.github.io/wheres-dxerr-lib/ this
# thing is long deprecated.
if(MINGW)
set_property(TARGET SDL2::SDL2 APPEND PROPERTY INTERFACE_LINK_LIBRARIES
# https://github.com/SDL-mirror/SDL/blob/release-2.0.10/CMakeLists.txt#L1386
dxerr8
# https://github.com/SDL-mirror/SDL/blob/release-2.0.10/CMakeLists.txt#L1388
mingw32)
endif()
endif()
else()
add_library(SDL2::SDL2 INTERFACE IMPORTED)
endif()
set_property(TARGET SDL2::SDL2 PROPERTY
INTERFACE_INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR})
endif()
mark_as_advanced(SDL2_INCLUDE_DIR)

14
src/Application.manifest Normal file
View File

@ -0,0 +1,14 @@
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="amd64"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</assembly>

72
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,72 @@
# MassBuilderSaveTool
# Copyright (C) 2021 Guillaume Jacquemin
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
find_package(Corrade REQUIRED Main Containers Utility Interconnect)
find_package(Magnum REQUIRED GL Sdl2Application SceneGraph MeshTools Primitives)
find_package(MagnumIntegration REQUIRED ImGui)
set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON)
corrade_add_resource(Assets assets.conf)
add_executable(MassBuilderSaveTool WIN32
main.cpp
SaveTool/SaveTool.h
SaveTool/SaveTool.cpp
SaveTool/SaveTool_drawAbout.cpp
SaveTool/SaveTool_drawMainMenu.cpp
SaveTool/SaveTool_MainManager.cpp
SaveTool/SaveTool_ProfileManager.cpp
MassBuilderManager/MassBuilderManager.h
MassBuilderManager/MassBuilderManager.cpp
ProfileManager/ProfileManager.h
ProfileManager/ProfileManager.cpp
Profile/Profile.h
Profile/Profile.cpp
MassManager/MassManager.h
MassManager/MassManager.cpp
Mass/Mass.h
Mass/Mass.cpp
Maps/LastMissionId.h
Maps/StoryProgress.h
FontAwesome/IconsFontAwesome5.h
FontAwesome/IconsFontAwesome5Brands.h
resource.rc
${Assets})
if(CMAKE_BUILD_TYPE STREQUAL Debug)
add_compile_definitions(MANAGER_DEBUG_BUILD)
endif()
target_link_options(MassBuilderSaveTool PRIVATE -static -static-libgcc -static-libstdc++)
target_link_libraries(MassBuilderSaveTool PRIVATE
Corrade::Containers
Corrade::Utility
Corrade::Interconnect
Corrade::Main
Magnum::Magnum
Magnum::GL
Magnum::Sdl2Application
MagnumIntegration::ImGui
efsw
zip
imm32
wtsapi32)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,467 @@
// Generated by https://github.com/juliettef/IconFontCppHeaders script GenerateIconFontCppHeaders.py for languages C and C++
// from https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/metadata/icons.yml
// for use with https://github.com/FortAwesome/Font-Awesome/blob/master/webfonts/fa-brands-400.ttf
#pragma once
#define FONT_ICON_FILE_NAME_FAB "fa-brands-400.ttf"
#define ICON_MIN_FAB 0xe007
#define ICON_MAX_FAB 0xf8e8
#define ICON_FA_500PX "\xef\x89\xae" // U+f26e
#define ICON_FA_ACCESSIBLE_ICON "\xef\x8d\xa8" // U+f368
#define ICON_FA_ACCUSOFT "\xef\x8d\xa9" // U+f369
#define ICON_FA_ACQUISITIONS_INCORPORATED "\xef\x9a\xaf" // U+f6af
#define ICON_FA_ADN "\xef\x85\xb0" // U+f170
#define ICON_FA_ADVERSAL "\xef\x8d\xaa" // U+f36a
#define ICON_FA_AFFILIATETHEME "\xef\x8d\xab" // U+f36b
#define ICON_FA_AIRBNB "\xef\xa0\xb4" // U+f834
#define ICON_FA_ALGOLIA "\xef\x8d\xac" // U+f36c
#define ICON_FA_ALIPAY "\xef\x99\x82" // U+f642
#define ICON_FA_AMAZON "\xef\x89\xb0" // U+f270
#define ICON_FA_AMAZON_PAY "\xef\x90\xac" // U+f42c
#define ICON_FA_AMILIA "\xef\x8d\xad" // U+f36d
#define ICON_FA_ANDROID "\xef\x85\xbb" // U+f17b
#define ICON_FA_ANGELLIST "\xef\x88\x89" // U+f209
#define ICON_FA_ANGRYCREATIVE "\xef\x8d\xae" // U+f36e
#define ICON_FA_ANGULAR "\xef\x90\xa0" // U+f420
#define ICON_FA_APP_STORE "\xef\x8d\xaf" // U+f36f
#define ICON_FA_APP_STORE_IOS "\xef\x8d\xb0" // U+f370
#define ICON_FA_APPER "\xef\x8d\xb1" // U+f371
#define ICON_FA_APPLE "\xef\x85\xb9" // U+f179
#define ICON_FA_APPLE_PAY "\xef\x90\x95" // U+f415
#define ICON_FA_ARTSTATION "\xef\x9d\xba" // U+f77a
#define ICON_FA_ASYMMETRIK "\xef\x8d\xb2" // U+f372
#define ICON_FA_ATLASSIAN "\xef\x9d\xbb" // U+f77b
#define ICON_FA_AUDIBLE "\xef\x8d\xb3" // U+f373
#define ICON_FA_AUTOPREFIXER "\xef\x90\x9c" // U+f41c
#define ICON_FA_AVIANEX "\xef\x8d\xb4" // U+f374
#define ICON_FA_AVIATO "\xef\x90\xa1" // U+f421
#define ICON_FA_AWS "\xef\x8d\xb5" // U+f375
#define ICON_FA_BANDCAMP "\xef\x8b\x95" // U+f2d5
#define ICON_FA_BATTLE_NET "\xef\xa0\xb5" // U+f835
#define ICON_FA_BEHANCE "\xef\x86\xb4" // U+f1b4
#define ICON_FA_BEHANCE_SQUARE "\xef\x86\xb5" // U+f1b5
#define ICON_FA_BIMOBJECT "\xef\x8d\xb8" // U+f378
#define ICON_FA_BITBUCKET "\xef\x85\xb1" // U+f171
#define ICON_FA_BITCOIN "\xef\x8d\xb9" // U+f379
#define ICON_FA_BITY "\xef\x8d\xba" // U+f37a
#define ICON_FA_BLACK_TIE "\xef\x89\xbe" // U+f27e
#define ICON_FA_BLACKBERRY "\xef\x8d\xbb" // U+f37b
#define ICON_FA_BLOGGER "\xef\x8d\xbc" // U+f37c
#define ICON_FA_BLOGGER_B "\xef\x8d\xbd" // U+f37d
#define ICON_FA_BLUETOOTH "\xef\x8a\x93" // U+f293
#define ICON_FA_BLUETOOTH_B "\xef\x8a\x94" // U+f294
#define ICON_FA_BOOTSTRAP "\xef\xa0\xb6" // U+f836
#define ICON_FA_BTC "\xef\x85\x9a" // U+f15a
#define ICON_FA_BUFFER "\xef\xa0\xb7" // U+f837
#define ICON_FA_BUROMOBELEXPERTE "\xef\x8d\xbf" // U+f37f
#define ICON_FA_BUY_N_LARGE "\xef\xa2\xa6" // U+f8a6
#define ICON_FA_BUYSELLADS "\xef\x88\x8d" // U+f20d
#define ICON_FA_CANADIAN_MAPLE_LEAF "\xef\x9e\x85" // U+f785
#define ICON_FA_CC_AMAZON_PAY "\xef\x90\xad" // U+f42d
#define ICON_FA_CC_AMEX "\xef\x87\xb3" // U+f1f3
#define ICON_FA_CC_APPLE_PAY "\xef\x90\x96" // U+f416
#define ICON_FA_CC_DINERS_CLUB "\xef\x89\x8c" // U+f24c
#define ICON_FA_CC_DISCOVER "\xef\x87\xb2" // U+f1f2
#define ICON_FA_CC_JCB "\xef\x89\x8b" // U+f24b
#define ICON_FA_CC_MASTERCARD "\xef\x87\xb1" // U+f1f1
#define ICON_FA_CC_PAYPAL "\xef\x87\xb4" // U+f1f4
#define ICON_FA_CC_STRIPE "\xef\x87\xb5" // U+f1f5
#define ICON_FA_CC_VISA "\xef\x87\xb0" // U+f1f0
#define ICON_FA_CENTERCODE "\xef\x8e\x80" // U+f380
#define ICON_FA_CENTOS "\xef\x9e\x89" // U+f789
#define ICON_FA_CHROME "\xef\x89\xa8" // U+f268
#define ICON_FA_CHROMECAST "\xef\xa0\xb8" // U+f838
#define ICON_FA_CLOUDFLARE "\xee\x81\xbd" // U+e07d
#define ICON_FA_CLOUDSCALE "\xef\x8e\x83" // U+f383
#define ICON_FA_CLOUDSMITH "\xef\x8e\x84" // U+f384
#define ICON_FA_CLOUDVERSIFY "\xef\x8e\x85" // U+f385
#define ICON_FA_CODEPEN "\xef\x87\x8b" // U+f1cb
#define ICON_FA_CODIEPIE "\xef\x8a\x84" // U+f284
#define ICON_FA_CONFLUENCE "\xef\x9e\x8d" // U+f78d
#define ICON_FA_CONNECTDEVELOP "\xef\x88\x8e" // U+f20e
#define ICON_FA_CONTAO "\xef\x89\xad" // U+f26d
#define ICON_FA_COTTON_BUREAU "\xef\xa2\x9e" // U+f89e
#define ICON_FA_CPANEL "\xef\x8e\x88" // U+f388
#define ICON_FA_CREATIVE_COMMONS "\xef\x89\x9e" // U+f25e
#define ICON_FA_CREATIVE_COMMONS_BY "\xef\x93\xa7" // U+f4e7
#define ICON_FA_CREATIVE_COMMONS_NC "\xef\x93\xa8" // U+f4e8
#define ICON_FA_CREATIVE_COMMONS_NC_EU "\xef\x93\xa9" // U+f4e9
#define ICON_FA_CREATIVE_COMMONS_NC_JP "\xef\x93\xaa" // U+f4ea
#define ICON_FA_CREATIVE_COMMONS_ND "\xef\x93\xab" // U+f4eb
#define ICON_FA_CREATIVE_COMMONS_PD "\xef\x93\xac" // U+f4ec
#define ICON_FA_CREATIVE_COMMONS_PD_ALT "\xef\x93\xad" // U+f4ed
#define ICON_FA_CREATIVE_COMMONS_REMIX "\xef\x93\xae" // U+f4ee
#define ICON_FA_CREATIVE_COMMONS_SA "\xef\x93\xaf" // U+f4ef
#define ICON_FA_CREATIVE_COMMONS_SAMPLING "\xef\x93\xb0" // U+f4f0
#define ICON_FA_CREATIVE_COMMONS_SAMPLING_PLUS "\xef\x93\xb1" // U+f4f1
#define ICON_FA_CREATIVE_COMMONS_SHARE "\xef\x93\xb2" // U+f4f2
#define ICON_FA_CREATIVE_COMMONS_ZERO "\xef\x93\xb3" // U+f4f3
#define ICON_FA_CRITICAL_ROLE "\xef\x9b\x89" // U+f6c9
#define ICON_FA_CSS3 "\xef\x84\xbc" // U+f13c
#define ICON_FA_CSS3_ALT "\xef\x8e\x8b" // U+f38b
#define ICON_FA_CUTTLEFISH "\xef\x8e\x8c" // U+f38c
#define ICON_FA_D_AND_D "\xef\x8e\x8d" // U+f38d
#define ICON_FA_D_AND_D_BEYOND "\xef\x9b\x8a" // U+f6ca
#define ICON_FA_DAILYMOTION "\xee\x81\x92" // U+e052
#define ICON_FA_DASHCUBE "\xef\x88\x90" // U+f210
#define ICON_FA_DEEZER "\xee\x81\xb7" // U+e077
#define ICON_FA_DELICIOUS "\xef\x86\xa5" // U+f1a5
#define ICON_FA_DEPLOYDOG "\xef\x8e\x8e" // U+f38e
#define ICON_FA_DESKPRO "\xef\x8e\x8f" // U+f38f
#define ICON_FA_DEV "\xef\x9b\x8c" // U+f6cc
#define ICON_FA_DEVIANTART "\xef\x86\xbd" // U+f1bd
#define ICON_FA_DHL "\xef\x9e\x90" // U+f790
#define ICON_FA_DIASPORA "\xef\x9e\x91" // U+f791
#define ICON_FA_DIGG "\xef\x86\xa6" // U+f1a6
#define ICON_FA_DIGITAL_OCEAN "\xef\x8e\x91" // U+f391
#define ICON_FA_DISCORD "\xef\x8e\x92" // U+f392
#define ICON_FA_DISCOURSE "\xef\x8e\x93" // U+f393
#define ICON_FA_DOCHUB "\xef\x8e\x94" // U+f394
#define ICON_FA_DOCKER "\xef\x8e\x95" // U+f395
#define ICON_FA_DRAFT2DIGITAL "\xef\x8e\x96" // U+f396
#define ICON_FA_DRIBBBLE "\xef\x85\xbd" // U+f17d
#define ICON_FA_DRIBBBLE_SQUARE "\xef\x8e\x97" // U+f397
#define ICON_FA_DROPBOX "\xef\x85\xab" // U+f16b
#define ICON_FA_DRUPAL "\xef\x86\xa9" // U+f1a9
#define ICON_FA_DYALOG "\xef\x8e\x99" // U+f399
#define ICON_FA_EARLYBIRDS "\xef\x8e\x9a" // U+f39a
#define ICON_FA_EBAY "\xef\x93\xb4" // U+f4f4
#define ICON_FA_EDGE "\xef\x8a\x82" // U+f282
#define ICON_FA_EDGE_LEGACY "\xee\x81\xb8" // U+e078
#define ICON_FA_ELEMENTOR "\xef\x90\xb0" // U+f430
#define ICON_FA_ELLO "\xef\x97\xb1" // U+f5f1
#define ICON_FA_EMBER "\xef\x90\xa3" // U+f423
#define ICON_FA_EMPIRE "\xef\x87\x91" // U+f1d1
#define ICON_FA_ENVIRA "\xef\x8a\x99" // U+f299
#define ICON_FA_ERLANG "\xef\x8e\x9d" // U+f39d
#define ICON_FA_ETHEREUM "\xef\x90\xae" // U+f42e
#define ICON_FA_ETSY "\xef\x8b\x97" // U+f2d7
#define ICON_FA_EVERNOTE "\xef\xa0\xb9" // U+f839
#define ICON_FA_EXPEDITEDSSL "\xef\x88\xbe" // U+f23e
#define ICON_FA_FACEBOOK "\xef\x82\x9a" // U+f09a
#define ICON_FA_FACEBOOK_F "\xef\x8e\x9e" // U+f39e
#define ICON_FA_FACEBOOK_MESSENGER "\xef\x8e\x9f" // U+f39f
#define ICON_FA_FACEBOOK_SQUARE "\xef\x82\x82" // U+f082
#define ICON_FA_FANTASY_FLIGHT_GAMES "\xef\x9b\x9c" // U+f6dc
#define ICON_FA_FEDEX "\xef\x9e\x97" // U+f797
#define ICON_FA_FEDORA "\xef\x9e\x98" // U+f798
#define ICON_FA_FIGMA "\xef\x9e\x99" // U+f799
#define ICON_FA_FIREFOX "\xef\x89\xa9" // U+f269
#define ICON_FA_FIREFOX_BROWSER "\xee\x80\x87" // U+e007
#define ICON_FA_FIRST_ORDER "\xef\x8a\xb0" // U+f2b0
#define ICON_FA_FIRST_ORDER_ALT "\xef\x94\x8a" // U+f50a
#define ICON_FA_FIRSTDRAFT "\xef\x8e\xa1" // U+f3a1
#define ICON_FA_FLICKR "\xef\x85\xae" // U+f16e
#define ICON_FA_FLIPBOARD "\xef\x91\x8d" // U+f44d
#define ICON_FA_FLY "\xef\x90\x97" // U+f417
#define ICON_FA_FONT_AWESOME "\xef\x8a\xb4" // U+f2b4
#define ICON_FA_FONT_AWESOME_ALT "\xef\x8d\x9c" // U+f35c
#define ICON_FA_FONT_AWESOME_FLAG "\xef\x90\xa5" // U+f425
#define ICON_FA_FONT_AWESOME_LOGO_FULL "\xef\x93\xa6" // U+f4e6
#define ICON_FA_FONTICONS "\xef\x8a\x80" // U+f280
#define ICON_FA_FONTICONS_FI "\xef\x8e\xa2" // U+f3a2
#define ICON_FA_FORT_AWESOME "\xef\x8a\x86" // U+f286
#define ICON_FA_FORT_AWESOME_ALT "\xef\x8e\xa3" // U+f3a3
#define ICON_FA_FORUMBEE "\xef\x88\x91" // U+f211
#define ICON_FA_FOURSQUARE "\xef\x86\x80" // U+f180
#define ICON_FA_FREE_CODE_CAMP "\xef\x8b\x85" // U+f2c5
#define ICON_FA_FREEBSD "\xef\x8e\xa4" // U+f3a4
#define ICON_FA_FULCRUM "\xef\x94\x8b" // U+f50b
#define ICON_FA_GALACTIC_REPUBLIC "\xef\x94\x8c" // U+f50c
#define ICON_FA_GALACTIC_SENATE "\xef\x94\x8d" // U+f50d
#define ICON_FA_GET_POCKET "\xef\x89\xa5" // U+f265
#define ICON_FA_GG "\xef\x89\xa0" // U+f260
#define ICON_FA_GG_CIRCLE "\xef\x89\xa1" // U+f261
#define ICON_FA_GIT "\xef\x87\x93" // U+f1d3
#define ICON_FA_GIT_ALT "\xef\xa1\x81" // U+f841
#define ICON_FA_GIT_SQUARE "\xef\x87\x92" // U+f1d2
#define ICON_FA_GITHUB "\xef\x82\x9b" // U+f09b
#define ICON_FA_GITHUB_ALT "\xef\x84\x93" // U+f113
#define ICON_FA_GITHUB_SQUARE "\xef\x82\x92" // U+f092
#define ICON_FA_GITKRAKEN "\xef\x8e\xa6" // U+f3a6
#define ICON_FA_GITLAB "\xef\x8a\x96" // U+f296
#define ICON_FA_GITTER "\xef\x90\xa6" // U+f426
#define ICON_FA_GLIDE "\xef\x8a\xa5" // U+f2a5
#define ICON_FA_GLIDE_G "\xef\x8a\xa6" // U+f2a6
#define ICON_FA_GOFORE "\xef\x8e\xa7" // U+f3a7
#define ICON_FA_GOODREADS "\xef\x8e\xa8" // U+f3a8
#define ICON_FA_GOODREADS_G "\xef\x8e\xa9" // U+f3a9
#define ICON_FA_GOOGLE "\xef\x86\xa0" // U+f1a0
#define ICON_FA_GOOGLE_DRIVE "\xef\x8e\xaa" // U+f3aa
#define ICON_FA_GOOGLE_PAY "\xee\x81\xb9" // U+e079
#define ICON_FA_GOOGLE_PLAY "\xef\x8e\xab" // U+f3ab
#define ICON_FA_GOOGLE_PLUS "\xef\x8a\xb3" // U+f2b3
#define ICON_FA_GOOGLE_PLUS_G "\xef\x83\x95" // U+f0d5
#define ICON_FA_GOOGLE_PLUS_SQUARE "\xef\x83\x94" // U+f0d4
#define ICON_FA_GOOGLE_WALLET "\xef\x87\xae" // U+f1ee
#define ICON_FA_GRATIPAY "\xef\x86\x84" // U+f184
#define ICON_FA_GRAV "\xef\x8b\x96" // U+f2d6
#define ICON_FA_GRIPFIRE "\xef\x8e\xac" // U+f3ac
#define ICON_FA_GRUNT "\xef\x8e\xad" // U+f3ad
#define ICON_FA_GUILDED "\xee\x81\xbe" // U+e07e
#define ICON_FA_GULP "\xef\x8e\xae" // U+f3ae
#define ICON_FA_HACKER_NEWS "\xef\x87\x94" // U+f1d4
#define ICON_FA_HACKER_NEWS_SQUARE "\xef\x8e\xaf" // U+f3af
#define ICON_FA_HACKERRANK "\xef\x97\xb7" // U+f5f7
#define ICON_FA_HIPS "\xef\x91\x92" // U+f452
#define ICON_FA_HIRE_A_HELPER "\xef\x8e\xb0" // U+f3b0
#define ICON_FA_HIVE "\xee\x81\xbf" // U+e07f
#define ICON_FA_HOOLI "\xef\x90\xa7" // U+f427
#define ICON_FA_HORNBILL "\xef\x96\x92" // U+f592
#define ICON_FA_HOTJAR "\xef\x8e\xb1" // U+f3b1
#define ICON_FA_HOUZZ "\xef\x89\xbc" // U+f27c
#define ICON_FA_HTML5 "\xef\x84\xbb" // U+f13b
#define ICON_FA_HUBSPOT "\xef\x8e\xb2" // U+f3b2
#define ICON_FA_IDEAL "\xee\x80\x93" // U+e013
#define ICON_FA_IMDB "\xef\x8b\x98" // U+f2d8
#define ICON_FA_INNOSOFT "\xee\x82\x80" // U+e080
#define ICON_FA_INSTAGRAM "\xef\x85\xad" // U+f16d
#define ICON_FA_INSTAGRAM_SQUARE "\xee\x81\x95" // U+e055
#define ICON_FA_INSTALOD "\xee\x82\x81" // U+e081
#define ICON_FA_INTERCOM "\xef\x9e\xaf" // U+f7af
#define ICON_FA_INTERNET_EXPLORER "\xef\x89\xab" // U+f26b
#define ICON_FA_INVISION "\xef\x9e\xb0" // U+f7b0
#define ICON_FA_IOXHOST "\xef\x88\x88" // U+f208
#define ICON_FA_ITCH_IO "\xef\xa0\xba" // U+f83a
#define ICON_FA_ITUNES "\xef\x8e\xb4" // U+f3b4
#define ICON_FA_ITUNES_NOTE "\xef\x8e\xb5" // U+f3b5
#define ICON_FA_JAVA "\xef\x93\xa4" // U+f4e4
#define ICON_FA_JEDI_ORDER "\xef\x94\x8e" // U+f50e
#define ICON_FA_JENKINS "\xef\x8e\xb6" // U+f3b6
#define ICON_FA_JIRA "\xef\x9e\xb1" // U+f7b1
#define ICON_FA_JOGET "\xef\x8e\xb7" // U+f3b7
#define ICON_FA_JOOMLA "\xef\x86\xaa" // U+f1aa
#define ICON_FA_JS "\xef\x8e\xb8" // U+f3b8
#define ICON_FA_JS_SQUARE "\xef\x8e\xb9" // U+f3b9
#define ICON_FA_JSFIDDLE "\xef\x87\x8c" // U+f1cc
#define ICON_FA_KAGGLE "\xef\x97\xba" // U+f5fa
#define ICON_FA_KEYBASE "\xef\x93\xb5" // U+f4f5
#define ICON_FA_KEYCDN "\xef\x8e\xba" // U+f3ba
#define ICON_FA_KICKSTARTER "\xef\x8e\xbb" // U+f3bb
#define ICON_FA_KICKSTARTER_K "\xef\x8e\xbc" // U+f3bc
#define ICON_FA_KORVUE "\xef\x90\xaf" // U+f42f
#define ICON_FA_LARAVEL "\xef\x8e\xbd" // U+f3bd
#define ICON_FA_LASTFM "\xef\x88\x82" // U+f202
#define ICON_FA_LASTFM_SQUARE "\xef\x88\x83" // U+f203
#define ICON_FA_LEANPUB "\xef\x88\x92" // U+f212
#define ICON_FA_LESS "\xef\x90\x9d" // U+f41d
#define ICON_FA_LINE "\xef\x8f\x80" // U+f3c0
#define ICON_FA_LINKEDIN "\xef\x82\x8c" // U+f08c
#define ICON_FA_LINKEDIN_IN "\xef\x83\xa1" // U+f0e1
#define ICON_FA_LINODE "\xef\x8a\xb8" // U+f2b8
#define ICON_FA_LINUX "\xef\x85\xbc" // U+f17c
#define ICON_FA_LYFT "\xef\x8f\x83" // U+f3c3
#define ICON_FA_MAGENTO "\xef\x8f\x84" // U+f3c4
#define ICON_FA_MAILCHIMP "\xef\x96\x9e" // U+f59e
#define ICON_FA_MANDALORIAN "\xef\x94\x8f" // U+f50f
#define ICON_FA_MARKDOWN "\xef\x98\x8f" // U+f60f
#define ICON_FA_MASTODON "\xef\x93\xb6" // U+f4f6
#define ICON_FA_MAXCDN "\xef\x84\xb6" // U+f136
#define ICON_FA_MDB "\xef\xa3\x8a" // U+f8ca
#define ICON_FA_MEDAPPS "\xef\x8f\x86" // U+f3c6
#define ICON_FA_MEDIUM "\xef\x88\xba" // U+f23a
#define ICON_FA_MEDIUM_M "\xef\x8f\x87" // U+f3c7
#define ICON_FA_MEDRT "\xef\x8f\x88" // U+f3c8
#define ICON_FA_MEETUP "\xef\x8b\xa0" // U+f2e0
#define ICON_FA_MEGAPORT "\xef\x96\xa3" // U+f5a3
#define ICON_FA_MENDELEY "\xef\x9e\xb3" // U+f7b3
#define ICON_FA_MICROBLOG "\xee\x80\x9a" // U+e01a
#define ICON_FA_MICROSOFT "\xef\x8f\x8a" // U+f3ca
#define ICON_FA_MIX "\xef\x8f\x8b" // U+f3cb
#define ICON_FA_MIXCLOUD "\xef\x8a\x89" // U+f289
#define ICON_FA_MIXER "\xee\x81\x96" // U+e056
#define ICON_FA_MIZUNI "\xef\x8f\x8c" // U+f3cc
#define ICON_FA_MODX "\xef\x8a\x85" // U+f285
#define ICON_FA_MONERO "\xef\x8f\x90" // U+f3d0
#define ICON_FA_NAPSTER "\xef\x8f\x92" // U+f3d2
#define ICON_FA_NEOS "\xef\x98\x92" // U+f612
#define ICON_FA_NIMBLR "\xef\x96\xa8" // U+f5a8
#define ICON_FA_NODE "\xef\x90\x99" // U+f419
#define ICON_FA_NODE_JS "\xef\x8f\x93" // U+f3d3
#define ICON_FA_NPM "\xef\x8f\x94" // U+f3d4
#define ICON_FA_NS8 "\xef\x8f\x95" // U+f3d5
#define ICON_FA_NUTRITIONIX "\xef\x8f\x96" // U+f3d6
#define ICON_FA_OCTOPUS_DEPLOY "\xee\x82\x82" // U+e082
#define ICON_FA_ODNOKLASSNIKI "\xef\x89\xa3" // U+f263
#define ICON_FA_ODNOKLASSNIKI_SQUARE "\xef\x89\xa4" // U+f264
#define ICON_FA_OLD_REPUBLIC "\xef\x94\x90" // U+f510
#define ICON_FA_OPENCART "\xef\x88\xbd" // U+f23d
#define ICON_FA_OPENID "\xef\x86\x9b" // U+f19b
#define ICON_FA_OPERA "\xef\x89\xaa" // U+f26a
#define ICON_FA_OPTIN_MONSTER "\xef\x88\xbc" // U+f23c
#define ICON_FA_ORCID "\xef\xa3\x92" // U+f8d2
#define ICON_FA_OSI "\xef\x90\x9a" // U+f41a
#define ICON_FA_PAGE4 "\xef\x8f\x97" // U+f3d7
#define ICON_FA_PAGELINES "\xef\x86\x8c" // U+f18c
#define ICON_FA_PALFED "\xef\x8f\x98" // U+f3d8
#define ICON_FA_PATREON "\xef\x8f\x99" // U+f3d9
#define ICON_FA_PAYPAL "\xef\x87\xad" // U+f1ed
#define ICON_FA_PENNY_ARCADE "\xef\x9c\x84" // U+f704
#define ICON_FA_PERBYTE "\xee\x82\x83" // U+e083
#define ICON_FA_PERISCOPE "\xef\x8f\x9a" // U+f3da
#define ICON_FA_PHABRICATOR "\xef\x8f\x9b" // U+f3db
#define ICON_FA_PHOENIX_FRAMEWORK "\xef\x8f\x9c" // U+f3dc
#define ICON_FA_PHOENIX_SQUADRON "\xef\x94\x91" // U+f511
#define ICON_FA_PHP "\xef\x91\x97" // U+f457
#define ICON_FA_PIED_PIPER "\xef\x8a\xae" // U+f2ae
#define ICON_FA_PIED_PIPER_ALT "\xef\x86\xa8" // U+f1a8
#define ICON_FA_PIED_PIPER_HAT "\xef\x93\xa5" // U+f4e5
#define ICON_FA_PIED_PIPER_PP "\xef\x86\xa7" // U+f1a7
#define ICON_FA_PIED_PIPER_SQUARE "\xee\x80\x9e" // U+e01e
#define ICON_FA_PINTEREST "\xef\x83\x92" // U+f0d2
#define ICON_FA_PINTEREST_P "\xef\x88\xb1" // U+f231
#define ICON_FA_PINTEREST_SQUARE "\xef\x83\x93" // U+f0d3
#define ICON_FA_PLAYSTATION "\xef\x8f\x9f" // U+f3df
#define ICON_FA_PRODUCT_HUNT "\xef\x8a\x88" // U+f288
#define ICON_FA_PUSHED "\xef\x8f\xa1" // U+f3e1
#define ICON_FA_PYTHON "\xef\x8f\xa2" // U+f3e2
#define ICON_FA_QQ "\xef\x87\x96" // U+f1d6
#define ICON_FA_QUINSCAPE "\xef\x91\x99" // U+f459
#define ICON_FA_QUORA "\xef\x8b\x84" // U+f2c4
#define ICON_FA_R_PROJECT "\xef\x93\xb7" // U+f4f7
#define ICON_FA_RASPBERRY_PI "\xef\x9e\xbb" // U+f7bb
#define ICON_FA_RAVELRY "\xef\x8b\x99" // U+f2d9
#define ICON_FA_REACT "\xef\x90\x9b" // U+f41b
#define ICON_FA_REACTEUROPE "\xef\x9d\x9d" // U+f75d
#define ICON_FA_README "\xef\x93\x95" // U+f4d5
#define ICON_FA_REBEL "\xef\x87\x90" // U+f1d0
#define ICON_FA_RED_RIVER "\xef\x8f\xa3" // U+f3e3
#define ICON_FA_REDDIT "\xef\x86\xa1" // U+f1a1
#define ICON_FA_REDDIT_ALIEN "\xef\x8a\x81" // U+f281
#define ICON_FA_REDDIT_SQUARE "\xef\x86\xa2" // U+f1a2
#define ICON_FA_REDHAT "\xef\x9e\xbc" // U+f7bc
#define ICON_FA_RENREN "\xef\x86\x8b" // U+f18b
#define ICON_FA_REPLYD "\xef\x8f\xa6" // U+f3e6
#define ICON_FA_RESEARCHGATE "\xef\x93\xb8" // U+f4f8
#define ICON_FA_RESOLVING "\xef\x8f\xa7" // U+f3e7
#define ICON_FA_REV "\xef\x96\xb2" // U+f5b2
#define ICON_FA_ROCKETCHAT "\xef\x8f\xa8" // U+f3e8
#define ICON_FA_ROCKRMS "\xef\x8f\xa9" // U+f3e9
#define ICON_FA_RUST "\xee\x81\xba" // U+e07a
#define ICON_FA_SAFARI "\xef\x89\xa7" // U+f267
#define ICON_FA_SALESFORCE "\xef\xa0\xbb" // U+f83b
#define ICON_FA_SASS "\xef\x90\x9e" // U+f41e
#define ICON_FA_SCHLIX "\xef\x8f\xaa" // U+f3ea
#define ICON_FA_SCRIBD "\xef\x8a\x8a" // U+f28a
#define ICON_FA_SEARCHENGIN "\xef\x8f\xab" // U+f3eb
#define ICON_FA_SELLCAST "\xef\x8b\x9a" // U+f2da
#define ICON_FA_SELLSY "\xef\x88\x93" // U+f213
#define ICON_FA_SERVICESTACK "\xef\x8f\xac" // U+f3ec
#define ICON_FA_SHIRTSINBULK "\xef\x88\x94" // U+f214
#define ICON_FA_SHOPIFY "\xee\x81\x97" // U+e057
#define ICON_FA_SHOPWARE "\xef\x96\xb5" // U+f5b5
#define ICON_FA_SIMPLYBUILT "\xef\x88\x95" // U+f215
#define ICON_FA_SISTRIX "\xef\x8f\xae" // U+f3ee
#define ICON_FA_SITH "\xef\x94\x92" // U+f512
#define ICON_FA_SKETCH "\xef\x9f\x86" // U+f7c6
#define ICON_FA_SKYATLAS "\xef\x88\x96" // U+f216
#define ICON_FA_SKYPE "\xef\x85\xbe" // U+f17e
#define ICON_FA_SLACK "\xef\x86\x98" // U+f198
#define ICON_FA_SLACK_HASH "\xef\x8f\xaf" // U+f3ef
#define ICON_FA_SLIDESHARE "\xef\x87\xa7" // U+f1e7
#define ICON_FA_SNAPCHAT "\xef\x8a\xab" // U+f2ab
#define ICON_FA_SNAPCHAT_GHOST "\xef\x8a\xac" // U+f2ac
#define ICON_FA_SNAPCHAT_SQUARE "\xef\x8a\xad" // U+f2ad
#define ICON_FA_SOUNDCLOUD "\xef\x86\xbe" // U+f1be
#define ICON_FA_SOURCETREE "\xef\x9f\x93" // U+f7d3
#define ICON_FA_SPEAKAP "\xef\x8f\xb3" // U+f3f3
#define ICON_FA_SPEAKER_DECK "\xef\xa0\xbc" // U+f83c
#define ICON_FA_SPOTIFY "\xef\x86\xbc" // U+f1bc
#define ICON_FA_SQUARESPACE "\xef\x96\xbe" // U+f5be
#define ICON_FA_STACK_EXCHANGE "\xef\x86\x8d" // U+f18d
#define ICON_FA_STACK_OVERFLOW "\xef\x85\xac" // U+f16c
#define ICON_FA_STACKPATH "\xef\xa1\x82" // U+f842
#define ICON_FA_STAYLINKED "\xef\x8f\xb5" // U+f3f5
#define ICON_FA_STEAM "\xef\x86\xb6" // U+f1b6
#define ICON_FA_STEAM_SQUARE "\xef\x86\xb7" // U+f1b7
#define ICON_FA_STEAM_SYMBOL "\xef\x8f\xb6" // U+f3f6
#define ICON_FA_STICKER_MULE "\xef\x8f\xb7" // U+f3f7
#define ICON_FA_STRAVA "\xef\x90\xa8" // U+f428
#define ICON_FA_STRIPE "\xef\x90\xa9" // U+f429
#define ICON_FA_STRIPE_S "\xef\x90\xaa" // U+f42a
#define ICON_FA_STUDIOVINARI "\xef\x8f\xb8" // U+f3f8
#define ICON_FA_STUMBLEUPON "\xef\x86\xa4" // U+f1a4
#define ICON_FA_STUMBLEUPON_CIRCLE "\xef\x86\xa3" // U+f1a3
#define ICON_FA_SUPERPOWERS "\xef\x8b\x9d" // U+f2dd
#define ICON_FA_SUPPLE "\xef\x8f\xb9" // U+f3f9
#define ICON_FA_SUSE "\xef\x9f\x96" // U+f7d6
#define ICON_FA_SWIFT "\xef\xa3\xa1" // U+f8e1
#define ICON_FA_SYMFONY "\xef\xa0\xbd" // U+f83d
#define ICON_FA_TEAMSPEAK "\xef\x93\xb9" // U+f4f9
#define ICON_FA_TELEGRAM "\xef\x8b\x86" // U+f2c6
#define ICON_FA_TELEGRAM_PLANE "\xef\x8f\xbe" // U+f3fe
#define ICON_FA_TENCENT_WEIBO "\xef\x87\x95" // U+f1d5
#define ICON_FA_THE_RED_YETI "\xef\x9a\x9d" // U+f69d
#define ICON_FA_THEMECO "\xef\x97\x86" // U+f5c6
#define ICON_FA_THEMEISLE "\xef\x8a\xb2" // U+f2b2
#define ICON_FA_THINK_PEAKS "\xef\x9c\xb1" // U+f731
#define ICON_FA_TIKTOK "\xee\x81\xbb" // U+e07b
#define ICON_FA_TRADE_FEDERATION "\xef\x94\x93" // U+f513
#define ICON_FA_TRELLO "\xef\x86\x81" // U+f181
#define ICON_FA_TRIPADVISOR "\xef\x89\xa2" // U+f262
#define ICON_FA_TUMBLR "\xef\x85\xb3" // U+f173
#define ICON_FA_TUMBLR_SQUARE "\xef\x85\xb4" // U+f174
#define ICON_FA_TWITCH "\xef\x87\xa8" // U+f1e8
#define ICON_FA_TWITTER "\xef\x82\x99" // U+f099
#define ICON_FA_TWITTER_SQUARE "\xef\x82\x81" // U+f081
#define ICON_FA_TYPO3 "\xef\x90\xab" // U+f42b
#define ICON_FA_UBER "\xef\x90\x82" // U+f402
#define ICON_FA_UBUNTU "\xef\x9f\x9f" // U+f7df
#define ICON_FA_UIKIT "\xef\x90\x83" // U+f403
#define ICON_FA_UMBRACO "\xef\xa3\xa8" // U+f8e8
#define ICON_FA_UNCHARTED "\xee\x82\x84" // U+e084
#define ICON_FA_UNIREGISTRY "\xef\x90\x84" // U+f404
#define ICON_FA_UNITY "\xee\x81\x89" // U+e049
#define ICON_FA_UNSPLASH "\xee\x81\xbc" // U+e07c
#define ICON_FA_UNTAPPD "\xef\x90\x85" // U+f405
#define ICON_FA_UPS "\xef\x9f\xa0" // U+f7e0
#define ICON_FA_USB "\xef\x8a\x87" // U+f287
#define ICON_FA_USPS "\xef\x9f\xa1" // U+f7e1
#define ICON_FA_USSUNNAH "\xef\x90\x87" // U+f407
#define ICON_FA_VAADIN "\xef\x90\x88" // U+f408
#define ICON_FA_VIACOIN "\xef\x88\xb7" // U+f237
#define ICON_FA_VIADEO "\xef\x8a\xa9" // U+f2a9
#define ICON_FA_VIADEO_SQUARE "\xef\x8a\xaa" // U+f2aa
#define ICON_FA_VIBER "\xef\x90\x89" // U+f409
#define ICON_FA_VIMEO "\xef\x90\x8a" // U+f40a
#define ICON_FA_VIMEO_SQUARE "\xef\x86\x94" // U+f194
#define ICON_FA_VIMEO_V "\xef\x89\xbd" // U+f27d
#define ICON_FA_VINE "\xef\x87\x8a" // U+f1ca
#define ICON_FA_VK "\xef\x86\x89" // U+f189
#define ICON_FA_VNV "\xef\x90\x8b" // U+f40b
#define ICON_FA_VUEJS "\xef\x90\x9f" // U+f41f
#define ICON_FA_WATCHMAN_MONITORING "\xee\x82\x87" // U+e087
#define ICON_FA_WAZE "\xef\xa0\xbf" // U+f83f
#define ICON_FA_WEEBLY "\xef\x97\x8c" // U+f5cc
#define ICON_FA_WEIBO "\xef\x86\x8a" // U+f18a
#define ICON_FA_WEIXIN "\xef\x87\x97" // U+f1d7
#define ICON_FA_WHATSAPP "\xef\x88\xb2" // U+f232
#define ICON_FA_WHATSAPP_SQUARE "\xef\x90\x8c" // U+f40c
#define ICON_FA_WHMCS "\xef\x90\x8d" // U+f40d
#define ICON_FA_WIKIPEDIA_W "\xef\x89\xa6" // U+f266
#define ICON_FA_WINDOWS "\xef\x85\xba" // U+f17a
#define ICON_FA_WIX "\xef\x97\x8f" // U+f5cf
#define ICON_FA_WIZARDS_OF_THE_COAST "\xef\x9c\xb0" // U+f730
#define ICON_FA_WODU "\xee\x82\x88" // U+e088
#define ICON_FA_WOLF_PACK_BATTALION "\xef\x94\x94" // U+f514
#define ICON_FA_WORDPRESS "\xef\x86\x9a" // U+f19a
#define ICON_FA_WORDPRESS_SIMPLE "\xef\x90\x91" // U+f411
#define ICON_FA_WPBEGINNER "\xef\x8a\x97" // U+f297
#define ICON_FA_WPEXPLORER "\xef\x8b\x9e" // U+f2de
#define ICON_FA_WPFORMS "\xef\x8a\x98" // U+f298
#define ICON_FA_WPRESSR "\xef\x8f\xa4" // U+f3e4
#define ICON_FA_XBOX "\xef\x90\x92" // U+f412
#define ICON_FA_XING "\xef\x85\xa8" // U+f168
#define ICON_FA_XING_SQUARE "\xef\x85\xa9" // U+f169
#define ICON_FA_Y_COMBINATOR "\xef\x88\xbb" // U+f23b
#define ICON_FA_YAHOO "\xef\x86\x9e" // U+f19e
#define ICON_FA_YAMMER "\xef\xa1\x80" // U+f840
#define ICON_FA_YANDEX "\xef\x90\x93" // U+f413
#define ICON_FA_YANDEX_INTERNATIONAL "\xef\x90\x94" // U+f414
#define ICON_FA_YARN "\xef\x9f\xa3" // U+f7e3
#define ICON_FA_YELP "\xef\x87\xa9" // U+f1e9
#define ICON_FA_YOAST "\xef\x8a\xb1" // U+f2b1
#define ICON_FA_YOUTUBE "\xef\x85\xa7" // U+f167
#define ICON_FA_YOUTUBE_SQUARE "\xef\x90\xb1" // U+f431
#define ICON_FA_ZHIHU "\xef\x98\xbf" // U+f63f

49
src/Maps/LastMissionId.h Normal file
View File

@ -0,0 +1,49 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cstdint>
#include <map>
static const std::map<std::int32_t, const char*> mission_id_map {{
// Story missions
{0x64, "Mission 1 - Training"},
{0x65, "Mission 2 - Patrol Operation"},
{0x66, "Mission 3 - Fusion Cells in the Snow"},
{0x67, "Mission 4 - Earning Changes"},
{0x68, "Mission 5 - Unexpected Coordination"},
{0x69, "Mission 6 - Empowering Void"},
{0x6A, "Mission 7 - Logisitics Obstacles"},
{0x6B, "Mission 8 - Wrath of the Wastelands"},
{0x6C, "Mission 9 - Suspicious Originator"},
{0x6D, "Mission 10 - Researchers Data Recovery"},
{0x6E, "Mission 11 - Tempestuous Sector"},
{0x6F, "Mission 12 - Clashes of Metal"},
{0x70, "Mission 13 - The Sandstorm Glutton"},
// Hunting grounds
{0xC8, "Hunt 1 - Desert Pathway Safety"},
{0xC9, "Hunt 2 - Snowfield Custodian"},
{0xCA, "Hunt 3 - Abandoned Valley Raid"},
{0xCB, "Hunt 4 - Depths of the Machineries"},
// Challenges
{0x12C, "Challenge 1 - Redline Battlefront"},
{0x140, "Challenge 2 - Void Convergence"},
{0x190, "Challenge 3 - Gates of Ascension"}
}};

93
src/Maps/StoryProgress.h Normal file
View File

@ -0,0 +1,93 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cstdint>
#include <Corrade/Containers/Array.h>
struct StoryProgressPoint {
std::int32_t id;
const char* chapter;
const char* point;
const char* after = "";
};
static const Corrade::Containers::Array<StoryProgressPoint> story_progress
{
InPlaceInit,
{
{0x0000, "Chapter 1", "Chapter start (company isn't named yet)"},
{0x0064, "Chapter 1", "First time in the hangar"},
{0x0065, "Chapter 1", "After 1st meeting with Quin in mission section"},
{0x0066, "Chapter 1", "Talking with Reina and Quin in hangar", "After training"},
{0x0067, "Chapter 1", "Returned to hangar","After training"},
{0x0068, "Chapter 1", "Talked with Quin in development section", "After training"},
{0x0069, "Chapter 1", "Talked with Waltz in armour section", "After training"},
{0x00C8, "Chapter 1", "Talked with Kael in tuning section", "After training"},
{0x00C9, "Chapter 1", "Got mission 2 briefing", "After training"},
{0x012C, "Chapter 1", "Talking with Reina", "After mission 2"},
{0x012D, "Chapter 1", "Returned to hangar", "After mission 2"},
{0x012E, "Chapter 1", "Talked with Kael in tuning section", "After mission 2"},
{0x012F, "Chapter 1", "Talked with Reina in hangar", "After mission 2"},
{0x0130, "Chapter 1", "Got mission 3 briefing", "After mission 2"},
{0x0190, "Chapter 1", "Talking with Reina", "After mission 3"},
{0x0191, "Chapter 1", "Returned to hangar", "After mission 3"},
{0x0192, "Chapter 1", "Talked with Waltz in armour section", "After mission 3"},
{0x0193, "Chapter 1", "Got mission 4 briefing", "After mission 3"},
{0x01F4, "Chapter 1", "Talking with Reina", "After mission 4"},
{0x01F5, "Chapter 1", "Returned to hangar", "After mission 4"},
{0x01F6, "Chapter 1", "Talked with Waltz in armour section", "After mission 4"},
{0x01F7, "Chapter 1", "Talked with Reina in hangar", "After mission 4"},
{0x01F8, "Chapter 1", "Got mission 5 and hunt 1 briefing", "After mission 4"},
{0x0258, "Chapter 1", "Meeting Neon and Aine", "After mission 5"},
{0x0259, "Chapter 1", "Returned to hangar", "After mission 5"},
{0x025A, "Chapter 1", "Got mission 6 briefing", "After mission 5"},
{0x02BC, "Chapter 1", "Talking with Reina", "After mission 6"},
{0x02BD, "Chapter 1", "Returned to hangar", "After mission 6"},
{0x02BE, "Chapter 1", "Got hunt 2 briefing", "After mission 6"},
{0x02BF, "Chapter 1", "Met Ellenier", "After mission 6"},
{0x02C0, "Chapter 1", "Got mission 7 briefing", "After mission 6"},
{0x0320, "Chapter 1", "Talking with Nier", "After mission 7"},
{0x0321, "Chapter 1", "Returned to hangar", "After mission 7"},
{0x0322, "Chapter 1", "Talked with Quin, Reina, and Nier in development section", "After mission 7"},
{0x0323, "Chapter 1", "Got mission 8 briefing", "After mission 7"},
{0x0384, "Chapter 1", "Talking with crew in hangar", "After mission 8"},
{0x0385, "Chapter 1", "Returned to hangar", "After mission 8"},
{0x0386, "Chapter 1", "Got hunt 3 briefing", "After mission 8"},
{0x0387, "Chapter 1", "Talked with Reina, Nier, and Quin in development section", "After mission 8"},
{0x0388, "Chapter 2", "Chapter start"},
{0x0389, "Chapter 2", "Got mission 9 briefing"},
{0x03E8, "Chapter 2", "Talking with Reina in hangar", "After mission 9"},
{0x03E9, "Chapter 2", "Returned to hangar", "After mission 9"},
{0x03EA, "Chapter 2", "Talked with crew in armour section", "After mission 9"},
{0x03EB, "Chapter 2", "Got mission 10 briefing", "After mission 9"},
{0x044C, "Chapter 2", "Talking with Reina in hangar", "After mission 10"},
{0x044D, "Chapter 2", "Returned to hangar", "After mission 10"},
{0x044E, "Chapter 2", "Got mission 11 briefing", "After mission 10"},
{0x04B0, "Chapter 2", "Talking with Reina and Nier in hangar", "After mission 11"},
{0x04B1, "Chapter 2", "Returned to hangar", "After mission 11"},
{0x04B2, "Chapter 2", "Got mission 12 briefing", "After mission 11"},
{0x0514, "Chapter 2", "Talking with Reina and Waltz in hangar", "After mission 12"},
{0x0515, "Chapter 2", "Returned to hangar", "After mission 12"},
{0x0516, "Chapter 2", "Got hunt 4 and mission 13 briefing", "After mission 12"},
{0x0578, "Chapter 2", "Talking with Reina in hangar", "After mission 13"},
{0x0579, "Chapter 2", "Returned to hangar", "After mission 13"},
{0x057A, "Chapter 2", "Talked with Reina in development section", "After mission 13"},
{0x057B, "Chapter 2", "Got briefing for challenges 1, 2, and 3", "After mission 13"},
}
};

151
src/Mass/Mass.cpp Normal file
View File

@ -0,0 +1,151 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cstring>
#include <algorithm>
#include <Corrade/Containers/Array.h>
#include <Corrade/Utility/Directory.h>
#include "Mass.h"
using namespace Corrade;
constexpr char mass_name_locator[] = "Name_45_A037C5D54E53456407BDF091344529BB\0\x0c\0\0\0StrProperty";
constexpr char steamid_locator[] = "Account\0\x0c\0\0\0StrProperty";
std::string Mass::_lastError;
Mass::Mass(const std::string& filename) {
_filename = filename;
if(!Utility::Directory::exists(_filename)) {
_lastError = "The file " + _filename + " couldn't be found.";
return;
}
auto mmap = Utility::Directory::mapRead(_filename);
auto iter = std::search(mmap.begin(), mmap.end(), &mass_name_locator[0], &mass_name_locator[56]);
if(iter != mmap.end()) {
_name = std::string{iter + 70};
_state = MassState::Valid;
}
else {
_lastError = "The name couldn't be found in " + filename;
_state = MassState::Invalid;
}
}
auto Mass::lastError() -> std::string const& {
return _lastError;
}
auto Mass::getNameFromFile(const std::string& filename) -> std::string {
if(!Utility::Directory::exists(filename)) {
_lastError = "The file " + filename + " couldn't be found.";
return "";
}
std::string name = "";
auto mmap = Utility::Directory::mapRead(filename);
auto iter = std::search(mmap.begin(), mmap.end(), &mass_name_locator[0], &mass_name_locator[56]);
if(iter != mmap.end()) {
name = std::string{iter + 70};
}
else {
_lastError = "The name couldn't be found in " + filename;
}
return name;
}
auto Mass::filename() -> std::string const&{
return _filename;
}
auto Mass::name() -> std::string const&{
return _name;
}
auto Mass::getName() -> std::string const& {
if(!Utility::Directory::exists(_filename)) {
_lastError = "The file " + _filename + " couldn't be found.";
_state = MassState::Empty;
return _name = "";
}
auto mmap = Utility::Directory::mapRead(_filename);
auto iter = std::search(mmap.begin(), mmap.end(), &mass_name_locator[0], &mass_name_locator[56]);
if(iter != mmap.end()) {
_state = MassState::Valid;
return _name = std::string{iter + 70};
}
else {
_lastError = "The name couldn't be found in " + _filename;
_state = MassState::Invalid;
return _name = "";
}
}
auto Mass::state() -> MassState {
return _state;
}
auto Mass::updateSteamId(const std::string& steam_id) -> bool {
if(!Utility::Directory::exists(_filename)) {
_lastError = "The file " + _filename + " couldn't be found.";
_state = MassState::Empty;
return false;
}
Utility::Directory::copy(_filename, _filename + ".tmp");
{
auto mmap = Utility::Directory::map(_filename + ".tmp");
auto iter = std::search(mmap.begin(), mmap.end(), &steamid_locator[0], &steamid_locator[23]);
if(iter == mmap.end()) {
_lastError = "The M.A.S.S. file at " + _filename + " seems to be corrupt.";
Utility::Directory::rm(_filename + ".tmp");
return false;
}
iter += 37;
if(std::strncmp(iter, steam_id.c_str(), steam_id.length()) != 0) {
for(int i = 0; i < 17; ++i) {
*(iter + i) = steam_id[i];
}
}
}
if(Utility::Directory::exists(_filename)) {
Utility::Directory::rm(_filename);
}
Utility::Directory::move(_filename + ".tmp", _filename);
return true;
}

54
src/Mass/Mass.h Normal file
View File

@ -0,0 +1,54 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <string>
enum class MassState : std::uint8_t {
Empty, Invalid, Valid
};
class Mass {
public:
Mass(const std::string& filename);
Mass(const Mass&) = delete;
Mass& operator=(const Mass&) = delete;
Mass(Mass&&) = default;
Mass& operator=(Mass&&) = default;
static auto lastError() -> std::string const&;
static auto getNameFromFile(const std::string& filename) -> std::string;
auto filename() -> std::string const&;
auto name() -> std::string const&;
auto getName() -> std::string const&;
auto state() -> MassState;
auto updateSteamId(const std::string& steam_id) -> bool;
private:
static std::string _lastError;
std::string _filename = "";
std::string _name = "";
MassState _state = MassState::Empty;
};

View File

@ -0,0 +1,85 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "MassBuilderManager.h"
#include <Corrade/Containers/ScopeGuard.h>
#include <Corrade/Utility/Directory.h>
#include <Corrade/Utility/Unicode.h>
#include <shlobj.h>
#include <wtsapi32.h>
using namespace Corrade;
MassBuilderManager::MassBuilderManager() {
_ready = findSaveDirectory();
}
auto MassBuilderManager::ready() const -> bool {
return _ready;
}
auto MassBuilderManager::lastError() -> std::string const& {
return _lastError;
}
auto MassBuilderManager::saveDirectory() -> std::string const& {
return _saveDirectory;
}
void MassBuilderManager::checkGameState() {
WTS_PROCESS_INFOW* process_infos = nullptr;
unsigned long process_count = 0;
if(WTSEnumerateProcessesW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &process_infos, &process_count)) {
Containers::ScopeGuard guard{process_infos, WTSFreeMemory};
for(unsigned long i = 0; i < process_count; ++i) {
if(std::wcscmp(process_infos[i].pProcessName, L"MASS_Builder-Win64-Shipping.exe") == 0) {
_gameState = GameState::Running;
break;
}
else {
_gameState = GameState::NotRunning;
}
}
}
else {
_gameState = GameState::Unknown;
}
}
auto MassBuilderManager::gameState() -> GameState {
return _gameState;
}
auto MassBuilderManager::findSaveDirectory() -> bool {
wchar_t h[MAX_PATH];
if(!SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, h))) {
_lastError = "SHGetFolderPathW() failed in MassBuilderManager::findSaveDirectory()";
return false;
}
_saveDirectory = Utility::Directory::join(Utility::Directory::fromNativeSeparators(Utility::Unicode::narrow(h)), "MASS_Builder");
if(!Utility::Directory::exists(_saveDirectory)) {
_lastError = _saveDirectory + " wasn't found.";
return false;
}
return true;
}

View File

@ -0,0 +1,49 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cstdint>
#include <string>
enum class GameState : std::uint8_t {
Unknown, NotRunning, Running
};
class MassBuilderManager {
public:
MassBuilderManager();
auto ready() const -> bool;
auto lastError() -> std::string const&;
auto saveDirectory() -> std::string const&;
void checkGameState();
auto gameState() -> GameState;
private:
auto findSaveDirectory() -> bool;
bool _ready = false;
std::string _lastError = "";
std::string _saveDirectory = "";
GameState _gameState = GameState::Unknown;
};

View File

@ -0,0 +1,232 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <algorithm>
#include <Corrade/Utility/Directory.h>
#include <Corrade/Utility/FormatStl.h>
#include <Corrade/Utility/String.h>
#include "MassManager.h"
static const std::string empty_string = "";
MassManager::MassManager(const std::string& save_path, const std::string& steam_id, bool demo):
_stagingAreaDirectory{Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "staging")}
{
_saveDirectory = save_path;
_steamId = steam_id;
_demo = demo;
Containers::arrayReserve(_hangars, 32);
std::string mass_filename = "";
for(int i = 0; i < 32; i++) {
mass_filename = Utility::Directory::join(_saveDirectory, Utility::formatString("{}Unit{:.2d}{}.sav", demo ? "Demo" : "", i, _steamId));
Containers::arrayAppend(_hangars, Mass{mass_filename});
}
if(!Utility::Directory::exists(_stagingAreaDirectory)) {
Utility::Directory::mkpath(_stagingAreaDirectory);
}
refreshStagedMasses();
}
auto MassManager::saveDirectory() -> std::string const& {
return _saveDirectory;
}
auto MassManager::stagingAreaDirectory() -> std::string const& {
return _stagingAreaDirectory;
}
auto MassManager::lastError() -> std::string const& {
return _lastError;
}
auto MassManager::massName(int hangar) -> std::string const& {
if(hangar < 0 || hangar >= 32) {
return empty_string;
}
return _hangars[hangar].name();
}
auto MassManager::massState(int hangar) -> MassState {
if(hangar < 0 || hangar >= 32) {
return MassState::Empty;
}
return _hangars[hangar].state();
}
void MassManager::refreshHangar(int hangar) {
if(hangar < 0 || hangar >= 32) {
return;
}
std::string mass_filename =
Utility::Directory::join(_saveDirectory, Utility::formatString("{}Unit{:.2d}{}.sav", _demo ? "Demo" : "", hangar, _steamId));
_hangars[hangar] = Mass{mass_filename};
}
auto MassManager::importMass(const std::string& staged_fn, int hangar) -> bool {
if(hangar < 0 || hangar >= 32) {
_lastError = "Hangar out of range in MassManager::importMass()";
return false;
}
auto it = _stagedMasses.find(staged_fn);
if(it == _stagedMasses.end()) {
_lastError = "Couldn't find " + staged_fn + " in the staged M.A.S.S.es.";
return false;
}
std::string source = Utility::Directory::join(_stagingAreaDirectory, staged_fn);
Utility::Directory::copy(source, source + ".tmp");
if(!Mass{source + ".tmp"}.updateSteamId(_steamId))
{
_lastError = "The M.A.S.S. file at " + source + " seems to be corrupt.";
Utility::Directory::rm(source + ".tmp");
return false;
}
if(Utility::Directory::exists(_hangars[hangar].filename())) {
Utility::Directory::rm(_hangars[hangar].filename());
}
if(!Utility::Directory::move(source + ".tmp", _hangars[hangar].filename())) {
_lastError = Utility::formatString("Couldn't move {} to hangar {:.2d}", staged_fn, hangar + 1);
return false;
}
return true;
}
auto MassManager::exportMass(int hangar) -> bool {
if(hangar < 0 || hangar >= 32) {
_lastError = "Hangar out of range in MassManager::exportMass()";
return false;
}
if(_hangars[hangar].state() == MassState::Empty ||
_hangars[hangar].state() == MassState::Invalid) {
_lastError = Utility::formatString("There is no valid data to export in hangar {:.2d}", hangar + 1);
return false;
}
std::string source = Utility::Directory::join(_saveDirectory, _hangars[hangar].filename());
std::string dest = Utility::Directory::join(_stagingAreaDirectory,
Utility::formatString("{}_{}.sav", _hangars[hangar].name(), _steamId));
if(!Utility::Directory::copy(source, dest)) {
_lastError = Utility::formatString("Couldn't export data from hangar {:.2d} to {}", hangar, dest);
return false;
}
return true;
}
auto MassManager::moveMass(int source, int destination) -> bool {
if(source < 0 || source >= 32) {
_lastError = "Source hangar out of range.";
return false;
}
if(destination < 0 || destination >= 32) {
_lastError = "Destination hangar out of range.";
return false;
}
std::string source_file = _hangars[source].filename();
std::string dest_file = _hangars[destination].filename();
MassState dest_state = _hangars[destination].state();
switch(dest_state) {
case MassState::Empty:
break;
case MassState::Invalid:
Utility::Directory::rm(dest_file);
break;
case MassState::Valid:
Utility::Directory::move(dest_file, dest_file + ".tmp");
break;
}
Utility::Directory::move(source_file, dest_file);
if(dest_state == MassState::Valid) {
Utility::Directory::move(dest_file + ".tmp", source_file);
}
return true;
}
auto MassManager::deleteMass(int hangar) -> bool {
if(hangar < 0 || hangar >= 32) {
_lastError = "Hangar out of bounds";
return false;
}
if(!Utility::Directory::rm(_hangars[hangar].filename())) {
_lastError = "Deletion failed. Maybe the file was already deleted, or it's locked by another application.";
return false;
}
return true;
}
auto MassManager::stagedMasses() -> std::map<std::string, std::string> const& {
return _stagedMasses;
}
void MassManager::refreshStagedMasses() {
_stagedMasses.clear();
using Utility::Directory::Flag;
std::vector<std::string> file_list = Utility::Directory::list(_stagingAreaDirectory, Flag::SkipSpecial|Flag::SkipDirectories|Flag::SkipDotAndDotDot);
auto iter = std::remove_if(file_list.begin(), file_list.end(), [](std::string& file){
return !Utility::String::endsWith(file, ".sav");
});
file_list.erase(iter, file_list.end());
for(const std::string& file : file_list) {
std::string name = Mass::getNameFromFile(Utility::Directory::join(_stagingAreaDirectory, file));
if(!name.empty()) {
_stagedMasses[file] = name;
}
}
}
auto MassManager::deleteStagedMass(const std::string& filename) -> bool {
if(_stagedMasses.find(filename) == _stagedMasses.cend()) {
_lastError = "The file " + filename + " couldn't be found in the list of staged M.A.S.S.es.";
return false;
}
if(!Utility::Directory::rm(Utility::Directory::join(_stagingAreaDirectory, filename))) {
_lastError = "The file " + filename + " couldn't be deleted for unknown reasons.";
return false;
}
return true;
}

View File

@ -0,0 +1,64 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <map>
#include <string>
#include <Corrade/Containers/GrowableArray.h>
#include "../Mass/Mass.h"
using namespace Corrade;
class MassManager {
public:
MassManager(const std::string& save_path, const std::string& steam_id, bool demo);
auto saveDirectory() -> std::string const&;
auto stagingAreaDirectory() -> std::string const&;
auto lastError() -> std::string const&;
auto massName(int hangar) -> std::string const&;
auto massState(int hangar) -> MassState;
void refreshHangar(int hangar);
auto importMass(const std::string& staged_fn, int hangar) -> bool;
auto exportMass(int hangar) -> bool;
auto moveMass(int source, int destination) -> bool;
auto deleteMass(int hangar) -> bool;
auto stagedMasses() -> std::map<std::string, std::string> const&;
void refreshStagedMasses();
auto deleteStagedMass(const std::string& filename) -> bool;
private:
std::string _saveDirectory;
std::string _steamId;
bool _demo;
std::string _lastError = "";
Containers::Array<Mass> _hangars;
const std::string _stagingAreaDirectory;
std::map<std::string, std::string> _stagedMasses;
};

87
src/Profile/Locators.h Normal file
View File

@ -0,0 +1,87 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
constexpr char company_name_locator[] = "CompanyName\0\x0c\0\0\0StrProperty";
constexpr char active_slot_locator[] = "ActiveFrameSlot\0\x0c\0\0\0IntProperty";
constexpr char credits_locator[] = "Credit\0\x0c\0\0\0IntProperty";
constexpr char story_progress_locator[] = "StoryProgress\0\x0c\0\0\0IntProperty";
constexpr char last_mission_id_locator[] = "LastMissionID\0\x0c\0\0\0IntProperty";
constexpr char verse_steel_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x00\x35\x0c\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char undinium_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x01\x35\x0c\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char necrium_alloy_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x02\x35\x0c\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char lunarite_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x03\x35\x0c\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char asterite_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x04\x35\x0c\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char ednil_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x0a\x35\x0c\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char nuflalt_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x0b\x35\x0c\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char aurelene_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x0c\x35\x0c\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char soldus_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x0d\x35\x0c\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char synthesized_n_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x0e\x35\x0c\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char alcarbonite_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x14\x35\x0c\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char keriphene_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x15\x35\x0c\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char nitinol_cm_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x16\x35\x0c\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char quarkium_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x17\x35\x0c\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char alterene_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x18\x35\x0c\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char mixed_composition_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\xa0\xbb\x0d\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char void_residue_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\xa1\xbb\x0d\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char muscular_construction_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\xa2\xbb\x0d\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char mineral_exoskeletology_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\xa3\xbb\x0d\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";
constexpr char carbonized_skin_locator[] =
"ID_4_AAE08F17428E229EC7A2209F51081A21\0\x0c\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\xa4\xbb\x0d\0,\0\0\0"
"Quantity_3_560F09B5485C365D3041888910019CE3\0\x0c\0\0\0IntProperty";

1003
src/Profile/Profile.cpp Normal file

File diff suppressed because it is too large Load Diff

184
src/Profile/Profile.h Normal file
View File

@ -0,0 +1,184 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cstdint>
#include <string>
enum class ProfileType : std::uint8_t {
Demo,
FullGame
};
class Profile {
public:
explicit Profile(const std::string& path);
auto valid() const -> bool;
auto lastError() const -> std::string const&;
auto filename() const -> std::string const&;
auto type() const -> ProfileType;
auto steamId() const -> std::string const&;
void refreshValues();
auto companyName() const -> std::string const&;
auto getCompanyName() -> std::string const&;
auto renameCompany(const std::string& new_name) -> bool;
auto activeFrameSlot() const -> std::int8_t;
auto getActiveFrameSlot() -> std::int8_t;
auto credits() const -> std::int32_t;
auto getCredits() -> std::int32_t;
auto setCredits(std::int32_t) -> bool;
auto storyProgress() const -> std::int32_t;
auto getStoryProgress() -> std::int32_t;
auto setStoryProgress(std::int32_t progress) -> bool;
auto lastMissionId() const -> std::int32_t;
auto getLastMissionId() -> std::int32_t;
auto verseSteel() const -> std::int32_t;
auto getVerseSteel() -> std::int32_t;
auto setVerseSteel(std::int32_t amount) -> bool;
auto undinium() const -> std::int32_t;
auto getUndinium() -> std::int32_t;
auto setUndinium(std::int32_t amount) -> bool;
auto necriumAlloy() const -> std::int32_t;
auto getNecriumAlloy() -> std::int32_t;
auto setNecriumAlloy(std::int32_t amount) -> bool;
auto lunarite() const -> std::int32_t;
auto getLunarite() -> std::int32_t;
auto setLunarite(std::int32_t amount) -> bool;
auto asterite() const -> std::int32_t;
auto getAsterite() -> std::int32_t;
auto setAsterite(std::int32_t amount) -> bool;
auto ednil() const -> std::int32_t;
auto getEdnil() -> std::int32_t;
auto setEdnil(std::int32_t amount) -> bool;
auto nuflalt() const -> std::int32_t;
auto getNuflalt() -> std::int32_t;
auto setNuflalt(std::int32_t amount) -> bool;
auto aurelene() const -> std::int32_t;
auto getAurelene() -> std::int32_t;
auto setAurelene(std::int32_t amount) -> bool;
auto soldus() const -> std::int32_t;
auto getSoldus() -> std::int32_t;
auto setSoldus(std::int32_t amount) -> bool;
auto synthesizedN() const -> std::int32_t;
auto getSynthesizedN() -> std::int32_t;
auto setSynthesizedN(std::int32_t amount) -> bool;
auto alcarbonite() const -> std::int32_t;
auto getAlcarbonite() -> std::int32_t;
auto setAlcarbonite(std::int32_t amount) -> bool;
auto keriphene() const -> std::int32_t;
auto getKeriphene() -> std::int32_t;
auto setKeriphene(std::int32_t amount) -> bool;
auto nitinolCM() const -> std::int32_t;
auto getNitinolCM() -> std::int32_t;
auto setNitinolCM(std::int32_t amount) -> bool;
auto quarkium() const -> std::int32_t;
auto getQuarkium() -> std::int32_t;
auto setQuarkium(std::int32_t amount) -> bool;
auto alterene() const -> std::int32_t;
auto getAlterene() -> std::int32_t;
auto setAlterene(std::int32_t amount) -> bool;
auto mixedComposition() const -> std::int32_t;
auto getMixedComposition() -> std::int32_t;
auto setMixedComposition(std::int32_t amount) -> bool;
auto voidResidue() const -> std::int32_t;
auto getVoidResidue() -> std::int32_t;
auto setVoidResidue(std::int32_t amount) -> bool;
auto muscularConstruction() const -> std::int32_t;
auto getMuscularConstruction() -> std::int32_t;
auto setMuscularConstruction(std::int32_t amount) -> bool;
auto mineralExoskeletology() const -> std::int32_t;
auto getMineralExoskeletology() -> std::int32_t;
auto setMineralExoskeletology(std::int32_t amount) -> bool;
auto carbonizedSkin() const -> std::int32_t;
auto getCarbonizedSkin() -> std::int32_t;
auto setCarbonizedSkin(std::int32_t amount) -> bool;
private:
std::string _profileDirectory;
std::string _filename;
ProfileType _type;
std::string _steamId;
bool _valid = false;
std::string _lastError = "";
std::string _companyName;
std::int8_t _activeFrameSlot = 0;
std::int32_t _credits;
std::int32_t _storyProgress;
std::int32_t _lastMissionId;
std::int32_t _verseSteel;
std::int32_t _undinium;
std::int32_t _necriumAlloy;
std::int32_t _lunarite;
std::int32_t _asterite;
std::int32_t _ednil;
std::int32_t _nuflalt;
std::int32_t _aurelene;
std::int32_t _soldus;
std::int32_t _synthesizedN;
std::int32_t _alcarbonite;
std::int32_t _keriphene;
std::int32_t _nitinolCM;
std::int32_t _quarkium;
std::int32_t _alterene;
std::int32_t _mixedComposition;
std::int32_t _voidResidue;
std::int32_t _muscularConstruction;
std::int32_t _mineralExoskeletology;
std::int32_t _carbonizedSkin;
};

View File

@ -0,0 +1,349 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cstdio>
#include <algorithm>
#include <chrono>
#include <iomanip>
#include <regex>
#include <Corrade/Containers/ScopeGuard.h>
#include <Corrade/Containers/StaticArray.h>
#include <Corrade/Utility/Directory.h>
#include <Corrade/Utility/FormatStl.h>
#include <Corrade/Utility/String.h>
#include <zip.h>
#include "ProfileManager.h"
using namespace Corrade;
ProfileManager::ProfileManager(const std::string& base_path):
_backupsDirectory{Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "backups")}
{
_saveDirectory = Utility::Directory::join(base_path, "Saved/SaveGames");
if(!Utility::Directory::exists(_backupsDirectory)) {
Utility::Directory::mkpath(_backupsDirectory);
}
if(!Utility::Directory::exists(_saveDirectory)) {
_lastError = "Couldn't find the profile directory. Make sure you played enough of the game.";
return;
}
_ready = refreshProfiles();
}
auto ProfileManager::ready() const -> bool {
return _ready;
}
auto ProfileManager::lastError() -> std::string const& {
return _lastError;
}
auto ProfileManager::saveDirectory() -> std::string const& {
return _saveDirectory;
}
auto ProfileManager::backupsDirectory() -> std::string const& {
return _backupsDirectory;
}
auto ProfileManager::profiles() -> std::vector<Profile> const& {
return _profiles;
}
auto ProfileManager::refreshProfiles() -> bool {
_profiles.clear();
using Utility::Directory::Flag;
std::vector<std::string> files = Utility::Directory::list(_saveDirectory, Flag::SkipSpecial|Flag::SkipDirectories|Flag::SkipDotAndDotDot);
auto predicate = [](const std::string& file)->bool{
std::regex regex("(Demo)?Profile[0-9]{17}\\.sav", std::regex::nosubs);
std::cmatch m;
return !std::regex_match(file.c_str(), m, regex);
};
files.erase(std::remove_if(files.begin(), files.end(), predicate), files.end());
for(const std::string& file : files) {
Profile profile{Utility::Directory::join(_saveDirectory, file)};
if(!profile.valid()) {
Utility::Warning{} << "Profile" << file.c_str() << "is invalid:" << profile.lastError().c_str();
continue;
}
_profiles.push_back(std::move(profile));
}
if(_profiles.empty()) {
_lastError = "No profiles were found.";
return false;
}
return true;
}
auto ProfileManager::getProfile(std::size_t index) -> Profile* {
return &(_profiles.at(index));
}
auto ProfileManager::deleteProfile(std::size_t index, bool delete_builds) -> bool {
if(!Utility::Directory::rm(Utility::Directory::join(_saveDirectory, _profiles.at(index).filename()))) {
_lastError = Utility::formatString("Couldn't delete {} (filename: {}).",
_profiles.at(index).companyName(),
_profiles.at(index).filename());
refreshProfiles();
return false;
}
if(delete_builds) {
for(std::uint8_t i = 0; i < 32; ++i) {
std::string filename = Utility::formatString("{}Unit{:.2d}{}.sav",
_profiles.at(index).type() == ProfileType::Demo ? "Demo": "",
i, _profiles.at(index).steamId());
Utility::Directory::rm(Utility::Directory::join(_saveDirectory, filename));
}
}
_profiles.erase(_profiles.cbegin() + index);
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(&timestamp);
std::string filename = Utility::formatString("{}_{}{:.2d}{:.2d}_{:.2d}{:.2d}{:.2d}.mbprofbackup",
Utility::String::replaceAll(_profiles.at(index).companyName(), " ", "_"),
time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
time->tm_hour, time->tm_min, time->tm_sec);
int error_code = 0;
zip_error_t error;
zip_t* zip = zip_open(Utility::Directory::join(_backupsDirectory, filename).c_str(), ZIP_CREATE|ZIP_TRUNCATE, &error_code);
if(zip == nullptr) {
zip_error_init_with_code(&error, error_code);
_lastError = zip_error_strerror(&error);
return false;
}
zip_source_t* profile_source = zip_source_file(zip, Utility::Directory::toNativeSeparators(Utility::Directory::join(_saveDirectory, _profiles.at(index).filename())).c_str(), 0, 0);
if(profile_source == nullptr) {
_lastError = zip_strerror(zip);
zip_source_free(profile_source);
return false;
}
if(zip_file_add(zip, _profiles.at(index).filename().c_str(), profile_source, ZIP_FL_ENC_UTF_8) == -1) {
_lastError = zip_strerror(zip);
zip_source_free(profile_source);
return false;
}
std::string comment = Utility::String::join({_profiles.at(index).companyName(),
_profiles.at(index).type() == ProfileType::Demo ? "demo" : "full",
Utility::formatString("{}-{:.2d}-{:.2d}-{:.2d}-{:.2d}-{:.2d}",
time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
time->tm_hour, time->tm_min, time->tm_sec)
}, '|');
zip_set_archive_comment(zip, comment.c_str(), comment.length());
if(backup_builds) {
for(std::uint8_t i = 0; i < 32; ++i) {
std::string build_filename = Utility::formatString("{}Unit{:.2d}{}.sav",
_profiles.at(index).type() == ProfileType::Demo ? "Demo": "",
i, _profiles.at(index).steamId());
if(!Utility::Directory::exists(Utility::Directory::join(_saveDirectory, build_filename))) {
continue;
}
zip_source_t* build_source = zip_source_file(zip, Utility::Directory::toNativeSeparators(Utility::Directory::join(_saveDirectory, build_filename)).c_str(), 0, 0);
if(build_source == nullptr) {
zip_source_free(build_source);
continue;
}
if(zip_file_add(zip, build_filename.c_str(), build_source, ZIP_FL_ENC_UTF_8) == -1) {
zip_source_free(build_source);
continue;
}
}
}
if(zip_close(zip) == -1) {
_lastError = zip_strerror(zip);
return false;
}
refreshBackups();
return true;
}
auto ProfileManager::backups() -> std::vector<Backup> const& {
return _backups;
}
void ProfileManager::refreshBackups() {
_backups.clear();
using Utility::Directory::Flag;
std::vector<std::string> files = Utility::Directory::list(_backupsDirectory, Flag::SkipSpecial|Flag::SkipDirectories|Flag::SkipDotAndDotDot);
auto predicate = [](const std::string& file)->bool{
return !Utility::String::endsWith(file, ".mbprofbackup");
};
files.erase(std::remove_if(files.begin(), files.end(), predicate), files.end());
int error_code = 0;
zip_t* zip = nullptr;
for(const std::string& file : files) {
Backup backup;
backup.filename = file;
zip = zip_open(Utility::Directory::join(_backupsDirectory, file).c_str(), ZIP_RDONLY, &error_code);
if(zip == nullptr) {
continue;
}
Containers::ScopeGuard guard{zip, zip_close};
int comment_length;
const char* comment = zip_get_archive_comment(zip, &comment_length, ZIP_FL_UNCHANGED);
if(comment == nullptr) {
continue;
}
auto info = Utility::String::split(comment, '|');
if(info.size() != 3) {
continue;
}
backup.company = info.at(0);
if(info.at(1) == "full") {
backup.type = ProfileType::FullGame;
}
else if(info.at(1) == "demo") {
backup.type = ProfileType::Demo;
}
else {
continue;
}
auto ts = Utility::String::split(info.at(2), '-');
if(ts.size() != 6) {
continue;
}
backup.timestamp.year = std::stoi(ts.at(0));
backup.timestamp.month = std::stoi(ts.at(1));
backup.timestamp.day = std::stoi(ts.at(2));
backup.timestamp.hour = std::stoi(ts.at(3));
backup.timestamp.minute = std::stoi(ts.at(4));
backup.timestamp.second = std::stoi(ts.at(5));
std::int64_t num_entries = zip_get_num_entries(zip, ZIP_FL_UNCHANGED);
if(num_entries == 0) {
continue;
}
backup.includedFiles.reserve(num_entries);
for(std::int64_t i = 0; i < num_entries; i++) {
backup.includedFiles.emplace_back(zip_get_name(zip, i, ZIP_FL_UNCHANGED));
}
_backups.push_back(std::move(backup));
}
}
auto ProfileManager::deleteBackup(std::size_t index) -> bool {
if(!Utility::Directory::rm(Utility::Directory::join(_backupsDirectory, _backups.at(index).filename))) {
_lastError = "Couldn't delete " + _backups.at(index).filename;
return false;
}
_backups.erase(_backups.begin() + index);
return true;
}
auto ProfileManager::restoreBackup(std::size_t index) -> bool {
const Backup& backup = _backups.at(index);
static const char* error_format = "Extraction of file {} failed: {}";
int error_code = 0;
zip_t* zip = nullptr;
zip = zip_open(Utility::Directory::join(_backupsDirectory, backup.filename).c_str(), ZIP_RDONLY, &error_code);
if(zip == nullptr) {
zip_error_t error;
zip_error_init_with_code(&error, error_code);
_lastError = zip_error_strerror(&error);
return false;
}
Containers::ScopeGuard zip_guard{zip, zip_close};
for(const std::string& file : backup.includedFiles) {
FILE* out = std::fopen(Utility::Directory::join(_saveDirectory, file).c_str(), "wb");
if(out == nullptr) {
_lastError = Utility::formatString(error_format, file, std::strerror(errno));
return false;
}
Containers::ScopeGuard out_guard{out, std::fclose};
zip_file_t* zf = zip_fopen(zip, file.c_str(), ZIP_FL_ENC_GUESS);
if(zf == nullptr) {
_lastError = Utility::formatString(error_format, file, zip_strerror(zip));
return false;
}
Containers::ScopeGuard zf_guard{zf, zip_fclose};
Containers::StaticArray<8192, char> buf{ValueInit};
std::int64_t bytes_read = 0;
while((bytes_read = zip_fread(zf, buf.data(), buf.size())) > 0) {
if(std::fwrite(buf.data(), sizeof(char), bytes_read, out) < static_cast<std::size_t>(bytes_read)) {
_lastError = Utility::formatString(error_format, file, "not enough bytes written.");
return false;
}
}
if(bytes_read == -1) {
_lastError = Utility::formatString(error_format, file, "couldn't read bytes from archive.");
return false;
}
}
return true;
}

View File

@ -0,0 +1,71 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <string>
#include <vector>
#include "../Profile/Profile.h"
struct Backup {
std::string filename;
std::string company;
ProfileType type;
struct {
int year;
int month;
int day;
int hour;
int minute;
int second;
} timestamp;
std::vector<std::string> includedFiles;
};
class ProfileManager {
public:
ProfileManager(const std::string& base_path);
auto ready() const -> bool;
auto lastError() -> std::string const&;
auto saveDirectory() -> std::string const&;
auto backupsDirectory() -> std::string const&;
auto profiles() -> std::vector<Profile> const&;
auto refreshProfiles() -> bool;
auto getProfile(std::size_t index) -> Profile*;
auto deleteProfile(std::size_t index, bool delete_builds) -> bool;
auto backupProfile(std::size_t index, bool backup_builds) -> bool;
auto backups() -> std::vector<Backup> const&;
void refreshBackups();
auto deleteBackup(std::size_t index) -> bool;
auto restoreBackup(std::size_t index) -> bool;
private:
bool _ready = false;
std::string _lastError = "";
std::string _saveDirectory;
const std::string _backupsDirectory;
std::vector<Profile> _profiles;
std::vector<Backup> _backups;
};

506
src/SaveTool/SaveTool.cpp Normal file
View File

@ -0,0 +1,506 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "SaveTool.h"
#include <cstring>
#include <cstdarg>
#include <Corrade/Utility/FormatStl.h>
#include <Corrade/Utility/String.h>
#include <Corrade/Utility/Unicode.h>
#include <Magnum/GL/DefaultFramebuffer.h>
#include <Magnum/GL/Renderer.h>
#include <Magnum/ImGuiIntegration/Integration.h>
#include <Magnum/ImGuiIntegration/Context.hpp>
#include <windef.h>
#include <winuser.h>
#include <processthreadsapi.h>
#include <shellapi.h>
#include "../FontAwesome/IconsFontAwesome5.h"
#include "../FontAwesome/IconsFontAwesome5Brands.h"
extern const ImVec2 center_pivot = {0.5f, 0.5f};
#ifdef MANAGER_DEBUG_BUILD
#include <Corrade/Utility/Tweakable.h>
#define _tw CORRADE_TWEAKABLE
Utility::Tweakable tweak;
#endif
SaveTool::SaveTool(const Arguments& arguments):
Platform::Sdl2Application{arguments,
Configuration{}.setTitle("M.A.S.S. Builder Save Tool version rewrite-0.0.1")
.setSize({960, 720})}
{
#ifdef MANAGER_DEBUG_BUILD
tweak.enable("", "../../");
#endif
if(SDL_VERSION_ATLEAST(2, 0, 5)) {
if(SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1") == SDL_TRUE) {
Utility::Debug{} << "Clickthrough is available.";
}
else {
Utility::Debug{} << "Clickthrough is not available (hint couldn't be set).";
}
}
else {
Utility::Debug{} << "Clickthrough is not available (SDL2 is too old).";
}
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,
GL::Renderer::BlendEquation::Add);
initialiseGui();
if((_initEventId = SDL_RegisterEvents(1)) == std::uint32_t(-1)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
"SDL_RegisterEvents failed in SaveTool::SaveTool(). Exiting...", nullptr);
exit(EXIT_FAILURE);
}
}
SaveTool::~SaveTool() {
SDL_RemoveTimer(_gameCheckTimerId);
}
void SaveTool::handleFileAction(efsw::WatchID watch_id,
const std::string& dir,
const std::string& filename,
efsw::Action action,
std::string old_filename)
{
if(watch_id == _watchIDs[StagingDir] && Utility::String::endsWith(filename, ".sav")) {
_massManager->refreshStagedMasses();
return;
}
if(Utility::String::endsWith(filename, "Config.sav")) {
return;
}
switch(action) {
case efsw::Actions::Add:
if(Utility::String::endsWith(filename, _currentProfile->steamId() + ".sav")) {
if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) {
int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) +
(filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30);
_massManager->refreshHangar(index);
}
}
break;
case efsw::Actions::Delete:
if(Utility::String::endsWith(filename, _currentProfile->steamId() + ".sav")) {
if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) {
int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) +
(filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30);
_massManager->refreshHangar(index);
}
}
break;
case efsw::Actions::Modified:
if(filename == _currentProfile->filename()) {
_currentProfile->refreshValues();
}
else if(Utility::String::endsWith(filename, _currentProfile->steamId() + ".sav")) {
if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) {
int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) +
(filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30);
_massManager->refreshHangar(index);
}
}
break;
case efsw::Actions::Moved:
if(Utility::String::endsWith(filename, _currentProfile->steamId() + ".sav")) {
if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) {
int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) +
(filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30);
_massManager->refreshHangar(index);
int old_index = ((old_filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) +
(old_filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30);
_massManager->refreshHangar(old_index);
}
}
break;
default:
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Unknown file watcher action type.", window());
break;
}
}
void SaveTool::drawEvent() {
#ifdef MANAGER_DEBUG_BUILD
tweak.update();
#endif
GL::defaultFramebuffer.clear(GL::FramebufferClear::Color);
drawImGui();
swapBuffers();
redraw();
}
void SaveTool::viewportEvent(ViewportEvent& event) {
GL::defaultFramebuffer.setViewport({{}, event.framebufferSize()});
_imgui.relayout(event.windowSize());
}
void SaveTool::keyPressEvent(KeyEvent& event) {
if(_imgui.handleKeyPressEvent(event)) return;
}
void SaveTool::keyReleaseEvent(KeyEvent& event) {
if(_imgui.handleKeyReleaseEvent(event)) return;
}
void SaveTool::mousePressEvent(MouseEvent& event) {
if(_imgui.handleMousePressEvent(event)) return;
}
void SaveTool::mouseReleaseEvent(MouseEvent& event) {
if(_imgui.handleMouseReleaseEvent(event)) return;
}
void SaveTool::mouseMoveEvent(MouseMoveEvent& event) {
if(_imgui.handleMouseMoveEvent(event)) return;
}
void SaveTool::mouseScrollEvent(MouseScrollEvent& event) {
if(_imgui.handleMouseScrollEvent(event)) {
event.setAccepted();
return;
}
}
void SaveTool::textInputEvent(TextInputEvent& event) {
if(_imgui.handleTextInputEvent(event)) return;
}
void SaveTool::anyEvent(SDL_Event& event) {
if(event.type == _initEventId) {
initEvent(event);
}
}
void SaveTool::initEvent(SDL_Event& event) {
_thread.join();
switch(event.user.code) {
case InitSuccess:
_uiState = UiState::ProfileManager;
ImGui::CloseCurrentPopup();
SDL_InitSubSystem(SDL_INIT_TIMER);
_mbManager->checkGameState();
_gameCheckTimerId = SDL_AddTimer(2000,
[](std::uint32_t interval, void* param)->std::uint32_t{
static_cast<MassBuilderManager*>(param)->checkGameState();
return interval;
},
_mbManager.get());
if(_gameCheckTimerId == 0) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", SDL_GetError(), window());
}
break;
case MbManagerFailure:
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising MassBuilderManager", _mbManager->lastError().c_str(), window());
exit(EXIT_FAILURE);
break;
case ProfileManagerFailure:
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising ProfileManager", _profileManager->lastError().c_str(), window());
exit(EXIT_FAILURE);
break;
default:
break;
}
}
void SaveTool::initialiseGui() {
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
auto reg_font = _rs.getRaw("SourceSansPro-Regular.ttf");
ImFontConfig font_config;
font_config.FontDataOwnedByAtlas = false;
std::strcpy(font_config.Name, "Source Sans Pro");
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(reg_font.data()), reg_font.size(), 20.0f, &font_config);
auto icon_font = _rs.getRaw(FONT_ICON_FILE_NAME_FAS);
static const ImWchar icon_range[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
ImFontConfig icon_config;
icon_config.FontDataOwnedByAtlas = false;
icon_config.MergeMode = true;
icon_config.PixelSnapH = true;
icon_config.OversampleH = icon_config.OversampleV = 1;
icon_config.GlyphMinAdvanceX = 18.0f;
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(icon_font.data()), icon_font.size(), 16.0f, &icon_config, icon_range);
auto brand_font = _rs.getRaw(FONT_ICON_FILE_NAME_FAB);
static const ImWchar brand_range[] = { ICON_MIN_FAB, ICON_MAX_FAB, 0 };
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(brand_font.data()), brand_font.size(), 16.0f, &icon_config, brand_range);
auto mono_font = _rs.getRaw("SourceCodePro-Regular.ttf");
ImVector<ImWchar> range;
ImFontGlyphRangesBuilder builder;
builder.AddRanges(io.Fonts->GetGlyphRangesDefault());
builder.AddChar(u'š'); // This allows displaying Vladimír Vondruš' name in Corrade's and Magnum's licences.
builder.BuildRanges(&range);
io.Fonts->AddFontFromMemoryTTF(const_cast<char*>(mono_font.data()), mono_font.size(), 18.0f, &font_config, range.Data);
_imgui = ImGuiIntegration::Context(*ImGui::GetCurrentContext(), windowSize());
io.IniFilename = nullptr;
ImGuiStyle& style = ImGui::GetStyle();
style.WindowTitleAlign = {0.5f, 0.5f};
style.FrameRounding = 3.2f;
style.Colors[ImGuiCol_WindowBg] = ImColor(0xff1f1f1f);
}
void SaveTool::initialiseManager() {
SDL_Event event;
SDL_zero(event);
event.type = _initEventId;
_mbManager.emplace();
if(!_mbManager->ready()) {
event.user.code = MbManagerFailure;
SDL_PushEvent(&event);
return;
}
_profileManager.emplace(_mbManager->saveDirectory());
if(!_profileManager->ready()) {
event.user.code = ProfileManagerFailure;
SDL_PushEvent(&event);
return;
}
event.user.code = InitSuccess;
SDL_PushEvent(&event);
}
void SaveTool::initialiseMassManager() {
_currentProfile->refreshValues();
_massManager.emplace(_profileManager->saveDirectory(),
_currentProfile->steamId(),
_currentProfile->type() == ProfileType::Demo);
initialiseFileWatcher();
}
void SaveTool::initialiseFileWatcher() {
_fileWatcher.emplace();
_watchIDs[SaveDir] = _fileWatcher->addWatch(_profileManager->saveDirectory(), this, false);
_watchIDs[StagingDir] = _fileWatcher->addWatch(_massManager->stagingAreaDirectory(), this, false);
_fileWatcher->watch();
}
void SaveTool::drawImGui() {
_imgui.newFrame();
if(ImGui::GetIO().WantTextInput && !isTextInputActive()) {
startTextInput();
}
else if(!ImGui::GetIO().WantTextInput && isTextInputActive()) {
stopTextInput();
}
drawGui();
_imgui.updateApplicationCursor(*this);
//GL::Renderer::enable(GL::Renderer::Feature::ScissorTest);
//GL::Renderer::disable(GL::Renderer::Feature::FaceCulling);
//GL::Renderer::disable(GL::Renderer::Feature::DepthTest);
_imgui.drawFrame();
//GL::Renderer::enable(GL::Renderer::Feature::DepthTest);
//GL::Renderer::enable(GL::Renderer::Feature::FaceCulling);
//GL::Renderer::disable(GL::Renderer::Feature::ScissorTest);
}
void SaveTool::drawGui() {
drawMainMenu();
if(_uiState == UiState::Disclaimer) {
drawDisclaimer();
}
else if(_uiState == UiState::Initialising) {
drawInitialisation();
}
else if(_uiState == UiState::ProfileManager) {
drawProfileManager();
}
else if(_uiState == UiState::MainManager) {
drawManager();
}
if(_aboutPopup) {
drawAbout();
}
#ifdef MANAGER_DEBUG_BUILD
if(_demoWindow) {
ImGui::ShowDemoWindow(&_demoWindow);
}
if(_styleEditor) {
ImGui::ShowStyleEditor(&ImGui::GetStyle());
}
if(_metricsWindow) {
ImGui::ShowMetricsWindow(&_metricsWindow);
}
#endif
}
void SaveTool::drawDisclaimer() {
ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f}}, ImGuiCond_Always, center_pivot);
if(ImGui::Begin("Disclaimer##DisclaimerWindow", nullptr,
ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove|
ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_MenuBar))
{
if(ImGui::BeginMenuBar()) {
ImGui::TextUnformatted("Disclaimer");
ImGui::EndMenuBar();
}
ImGui::TextUnformatted("Before you start using the app, there are a few things you should know:");
ImGui::PushTextWrapPos(windowSize().x() * 0.67f);
ImGui::Bullet();
ImGui::SameLine();
ImGui::TextUnformatted("For this application to work properly, it is recommended to disable Steam Cloud syncing for the game. To disable it, right-click the game in your Steam library, click \"Properties\", go to the \"General\" tab, and uncheck \"Keep game saves in the Steam Cloud for M.A.S.S. Builder\".");
ImGui::Bullet();
ImGui::SameLine();
ImGui::TextUnformatted("The developer of this application (Guillaume Jacquemin) isn't associated with Vermillion Digital, and both parties cannot be held responsible for data loss or corruption this app might cause. PLEASE USE AT YOUR OWN RISK!");
ImGui::Bullet();
ImGui::SameLine();
ImGui::TextUnformatted("This application is released under the terms of the GNU General Public Licence version 3. Please see the COPYING file for more details, or the About screen if you somehow didn't get that file with your download of the program.");
ImGui::Bullet();
ImGui::SameLine();
ImGui::TextUnformatted("This version of the application was tested on M.A.S.S. Builder early access version 0.6.5. It may or may not work with other versions of the game.");
ImGui::PopTextWrapPos();
if(ImGui::BeginTable("##DisclaimerLayoutTable", 3)) {
ImGui::TableSetupColumn("##Empty1", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##Button", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("##Empty2", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(1);
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, {24.0f, 12.0f});
if(ImGui::Button("I understand the risks")) {
_uiState = UiState::Initialising;
_thread = std::thread{[this]{ initialiseManager(); }};
}
ImGui::PopStyleVar();
ImGui::EndTable();
}
}
ImGui::End();
}
void SaveTool::drawInitialisation() {
ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f}}, ImGuiCond_Always, center_pivot);
if(ImGui::BeginPopupModal("##InitPopup", nullptr,
ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar))
{
ImGui::TextUnformatted("Initialising the manager. Please wait...");
ImGui::EndPopup();
}
ImGui::OpenPopup("##InitPopup");
}
void SaveTool::drawGameState() {
ImGui::TextUnformatted("Game state:");
ImGui::SameLine();
{
switch(_mbManager->gameState()) {
case GameState::Unknown:
ImGui::TextColored(ImColor{0xff00a5ff}, "unknown");
break;
case GameState::NotRunning:
ImGui::TextColored(ImColor{0xff32cd32}, "not running");
break;
case GameState::Running:
ImGui::TextColored(ImColor{0xff0000ff}, "running");
break;
}
}
}
void SaveTool::drawHelpMarker(const char* text, float wrap_pos) {
ImGui::TextUnformatted(ICON_FA_QUESTION_CIRCLE);
drawTooltip(text, wrap_pos);
}
void SaveTool::drawTooltip(const char* text, float wrap_pos) {
if(ImGui::IsItemHovered()){
ImGui::BeginTooltip();
if(wrap_pos > 0.0f) {
ImGui::PushTextWrapPos(wrap_pos);
}
ImGui::TextUnformatted(text);
if(wrap_pos > 0.0f) {
ImGui::PopTextWrapPos();
}
ImGui::EndTooltip();
}
}
void SaveTool::drawUnsafeText(const char* text, ...) {
va_list args;
va_start(args, text);
if(!_unsafeMode && _mbManager->gameState() != GameState::NotRunning) {
ImGui::TextDisabledV(text, args);
}
else {
ImGui::TextV(text, args);
}
va_end(args);
}
void SaveTool::openUri(const std::string& uri) {
ShellExecuteW(nullptr, nullptr, Utility::Unicode::widen(uri).c_str(), nullptr, nullptr, SW_SHOW);
}

168
src/SaveTool/SaveTool.h Normal file
View File

@ -0,0 +1,168 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <thread>
#include <Corrade/Containers/Pointer.h>
#include <Corrade/Utility/Resource.h>
#include <Magnum/Platform/Sdl2Application.h>
#include <Magnum/ImGuiIntegration/Context.h>
#include <SDL2/SDL.h>
#include <imgui.h>
#include <imgui_internal.h>
#include <efsw/efsw.hpp>
#include "../MassBuilderManager/MassBuilderManager.h"
#include "../ProfileManager/ProfileManager.h"
#include "../MassManager/MassManager.h"
using namespace Corrade;
using namespace Magnum;
class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener {
public:
explicit SaveTool(const Arguments& arguments);
~SaveTool() override;
void handleFileAction(efsw::WatchID watch_id,
const std::string& dir,
const std::string& filename,
efsw::Action action,
std::string old_filename = "") override;
private:
// Events
void drawEvent() override;
void viewportEvent(ViewportEvent& event) override;
void keyPressEvent(KeyEvent& event) override;
void keyReleaseEvent(KeyEvent& event) override;
void mousePressEvent(MouseEvent& event) override;
void mouseReleaseEvent(MouseEvent& event) override;
void mouseMoveEvent(MouseMoveEvent& event) override;
void mouseScrollEvent(MouseScrollEvent& event) override;
void textInputEvent(TextInputEvent& event) override;
void anyEvent(SDL_Event& event) override;
enum InitStatus: std::int32_t {
InitSuccess,
MbManagerFailure,
ProfileManagerFailure
};
void initEvent(SDL_Event& event);
// Initialisation methods
void initialiseGui();
void initialiseManager();
void initialiseMassManager();
void initialiseFileWatcher();
// GUI-related methods
void drawImGui();
void drawGui();
void drawMainMenu();
void drawDisclaimer();
void drawInitialisation();
void drawProfileManager();
auto drawBackupListPopup() -> ImGuiID;
auto drawBackupProfilePopup(std::size_t profile_index) -> ImGuiID;
auto drawDeleteProfilePopup(std::size_t profile_index) -> ImGuiID;
void drawManager();
auto drawIntEditPopup(int* value_to_edit, int max) -> bool;
auto drawRenamePopup(Containers::ArrayView<char> name_view) -> bool;
void drawGeneralInfo();
void drawResearchInventory();
void drawMassManager();
auto drawDeleteMassPopup(int mass_index) -> ImGuiID;
auto drawDeleteStagedMassPopup(const std::string& filename) -> ImGuiID;
void drawAbout();
void drawGameState();
// Convenience wrappers over ImGui stuff
void drawHelpMarker(const char* text, float wrap_pos = 0.0f);
void drawTooltip(const char* text, float wrap_pos = 0.0f);
template<typename Functor, typename... Args>
auto drawUnsafeWidget(Functor func, Args... args) -> bool {
if(!_unsafeMode && _mbManager->gameState() != GameState::NotRunning) {
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f);
}
bool result = func(std::forward<Args>(args)...);
if(!_unsafeMode && _mbManager->gameState() != GameState::NotRunning) {
ImGui::PopItemFlag();
ImGui::PopStyleVar();
}
return result;
} // Obviously, should only be used with ImGui widgets that return a bool.
// Also, should be called with a lambda if there are any default arguments, like ImGui::Button(), etc...
void drawUnsafeText(const char* text, ...); // Alternative to the above, for ImGui::Text*() variants.
void openUri(const std::string& uri);
Utility::Resource _rs{"assets"};
// GUI-related members
ImGuiIntegration::Context _imgui{NoCreate};
enum class UiState: uint8_t {
Disclaimer,
Initialising,
ProfileManager,
MainManager
};
UiState _uiState{UiState::Disclaimer};
bool _aboutPopup{false};
#ifdef MANAGER_DEBUG_BUILD
bool _demoWindow{false};
bool _styleEditor{false};
bool _metricsWindow{false};
#endif
std::thread _thread;
std::uint32_t _initEventId;
Containers::Pointer<MassBuilderManager> _mbManager;
SDL_TimerID _gameCheckTimerId;
Containers::Pointer<ProfileManager> _profileManager;
Profile* _currentProfile{nullptr};
Containers::Pointer<MassManager> _massManager;
Containers::Pointer<efsw::FileWatcher> _fileWatcher;
enum watchID {
SaveDir = 0,
StagingDir = 1
};
Containers::StaticArray<2, efsw::WatchID> _watchIDs;
bool _unsafeMode{false};
};

View File

@ -0,0 +1,694 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "SaveTool.h"
#include <algorithm>
#include <Corrade/Containers/Reference.h>
#include <Corrade/Utility/FormatStl.h>
#include <Corrade/Utility/String.h>
#include "../FontAwesome/IconsFontAwesome5.h"
#include "../Maps/LastMissionId.h"
#include "../Maps/StoryProgress.h"
static const std::string empty_str = "";
void SaveTool::drawManager() {
ImGui::SetNextWindowPos({0.0f, ImGui::GetItemRectSize().y}, ImGuiCond_Always);
ImGui::SetNextWindowSize({float(windowSize().x()), float(windowSize().y()) - ImGui::GetItemRectSize().y},
ImGuiCond_Always);
if(!ImGui::Begin("##MainWindow", nullptr,
ImGuiWindowFlags_NoDecoration|ImGuiWindowFlags_NoMove|
ImGuiWindowFlags_NoBackground|ImGuiWindowFlags_NoBringToFrontOnFocus))
{
ImGui::End();
return;
}
if(ImGui::BeginTable("##TopRow", 2)) {
ImGui::TableSetupColumn("##ProfileInfo", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##Unsafe", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::AlignTextToFramePadding();
ImGui::Text("Current profile: %s (%s)",
_currentProfile->companyName().c_str(),
_currentProfile->type() == ProfileType::Demo ? "demo" : "full game");
ImGui::SameLine();
if(ImGui::Button("Close")) {
_currentProfile = nullptr;
_massManager.reset();
_fileWatcher.reset();
_uiState = UiState::ProfileManager;
}
ImGui::TableSetColumnIndex(1);
ImGui::Checkbox("Unsafe mode", &_unsafeMode);
drawTooltip("Enabling this allows interactions deemed to be unsafe to do while the game is running.",
float(windowSize().x()) * 0.35f);
ImGui::EndTable();
}
ImGui::BeginGroup();
drawGeneralInfo();
ImGui::Dummy({0.0f, 0.0f});
drawResearchInventory();
ImGui::EndGroup();
ImGui::SameLine();
drawMassManager();
ImGui::End();
}
auto SaveTool::drawIntEditPopup(int* value_to_edit, int max) -> bool {
bool apply = false;
if(ImGui::BeginPopup("int_edit")) {
ImGui::Text("Please enter a value between 0 and %i:", max);
ImGui::AlignTextToFramePadding();
drawHelpMarker("You can either drag the widget left or right to change the value,\n"
"or click on it while holding Ctrl to edit the value directly.");
ImGui::SameLine();
drawUnsafeWidget([](auto... args){ return ImGui::DragInt("", args...); },
value_to_edit, 1.0f, 0, max, "%d", ImGuiSliderFlags_AlwaysClamp);
ImGui::SameLine();
if(drawUnsafeWidget([]{ return ImGui::Button("Apply"); })) {
apply = true;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
return apply;
}
auto SaveTool::drawRenamePopup(Containers::ArrayView<char> name_view) -> bool {
bool apply = false;
if(ImGui::BeginPopup("name_edit")) {
ImGui::TextUnformatted("Please enter a new name. Conditions:");
std::size_t len = std::strlen(name_view.data());
ImGui::BulletText("Length between 6 and 32 characters included. %s",
(len >= 6 && len <= 32) ? ICON_FA_CHECK : ICON_FA_TIMES);
ImGui::BulletText("Only A-Z, a-z, 0-9, -, and whitespaces. " ICON_FA_CHECK);
ImGui::BulletText("No whitespace at the beginning or end. %s",
(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))) {
return 0;
}
return 1;
};
drawUnsafeWidget([](auto... args){ return ImGui::InputText("", args...); },
name_view.data(), name_view.size(),
ImGuiInputTextFlags_CallbackCharFilter,
callback, nullptr);
ImGui::SameLine();
if((!_unsafeMode && _mbManager->gameState() != GameState::NotRunning) ||
!(len >= 6 && len <= 32) || !(name_view[0] != ' ' && name_view[len - 1] != ' '))
{
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f);
}
if(ImGui::Button("Apply")) {
apply = true;
ImGui::CloseCurrentPopup();
}
if((!_unsafeMode && _mbManager->gameState() != GameState::NotRunning) ||
!(len >= 6 && len <= 32) || !(name_view[0] != ' ' && name_view[len - 1] != ' '))
{
ImGui::PopItemFlag();
ImGui::PopStyleVar();
}
ImGui::EndPopup();
}
return apply;
}
void SaveTool::drawGeneralInfo() {
if(!_currentProfile) {
return;
}
if(!ImGui::BeginChild("##GeneralProfileInfo",
{ImGui::GetContentRegionAvailWidth() * 0.60f, (ImGui::GetContentRegionAvail().y - ImGui::GetStyle().ItemSpacing.x) / 2.0f},
true, ImGuiWindowFlags_MenuBar))
{
ImGui::EndChild();
return;
}
if(ImGui::BeginMenuBar()) {
ImGui::TextUnformatted("General profile information");
ImGui::EndMenuBar();
}
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())
{
ImGui::TextUnformatted("Story progress:");
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.x / 4.0f);
if(std::strcmp(it->after, "") == 0) {
ImGui::TextWrapped("%s - %s", it->chapter, it->point);
}
else {
ImGui::TextWrapped("%s - %s - %s", it->chapter, it->after, it->point);
}
}
else {
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()));
}
else if(_currentProfile->lastMissionId() == -1) {
ImGui::TextUnformatted("Last mission: none");
}
else {
ImGui::Text("Last mission: 0x%x", _currentProfile->lastMissionId());
}
drawTooltip("This is the last mission selected in the mission selection screen, not the last mission played.",
windowSize().x() * 0.35f);
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();
static Containers::StaticArray<33, char> name_buf{ValueInit};
if(drawUnsafeWidget([]{ return ImGui::Button("Rename company"); })) {
for(auto& c : name_buf) {
c = '\0';
}
std::strncpy(name_buf.data(), _currentProfile->companyName().c_str(), 32);
ImGui::OpenPopup("name_edit");
}
if(drawRenamePopup(name_buf)) {
if(!_currentProfile->renameCompany(name_buf.data())) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_currentProfile->lastError().c_str(), window());
}
}
ImGui::SameLine();
static std::int32_t credits;
if(drawUnsafeWidget([]{ return ImGui::Button("Edit credits"); })) {
credits = _currentProfile->credits();
ImGui::OpenPopup("int_edit");
}
if(drawIntEditPopup(&credits, 2000000)) {
if(!_currentProfile->setCredits(credits)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_currentProfile->lastError().c_str(), window());
}
}
ImGui::SameLine();
if(drawUnsafeWidget([]{ return ImGui::Button("Change story progression"); })) {
ImGui::OpenPopup("StoryProgressMenu");
}
drawTooltip("Story progress directly affects unlocked levels.");
if(ImGui::BeginPopup("StoryProgressMenu")) {
if(!_unsafeMode && _mbManager->gameState() != GameState::NotRunning) {
ImGui::CloseCurrentPopup();
}
for(const auto& sp : story_progress) {
if(ImGui::BeginMenu(sp.chapter)) {
if(std::strcmp(sp.after, "") == 0) {
if(ImGui::MenuItem(sp.point)) {
if(!_currentProfile->setStoryProgress(sp.id)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_currentProfile->lastError().c_str(), window());
}
}
}
else {
if(ImGui::BeginMenu(sp.after)) {
if(ImGui::MenuItem(sp.point)) {
if(!_currentProfile->setStoryProgress(sp.id)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_currentProfile->lastError().c_str(), window());
}
}
ImGui::EndMenu();
}
}
ImGui::EndMenu();
}
}
ImGui::EndPopup();
}
ImGui::EndChild();
}
void SaveTool::drawResearchInventory() {
if(!_currentProfile) {
return;
}
if(!ImGui::BeginChild("##ResearchInventory",
{ImGui::GetContentRegionAvailWidth() * 0.60f, 0.0f},
true, ImGuiWindowFlags_MenuBar))
{
ImGui::EndChild();
return;
}
if(ImGui::BeginMenuBar()) {
ImGui::TextUnformatted("Research inventory");
ImGui::EndMenuBar();
}
#define unavRow(name) \
ImGui::TableNextRow(); \
ImGui::TableSetColumnIndex(0); \
ImGui::TextUnformatted(name); \
ImGui::TableSetColumnIndex(1); \
ImGui::TextDisabled("Unavailable as of M.A.S.S. Builder version 0.6.5");
#define matRow(name, var, getter, setter) \
ImGui::TableNextRow(); \
ImGui::TableSetColumnIndex(0); \
ImGui::TextUnformatted((name)); \
ImGui::TableSetColumnIndex(1); \
if(_currentProfile->getter() != -1) { \
drawUnsafeText("%i", _currentProfile->getter()); \
ImGui::TableSetColumnIndex(2); \
ImGui::PushID(#setter); \
static std::int32_t var = _currentProfile->getter(); \
if(drawUnsafeWidget([]{ return ImGui::SmallButton(ICON_FA_EDIT); })) { \
(var) = _currentProfile->getter(); \
ImGui::OpenPopup("int_edit"); \
} \
if(drawIntEditPopup(&(var), 10000)) { \
if(!_currentProfile->set##setter((var))) { \
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", \
_currentProfile->lastError().c_str(), window()); \
} \
} \
ImGui::PopID(); \
} \
else { \
ImGui::TextDisabled("Not found in the save file"); \
}
if(ImGui::BeginTable("##ResearchInventoryTable", 3,
ImGuiTableFlags_BordersOuter|ImGuiTableFlags_ScrollY|ImGuiTableFlags_RowBg))
{
ImGui::TableSetupColumn("##Name", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("##Value", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##Edit", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableSetColumnIndex(0);
ImGui::Text("Engine materials");
matRow("Verse steel", verse_steel, verseSteel, VerseSteel)
matRow("Undinium", undinium, undinium, Undinium)
matRow("Necrium alloy", necrium_alloy, necriumAlloy, NecriumAlloy)
matRow("Lunarite", lunarite, lunarite, Lunarite)
matRow("Asterite", asterite, asterite, Asterite)
unavRow("Hallite fragma")
unavRow("Unnoctinium")
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableSetColumnIndex(0);
ImGui::Text("OS materials");
matRow("Ednil", ednil, ednil, Ednil)
matRow("Nuflalt", nuflalt, nuflalt, Nuflalt)
matRow("Aurelene", aurelene, aurelene, Aurelene)
matRow("Soldus", soldus, soldus, Soldus)
matRow("Synthesized N", synthesized_n, synthesizedN, SynthesizedN)
unavRow("Nanoc")
unavRow("Abyssillite")
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableSetColumnIndex(0);
ImGui::Text("Architect materials");
matRow("Alcarbonite", alcarbonite, alcarbonite, Alcarbonite)
matRow("Keriphene", keriphene, keriphene, Keriphene)
matRow("Nitinol-CM", nitinol_cm, nitinolCM, NitinolCM)
matRow("Quarkium", quarkium, quarkium, Quarkium)
matRow("Alterene", alterene, alterene, Alterene)
unavRow("Cosmium")
unavRow("Purified quarkium")
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableSetColumnIndex(0);
ImGui::Text("Quark data");
matRow("Mixed composition", mixed_composition, mixedComposition, MixedComposition)
matRow("Void residue", void_residue, voidResidue, VoidResidue)
matRow("Muscular construction", muscular_construction, muscularConstruction, MuscularConstruction)
matRow("Mineral exoskeletology", mineral_exoskeletology, mineralExoskeletology, MineralExoskeletology)
matRow("Carbonized skin", carbonized_skin, carbonizedSkin, CarbonizedSkin)
unavRow("Isolated void particle")
unavRow("Weaponised physiology")
ImGui::EndTable();
}
#undef unavRow
#undef matRow
ImGui::EndChild();
}
void SaveTool::drawMassManager() {
if(!_massManager) {
return;
}
if(!ImGui::BeginChild("##MASSManager", {0.0f, 0.0f},
true, ImGuiWindowFlags_MenuBar))
{
ImGui::EndChild();
return;
}
if(ImGui::BeginMenuBar()) {
ImGui::TextUnformatted("M.A.S.S. management");
drawHelpMarker("To move, import, or export builds, drag-and-drop them.");
ImGui::EndMenuBar();
}
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,
{0.0f, ImGui::GetContentRegionAvail().y * 0.45f}))
{
ImGui::TableSetupColumn("##Hangar", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("##MASSName", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##Active", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("##DeleteButton", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableSetColumnIndex(0);
ImGui::TextUnformatted("#");
ImGui::TableSetColumnIndex(1);
ImGui::TextUnformatted("Name");
for(int i = 0; i < 32; i++) {
ImGui::TableNextRow();
static int drag_drop_index = 0;
ImGui::TableSetColumnIndex(0);
ImGui::Selectable(Utility::formatString("{:.2d}", i + 1).c_str(),
false, ImGuiSelectableFlags_SpanAllColumns|ImGuiSelectableFlags_AllowItemOverlap);
if(_massManager->massState(i) == MassState::Valid &&
ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoHoldToOpenOthers))
{
drag_drop_index = i;
ImGui::SetDragDropPayload("Mass", &drag_drop_index, sizeof(int));
ImGui::Text("%s - Hangar %.2d", _massManager->massName(i).c_str(), i + 1);
ImGui::EndDragDropSource();
}
if((!_unsafeMode && _mbManager->gameState() == GameState::NotRunning) && ImGui::BeginDragDropTarget()) {
if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("StagedMass")) {
if(payload->DataSize != sizeof(std::string)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error",
"payload->DataSize != sizeof(std::string) in SaveTool::drawMassManager()",
window());
exit(EXIT_FAILURE);
}
std::string file = *(static_cast<std::string*>(payload->Data));
if(!_massManager->importMass(file, i)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error importing M.A.S.S.",
_massManager->lastError().c_str(), window());
}
}
else 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()",
window());
exit(EXIT_FAILURE);
}
int index = *(static_cast<int*>(payload->Data));
if(!_massManager->moveMass(index, i)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_massManager->lastError().c_str(),
window());
}
}
ImGui::EndDragDropTarget();
}
ImGui::TableSetColumnIndex(1);
switch(_massManager->massState(i)) {
case MassState::Empty:
ImGui::TextUnformatted("<empty>");
break;
case MassState::Invalid:
ImGui::TextUnformatted("<invalid>");
break;
case MassState::Valid:
ImGui::TextUnformatted(_massManager->massName(i).c_str());
break;
}
if(i == _currentProfile->activeFrameSlot()) {
ImGui::TableSetColumnIndex(2);
ImGui::TextUnformatted(ICON_FA_CHECK);
}
if(_massManager->massState(i) != MassState::Empty) {
ImGui::TableSetColumnIndex(3);
ImGui::PushID(i);
if(drawUnsafeWidget(ImGui::SmallButton, ICON_FA_TRASH_ALT)) {
mass_to_delete = i;
ImGui::OpenPopup(mass_deletion_popup_ID);
}
ImGui::PopID();
}
}
ImGui::EndTable();
}
drawDeleteMassPopup(mass_to_delete);
static ImGuiID staged_mass_deletion_popup_ID = drawDeleteStagedMassPopup("");
static Containers::Reference<const std::string> staged_mass_to_delete{empty_str};
if(ImGui::BeginTable("##StagingArea", 2,
ImGuiTableFlags_ScrollY|ImGuiTableFlags_BordersOuter|ImGuiTableFlags_RowBg))
{
ImGui::TableSetupColumn("##NameColumn", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##DeleteColumn", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableSetColumnIndex(0);
ImGui::TextUnformatted("Staging area");
ImGui::SameLine();
if(ImGui::SmallButton(ICON_FA_FOLDER_OPEN " Open staging folder")) {
openUri(_massManager->stagingAreaDirectory());
}
for(const auto& pair : _massManager->stagedMasses()) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
std::string staged_formatted = Utility::formatString("{} ({})", pair.second, pair.first);
ImGui::Selectable(staged_formatted.c_str());
if((ImGui::CalcTextSize(staged_formatted.c_str()).x + ImGui::GetStyle().FramePadding.x) > ImGui::GetContentRegionAvailWidth()) {
drawTooltip(staged_formatted.c_str());
}
if(ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) {
ImGui::SetDragDropPayload("StagedMass", &(pair.first), sizeof(std::string));
ImGui::Text("%s - Staged", pair.second.c_str());
ImGui::EndDragDropSource();
}
ImGui::TableSetColumnIndex(1);
ImGui::PushID(pair.first.c_str());
if(ImGui::SmallButton(ICON_FA_TRASH_ALT)) {
staged_mass_to_delete = Containers::Reference<const std::string>{pair.first};
ImGui::OpenPopup(staged_mass_deletion_popup_ID);
}
ImGui::PopID();
}
ImGui::EndTable();
}
if(ImGui::BeginDragDropTarget()) {
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()",
window());
exit(EXIT_FAILURE);
}
int index = *(static_cast<int*>(payload->Data));
if(!_massManager->exportMass(index)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_massManager->lastError().c_str(),
window());
}
}
ImGui::EndDragDropTarget();
}
drawDeleteStagedMassPopup(staged_mass_to_delete.get());
ImGui::EndChild();
}
auto SaveTool::drawDeleteMassPopup(int mass_index) -> ImGuiID {
if(!ImGui::BeginPopupModal("Confirmation##DeleteMassConfirmation", nullptr,
ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove))
{
return ImGui::GetID("Confirmation##DeleteMassConfirmation");
}
if(_massManager->massState(mass_index) == MassState::Empty) {
ImGui::CloseCurrentPopup();
ImGui::EndPopup();
return 0;
}
if(_mbManager->gameState() != GameState::NotRunning) {
ImGui::CloseCurrentPopup();
ImGui::EndPopup();
return 0;
}
ImGui::PushTextWrapPos(windowSize().x() * 0.40f);
if(_massManager->massState(mass_index) == MassState::Invalid) {
ImGui::Text("Are you sure you want to delete the invalid M.A.S.S. data in hangar %.2i ? This operation is irreversible.",
mass_index + 1);
}
else {
ImGui::Text("Are you sure you want to delete the M.A.S.S. named %s in hangar %.2i ? This operation is irreversible.",
_massManager->massName(mass_index).c_str(), mass_index + 1);
}
ImGui::PopTextWrapPos();
if(ImGui::BeginTable("##DeleteMassLayout", 2)) {
ImGui::TableSetupColumn("##Dummy", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##YesNo", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(1);
if(ImGui::Button("Yes")) {
if(!_massManager->deleteMass(mass_index)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when deleting M.A.S.S.",
_massManager->lastError().c_str(), window());
}
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if(ImGui::Button("No", ImGui::GetItemRectSize())) {
ImGui::CloseCurrentPopup();
}
ImGui::EndTable();
}
ImGui::EndPopup();
return 0;
}
auto SaveTool::drawDeleteStagedMassPopup(const std::string& filename) -> ImGuiID {
if(!ImGui::BeginPopupModal("Confirmation##DeleteStagedMassConfirmation", nullptr,
ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove))
{
return ImGui::GetID("Confirmation##DeleteStagedMassConfirmation");
}
ImGui::PushTextWrapPos(windowSize().x() * 0.40f);
ImGui::Text("Are you sure you want to delete the staged M.A.S.S. named %s ? This operation is irreversible.",
_massManager->stagedMasses().at(filename).c_str());
ImGui::PopTextWrapPos();
if(ImGui::BeginTable("##DeleteStagedMassLayout", 2)) {
ImGui::TableSetupColumn("##Dummy", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##YesNo", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(1);
if(ImGui::Button("Yes")) {
if(!_massManager->deleteStagedMass(filename)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when deleting M.A.S.S.",
_massManager->lastError().c_str(), window());
}
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if(ImGui::Button("No", ImGui::GetItemRectSize())) {
ImGui::CloseCurrentPopup();
}
ImGui::EndTable();
}
ImGui::EndPopup();
return 0;
}

View File

@ -0,0 +1,392 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "SaveTool.h"
#include <Magnum/ImGuiIntegration/Integration.h>
#include "../FontAwesome/IconsFontAwesome5.h"
extern const ImVec2 center_pivot;
void SaveTool::drawProfileManager() {
static std::size_t profile_index = 0;
ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f}}, ImGuiCond_Always, center_pivot);
if(ImGui::Begin("Profile management##ProfileManager", nullptr,
ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_AlwaysAutoResize|
ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_MenuBar))
{
if(ImGui::BeginMenuBar()) {
ImGui::TextUnformatted("Profile manager");
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);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::TextUnformatted("Here are the detected profiles:");
ImGui::TableSetColumnIndex(1);
if(ImGui::SmallButton("Refresh")) {
if(!_profileManager->refreshProfiles()) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error in ProfileManager", _profileManager->lastError().c_str(), window());
exit(EXIT_FAILURE);
}
}
ImGui::SameLine();
if(ImGui::SmallButton("Backups")) {
_profileManager->refreshBackups();
ImGui::OpenPopup(backup_list_popup_id);
}
ImGui::EndTable();
}
if(ImGui::BeginTable("##Profiles", 3, ImGuiTableFlags_BordersOuter|ImGuiTableFlags_RowBg)) {
ImGui::TableSetupColumn("Company name", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("##Buttons", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableSetColumnIndex(0);
ImGui::TextUnformatted("Company name");
ImGui::TableSetColumnIndex(1);
ImGui::TextUnformatted("Type");
ImGui::TableSetColumnIndex(2);
ImGui::TextUnformatted("Actions");
for(std::size_t i = 0; i < _profileManager->profiles().size(); ++i) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
if(ImGui::Selectable(_profileManager->profiles().at(i).companyName().c_str(), false,
ImGuiSelectableFlags_SpanAllColumns|ImGuiSelectableFlags_AllowItemOverlap))
{
_currentProfile = _profileManager->getProfile(i);
initialiseMassManager();
_uiState = UiState::MainManager;
}
ImGui::TableSetColumnIndex(1);
ImGui::TextUnformatted(_profileManager->profiles().at(i).type() == ProfileType::Demo ? "Demo" : "Full");
ImGui::TableSetColumnIndex(2);
ImGui::PushID(i);
if(ImGui::SmallButton(ICON_FA_FILE_ARCHIVE)) {
profile_index = i;
ImGui::OpenPopup(backup_popup_id);
}
ImGui::SameLine(0.0f, 2.0f);
if(ImGui::SmallButton(ICON_FA_TRASH_ALT)) {
profile_index = i;
ImGui::OpenPopup(delete_popup_id);
}
ImGui::PopID();
}
ImGui::EndTable();
}
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);
if(!ImGui::BeginPopupModal("Backups##BackupsModal", nullptr,
ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove))
{
return ImGui::GetID("Backups##BackupsModal");
}
static std::size_t backup_index;
if(ImGui::BeginPopupModal("Restore backup", nullptr,
ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize))
{
ImGui::PushTextWrapPos(windowSize().x() * 0.40f);
ImGui::Text("Are you sure you want to restore the %s backup from %.4i-%.2i-%.2i %.2i:%.2i:%.2i ? Any existing data will be overwritten.",
_profileManager->backups().at(backup_index).company.c_str(),
_profileManager->backups().at(backup_index).timestamp.year,
_profileManager->backups().at(backup_index).timestamp.month,
_profileManager->backups().at(backup_index).timestamp.day,
_profileManager->backups().at(backup_index).timestamp.hour,
_profileManager->backups().at(backup_index).timestamp.minute,
_profileManager->backups().at(backup_index).timestamp.second);
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)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when restoring backup",
_profileManager->lastError().c_str(), window());
}
_profileManager->refreshProfiles();
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(windowSize().x() * 0.40f);
ImGui::Text("Are you sure you want to delete the %s backup from %.4i-%.2i-%.2i %.2i:%.2i:%.2i ? This operation is irreversible.",
_profileManager->backups().at(backup_index).company.c_str(),
_profileManager->backups().at(backup_index).timestamp.year,
_profileManager->backups().at(backup_index).timestamp.month,
_profileManager->backups().at(backup_index).timestamp.day,
_profileManager->backups().at(backup_index).timestamp.hour,
_profileManager->backups().at(backup_index).timestamp.minute,
_profileManager->backups().at(backup_index).timestamp.second);
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)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when deleting backup",
_profileManager->lastError().c_str(), window());
}
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);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::TextUnformatted("Here's a list of detected backups:");
ImGui::TableSetColumnIndex(1);
if(ImGui::SmallButton("Refresh")) {
_profileManager->refreshBackups();
}
ImGui::EndTable();
}
if(ImGui::BeginTable("##Backups", 4,
ImGuiTableFlags_BordersOuter|ImGuiTableFlags_RowBg))
{
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("##CompanyName", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("##BackupDate", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("##Type", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("##Actions", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableSetColumnIndex(0);
ImGui::TextUnformatted("Company name");
ImGui::TableSetColumnIndex(1);
ImGui::TextUnformatted("Backup date");
ImGui::TableSetColumnIndex(2);
ImGui::TextUnformatted("Type");
ImGui::TableSetColumnIndex(3);
ImGui::TextUnformatted("Actions");
for(std::size_t i = 0; i < _profileManager->backups().size(); ++i) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::TextUnformatted(_profileManager->backups().at(i).company.c_str());
if(ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
for(const auto& file : _profileManager->backups().at(i).includedFiles) {
ImGui::TextUnformatted(file.c_str());
}
ImGui::EndTooltip();
}
ImGui::TableSetColumnIndex(1);
ImGui::Text("%.4i-%.2i-%.2i %.2i:%.2i:%.2i",
_profileManager->backups().at(i).timestamp.year,
_profileManager->backups().at(i).timestamp.month,
_profileManager->backups().at(i).timestamp.day,
_profileManager->backups().at(i).timestamp.hour,
_profileManager->backups().at(i).timestamp.minute,
_profileManager->backups().at(i).timestamp.second);
ImGui::TableSetColumnIndex(2);
ImGui::TextUnformatted(_profileManager->backups().at(i).type == ProfileType::Demo ? "Demo" : "Full");
ImGui::TableSetColumnIndex(3);
if(ImGui::SmallButton(ICON_FA_UNDO)) {
backup_index = i;
ImGui::OpenPopup(restore_backup_popup_id);
}
ImGui::SameLine(0.0f, 2.0f);
if(ImGui::SmallButton(ICON_FA_TRASH_ALT)) {
backup_index = i;
ImGui::OpenPopup(delete_backup_popup_id);
}
}
ImGui::EndTable();
}
ImGui::PushTextWrapPos(ImGui::GetWindowContentRegionWidth());
ImGui::TextUnformatted("Hover over a company name to see which files are included in the backup.");
ImGui::PopTextWrapPos();
if(ImGui::BeginTable("##BackupListCloseLayout", 2)) {
ImGui::TableSetupColumn("##Dummy", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##Close", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(1);
if(ImGui::Button("Close")) {
ImGui::CloseCurrentPopup();
}
ImGui::EndTable();
}
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");
}
ImGui::TextUnformatted("Should builds be added to the backup ?");
if(ImGui::BeginTable("##NameBackupLayout", 2)) {
ImGui::TableSetupColumn("##Dummy", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##YesNo", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(1);
if(ImGui::Button("Yes")) {
_profileManager->backupProfile(profile_index, true);
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if(ImGui::Button("No", ImGui::GetItemRectSize())) {
_profileManager->backupProfile(profile_index, false);
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if(ImGui::Button("Cancel")) {
ImGui::CloseCurrentPopup();
}
ImGui::EndTable();
}
ImGui::EndPopup();
return 0;
}
auto SaveTool::drawDeleteProfilePopup(std::size_t profile_index) -> ImGuiID {
if(!ImGui::BeginPopupModal("Confirmation##DeleteProfileConfirmation", nullptr,
ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoMove))
{
return ImGui::GetID("Confirmation##DeleteProfileConfirmation");
}
static bool delete_builds = false;
if(ImGui::IsWindowAppearing()) {
delete_builds = false;
}
ImGui::PushTextWrapPos(windowSize().x() * 0.40f);
ImGui::Text("Are you sure you want to delete the %s %s profile ? This operation is irreversible.",
_profileManager->profiles().at(profile_index).companyName().c_str(),
_profileManager->profiles().at(profile_index).type() == ProfileType::Demo ? "demo" : "full game");
ImGui::PopTextWrapPos();
if(ImGui::BeginTable("##DeleteProfileLayout", 2)) {
ImGui::TableSetupColumn("##Checkbox", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##YesNo", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::Checkbox("Delete builds", &delete_builds);
ImGui::TableSetColumnIndex(1);
if(ImGui::Button("Yes")) {
if(!_profileManager->deleteProfile(profile_index, delete_builds)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when deleting profile",
_profileManager->lastError().c_str(), window());
}
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if(ImGui::Button("No", ImGui::GetItemRectSize())) {
ImGui::CloseCurrentPopup();
}
ImGui::EndTable();
}
ImGui::EndPopup();
return 0;
}

View File

@ -0,0 +1,295 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "SaveTool.h"
#include <Corrade/version.h>
#include <Magnum/version.h>
#include <Magnum/versionIntegration.h>
#include <Magnum/ImGuiIntegration/Integration.h>
#include <zipconf.h>
#include "../FontAwesome/IconsFontAwesome5.h"
#include "../FontAwesome/IconsFontAwesome5Brands.h"
extern const ImVec2 center_pivot;
void SaveTool::drawAbout() {
ImGui::SetNextWindowPos(ImVec2{Vector2{windowSize() / 2.0f}}, ImGuiCond_Always, center_pivot);
ImGui::SetNextWindowSize({windowSize().x() * 0.8f, windowSize().y() * 0.75f}, ImGuiCond_Always);
ImGui::OpenPopup("About##AboutPopup");
if(!ImGui::BeginPopupModal("About##AboutPopup", &_aboutPopup,
ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoCollapse))
{
return;
}
if(ImGui::BeginTable("##TitleTable", 3)) {
ImGui::TableSetupColumn("##Empty1", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##Button", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("##Empty2", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(1);
ImGui::Text("%s (<insert codename here>)", SDL_GetWindowTitle(window()));
ImGui::EndTable();
}
ImGui::Dummy({0.0f, ImGui::GetFontSize()});
ImGui::TextWrapped("This application, made for the M.A.S.S. Builder community by Guillaume Jacquemin (aka William JCM), "
"is a rewrite of the wxWidgets-powered M.A.S.S. Builder Save Tool (formerly known as wxMASSManager).");
ImGui::AlignTextToFramePadding();
const char* repo = "https://github.com/williamjcm/MassBuilderSaveTool";
ImGui::Text(ICON_FA_GITHUB " %s", repo);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(repo);
}
ImGui::SameLine();
if(ImGui::Button("Open in browser")) {
openUri(repo);
}
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:");
if(ImGui::BeginChild("##GPL", {0.0f, windowSize().y() * 0.3f}, true)) {
static const auto licence = _rs.get("COPYING");
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
ImGui::TextUnformatted(licence.c_str());
ImGui::PopFont();
}
ImGui::EndChild();
}
if(ImGui::CollapsingHeader("Third-party components")) {
ImGui::TextWrapped("This application uses the following third-party components:");
ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 0.0f);
if(ImGui::TreeNodeEx("Corrade", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::Text("Version used: %s", CORRADE_VERSION_STRING);
ImGui::AlignTextToFramePadding();
const char* corrade_website = "https://magnum.graphics/corrade";
ImGui::Text(ICON_FA_GLOBE " %s", corrade_website);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(corrade_website);
}
ImGui::SameLine();
if(ImGui::Button("Open in browser")) {
openUri(corrade_website);
}
ImGui::TextUnformatted("Licence: MIT");
static const auto corrade_licence = _rs.get("COPYING.Corrade");
if(ImGui::BeginChild("##CorradeLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
ImGui::TextUnformatted(corrade_licence.c_str());
ImGui::PopFont();
}
ImGui::EndChild();
ImGui::TreePop();
}
if(ImGui::TreeNodeEx("Magnum and integration libraries", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::TextUnformatted("Versions used:");
ImGui::BulletText("Magnum: %s", MAGNUM_VERSION_STRING);
ImGui::BulletText("Integration: %s", MAGNUMINTEGRATION_VERSION_STRING);
ImGui::AlignTextToFramePadding();
const char* magnum_website = "https://magnum.graphics";
ImGui::Text(ICON_FA_GLOBE " %s", magnum_website);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(magnum_website);
}
ImGui::SameLine();
if(ImGui::Button("Open in browser")) {
openUri(magnum_website);
}
ImGui::TextUnformatted("Licence: MIT");
static const auto magnum_licence = _rs.get("COPYING.Magnum");
if(ImGui::BeginChild("##MagnumLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
ImGui::TextUnformatted(magnum_licence.c_str());
ImGui::PopFont();
}
ImGui::EndChild();
ImGui::TreePop();
}
if(ImGui::TreeNodeEx("Dear ImGui", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::Text("Version used: %s", IMGUI_VERSION);
ImGui::AlignTextToFramePadding();
const char* imgui_repo = "https://github.com/ocornut/imgui";
ImGui::Text(ICON_FA_GITHUB " %s", imgui_repo);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(imgui_repo);
}
ImGui::SameLine();
if(ImGui::Button("Open in browser")) {
openUri(imgui_repo);
}
ImGui::TextUnformatted("Licence: MIT");
static const auto imgui_licence = _rs.get("LICENSE.ImGui");
if(ImGui::BeginChild("##ImGuiLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
ImGui::TextUnformatted(imgui_licence.c_str());
ImGui::PopFont();
}
ImGui::EndChild();
ImGui::TreePop();
}
if(ImGui::TreeNodeEx("Simple DirectMedia Layer (SDL) 2", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::Text("Version used: %i.%i.%i", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
ImGui::AlignTextToFramePadding();
const char* sdl_website = "https://www.libsdl.org/";
ImGui::Text(ICON_FA_GLOBE " %s", sdl_website);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(sdl_website);
}
ImGui::SameLine();
if(ImGui::Button("Open in browser")) {
openUri(sdl_website);
}
ImGui::TextUnformatted("Licence: zlib");
static const auto sdl_licence = _rs.get("LICENSE.SDL");
if(ImGui::BeginChild("##SDLLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
ImGui::TextUnformatted(sdl_licence.c_str());
ImGui::PopFont();
}
ImGui::EndChild();
ImGui::TreePop();
}
if(ImGui::TreeNodeEx("libzip", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::Text("Version used: %s", LIBZIP_VERSION);
ImGui::AlignTextToFramePadding();
const char* libzip_website = "https://libzip.org/";
ImGui::Text(ICON_FA_GLOBE " %s", libzip_website);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(libzip_website);
}
ImGui::SameLine();
if(ImGui::Button("Open in browser")) {
openUri(libzip_website);
}
ImGui::TextUnformatted("Licence: 3-clause BSD");
static const auto libzip_licence = _rs.get("LICENSE.libzip");
if(ImGui::BeginChild("##libzipLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
ImGui::TextUnformatted(libzip_licence.c_str());
ImGui::PopFont();
}
ImGui::EndChild();
ImGui::TreePop();
}
if(ImGui::TreeNodeEx("Entropia File System Watcher (efsw)", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::AlignTextToFramePadding();
const char* efsw_repo = "https://github.com/SpartanJ/efsw";
ImGui::Text(ICON_FA_GITHUB " %s", efsw_repo);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(efsw_repo);
}
ImGui::SameLine();
if(ImGui::Button("Open in browser")) {
openUri(efsw_repo);
}
ImGui::TextUnformatted("Licence: MIT");
static const auto efsw_licence = _rs.get("LICENSE.efsw");
if(ImGui::BeginChild("##efswLicence", {0.0f, windowSize().y() * 0.3f}, true)) {
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
ImGui::TextUnformatted(efsw_licence.c_str());
ImGui::PopFont();
}
ImGui::EndChild();
ImGui::TreePop();
}
if(ImGui::TreeNodeEx("Font Awesome", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::TextUnformatted("Version used: 5.15.3");
ImGui::AlignTextToFramePadding();
const char* fa_website = "https://fontawesome.com/";
ImGui::Text(ICON_FA_GLOBE " %s", fa_website);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(fa_website);
}
ImGui::SameLine();
if(ImGui::Button("Open in browser")) {
openUri(fa_website);
}
ImGui::TextUnformatted("Licence: SIL Open Font License 1.1");
ImGui::TreePop();
}
if(ImGui::TreeNodeEx("IconFontCppHeaders", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::AlignTextToFramePadding();
const char* icon_repo = "https://github.com/juliettef/IconFontCppHeaders";
ImGui::Text(ICON_FA_GITHUB " %s", icon_repo);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(icon_repo);
}
ImGui::SameLine();
if(ImGui::Button("Open in browser")) {
openUri(icon_repo);
}
ImGui::TextUnformatted("Licence: zlib");
ImGui::TreePop();
}
ImGui::PopStyleVar();
}
ImGui::EndPopup();
}

View File

@ -0,0 +1,111 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "SaveTool.h"
#include <Corrade/Utility/Directory.h>
#include "../FontAwesome/IconsFontAwesome5.h"
#include "../FontAwesome/IconsFontAwesome5Brands.h"
void SaveTool::drawMainMenu() {
if(ImGui::BeginMainMenuBar()) {
if(ImGui::BeginMenu("Save Tool##SaveToolMenu")) {
if(ImGui::BeginMenu(ICON_FA_FOLDER_OPEN " Open data directory", _mbManager != nullptr)) {
if(ImGui::MenuItem(ICON_FA_COG " Configuration", nullptr, false, _mbManager != nullptr)) {
openUri(Utility::Directory::toNativeSeparators(_mbManager->saveDirectory() + "/Saved/Config/WindowsNoEditor"));
}
if(ImGui::MenuItem(ICON_FA_SAVE " Saves", nullptr, false, _profileManager != nullptr)) {
openUri(Utility::Directory::toNativeSeparators(_profileManager->saveDirectory()));
}
static bool _screenshotsAvailable = Utility::Directory::exists(_mbManager->saveDirectory() + "/Saved/Screenshots/WindowsNoEditor");
if(ImGui::MenuItem(ICON_FA_IMAGE " Screenshots", nullptr, false, _screenshotsAvailable)) {
openUri(Utility::Directory::toNativeSeparators(_mbManager->saveDirectory() + "/Screenshots/WindowsNoEditor"));
}
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/quS7E46");
}
if(ImGui::MenuItem("Community server")) {
openUri("https://discord.gg/YSSRTRB");
}
ImGui::EndMenu();
}
ImGui::EndMenu();
}
#ifdef MANAGER_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")) {
ImGui::MenuItem(ICON_FA_INFO_CIRCLE " About", nullptr, &_aboutPopup);
ImGui::EndMenu();
}
if(_mbManager != nullptr) {
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();
}
}

45
src/assets.conf Normal file
View File

@ -0,0 +1,45 @@
group=assets
[file]
filename=assets/SourceSansPro-Regular.ttf
alias=SourceSansPro-Regular.ttf
[file]
filename=assets/SourceCodePro-Regular.ttf
alias=SourceCodePro-Regular.ttf
[file]
filename=assets/fa-solid-900.ttf
alias=fa-solid-900.ttf
[file]
filename=assets/fa-brands-400.ttf
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

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
src/assets/fa-solid-900.ttf Normal file

Binary file not shown.

19
src/main.cpp Normal file
View File

@ -0,0 +1,19 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "SaveTool/SaveTool.h"
MAGNUM_SDL2APPLICATION_MAIN(SaveTool)

BIN
src/mbst.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
src/mbst.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

19
src/resource.rc Normal file
View File

@ -0,0 +1,19 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
MAINICON ICON "mbst.ico"
1 24 "Application.manifest"

1
third-party/SDL vendored Submodule

@ -0,0 +1 @@
Subproject commit d956636c85aafc055d37153d52370e9b1c2c5929

1
third-party/corrade vendored Submodule

@ -0,0 +1 @@
Subproject commit 76d54922ba22e6a1abfffb5c9dc1012dfbae149e

1
third-party/efsw vendored Submodule

@ -0,0 +1 @@
Subproject commit 6fb0c9ccd284445330723914249b7be46798ee76

1
third-party/imgui vendored Submodule

@ -0,0 +1 @@
Subproject commit 94b680e83052b9ff2e877360309020b72db057f2

1
third-party/libzip vendored Submodule

@ -0,0 +1 @@
Subproject commit 821cbfe1b260193563b04d3db1bb7eb4a4ed0d8a

1
third-party/magnum vendored Submodule

@ -0,0 +1 @@
Subproject commit dd3ce93888ada70074d63d72c5998d950fa2eba6

1
third-party/magnum-integration vendored Submodule

@ -0,0 +1 @@
Subproject commit 73018a6d5daf04ef69449e82fa757c3acf797b1b