diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..cf03968 --- /dev/null +++ b/.gitmodules @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b0d0a34 --- /dev/null +++ b/CMakeLists.txt @@ -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 . + +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) diff --git a/README.md b/README.md new file mode 100644 index 0000000..5e388a3 --- /dev/null +++ b/README.md @@ -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-.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`. diff --git a/modules/FindCorrade.cmake b/modules/FindCorrade.cmake new file mode 100644 index 0000000..9a36cce --- /dev/null +++ b/modules/FindCorrade.cmake @@ -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( +# ... +# [LIBRARIES ...] +# [FILES ...] +# [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 ``@``, in which case the ```` is +# treated as local filesystem location and ```` 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( ) +# +# 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 ```` 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( +# ";" +# ";" +# +# ...) +# +# The macro adds a preprocessor directive ``CORRADE_DYNAMIC_PLUGIN`` when +# compiling ````. Additional libraries can be linked in via +# :command:`target_link_libraries(plugin_name ...) `. +# On DLL platforms, the plugin DLLs and metadata files are put into +# ```` / ```` and the +# ``*.lib`` files into ```` / +# ````. On non-DLL platforms everything is put +# into ```` / ````. +# +# If the plugin interface disables plugin metadata files, the +# ```` can be set to ``""``, in which case no metadata file is +# copied anywhere. Otherwise the metadata file is copied and renamed to +# ````, retaining its original extension. +# +# corrade_add_plugin( +# +# +# +# ...) +# +# Unline the above version this puts everything into ```` on +# both DLL and non-DLL platforms. If ```` is set to +# :variable:`CMAKE_CURRENT_BINARY_DIR` (e.g. for testing purposes), the files +# are copied directly, without the need to perform install step. Note that the +# 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( +# ";" +# +# ...) +# +# The macro adds a preprocessor directive ``CORRADE_STATIC_PLUGIN`` when +# compiling ````. Additional libraries can be linked in via +# :command:`target_link_libraries(plugin_name ...) `. +# The ```` is ignored and included just for compatibility +# with the :command:`corrade_add_plugin` command, everything is installed into +# ````. 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 +# ```` can be set to ``""``, in which case no metadata file is +# used. Otherwise the metadata file is bundled and renamed to +# ````, retaining its original extension. +# +# corrade_add_static_plugin( +# +# +# ...) +# +# Equivalent to the above with ```` set to ````. +# If ```` 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( ...) +# +# 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š +# +# 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:$<$>>:wmainCRTStartup>$<$>: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() diff --git a/modules/FindImGui.cmake b/modules/FindImGui.cmake new file mode 100644 index 0000000..14f0e67 --- /dev/null +++ b/modules/FindImGui.cmake @@ -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š +# Copyright © 2018 Jonathan Hale +# +# 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) diff --git a/modules/FindMagnum.cmake b/modules/FindMagnum.cmake new file mode 100644 index 0000000..1c150aa --- /dev/null +++ b/modules/FindMagnum.cmake @@ -0,0 +1,1269 @@ +#.rst: +# Find Magnum +# ----------- +# +# Finds the Magnum library. Basic usage:: +# +# find_package(Magnum REQUIRED) +# +# This module tries to find the base Magnum library and then defines the +# following: +# +# Magnum_FOUND - Whether the base library was found +# MAGNUM_DEPLOY_PREFIX - Prefix where to put final application +# executables, defaults to ``.``. If a relative path is used, it's relative +# to :variable:`CMAKE_INSTALL_PREFIX`. +# MAGNUM_PLUGINS_DEBUG_DIR - Base directory with dynamic plugins for +# debug builds, defaults to magnum-d/ subdirectory of dir where Magnum +# library was found +# MAGNUM_PLUGINS_RELEASE_DIR - Base directory with dynamic plugins for +# release builds, defaults to magnum/ subdirectory of dir where Magnum +# library was found +# MAGNUM_PLUGINS_DIR - Base directory with dynamic plugins, defaults +# to :variable:`MAGNUM_PLUGINS_RELEASE_DIR` in release builds and +# multi-configuration builds or to :variable:`MAGNUM_PLUGINS_DEBUG_DIR` in +# debug builds +# MAGNUM_PLUGINS_FONT[|_DEBUG|_RELEASE]_DIR - Directory with dynamic font +# plugins +# MAGNUM_PLUGINS_FONTCONVERTER[|_DEBUG|_RELEASE]_DIR - Directory with dynamic +# font converter plugins +# MAGNUM_PLUGINS_IMAGECONVERTER[|_DEBUG|_RELEASE]_DIR - Directory with dynamic +# image converter plugins +# MAGNUM_PLUGINS_SCENECONVERTER[|_DEBUG|_RELEASE]_DIR - Directory with dynamic +# scene converter plugins +# MAGNUM_PLUGINS_IMPORTER[|_DEBUG|_RELEASE]_DIR - Directory with dynamic +# importer plugins +# MAGNUM_PLUGINS_AUDIOIMPORTER[|_DEBUG|_RELEASE]_DIR - Directory with dynamic +# audio importer plugins +# +# If Magnum is built for Emscripten, the following variables contain paths to +# various support files: +# +# MAGNUM_EMSCRIPTENAPPLICATION_JS - Path to the EmscriptenApplication.js file +# MAGNUM_WINDOWLESSEMSCRIPTENAPPLICATION_JS - Path to the +# WindowlessEmscriptenApplication.js file +# MAGNUM_WEBAPPLICATION_CSS - Path to the WebApplication.css file +# +# This command will try to find only the base library, not the optional +# components. The base library depends on Corrade and OpenGL libraries (or +# OpenGL ES libraries). Additional dependencies are specified by the +# components. The optional components are: +# +# AnyAudioImporter - Any audio importer +# AnyImageConverter - Any image converter +# AnyImageImporter - Any image importer +# AnySceneConverter - Any scene converter +# AnySceneImporter - Any scene importer +# Audio - Audio library +# DebugTools - DebugTools library +# GL - GL library +# MeshTools - MeshTools library +# Primitives - Primitives library +# SceneGraph - SceneGraph library +# Shaders - Shaders library +# ShaderTools - ShaderTools library +# Text - Text library +# TextureTools - TextureTools library +# Trade - Trade library +# Vk - Vk library +# AndroidApplication - Android application +# EmscriptenApplication - Emscripten application +# GlfwApplication - GLFW application +# GlxApplication - GLX application +# Sdl2Application - SDL2 application +# XEglApplication - X/EGL application +# WindowlessCglApplication - Windowless CGL application +# WindowlessEglApplication - Windowless EGL application +# WindowlessGlxApplication - Windowless GLX application +# WindowlessIosApplication - Windowless iOS application +# WindowlessWglApplication - Windowless WGL application +# WindowlessWindowsEglApplication - Windowless Windows/EGL application +# CglContext - CGL context +# EglContext - EGL context +# GlxContext - GLX context +# WglContext - WGL context +# OpenGLTester - OpenGLTester class +# VulkanTester - VulkanTester class +# MagnumFont - Magnum bitmap font plugin +# MagnumFontConverter - Magnum bitmap font converter plugin +# ObjImporter - OBJ importer plugin +# TgaImageConverter - TGA image converter plugin +# TgaImporter - TGA importer plugin +# WavAudioImporter - WAV audio importer plugin +# distancefieldconverter - magnum-distancefieldconverter executable +# fontconverter - magnum-fontconverter executable +# imageconverter - magnum-imageconverter executable +# sceneconverterter - magnum-sceneconverter executable +# shaderconverterter - magnum-shaderconverter executable +# gl-info - magnum-gl-info executable +# vk-info - magnum-vk-info executable +# al-info - magnum-al-info executable +# +# Example usage with specifying additional components is:: +# +# find_package(Magnum REQUIRED Trade MeshTools Primitives GlfwApplication) +# +# For each component is then defined: +# +# Magnum_*_FOUND - Whether the component was found +# Magnum::* - Component imported target +# +# If exactly one ``*Application`` or exactly one ``Windowless*Application`` +# component is requested and found, its target is available in convenience +# alias ``Magnum::Application`` / ``Magnum::WindowlessApplication`` to simplify +# porting. Similarly, if exactly one ``*Context`` component is requested and +# found, its target is available in convenience alias ``Magnum::GLContext``. +# +# The package is found if either debug or release version of each requested +# library (or plugin) is found. If both debug and release libraries (or +# plugins) 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). Note that this autodetection might fail +# for the :variable:`MAGNUM_PLUGINS_DIR` variable, especially on +# multi-configuration build systems. You can make use of +# ``CORRADE_IS_DEBUG_BUILD`` preprocessor variable along with +# ``MAGNUM_PLUGINS_*_DEBUG_DIR`` / ``MAGNUM_PLUGINS_*_RELEASE_DIR`` variables +# to decide in preprocessing step. +# +# Features of found Magnum library are exposed in these variables: +# +# MAGNUM_BUILD_DEPRECATED - Defined if compiled with deprecated APIs +# included +# MAGNUM_BUILD_STATIC - Defined if compiled as static libraries +# MAGNUM_BUILD_STATIC_UNIQUE_GLOBALS - Defined if static libraries keep the +# globals unique even across different shared libraries +# MAGNUM_TARGET_GL - Defined if compiled with OpenGL interop +# MAGNUM_TARGET_GLES - Defined if compiled for OpenGL ES +# MAGNUM_TARGET_GLES2 - Defined if compiled for OpenGL ES 2.0 +# MAGNUM_TARGET_GLES3 - Defined if compiled for OpenGL ES 3.0 +# MAGNUM_TARGET_DESKTOP_GLES - Defined if compiled with OpenGL ES +# emulation on desktop OpenGL +# MAGNUM_TARGET_WEBGL - Defined if compiled for WebGL +# MAGNUM_TARGET_HEADLESS - Defined if compiled for headless machines +# MAGNUM_TARGET_VK - Defined if compiled with Vulkan interop +# +# The following variables are provided for backwards compatibility purposes +# only when MAGNUM_BUILD_DEPRECATED is enabled and will be removed in a future +# release: +# +# MAGNUM_BUILD_MULTITHREADED - Alias to CORRADE_BUILD_MULTITHREADED. Use +# CORRADE_BUILD_MULTITHREADED instead. +# +# Additionally these variables are defined for internal usage: +# +# MAGNUM_INCLUDE_DIR - Root include dir (w/o dependencies) +# MAGNUM_LIBRARY - Magnum library (w/o dependencies) +# MAGNUM_LIBRARY_DEBUG - Debug version of Magnum library, if found +# MAGNUM_LIBRARY_RELEASE - Release version of Magnum library, if found +# MAGNUM_*_LIBRARY - Component libraries (w/o dependencies) +# MAGNUM_*_LIBRARY_DEBUG - Debug version of given library, if found +# MAGNUM_*_LIBRARY_RELEASE - Release version of given library, if found +# MAGNUM_BINARY_INSTALL_DIR - Binary installation directory +# MAGNUM_LIBRARY_INSTALL_DIR - Library installation directory +# MAGNUM_DATA_INSTALL_DIR - Data installation directory +# MAGNUM_PLUGINS_[DEBUG|RELEASE]_BINARY_INSTALL_DIR - Plugin binary +# installation directory +# MAGNUM_PLUGINS_[DEBUG|RELEASE]_LIBRARY_INSTALL_DIR - Plugin library +# installation directory +# MAGNUM_PLUGINS_SHADERCONVERTER_[DEBUG|RELEASE]_BINARY_INSTALL_DIR - Shader +# converter plugin binary installation directory +# MAGNUM_PLUGINS_SHADERCONVERTER_[DEBUG|RELEASE]_LIBRARY_INSTALL_DIR - Shader +# converter plugin library installation directory +# MAGNUM_PLUGINS_FONT_[DEBUG|RELEASE]_BINARY_INSTALL_DIR - Font plugin binary +# installation directory +# MAGNUM_PLUGINS_FONT_[DEBUG|RELEASE]_LIBRARY_INSTALL_DIR - Font plugin +# library installation directory +# MAGNUM_PLUGINS_FONTCONVERTER_[DEBUG|RELEASE]_BINARY_INSTALL_DIR - Font +# converter plugin binary installation directory +# MAGNUM_PLUGINS_FONTCONVERTER_[DEBUG|RELEASE]_LIBRARY_INSTALL_DIR - Font +# converter plugin library installation directory +# MAGNUM_PLUGINS_IMAGECONVERTER_[DEBUG|RELEASE]_BINARY_INSTALL_DIR - Image +# converter plugin binary installation directory +# MAGNUM_PLUGINS_IMAGECONVERTER_[DEBUG|RELEASE]_LIBRARY_INSTALL_DIR - Image +# converter plugin library installation directory +# MAGNUM_PLUGINS_IMPORTER_[DEBUG|RELEASE]_BINARY_INSTALL_DIR - Importer +# plugin binary installation directory +# MAGNUM_PLUGINS_IMPORTER_[DEBUG|RELEASE]_LIBRARY_INSTALL_DIR - Importer +# plugin library installation directory +# MAGNUM_PLUGINS_SCENECONVERTER_[DEBUG|RELEASE]_BINARY_INSTALL_DIR - Scene +# converter plugin binary installation directory +# MAGNUM_PLUGINS_SCENECONVERTER_[DEBUG|RELEASE]_LIBRARY_INSTALL_DIR - Scene +# converter plugin library installation directory +# MAGNUM_PLUGINS_AUDIOIMPORTER_[DEBUG|RELEASE]_BINARY_INSTALL_DIR - Audio +# importer plugin binary installation directory +# MAGNUM_PLUGINS_AUDIOIMPORTER_[DEBUG|RELEASE]_LIBRARY_INSTALL_DIR - Audio +# importer plugin library installation directory +# MAGNUM_INCLUDE_INSTALL_DIR - Header installation directory +# MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR - Plugin header installation directory +# + +# +# This file is part of Magnum. +# +# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, +# 2020, 2021 Vladimír Vondruš +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# 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. +# + +# Corrade library dependencies +set(_MAGNUM_CORRADE_DEPENDENCIES ) +foreach(_component ${Magnum_FIND_COMPONENTS}) + string(TOUPPER ${_component} _COMPONENT) + + # Unrolling the transitive dependencies here so this doesn't need to be + # after resolving inter-component dependencies. Listing also all plugins. + if(_component MATCHES "^(Audio|DebugTools|MeshTools|Primitives|ShaderTools|Text|TextureTools|Trade|.+Importer|.+ImageConverter|.+Font|.+ShaderConverter)$") + set(_MAGNUM_${_COMPONENT}_CORRADE_DEPENDENCIES PluginManager) + endif() + + list(APPEND _MAGNUM_CORRADE_DEPENDENCIES ${_MAGNUM_${_COMPONENT}_CORRADE_DEPENDENCIES}) +endforeach() +find_package(Corrade REQUIRED Utility ${_MAGNUM_CORRADE_DEPENDENCIES}) + +# Root include dir +find_path(MAGNUM_INCLUDE_DIR + NAMES Magnum/Magnum.h) +mark_as_advanced(MAGNUM_INCLUDE_DIR) + +# Configuration file +find_file(_MAGNUM_CONFIGURE_FILE configure.h + HINTS ${MAGNUM_INCLUDE_DIR}/Magnum/) +mark_as_advanced(_MAGNUM_CONFIGURE_FILE) + +# We need to open configure.h file from MAGNUM_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 MAGNUM_INCLUDE_DIR) + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Magnum + REQUIRED_VARS MAGNUM_INCLUDE_DIR _MAGNUM_CONFIGURE_FILE) +endif() + +# Read flags from configuration +file(READ ${_MAGNUM_CONFIGURE_FILE} _magnumConfigure) +string(REGEX REPLACE ";" "\\\\;" _magnumConfigure "${_magnumConfigure}") +string(REGEX REPLACE "\n" ";" _magnumConfigure "${_magnumConfigure}") +set(_magnumFlags + BUILD_DEPRECATED + BUILD_STATIC + BUILD_STATIC_UNIQUE_GLOBALS + TARGET_GL + TARGET_GLES + TARGET_GLES2 + TARGET_GLES3 + TARGET_DESKTOP_GLES + TARGET_WEBGL + TARGET_HEADLESS + TARGET_VK) +foreach(_magnumFlag ${_magnumFlags}) + list(FIND _magnumConfigure "#define MAGNUM_${_magnumFlag}" _magnum_${_magnumFlag}) + if(NOT _magnum_${_magnumFlag} EQUAL -1) + set(MAGNUM_${_magnumFlag} 1) + endif() +endforeach() + +# For compatibility only, to be removed at some point +if(MAGNUM_BUILD_DEPRECATED AND CORRADE_BUILD_MULTITHREADED) + set(MAGNUM_BUILD_MULTITHREADED 1) +endif() + +# OpenGL library preference. Prefer to use GLVND, since that's the better +# approach nowadays, but allow the users to override it from outside in case +# it is broken for some reason (Nvidia drivers in Debian's testing (Buster) -- +# reported on 2019-04-09). +if(NOT CMAKE_VERSION VERSION_LESS 3.10 AND NOT OpenGL_GL_PREFERENCE) + set(OpenGL_GL_PREFERENCE GLVND) +endif() + +# Base Magnum library +if(NOT TARGET Magnum::Magnum) + add_library(Magnum::Magnum UNKNOWN IMPORTED) + + # Try to find both debug and release version + find_library(MAGNUM_LIBRARY_DEBUG Magnum-d) + find_library(MAGNUM_LIBRARY_RELEASE Magnum) + mark_as_advanced(MAGNUM_LIBRARY_DEBUG + MAGNUM_LIBRARY_RELEASE) + + # Set the MAGNUM_LIBRARY variable based on what was found, use that + # information to guess also build type of dynamic plugins + if(MAGNUM_LIBRARY_DEBUG AND MAGNUM_LIBRARY_RELEASE) + set(MAGNUM_LIBRARY ${MAGNUM_LIBRARY_RELEASE}) + get_filename_component(_MAGNUM_PLUGINS_DIR_PREFIX ${MAGNUM_LIBRARY_DEBUG} PATH) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(_MAGNUM_PLUGINS_DIR_SUFFIX "-d") + endif() + elseif(MAGNUM_LIBRARY_DEBUG) + set(MAGNUM_LIBRARY ${MAGNUM_LIBRARY_DEBUG}) + get_filename_component(_MAGNUM_PLUGINS_DIR_PREFIX ${MAGNUM_LIBRARY_DEBUG} PATH) + set(_MAGNUM_PLUGINS_DIR_SUFFIX "-d") + elseif(MAGNUM_LIBRARY_RELEASE) + set(MAGNUM_LIBRARY ${MAGNUM_LIBRARY_RELEASE}) + get_filename_component(_MAGNUM_PLUGINS_DIR_PREFIX ${MAGNUM_LIBRARY_RELEASE} PATH) + endif() + + # On DLL platforms the plugins are stored in bin/ instead of lib/, modify + # _MAGNUM_PLUGINS_DIR_PREFIX accordingly + if(CORRADE_TARGET_WINDOWS) + get_filename_component(_MAGNUM_PLUGINS_DIR_PREFIX ${_MAGNUM_PLUGINS_DIR_PREFIX} PATH) + set(_MAGNUM_PLUGINS_DIR_PREFIX ${_MAGNUM_PLUGINS_DIR_PREFIX}/bin) + endif() + + if(MAGNUM_LIBRARY_RELEASE) + set_property(TARGET Magnum::Magnum APPEND PROPERTY + IMPORTED_CONFIGURATIONS RELEASE) + set_property(TARGET Magnum::Magnum PROPERTY + IMPORTED_LOCATION_RELEASE ${MAGNUM_LIBRARY_RELEASE}) + endif() + + if(MAGNUM_LIBRARY_DEBUG) + set_property(TARGET Magnum::Magnum APPEND PROPERTY + IMPORTED_CONFIGURATIONS DEBUG) + set_property(TARGET Magnum::Magnum PROPERTY + IMPORTED_LOCATION_DEBUG ${MAGNUM_LIBRARY_DEBUG}) + endif() + + # Include directories + set_property(TARGET Magnum::Magnum APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES + ${MAGNUM_INCLUDE_DIR}) + + # Dependent libraries + set_property(TARGET Magnum::Magnum APPEND PROPERTY INTERFACE_LINK_LIBRARIES + Corrade::Utility) +else() + set(MAGNUM_LIBRARY Magnum::Magnum) +endif() + +# Component distinction (listing them explicitly to avoid mistakes with finding +# components from other repositories) +set(_MAGNUM_LIBRARY_COMPONENTS + Audio DebugTools GL MeshTools Primitives SceneGraph Shaders ShaderTools + Text TextureTools Trade + WindowlessEglApplication EglContext OpenGLTester) +set(_MAGNUM_PLUGIN_COMPONENTS + AnyAudioImporter AnyImageConverter AnyImageImporter AnySceneConverter + AnySceneImporter MagnumFont MagnumFontConverter ObjImporter + TgaImageConverter TgaImporter WavAudioImporter) +set(_MAGNUM_EXECUTABLE_COMPONENTS + imageconverter sceneconverter shaderconverter gl-info al-info) +# Audio and Vk libs aren't enabled by default, and none of the Context, +# Application, Tester libs nor plugins are. Keep in sync with Magnum's root +# CMakeLists.txt. +set(_MAGNUM_IMPLICITLY_ENABLED_COMPONENTS + DebugTools MeshTools SceneGraph Shaders ShaderTools Text TextureTools Trade + GL Primitives) +if(NOT CORRADE_TARGET_EMSCRIPTEN) + list(APPEND _MAGNUM_LIBRARY_COMPONENTS Vk VulkanTester) + list(APPEND _MAGNUM_EXECUTABLE_COMPONENTS vk-info) +endif() +if(NOT CORRADE_TARGET_ANDROID) + list(APPEND _MAGNUM_LIBRARY_COMPONENTS Sdl2Application) +endif() +if(NOT CORRADE_TARGET_ANDROID AND NOT CORRADE_TARGET_IOS AND NOT CORRADE_TARGET_EMSCRIPTEN) + list(APPEND _MAGNUM_LIBRARY_COMPONENTS GlfwApplication) +endif() +if(CORRADE_TARGET_ANDROID) + list(APPEND _MAGNUM_LIBRARY_COMPONENTS AndroidApplication) +endif() +if(CORRADE_TARGET_EMSCRIPTEN) + list(APPEND _MAGNUM_LIBRARY_COMPONENTS EmscriptenApplication) +endif() +if(CORRADE_TARGET_IOS) + list(APPEND _MAGNUM_LIBRARY_COMPONENTS WindowlessIosApplication) +endif() +if(CORRADE_TARGET_APPLE AND NOT CORRADE_TARGET_IOS) + list(APPEND _MAGNUM_LIBRARY_COMPONENTS WindowlessCglApplication CglContext) +endif() +if(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE) + list(APPEND _MAGNUM_LIBRARY_COMPONENTS GlxApplication XEglApplication WindowlessGlxApplication GlxContext) +endif() +if(CORRADE_TARGET_WINDOWS) + list(APPEND _MAGNUM_LIBRARY_COMPONENTS WindowlessWglApplication WglContext WindowlessWindowsEglApplication) +endif() +if(CORRADE_TARGET_UNIX OR CORRADE_TARGET_WINDOWS) + list(APPEND _MAGNUM_EXECUTABLE_COMPONENTS fontconverter distancefieldconverter) +endif() + +# Inter-component dependencies +set(_MAGNUM_Audio_DEPENDENCIES ) + +# Trade is used by CompareImage. If Trade is not enabled, CompareImage is not +# compiled at all. +set(_MAGNUM_DebugTools_DEPENDENCIES Trade) +set(_MAGNUM_DebugTools_Trade_DEPENDENCY_IS_OPTIONAL ON) +# MeshTools, Primitives, SceneGraph and Shaders are used only for GL renderers +# in DebugTools. All of this is optional, compiled in only if the base library +# was selected. +if(MAGNUM_TARGET_GL) + list(APPEND _MAGNUM_DebugTools_DEPENDENCIES MeshTools Primitives SceneGraph Shaders GL) + set(_MAGNUM_DebugTools_MeshTools_DEPENDENCY_IS_OPTIONAL ON) + set(_MAGNUM_DebugTools_Primitives_DEPENDENCY_IS_OPTIONAL ON) + set(_MAGNUM_DebugTools_SceneGraph_DEPENDENCY_IS_OPTIONAL ON) + set(_MAGNUM_DebugTools_Shaders_DEPENDENCY_IS_OPTIONAL ON) + set(_MAGNUM_DebugTools_GL_DEPENDENCY_IS_OPTIONAL ON) +endif() + +set(_MAGNUM_MeshTools_DEPENDENCIES Trade) +if(MAGNUM_TARGET_GL) + list(APPEND _MAGNUM_MeshTools_DEPENDENCIES GL) +endif() + +set(_MAGNUM_OpenGLTester_DEPENDENCIES GL) +if(MAGNUM_TARGET_HEADLESS OR CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID) + list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessEglApplication) +elseif(CORRADE_TARGET_IOS) + list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessIosApplication) +elseif(CORRADE_TARGET_APPLE) + list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessCglApplication) +elseif(CORRADE_TARGET_UNIX) + if(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES) + list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessEglApplication) + else() + list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessGlxApplication) + endif() +elseif(CORRADE_TARGET_WINDOWS) + if(NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES) + list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessWglApplication) + else() + list(APPEND _MAGNUM_OpenGLTester_DEPENDENCIES WindowlessWindowsEglApplication) + endif() +endif() + +set(_MAGNUM_Primitives_DEPENDENCIES MeshTools Trade) +if(MAGNUM_TARGET_GL) + # GL not required by Primitives themselves, but transitively by MeshTools + list(APPEND _MAGNUM_Primitives_DEPENDENCIES GL) +endif() +set(_MAGNUM_SceneGraph_DEPENDENCIES ) +set(_MAGNUM_Shaders_DEPENDENCIES GL) +set(_MAGNUM_Text_DEPENDENCIES TextureTools) +if(MAGNUM_TARGET_GL) + list(APPEND _MAGNUM_Text_DEPENDENCIES GL) +endif() + +set(_MAGNUM_TextureTools_DEPENDENCIES ) +if(MAGNUM_TARGET_GL) + list(APPEND _MAGNUM_TextureTools_DEPENDENCIES GL) +endif() + +set(_MAGNUM_Trade_DEPENDENCIES ) +set(_MAGNUM_VulkanTester_DEPENDENCIES Vk) +set(_MAGNUM_AndroidApplication_DEPENDENCIES GL) +set(_MAGNUM_EmscriptenApplication_DEPENDENCIES) +if(MAGNUM_TARGET_GL) + list(APPEND _MAGNUM_EmscriptenApplication_DEPENDENCIES GL) +endif() + +set(_MAGNUM_GlfwApplication_DEPENDENCIES ) +if(MAGNUM_TARGET_GL) + list(APPEND _MAGNUM_GlfwApplication_DEPENDENCIES GL) +endif() + +set(_MAGNUM_GlxApplication_DEPENDENCIES GL) + +set(_MAGNUM_Sdl2Application_DEPENDENCIES ) +if(MAGNUM_TARGET_GL) + list(APPEND _MAGNUM_Sdl2Application_DEPENDENCIES GL) +endif() + +set(_MAGNUM_WindowlessCglApplication_DEPENDENCIES GL) +set(_MAGNUM_WindowlessEglApplication_DEPENDENCIES GL) +set(_MAGNUM_WindowlessGlxApplication_DEPENDENCIES GL) +set(_MAGNUM_WindowlessIosApplication_DEPENDENCIES GL) +set(_MAGNUM_WindowlessWglApplication_DEPENDENCIES GL) +set(_MAGNUM_WindowlessWindowsEglApplication_DEPENDENCIES GL) +set(_MAGNUM_XEglApplication_DEPENDENCIES GL) +set(_MAGNUM_CglContext_DEPENDENCIES GL) +set(_MAGNUM_EglContext_DEPENDENCIES GL) +set(_MAGNUM_GlxContext_DEPENDENCIES GL) +set(_MAGNUM_WglContext_DEPENDENCIES GL) + +set(_MAGNUM_MagnumFont_DEPENDENCIES Trade TgaImporter GL) # and below +set(_MAGNUM_MagnumFontConverter_DEPENDENCIES Trade TgaImageConverter) # and below +set(_MAGNUM_ObjImporter_DEPENDENCIES MeshTools) # and below +foreach(_component ${_MAGNUM_PLUGIN_COMPONENTS}) + if(_component MATCHES ".+AudioImporter") + list(APPEND _MAGNUM_${_component}_DEPENDENCIES Audio) + elseif(_component MATCHES ".+ShaderConverter") + list(APPEND _MAGNUM_${_component}_DEPENDENCIES ShaderTools) + elseif(_component MATCHES ".+(Importer|ImageConverter|SceneConverter)") + list(APPEND _MAGNUM_${_component}_DEPENDENCIES Trade) + elseif(_component MATCHES ".+(Font|FontConverter)") + list(APPEND _MAGNUM_${_component}_DEPENDENCIES Text TextureTools) + endif() +endforeach() + +# Ensure that all inter-component dependencies are specified as well +set(_MAGNUM_ADDITIONAL_COMPONENTS ) +foreach(_component ${Magnum_FIND_COMPONENTS}) + # Mark the dependencies as required if the component is also required, but + # only if they themselves are not optional (for example parts of DebugTools + # are present only if their respective base library is compiled) + if(Magnum_FIND_REQUIRED_${_component}) + foreach(_dependency ${_MAGNUM_${_component}_DEPENDENCIES}) + if(NOT _MAGNUM_${_component}_${_dependency}_DEPENDENCY_IS_OPTIONAL) + set(Magnum_FIND_REQUIRED_${_dependency} TRUE) + endif() + endforeach() + endif() + + list(APPEND _MAGNUM_ADDITIONAL_COMPONENTS ${_MAGNUM_${_component}_DEPENDENCIES}) +endforeach() + +# Join the lists, remove duplicate components +set(_MAGNUM_ORIGINAL_FIND_COMPONENTS ${Magnum_FIND_COMPONENTS}) +if(_MAGNUM_ADDITIONAL_COMPONENTS) + list(INSERT Magnum_FIND_COMPONENTS 0 ${_MAGNUM_ADDITIONAL_COMPONENTS}) +endif() +if(Magnum_FIND_COMPONENTS) + list(REMOVE_DUPLICATES Magnum_FIND_COMPONENTS) +endif() + +# Find all components. Maintain a list of components that'll need to have +# their optional dependencies checked. +set(_MAGNUM_OPTIONAL_DEPENDENCIES_TO_ADD ) +foreach(_component ${Magnum_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 Magnum::${_component}) + set(Magnum_${_component}_FOUND TRUE) + else() + # Library components + if(_component IN_LIST _MAGNUM_LIBRARY_COMPONENTS) + add_library(Magnum::${_component} UNKNOWN IMPORTED) + + # Set library defaults, find the library + set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_SUFFIX Magnum/${_component}) + set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES ${_component}.h) + + # Try to find both debug and release version + find_library(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG Magnum${_component}-d) + find_library(MAGNUM_${_COMPONENT}_LIBRARY_RELEASE Magnum${_component}) + mark_as_advanced(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG + MAGNUM_${_COMPONENT}_LIBRARY_RELEASE) + + # Plugin components + elseif(_component IN_LIST _MAGNUM_PLUGIN_COMPONENTS) + add_library(Magnum::${_component} UNKNOWN IMPORTED) + + # AudioImporter plugin specific name suffixes + if(_component MATCHES ".+AudioImporter$") + set(_MAGNUM_${_COMPONENT}_PATH_SUFFIX audioimporters) + + # Audio importer class is Audio::*Importer, thus we need to + # convert *AudioImporter.h to *Importer.h + string(REPLACE "AudioImporter" "Importer" _MAGNUM_${_COMPONENT}_HEADER_NAME "${_component}") + set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES ${_MAGNUM_${_COMPONENT}_HEADER_NAME}.h) + + # ShaderConverter plugin specific name suffixes + elseif(_component MATCHES ".+ShaderConverter$") + set(_MAGNUM_${_COMPONENT}_PATH_SUFFIX shaderconverters) + + # Importer plugin specific name suffixes + elseif(_component MATCHES ".+Importer$") + set(_MAGNUM_${_COMPONENT}_PATH_SUFFIX importers) + + # Font plugin specific name suffixes + elseif(_component MATCHES ".+Font$") + set(_MAGNUM_${_COMPONENT}_PATH_SUFFIX fonts) + + # ImageConverter plugin specific name suffixes + elseif(_component MATCHES ".+ImageConverter$") + set(_MAGNUM_${_COMPONENT}_PATH_SUFFIX imageconverters) + + # SceneConverter plugin specific name suffixes + elseif(_component MATCHES ".+SceneConverter$") + set(_MAGNUM_${_COMPONENT}_PATH_SUFFIX sceneconverters) + + # FontConverter plugin specific name suffixes + elseif(_component MATCHES ".+FontConverter$") + set(_MAGNUM_${_COMPONENT}_PATH_SUFFIX fontconverters) + endif() + + # Don't override the exception for *AudioImporter plugins + set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_SUFFIX MagnumPlugins/${_component}) + if(NOT _MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES) + set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES ${_component}.h) + endif() + + # Dynamic plugins don't have any prefix (e.g. `lib` on Linux), + # search with empty prefix and then reset that back so we don't + # accidentaly break something else + set(_tmp_prefixes "${CMAKE_FIND_LIBRARY_PREFIXES}") + set(CMAKE_FIND_LIBRARY_PREFIXES "${CMAKE_FIND_LIBRARY_PREFIXES};") + + # Try to find both debug and release version. Dynamic and static + # debug libraries are in different places. Static debug plugins are + # in magnum/ with a -d suffix while dynamic debug plugins are in + # magnum-d/ with no suffix. Problem is that Vcpkg's library linking + # automagic needs the static libs to be in the root library + # directory along with everything else and so we need to search for + # the -d suffixed version *before* the unsuffixed so it doesn't + # pick the release library for both debug and release. + find_library(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG ${_component}-d + PATH_SUFFIXES magnum/${_MAGNUM_${_COMPONENT}_PATH_SUFFIX}) + find_library(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG ${_component} + PATH_SUFFIXES magnum-d/${_MAGNUM_${_COMPONENT}_PATH_SUFFIX}) + find_library(MAGNUM_${_COMPONENT}_LIBRARY_RELEASE ${_component} + PATH_SUFFIXES magnum/${_MAGNUM_${_COMPONENT}_PATH_SUFFIX}) + mark_as_advanced(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG + MAGNUM_${_COMPONENT}_LIBRARY_RELEASE) + + # Reset back + set(CMAKE_FIND_LIBRARY_PREFIXES "${_tmp_prefixes}") + + # Executables + elseif(_component IN_LIST _MAGNUM_EXECUTABLE_COMPONENTS) + add_executable(Magnum::${_component} IMPORTED) + + find_program(MAGNUM_${_COMPONENT}_EXECUTABLE magnum-${_component}) + mark_as_advanced(MAGNUM_${_COMPONENT}_EXECUTABLE) + + if(MAGNUM_${_COMPONENT}_EXECUTABLE) + set_property(TARGET Magnum::${_component} PROPERTY + IMPORTED_LOCATION ${MAGNUM_${_COMPONENT}_EXECUTABLE}) + endif() + + # Something unknown, skip. FPHSA will take care of handling this below. + else() + continue() + endif() + + # Library location for libraries/plugins + if(_component IN_LIST _MAGNUM_LIBRARY_COMPONENTS OR _component IN_LIST _MAGNUM_PLUGIN_COMPONENTS) + if(MAGNUM_${_COMPONENT}_LIBRARY_RELEASE) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + IMPORTED_CONFIGURATIONS RELEASE) + set_property(TARGET Magnum::${_component} PROPERTY + IMPORTED_LOCATION_RELEASE ${MAGNUM_${_COMPONENT}_LIBRARY_RELEASE}) + endif() + + if(MAGNUM_${_COMPONENT}_LIBRARY_DEBUG) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + IMPORTED_CONFIGURATIONS DEBUG) + set_property(TARGET Magnum::${_component} PROPERTY + IMPORTED_LOCATION_DEBUG ${MAGNUM_${_COMPONENT}_LIBRARY_DEBUG}) + endif() + endif() + + # Applications + if(_component MATCHES ".+Application") + set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_SUFFIX Magnum/Platform) + + # Android application dependencies + if(_component STREQUAL AndroidApplication) + find_package(EGL) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES android EGL::EGL) + + # EmscriptenApplication has no additional dependencies + + # GLFW application dependencies + elseif(_component STREQUAL GlfwApplication) + find_package(GLFW) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES GLFW::GLFW) + # Use the Foundation framework on Apple to query the DPI awareness + if(CORRADE_TARGET_APPLE) + find_library(_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_LIBRARY Foundation) + mark_as_advanced(_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_LIBRARY) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES ${_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_LIBRARY}) + # Needed for opt-in DPI queries + elseif(CORRADE_TARGET_UNIX) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS}) + endif() + + # With GLVND (since CMake 3.11) we need to explicitly link to + # GLX/EGL because libOpenGL doesn't provide it. For EGL we have + # our own EGL find module, which makes things simpler. The + # upstream FindOpenGL is anything but simple. Also can't use + # OpenGL_OpenGL_FOUND, because that one is set also if GLVND is + # *not* found. WTF. Also can't just check for + # OPENGL_opengl_LIBRARY because that's set even if + # OpenGL_GL_PREFERENCE is explicitly set to LEGACY. + if(MAGNUM_TARGET_GL) + if(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE AND (NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES)) + find_package(OpenGL) + if(OPENGL_opengl_LIBRARY AND OpenGL_GL_PREFERENCE STREQUAL GLVND) + set_property(TARGET Magnum::${_component} APPEND + PROPERTY INTERFACE_LINK_LIBRARIES OpenGL::GLX) + endif() + elseif(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES AND NOT CORRADE_TARGET_EMSCRIPTEN) + find_package(EGL) + set_property(TARGET Magnum::${_component} APPEND + PROPERTY INTERFACE_LINK_LIBRARIES EGL::EGL) + endif() + endif() + + # SDL2 application dependencies + elseif(_component STREQUAL Sdl2Application) + find_package(SDL2) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES SDL2::SDL2) + # Use the Foundation framework on Apple to query the DPI awareness + if(CORRADE_TARGET_APPLE) + find_library(_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_LIBRARY Foundation) + mark_as_advanced(_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_LIBRARY) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES ${_MAGNUM_APPLE_FOUNDATION_FRAMEWORK_LIBRARY}) + # Needed for opt-in DPI queries + elseif(CORRADE_TARGET_UNIX) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS}) + endif() + + # With GLVND (since CMake 3.11) we need to explicitly link to + # GLX/EGL because libOpenGL doesn't provide it. For EGL we have + # our own EGL find module, which makes things simpler. The + # upstream FindOpenGL is anything but simple. Also can't use + # OpenGL_OpenGL_FOUND, because that one is set also if GLVND is + # *not* found. WTF. Also can't just check for + # OPENGL_opengl_LIBRARY because that's set even if + # OpenGL_GL_PREFERENCE is explicitly set to LEGACY. + if(MAGNUM_TARGET_GL) + if(CORRADE_TARGET_UNIX AND NOT CORRADE_TARGET_APPLE AND (NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES)) + find_package(OpenGL) + if(OPENGL_opengl_LIBRARY AND OpenGL_GL_PREFERENCE STREQUAL GLVND) + set_property(TARGET Magnum::${_component} APPEND + PROPERTY INTERFACE_LINK_LIBRARIES OpenGL::GLX) + endif() + elseif(MAGNUM_TARGET_GLES AND NOT MAGNUM_TARGET_DESKTOP_GLES AND NOT CORRADE_TARGET_EMSCRIPTEN) + find_package(EGL) + set_property(TARGET Magnum::${_component} APPEND + PROPERTY INTERFACE_LINK_LIBRARIES EGL::EGL) + endif() + endif() + + # (Windowless) GLX application dependencies + elseif(_component STREQUAL GlxApplication OR _component STREQUAL WindowlessGlxApplication) + find_package(X11) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES ${X11_INCLUDE_DIR}) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES ${X11_LIBRARIES}) + + # With GLVND (since CMake 3.11) we need to explicitly link to + # GLX because libOpenGL doesn't provide it. Also can't use + # OpenGL_OpenGL_FOUND, because that one is set also if GLVND is + # *not* found. WTF. Also can't just check for + # OPENGL_opengl_LIBRARY because that's set even if + # OpenGL_GL_PREFERENCE is explicitly set to LEGACY. + find_package(OpenGL) + if(OPENGL_opengl_LIBRARY AND OpenGL_GL_PREFERENCE STREQUAL GLVND) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES OpenGL::GLX) + endif() + + # Windowless CGL application has no additional dependencies + + # Windowless EGL application dependencies + elseif(_component STREQUAL WindowlessEglApplication) + find_package(EGL) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES EGL::EGL) + + # Windowless iOS application dependencies + elseif(_component STREQUAL WindowlessIosApplication) + # We need to link to Foundation framework to use ObjC + find_library(_MAGNUM_IOS_FOUNDATION_FRAMEWORK_LIBRARY Foundation) + mark_as_advanced(_MAGNUM_IOS_FOUNDATION_FRAMEWORK_LIBRARY) + find_package(EGL) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES EGL::EGL ${_MAGNUM_IOS_FOUNDATION_FRAMEWORK_LIBRARY}) + + # Windowless WGL application has no additional dependencies + + # Windowless Windows/EGL application dependencies + elseif(_component STREQUAL WindowlessWindowsEglApplication) + find_package(EGL) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES EGL::EGL) + + # X/EGL application dependencies + elseif(_component STREQUAL XEglApplication) + find_package(EGL) + find_package(X11) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES ${X11_INCLUDE_DIR}) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES EGL::EGL ${X11_LIBRARIES}) + endif() + + # Context libraries + elseif(_component MATCHES ".+Context") + set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_SUFFIX Magnum/Platform) + set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES GLContext.h) + + # GLX context dependencies + if(_component STREQUAL GlxContext) + # With GLVND (since CMake 3.11) we need to explicitly link to + # GLX because libOpenGL doesn't provide it. Also can't use + # OpenGL_OpenGL_FOUND, because that one is set also if GLVND is + # *not* found. If GLVND is not used, link to X11 instead. Also + # can't just check for OPENGL_opengl_LIBRARY because that's set + # even if OpenGL_GL_PREFERENCE is explicitly set to LEGACY. + find_package(OpenGL) + if(OPENGL_opengl_LIBRARY AND OpenGL_GL_PREFERENCE STREQUAL GLVND) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES OpenGL::GLX) + else() + find_package(X11) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES ${X11_INCLUDE_DIR}) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES ${X11_LIBRARIES}) + endif() + + # EGL context dependencies + elseif(_component STREQUAL EglContext) + find_package(EGL) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES EGL::EGL) + endif() + + # No additional dependencies for CGL context + # No additional dependencies for WGL context + + # Audio library + elseif(_component STREQUAL Audio) + find_package(OpenAL) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES Corrade::PluginManager OpenAL::OpenAL) + + # No special setup for DebugTools library + + # GL library + elseif(_component STREQUAL GL) + if(NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES) + # If the GLVND library (CMake 3.11+) was found, link to the + # imported target. Otherwise (and also on all systems except + # Linux) link to the classic libGL. Can't use + # OpenGL_OpenGL_FOUND, because that one is set also if GLVND is + # *not* found. WTF. Also can't just check for + # OPENGL_opengl_LIBRARY because that's set even if + # OpenGL_GL_PREFERENCE is explicitly set to LEGACY. + find_package(OpenGL REQUIRED) + if(OPENGL_opengl_LIBRARY AND OpenGL_GL_PREFERENCE STREQUAL GLVND) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES OpenGL::OpenGL) + else() + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES ${OPENGL_gl_LIBRARY}) + endif() + elseif(MAGNUM_TARGET_GLES2) + find_package(OpenGLES2 REQUIRED) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES OpenGLES2::OpenGLES2) + elseif(MAGNUM_TARGET_GLES3) + find_package(OpenGLES3 REQUIRED) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES OpenGLES3::OpenGLES3) + endif() + + # MeshTools library + elseif(_component STREQUAL MeshTools) + set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES CompressIndices.h) + + # OpenGLTester library + elseif(_component STREQUAL OpenGLTester) + set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_SUFFIX Magnum/GL) + + # VulkanTester library + elseif(_component STREQUAL VulkanTester) + set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_SUFFIX Magnum/Vk) + + # Primitives library + elseif(_component STREQUAL Primitives) + set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES Cube.h) + + # No special setup for SceneGraph library + + # ShaderTools library + elseif(_component STREQUAL ShaderTools) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES Corrade::PluginManager) + + # No special setup for Shaders library + + # Text library + elseif(_component STREQUAL Text) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES Corrade::PluginManager) + + # TextureTools library + elseif(_component STREQUAL TextureTools) + set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES Atlas.h) + + # Trade library + elseif(_component STREQUAL Trade) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES Corrade::PluginManager) + + # Vk library + elseif(_component STREQUAL Vk) + find_package(Vulkan REQUIRED) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES Vulkan::Vulkan) + endif() + + # No special setup for AnyAudioImporter plugin + # No special setup for AnyImageConverter plugin + # No special setup for AnyImageImporter plugin + # No special setup for AnySceneImporter plugin + # No special setup for MagnumFont plugin + # No special setup for MagnumFontConverter plugin + # No special setup for ObjImporter plugin + # No special setup for TgaImageConverter plugin + # No special setup for TgaImporter plugin + # No special setup for WavAudioImporter plugin + + # Find library/plugin includes + if(_component IN_LIST _MAGNUM_LIBRARY_COMPONENTS OR _component IN_LIST _MAGNUM_PLUGIN_COMPONENTS) + find_path(_MAGNUM_${_COMPONENT}_INCLUDE_DIR + NAMES ${_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES} + HINTS ${MAGNUM_INCLUDE_DIR}/${_MAGNUM_${_COMPONENT}_INCLUDE_PATH_SUFFIX}) + mark_as_advanced(_MAGNUM_${_COMPONENT}_INCLUDE_DIR) + endif() + + # Automatic import of static plugins. Skip in case the include dir was + # not found -- that'll fail later with a proper message. + if(_component IN_LIST _MAGNUM_PLUGIN_COMPONENTS AND _MAGNUM_${_COMPONENT}_INCLUDE_DIR) + # Automatic import of static plugins + file(READ ${_MAGNUM_${_COMPONENT}_INCLUDE_DIR}/configure.h _magnum${_component}Configure) + string(FIND "${_magnum${_component}Configure}" "#define MAGNUM_${_COMPONENT}_BUILD_STATIC" _magnum${_component}_BUILD_STATIC) + if(NOT _magnum${_component}_BUILD_STATIC EQUAL -1) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_SOURCES ${_MAGNUM_${_COMPONENT}_INCLUDE_DIR}/importStaticPlugin.cpp) + endif() + endif() + + # Link to core Magnum library, add inter-library dependencies. If there + # are optional dependencies, defer adding them to later once we know if + # they were found or not. + if(_component IN_LIST _MAGNUM_LIBRARY_COMPONENTS OR _component IN_LIST _MAGNUM_PLUGIN_COMPONENTS) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES Magnum::Magnum) + set(_MAGNUM_${component}_OPTIONAL_DEPENDENCIES_TO_ADD ) + foreach(_dependency ${_MAGNUM_${_component}_DEPENDENCIES}) + if(NOT _MAGNUM_${_component}_${_dependency}_DEPENDENCY_IS_OPTIONAL) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES Magnum::${_dependency}) + else() + list(APPEND _MAGNUM_${_component}_OPTIONAL_DEPENDENCIES_TO_ADD + ${_dependency}) + endif() + endforeach() + if(_MAGNUM_${_component}_OPTIONAL_DEPENDENCIES_TO_ADD) + list(APPEND _MAGNUM_OPTIONAL_DEPENDENCIES_TO_ADD ${_component}) + endif() + endif() + + # Decide if the library was found + if(((_component IN_LIST _MAGNUM_LIBRARY_COMPONENTS OR _component IN_LIST _MAGNUM_PLUGIN_COMPONENTS) AND _MAGNUM_${_COMPONENT}_INCLUDE_DIR AND (MAGNUM_${_COMPONENT}_LIBRARY_DEBUG OR MAGNUM_${_COMPONENT}_LIBRARY_RELEASE)) OR (_component IN_LIST _MAGNUM_EXECUTABLE_COMPONENTS AND MAGNUM_${_COMPONENT}_EXECUTABLE)) + set(Magnum_${_component}_FOUND TRUE) + else() + set(Magnum_${_component}_FOUND FALSE) + endif() + endif() + + # Global aliases for Windowless*Application, *Application and *Context + # components. If already set, unset them to avoid ambiguity. + if(_component MATCHES "Windowless.+Application") + if(NOT DEFINED _MAGNUM_WINDOWLESSAPPLICATION_ALIAS) + set(_MAGNUM_WINDOWLESSAPPLICATION_ALIAS Magnum::${_component}) + else() + unset(_MAGNUM_WINDOWLESSAPPLICATION_ALIAS) + endif() + elseif(_component MATCHES ".+Application") + if(NOT DEFINED _MAGNUM_APPLICATION_ALIAS) + set(_MAGNUM_APPLICATION_ALIAS Magnum::${_component}) + else() + unset(_MAGNUM_APPLICATION_ALIAS) + endif() + elseif(_component MATCHES ".+Context") + if(NOT DEFINED _MAGNUM_GLCONTEXT_ALIAS) + set(_MAGNUM_GLCONTEXT_ALIAS Magnum::${_component}) + else() + unset(_MAGNUM_GLCONTEXT_ALIAS) + endif() + endif() +endforeach() + +# Emscripten-specific files and flags +if(CORRADE_TARGET_EMSCRIPTEN) + find_file(MAGNUM_EMSCRIPTENAPPLICATION_JS EmscriptenApplication.js + PATH_SUFFIXES share/magnum) + find_file(MAGNUM_WINDOWLESSEMSCRIPTENAPPLICATION_JS WindowlessEmscriptenApplication.js + PATH_SUFFIXES share/magnum) + find_file(MAGNUM_WEBAPPLICATION_CSS WebApplication.css + PATH_SUFFIXES share/magnum) + mark_as_advanced( + MAGNUM_EMSCRIPTENAPPLICATION_JS + MAGNUM_WINDOWLESSEMSCRIPTENAPPLICATION_JS + MAGNUM_WEBAPPLICATION_CSS) + set(MAGNUM_EXTRAS_NEEDED + MAGNUM_EMSCRIPTENAPPLICATION_JS + MAGNUM_WINDOWLESSEMSCRIPTENAPPLICATION_JS + MAGNUM_WEBAPPLICATION_CSS) + + # If we are on CMake 3.13 and up, `-s USE_WEBGL2=1` linker option is + # propagated from FindOpenGLES3.cmake already. If not (and the GL library + # is used), we need to modify the global CMAKE_EXE_LINKER_FLAGS. Do it here + # instead of in FindOpenGLES3.cmake so it works also for CMake subprojects + # (in which case find_package(OpenGLES3) is called in (and so + # CMAKE_EXE_LINKER_FLAGS would be modified in) Magnum's root CMakeLists.txt + # and thus can't affect the variable in the outer project). CMake supports + # IN_LIST as an operator since 3.1 (Emscripten needs at least 3.7), but + # it's behind a policy, so enable that one as well. + cmake_policy(SET CMP0057 NEW) + if(CMAKE_VERSION VERSION_LESS 3.13 AND GL IN_LIST Magnum_FIND_COMPONENTS AND NOT MAGNUM_TARGET_GLES2 AND NOT CMAKE_EXE_LINKER_FLAGS MATCHES "-s USE_WEBGL2=1") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s USE_WEBGL2=1") + endif() +endif() + +# For CMake 3.16+ with REASON_FAILURE_MESSAGE, provide additional potentially +# useful info about the failed components. +if(NOT CMAKE_VERSION VERSION_LESS 3.16) + set(_MAGNUM_REASON_FAILURE_MESSAGE ) + # Go only through the originally specified find_package() components, not + # the dependencies added by us afterwards + foreach(_component ${_MAGNUM_ORIGINAL_FIND_COMPONENTS}) + if(Magnum_${_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 _MAGNUM_LIBRARY_COMPONENTS AND NOT _component IN_LIST _MAGNUM_PLUGIN_COMPONENTS AND NOT _component IN_LIST _MAGNUM_EXECUTABLE_COMPONENTS) + list(APPEND _MAGNUM_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 _MAGNUM_IMPLICITLY_ENABLED_COMPONENTS) + string(TOUPPER ${_component} _COMPONENT) + list(APPEND _MAGNUM_REASON_FAILURE_MESSAGE "${_component} is not built by default. Make sure you enabled WITH_${_COMPONENT} when building Magnum.") + # Otherwise we have no idea. Better be silent than to print something + # misleading. + else() + endif() + endforeach() + + string(REPLACE ";" " " _MAGNUM_REASON_FAILURE_MESSAGE "${_MAGNUM_REASON_FAILURE_MESSAGE}") + set(_MAGNUM_REASON_FAILURE_MESSAGE REASON_FAILURE_MESSAGE "${_MAGNUM_REASON_FAILURE_MESSAGE}") +endif() + +# Complete the check with also all components +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Magnum + REQUIRED_VARS MAGNUM_INCLUDE_DIR MAGNUM_LIBRARY ${MAGNUM_EXTRAS_NEEDED} + HANDLE_COMPONENTS + ${_MAGNUM_REASON_FAILURE_MESSAGE}) + +# Components with optional dependencies -- add them once we know if they were +# found or not. +foreach(_component ${_MAGNUM_OPTIONAL_DEPENDENCIES_TO_ADD}) + foreach(_dependency ${_MAGNUM_${_component}_OPTIONAL_DEPENDENCIES_TO_ADD}) + if(Magnum_${_dependency}_FOUND) + set_property(TARGET Magnum::${_component} APPEND PROPERTY + INTERFACE_LINK_LIBRARIES Magnum::${_dependency}) + endif() + endforeach() +endforeach() + +# Create Windowless*Application, *Application and *Context aliases +# TODO: ugh why can't I make an alias of IMPORTED target? +if(_MAGNUM_WINDOWLESSAPPLICATION_ALIAS AND NOT TARGET Magnum::WindowlessApplication) + get_target_property(_MAGNUM_WINDOWLESSAPPLICATION_ALIASED_TARGET ${_MAGNUM_WINDOWLESSAPPLICATION_ALIAS} ALIASED_TARGET) + if(_MAGNUM_WINDOWLESSAPPLICATION_ALIASED_TARGET) + add_library(Magnum::WindowlessApplication ALIAS ${_MAGNUM_WINDOWLESSAPPLICATION_ALIASED_TARGET}) + else() + add_library(Magnum::WindowlessApplication UNKNOWN IMPORTED) + foreach(property IMPORTED_CONFIGURATIONS INTERFACE_INCLUDE_DIRECTORIES INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_OPTIONS INTERFACE_LINK_LIBRARIES) + get_target_property(_MAGNUM_WINDOWLESSAPPLICATION_${property} ${_MAGNUM_WINDOWLESSAPPLICATION_ALIAS} ${property}) + if(_MAGNUM_WINDOWLESSAPPLICATION_${property}) + set_target_properties(Magnum::WindowlessApplication PROPERTIES + ${property} "${_MAGNUM_WINDOWLESSAPPLICATION_${property}}") + endif() + endforeach() + get_target_property(_MAGNUM_WINDOWLESSAPPLICATION_IMPORTED_LOCATION_RELEASE ${_MAGNUM_WINDOWLESSAPPLICATION_ALIAS} IMPORTED_LOCATION_RELEASE) + get_target_property(_MAGNUM_WINDOWLESSAPPLICATION_IMPORTED_LOCATION_DEBUG ${_MAGNUM_WINDOWLESSAPPLICATION_ALIAS} IMPORTED_LOCATION_DEBUG) + if(_MAGNUM_WINDOWLESSAPPLICATION_IMPORTED_LOCATION_RELEASE) + set_target_properties(Magnum::WindowlessApplication PROPERTIES + IMPORTED_LOCATION_RELEASE ${_MAGNUM_WINDOWLESSAPPLICATION_IMPORTED_LOCATION_RELEASE}) + endif() + if(_MAGNUM_WINDOWLESSAPPLICATION_IMPORTED_LOCATION_DEBUG) + set_target_properties(Magnum::WindowlessApplication PROPERTIES + IMPORTED_LOCATION_DEBUG ${_MAGNUM_WINDOWLESSAPPLICATION_IMPORTED_LOCATION_DEBUG}) + endif() + endif() + # Prevent creating the alias again + unset(_MAGNUM_WINDOWLESSAPPLICATION_ALIAS) +endif() +if(_MAGNUM_APPLICATION_ALIAS AND NOT TARGET Magnum::Application) + get_target_property(_MAGNUM_APPLICATION_ALIASED_TARGET ${_MAGNUM_APPLICATION_ALIAS} ALIASED_TARGET) + if(_MAGNUM_APPLICATION_ALIASED_TARGET) + add_library(Magnum::Application ALIAS ${_MAGNUM_APPLICATION_ALIASED_TARGET}) + else() + add_library(Magnum::Application UNKNOWN IMPORTED) + foreach(property IMPORTED_CONFIGURATIONS INTERFACE_INCLUDE_DIRECTORIES INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_OPTIONS INTERFACE_LINK_LIBRARIES) + get_target_property(_MAGNUM_APPLICATION_${property} + ${_MAGNUM_APPLICATION_ALIAS} ${property}) + if(_MAGNUM_APPLICATION_${property}) + set_target_properties(Magnum::Application PROPERTIES ${property} + "${_MAGNUM_APPLICATION_${property}}") + endif() + endforeach() + get_target_property(_MAGNUM_APPLICATION_IMPORTED_LOCATION_RELEASE ${_MAGNUM_APPLICATION_ALIAS} IMPORTED_LOCATION_RELEASE) + get_target_property(_MAGNUM_APPLICATION_IMPORTED_LOCATION_DEBUG ${_MAGNUM_APPLICATION_ALIAS} IMPORTED_LOCATION_DEBUG) + if(_MAGNUM_APPLICATION_IMPORTED_LOCATION_RELEASE) + set_target_properties(Magnum::Application PROPERTIES + IMPORTED_LOCATION_RELEASE ${_MAGNUM_APPLICATION_IMPORTED_LOCATION_RELEASE}) + endif() + if(_MAGNUM_APPLICATION_IMPORTED_LOCATION_DEBUG) + set_target_properties(Magnum::Application PROPERTIES + IMPORTED_LOCATION_DEBUG ${_MAGNUM_APPLICATION_IMPORTED_LOCATION_DEBUG}) + endif() + endif() + # Prevent creating the alias again + unset(_MAGNUM_APPLICATION_ALIAS) +endif() +if(_MAGNUM_GLCONTEXT_ALIAS AND NOT TARGET Magnum::GLContext) + get_target_property(_MAGNUM_GLCONTEXT_ALIASED_TARGET ${_MAGNUM_GLCONTEXT_ALIAS} ALIASED_TARGET) + if(_MAGNUM_GLCONTEXT_ALIASED_TARGET) + add_library(Magnum::GLContext ALIAS ${_MAGNUM_GLCONTEXT_ALIASED_TARGET}) + else() + add_library(Magnum::GLContext UNKNOWN IMPORTED) + foreach(property IMPORTED_CONFIGURATIONS INTERFACE_INCLUDE_DIRECTORIES INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_OPTIONS INTERFACE_LINK_LIBRARIES) + get_target_property(_MAGNUM_GLCONTEXT_${property} ${_MAGNUM_GLCONTEXT_ALIAS} ${property}) + if(_MAGNUM_GLCONTEXT_${property}) + set_target_properties(Magnum::GLContext PROPERTIES ${property} + "${_MAGNUM_GLCONTEXT_${property}}") + endif() + endforeach() + get_target_property(_MAGNUM_GLCONTEXT_IMPORTED_LOCATION_RELEASE ${_MAGNUM_GLCONTEXT_ALIAS} IMPORTED_LOCATION_RELEASE) + get_target_property(_MAGNUM_GLCONTEXT_IMPORTED_LOCATION_DEBUG ${_MAGNUM_GLCONTEXT_ALIAS} IMPORTED_LOCATION_DEBUG) + if(_MAGNUM_GLCONTEXT_IMPORTED_LOCATION_RELEASE) + set_target_properties(Magnum::GLContext PROPERTIES + IMPORTED_LOCATION_RELEASE ${_MAGNUM_GLCONTEXT_IMPORTED_LOCATION_RELEASE}) + endif() + if(_MAGNUM_GLCONTEXT_IMPORTED_LOCATION_DEBUG) + set_target_properties(Magnum::GLContext PROPERTIES + IMPORTED_LOCATION_DEBUG ${_MAGNUM_GLCONTEXT_IMPORTED_LOCATION_DEBUG}) + endif() + endif() + # Prevent creating the alias again + unset(_MAGNUM_GLCONTEXT_ALIAS) +endif() + +# Installation and deploy dirs +set(MAGNUM_DEPLOY_PREFIX "." + CACHE STRING "Prefix where to put final application executables") + +include(${CORRADE_LIB_SUFFIX_MODULE}) +set(MAGNUM_BINARY_INSTALL_DIR bin) +set(MAGNUM_LIBRARY_INSTALL_DIR lib${LIB_SUFFIX}) +set(MAGNUM_DATA_INSTALL_DIR share/magnum) +set(MAGNUM_INCLUDE_INSTALL_DIR include/Magnum) +set(MAGNUM_EXTERNAL_INCLUDE_INSTALL_DIR include/MagnumExternal) +set(MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR include/MagnumPlugins) +if(MAGNUM_BUILD_DEPRECATED AND MAGNUM_INCLUDE_INSTALL_PREFIX AND NOT MAGNUM_INCLUDE_INSTALL_PREFIX STREQUAL ".") + message(DEPRECATION "MAGNUM_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(MAGNUM_DATA_INSTALL_DIR ${MAGNUM_INCLUDE_INSTALL_PREFIX}/${MAGNUM_DATA_INSTALL_DIR}) + set(MAGNUM_INCLUDE_INSTALL_DIR ${MAGNUM_INCLUDE_INSTALL_PREFIX}/${MAGNUM_INCLUDE_INSTALL_DIR}) + set(MAGNUM_EXTERNAL_INCLUDE_INSTALL_DIR ${MAGNUM_INCLUDE_INSTALL_PREFIX}/${MAGNUM_EXTERNAL_INCLUDE_INSTALL_DIR}) + set(MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR ${MAGNUM_INCLUDE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_INCLUDE_INSTALL_DIR}) +endif() + +set(MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR ${MAGNUM_BINARY_INSTALL_DIR}/magnum-d) +set(MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR ${MAGNUM_LIBRARY_INSTALL_DIR}/magnum-d) +set(MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR ${MAGNUM_BINARY_INSTALL_DIR}/magnum) +set(MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR ${MAGNUM_LIBRARY_INSTALL_DIR}/magnum) + +set(MAGNUM_PLUGINS_SHADERCONVERTER_DEBUG_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}/shaderconverters) +set(MAGNUM_PLUGINS_SHADERCONVERTER_DEBUG_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR}/shaderconverters) +set(MAGNUM_PLUGINS_SHADERCONVERTER_RELEASE_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}/shaderconverters) +set(MAGNUM_PLUGINS_SHADERCONVERTER_RELEASE_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR}/shaderconverters) +set(MAGNUM_PLUGINS_FONT_DEBUG_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}/fonts) +set(MAGNUM_PLUGINS_FONT_DEBUG_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR}/fonts) +set(MAGNUM_PLUGINS_FONT_RELEASE_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR}/fonts) +set(MAGNUM_PLUGINS_FONT_RELEASE_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}/fonts) +set(MAGNUM_PLUGINS_FONTCONVERTER_DEBUG_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}/fontconverters) +set(MAGNUM_PLUGINS_FONTCONVERTER_RELEASE_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}/fontconverters) +set(MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}/imageconverters) +set(MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR}/imageconverters) +set(MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}/imageconverters) +set(MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR}/imageconverters) +set(MAGNUM_PLUGINS_IMPORTER_DEBUG_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}/importers) +set(MAGNUM_PLUGINS_IMPORTER_DEBUG_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR}/importers) +set(MAGNUM_PLUGINS_IMPORTER_RELEASE_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR}/importers) +set(MAGNUM_PLUGINS_IMPORTER_RELEASE_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}/importers) +set(MAGNUM_PLUGINS_SCENECONVERTER_DEBUG_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}/sceneconverters) +set(MAGNUM_PLUGINS_SCENECONVERTER_DEBUG_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR}/sceneconverters) +set(MAGNUM_PLUGINS_SCENECONVERTER_RELEASE_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}/sceneconverters) +set(MAGNUM_PLUGINS_SCENECONVERTER_RELEASE_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR}/sceneconverters) +set(MAGNUM_PLUGINS_AUDIOIMPORTER_DEBUG_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}/audioimporters) +set(MAGNUM_PLUGINS_AUDIOIMPORTER_DEBUG_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR}/audioimporters) +set(MAGNUM_PLUGINS_AUDIOIMPORTER_RELEASE_BINARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR}/audioimporters) +set(MAGNUM_PLUGINS_AUDIOIMPORTER_RELEASE_LIBRARY_INSTALL_DIR ${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}/audioimporters) + +# Get base plugin directory from main library location. This is *not* PATH, +# because CMake always converts the path to an absolute location internally, +# making it impossible to specify relative paths there. Sorry in advance for +# not having the dir selection button in CMake GUI. +set(MAGNUM_PLUGINS_DEBUG_DIR "" + CACHE STRING "Base directory where to look for Magnum plugins for debug builds") +set(MAGNUM_PLUGINS_RELEASE_DIR "" + CACHE STRING "Base directory where to look for Magnum plugins for release builds") +set(MAGNUM_PLUGINS_DIR "" + CACHE STRING "Base directory where to look for Magnum plugins") + +# Plugin directories. Set only if the above are non-empty. otherwise empty as +# well. +if(MAGNUM_PLUGINS_DIR) + set(MAGNUM_PLUGINS_FONT_DIR ${MAGNUM_PLUGINS_DIR}/fonts) + set(MAGNUM_PLUGINS_FONTCONVERTER_DIR ${MAGNUM_PLUGINS_DIR}/fontconverters) + set(MAGNUM_PLUGINS_IMAGECONVERTER_DIR ${MAGNUM_PLUGINS_DIR}/imageconverters) + set(MAGNUM_PLUGINS_IMPORTER_DIR ${MAGNUM_PLUGINS_DIR}/importers) + set(MAGNUM_PLUGINS_SCENECONVERTER_DIR ${MAGNUM_PLUGINS_DIR}/sceneconverters) + set(MAGNUM_PLUGINS_AUDIOIMPORTER_DIR ${MAGNUM_PLUGINS_DIR}/audioimporters) +endif() +if(MAGNUM_PLUGINS_DEBUG_DIR) + set(MAGNUM_PLUGINS_FONT_DEBUG_DIR ${MAGNUM_PLUGINS_DEBUG_DIR}/fonts) + set(MAGNUM_PLUGINS_FONTCONVERTER_DEBUG_DIR ${MAGNUM_PLUGINS_DEBUG_DIR}/fontconverters) + set(MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_DIR ${MAGNUM_PLUGINS_DEBUG_DIR}/imageconverters) + set(MAGNUM_PLUGINS_IMPORTER_DEBUG_DIR ${MAGNUM_PLUGINS_DEBUG_DIR}/importers) + set(MAGNUM_PLUGINS_FONT_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/fonts) + set(MAGNUM_PLUGINS_SCENECONVERTER_DEBUG_DIR ${MAGNUM_PLUGINS_DEBUG_DIR}/sceneconverters) + set(MAGNUM_PLUGINS_AUDIOIMPORTER_DEBUG_DIR ${MAGNUM_PLUGINS_DEBUG_DIR}/audioimporters) +endif() +if(MAGNUM_PLUGINS_RELEASE_DIR) + set(MAGNUM_PLUGINS_FONTCONVERTER_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/fontconverters) + set(MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/imageconverters) + set(MAGNUM_PLUGINS_IMPORTER_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/importers) + set(MAGNUM_PLUGINS_SCENECONVERTER_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/sceneconverters) + set(MAGNUM_PLUGINS_AUDIOIMPORTER_RELEASE_DIR ${MAGNUM_PLUGINS_RELEASE_DIR}/audioimporters) +endif() diff --git a/modules/FindMagnumIntegration.cmake b/modules/FindMagnumIntegration.cmake new file mode 100644 index 0000000..05bfde7 --- /dev/null +++ b/modules/FindMagnumIntegration.cmake @@ -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š +# Copyright © 2018 Konstantinos Chatzilygeroudis +# +# 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}) diff --git a/modules/FindSDL2.cmake b/modules/FindSDL2.cmake new file mode 100644 index 0000000..54844d6 --- /dev/null +++ b/modules/FindSDL2.cmake @@ -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š +# Copyright © 2018 Jonathan Hale +# +# 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 + # $ + + + + + + \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..9f1cc1d --- /dev/null +++ b/src/CMakeLists.txt @@ -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 . + +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) diff --git a/src/FontAwesome/IconsFontAwesome5.h b/src/FontAwesome/IconsFontAwesome5.h new file mode 100644 index 0000000..67cddab --- /dev/null +++ b/src/FontAwesome/IconsFontAwesome5.h @@ -0,0 +1,1012 @@ +// 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-regular-400.ttf, https://github.com/FortAwesome/Font-Awesome/blob/master/webfonts/fa-solid-900.ttf +#pragma once + +#define FONT_ICON_FILE_NAME_FAR "fa-regular-400.ttf" +#define FONT_ICON_FILE_NAME_FAS "fa-solid-900.ttf" + +#define ICON_MIN_FA 0xe005 +#define ICON_MAX_FA 0xf8ff +#define ICON_FA_AD "\xef\x99\x81" // U+f641 +#define ICON_FA_ADDRESS_BOOK "\xef\x8a\xb9" // U+f2b9 +#define ICON_FA_ADDRESS_CARD "\xef\x8a\xbb" // U+f2bb +#define ICON_FA_ADJUST "\xef\x81\x82" // U+f042 +#define ICON_FA_AIR_FRESHENER "\xef\x97\x90" // U+f5d0 +#define ICON_FA_ALIGN_CENTER "\xef\x80\xb7" // U+f037 +#define ICON_FA_ALIGN_JUSTIFY "\xef\x80\xb9" // U+f039 +#define ICON_FA_ALIGN_LEFT "\xef\x80\xb6" // U+f036 +#define ICON_FA_ALIGN_RIGHT "\xef\x80\xb8" // U+f038 +#define ICON_FA_ALLERGIES "\xef\x91\xa1" // U+f461 +#define ICON_FA_AMBULANCE "\xef\x83\xb9" // U+f0f9 +#define ICON_FA_AMERICAN_SIGN_LANGUAGE_INTERPRETING "\xef\x8a\xa3" // U+f2a3 +#define ICON_FA_ANCHOR "\xef\x84\xbd" // U+f13d +#define ICON_FA_ANGLE_DOUBLE_DOWN "\xef\x84\x83" // U+f103 +#define ICON_FA_ANGLE_DOUBLE_LEFT "\xef\x84\x80" // U+f100 +#define ICON_FA_ANGLE_DOUBLE_RIGHT "\xef\x84\x81" // U+f101 +#define ICON_FA_ANGLE_DOUBLE_UP "\xef\x84\x82" // U+f102 +#define ICON_FA_ANGLE_DOWN "\xef\x84\x87" // U+f107 +#define ICON_FA_ANGLE_LEFT "\xef\x84\x84" // U+f104 +#define ICON_FA_ANGLE_RIGHT "\xef\x84\x85" // U+f105 +#define ICON_FA_ANGLE_UP "\xef\x84\x86" // U+f106 +#define ICON_FA_ANGRY "\xef\x95\x96" // U+f556 +#define ICON_FA_ANKH "\xef\x99\x84" // U+f644 +#define ICON_FA_APPLE_ALT "\xef\x97\x91" // U+f5d1 +#define ICON_FA_ARCHIVE "\xef\x86\x87" // U+f187 +#define ICON_FA_ARCHWAY "\xef\x95\x97" // U+f557 +#define ICON_FA_ARROW_ALT_CIRCLE_DOWN "\xef\x8d\x98" // U+f358 +#define ICON_FA_ARROW_ALT_CIRCLE_LEFT "\xef\x8d\x99" // U+f359 +#define ICON_FA_ARROW_ALT_CIRCLE_RIGHT "\xef\x8d\x9a" // U+f35a +#define ICON_FA_ARROW_ALT_CIRCLE_UP "\xef\x8d\x9b" // U+f35b +#define ICON_FA_ARROW_CIRCLE_DOWN "\xef\x82\xab" // U+f0ab +#define ICON_FA_ARROW_CIRCLE_LEFT "\xef\x82\xa8" // U+f0a8 +#define ICON_FA_ARROW_CIRCLE_RIGHT "\xef\x82\xa9" // U+f0a9 +#define ICON_FA_ARROW_CIRCLE_UP "\xef\x82\xaa" // U+f0aa +#define ICON_FA_ARROW_DOWN "\xef\x81\xa3" // U+f063 +#define ICON_FA_ARROW_LEFT "\xef\x81\xa0" // U+f060 +#define ICON_FA_ARROW_RIGHT "\xef\x81\xa1" // U+f061 +#define ICON_FA_ARROW_UP "\xef\x81\xa2" // U+f062 +#define ICON_FA_ARROWS_ALT "\xef\x82\xb2" // U+f0b2 +#define ICON_FA_ARROWS_ALT_H "\xef\x8c\xb7" // U+f337 +#define ICON_FA_ARROWS_ALT_V "\xef\x8c\xb8" // U+f338 +#define ICON_FA_ASSISTIVE_LISTENING_SYSTEMS "\xef\x8a\xa2" // U+f2a2 +#define ICON_FA_ASTERISK "\xef\x81\xa9" // U+f069 +#define ICON_FA_AT "\xef\x87\xba" // U+f1fa +#define ICON_FA_ATLAS "\xef\x95\x98" // U+f558 +#define ICON_FA_ATOM "\xef\x97\x92" // U+f5d2 +#define ICON_FA_AUDIO_DESCRIPTION "\xef\x8a\x9e" // U+f29e +#define ICON_FA_AWARD "\xef\x95\x99" // U+f559 +#define ICON_FA_BABY "\xef\x9d\xbc" // U+f77c +#define ICON_FA_BABY_CARRIAGE "\xef\x9d\xbd" // U+f77d +#define ICON_FA_BACKSPACE "\xef\x95\x9a" // U+f55a +#define ICON_FA_BACKWARD "\xef\x81\x8a" // U+f04a +#define ICON_FA_BACON "\xef\x9f\xa5" // U+f7e5 +#define ICON_FA_BACTERIA "\xee\x81\x99" // U+e059 +#define ICON_FA_BACTERIUM "\xee\x81\x9a" // U+e05a +#define ICON_FA_BAHAI "\xef\x99\xa6" // U+f666 +#define ICON_FA_BALANCE_SCALE "\xef\x89\x8e" // U+f24e +#define ICON_FA_BALANCE_SCALE_LEFT "\xef\x94\x95" // U+f515 +#define ICON_FA_BALANCE_SCALE_RIGHT "\xef\x94\x96" // U+f516 +#define ICON_FA_BAN "\xef\x81\x9e" // U+f05e +#define ICON_FA_BAND_AID "\xef\x91\xa2" // U+f462 +#define ICON_FA_BARCODE "\xef\x80\xaa" // U+f02a +#define ICON_FA_BARS "\xef\x83\x89" // U+f0c9 +#define ICON_FA_BASEBALL_BALL "\xef\x90\xb3" // U+f433 +#define ICON_FA_BASKETBALL_BALL "\xef\x90\xb4" // U+f434 +#define ICON_FA_BATH "\xef\x8b\x8d" // U+f2cd +#define ICON_FA_BATTERY_EMPTY "\xef\x89\x84" // U+f244 +#define ICON_FA_BATTERY_FULL "\xef\x89\x80" // U+f240 +#define ICON_FA_BATTERY_HALF "\xef\x89\x82" // U+f242 +#define ICON_FA_BATTERY_QUARTER "\xef\x89\x83" // U+f243 +#define ICON_FA_BATTERY_THREE_QUARTERS "\xef\x89\x81" // U+f241 +#define ICON_FA_BED "\xef\x88\xb6" // U+f236 +#define ICON_FA_BEER "\xef\x83\xbc" // U+f0fc +#define ICON_FA_BELL "\xef\x83\xb3" // U+f0f3 +#define ICON_FA_BELL_SLASH "\xef\x87\xb6" // U+f1f6 +#define ICON_FA_BEZIER_CURVE "\xef\x95\x9b" // U+f55b +#define ICON_FA_BIBLE "\xef\x99\x87" // U+f647 +#define ICON_FA_BICYCLE "\xef\x88\x86" // U+f206 +#define ICON_FA_BIKING "\xef\xa1\x8a" // U+f84a +#define ICON_FA_BINOCULARS "\xef\x87\xa5" // U+f1e5 +#define ICON_FA_BIOHAZARD "\xef\x9e\x80" // U+f780 +#define ICON_FA_BIRTHDAY_CAKE "\xef\x87\xbd" // U+f1fd +#define ICON_FA_BLENDER "\xef\x94\x97" // U+f517 +#define ICON_FA_BLENDER_PHONE "\xef\x9a\xb6" // U+f6b6 +#define ICON_FA_BLIND "\xef\x8a\x9d" // U+f29d +#define ICON_FA_BLOG "\xef\x9e\x81" // U+f781 +#define ICON_FA_BOLD "\xef\x80\xb2" // U+f032 +#define ICON_FA_BOLT "\xef\x83\xa7" // U+f0e7 +#define ICON_FA_BOMB "\xef\x87\xa2" // U+f1e2 +#define ICON_FA_BONE "\xef\x97\x97" // U+f5d7 +#define ICON_FA_BONG "\xef\x95\x9c" // U+f55c +#define ICON_FA_BOOK "\xef\x80\xad" // U+f02d +#define ICON_FA_BOOK_DEAD "\xef\x9a\xb7" // U+f6b7 +#define ICON_FA_BOOK_MEDICAL "\xef\x9f\xa6" // U+f7e6 +#define ICON_FA_BOOK_OPEN "\xef\x94\x98" // U+f518 +#define ICON_FA_BOOK_READER "\xef\x97\x9a" // U+f5da +#define ICON_FA_BOOKMARK "\xef\x80\xae" // U+f02e +#define ICON_FA_BORDER_ALL "\xef\xa1\x8c" // U+f84c +#define ICON_FA_BORDER_NONE "\xef\xa1\x90" // U+f850 +#define ICON_FA_BORDER_STYLE "\xef\xa1\x93" // U+f853 +#define ICON_FA_BOWLING_BALL "\xef\x90\xb6" // U+f436 +#define ICON_FA_BOX "\xef\x91\xa6" // U+f466 +#define ICON_FA_BOX_OPEN "\xef\x92\x9e" // U+f49e +#define ICON_FA_BOX_TISSUE "\xee\x81\x9b" // U+e05b +#define ICON_FA_BOXES "\xef\x91\xa8" // U+f468 +#define ICON_FA_BRAILLE "\xef\x8a\xa1" // U+f2a1 +#define ICON_FA_BRAIN "\xef\x97\x9c" // U+f5dc +#define ICON_FA_BREAD_SLICE "\xef\x9f\xac" // U+f7ec +#define ICON_FA_BRIEFCASE "\xef\x82\xb1" // U+f0b1 +#define ICON_FA_BRIEFCASE_MEDICAL "\xef\x91\xa9" // U+f469 +#define ICON_FA_BROADCAST_TOWER "\xef\x94\x99" // U+f519 +#define ICON_FA_BROOM "\xef\x94\x9a" // U+f51a +#define ICON_FA_BRUSH "\xef\x95\x9d" // U+f55d +#define ICON_FA_BUG "\xef\x86\x88" // U+f188 +#define ICON_FA_BUILDING "\xef\x86\xad" // U+f1ad +#define ICON_FA_BULLHORN "\xef\x82\xa1" // U+f0a1 +#define ICON_FA_BULLSEYE "\xef\x85\x80" // U+f140 +#define ICON_FA_BURN "\xef\x91\xaa" // U+f46a +#define ICON_FA_BUS "\xef\x88\x87" // U+f207 +#define ICON_FA_BUS_ALT "\xef\x95\x9e" // U+f55e +#define ICON_FA_BUSINESS_TIME "\xef\x99\x8a" // U+f64a +#define ICON_FA_CALCULATOR "\xef\x87\xac" // U+f1ec +#define ICON_FA_CALENDAR "\xef\x84\xb3" // U+f133 +#define ICON_FA_CALENDAR_ALT "\xef\x81\xb3" // U+f073 +#define ICON_FA_CALENDAR_CHECK "\xef\x89\xb4" // U+f274 +#define ICON_FA_CALENDAR_DAY "\xef\x9e\x83" // U+f783 +#define ICON_FA_CALENDAR_MINUS "\xef\x89\xb2" // U+f272 +#define ICON_FA_CALENDAR_PLUS "\xef\x89\xb1" // U+f271 +#define ICON_FA_CALENDAR_TIMES "\xef\x89\xb3" // U+f273 +#define ICON_FA_CALENDAR_WEEK "\xef\x9e\x84" // U+f784 +#define ICON_FA_CAMERA "\xef\x80\xb0" // U+f030 +#define ICON_FA_CAMERA_RETRO "\xef\x82\x83" // U+f083 +#define ICON_FA_CAMPGROUND "\xef\x9a\xbb" // U+f6bb +#define ICON_FA_CANDY_CANE "\xef\x9e\x86" // U+f786 +#define ICON_FA_CANNABIS "\xef\x95\x9f" // U+f55f +#define ICON_FA_CAPSULES "\xef\x91\xab" // U+f46b +#define ICON_FA_CAR "\xef\x86\xb9" // U+f1b9 +#define ICON_FA_CAR_ALT "\xef\x97\x9e" // U+f5de +#define ICON_FA_CAR_BATTERY "\xef\x97\x9f" // U+f5df +#define ICON_FA_CAR_CRASH "\xef\x97\xa1" // U+f5e1 +#define ICON_FA_CAR_SIDE "\xef\x97\xa4" // U+f5e4 +#define ICON_FA_CARAVAN "\xef\xa3\xbf" // U+f8ff +#define ICON_FA_CARET_DOWN "\xef\x83\x97" // U+f0d7 +#define ICON_FA_CARET_LEFT "\xef\x83\x99" // U+f0d9 +#define ICON_FA_CARET_RIGHT "\xef\x83\x9a" // U+f0da +#define ICON_FA_CARET_SQUARE_DOWN "\xef\x85\x90" // U+f150 +#define ICON_FA_CARET_SQUARE_LEFT "\xef\x86\x91" // U+f191 +#define ICON_FA_CARET_SQUARE_RIGHT "\xef\x85\x92" // U+f152 +#define ICON_FA_CARET_SQUARE_UP "\xef\x85\x91" // U+f151 +#define ICON_FA_CARET_UP "\xef\x83\x98" // U+f0d8 +#define ICON_FA_CARROT "\xef\x9e\x87" // U+f787 +#define ICON_FA_CART_ARROW_DOWN "\xef\x88\x98" // U+f218 +#define ICON_FA_CART_PLUS "\xef\x88\x97" // U+f217 +#define ICON_FA_CASH_REGISTER "\xef\x9e\x88" // U+f788 +#define ICON_FA_CAT "\xef\x9a\xbe" // U+f6be +#define ICON_FA_CERTIFICATE "\xef\x82\xa3" // U+f0a3 +#define ICON_FA_CHAIR "\xef\x9b\x80" // U+f6c0 +#define ICON_FA_CHALKBOARD "\xef\x94\x9b" // U+f51b +#define ICON_FA_CHALKBOARD_TEACHER "\xef\x94\x9c" // U+f51c +#define ICON_FA_CHARGING_STATION "\xef\x97\xa7" // U+f5e7 +#define ICON_FA_CHART_AREA "\xef\x87\xbe" // U+f1fe +#define ICON_FA_CHART_BAR "\xef\x82\x80" // U+f080 +#define ICON_FA_CHART_LINE "\xef\x88\x81" // U+f201 +#define ICON_FA_CHART_PIE "\xef\x88\x80" // U+f200 +#define ICON_FA_CHECK "\xef\x80\x8c" // U+f00c +#define ICON_FA_CHECK_CIRCLE "\xef\x81\x98" // U+f058 +#define ICON_FA_CHECK_DOUBLE "\xef\x95\xa0" // U+f560 +#define ICON_FA_CHECK_SQUARE "\xef\x85\x8a" // U+f14a +#define ICON_FA_CHEESE "\xef\x9f\xaf" // U+f7ef +#define ICON_FA_CHESS "\xef\x90\xb9" // U+f439 +#define ICON_FA_CHESS_BISHOP "\xef\x90\xba" // U+f43a +#define ICON_FA_CHESS_BOARD "\xef\x90\xbc" // U+f43c +#define ICON_FA_CHESS_KING "\xef\x90\xbf" // U+f43f +#define ICON_FA_CHESS_KNIGHT "\xef\x91\x81" // U+f441 +#define ICON_FA_CHESS_PAWN "\xef\x91\x83" // U+f443 +#define ICON_FA_CHESS_QUEEN "\xef\x91\x85" // U+f445 +#define ICON_FA_CHESS_ROOK "\xef\x91\x87" // U+f447 +#define ICON_FA_CHEVRON_CIRCLE_DOWN "\xef\x84\xba" // U+f13a +#define ICON_FA_CHEVRON_CIRCLE_LEFT "\xef\x84\xb7" // U+f137 +#define ICON_FA_CHEVRON_CIRCLE_RIGHT "\xef\x84\xb8" // U+f138 +#define ICON_FA_CHEVRON_CIRCLE_UP "\xef\x84\xb9" // U+f139 +#define ICON_FA_CHEVRON_DOWN "\xef\x81\xb8" // U+f078 +#define ICON_FA_CHEVRON_LEFT "\xef\x81\x93" // U+f053 +#define ICON_FA_CHEVRON_RIGHT "\xef\x81\x94" // U+f054 +#define ICON_FA_CHEVRON_UP "\xef\x81\xb7" // U+f077 +#define ICON_FA_CHILD "\xef\x86\xae" // U+f1ae +#define ICON_FA_CHURCH "\xef\x94\x9d" // U+f51d +#define ICON_FA_CIRCLE "\xef\x84\x91" // U+f111 +#define ICON_FA_CIRCLE_NOTCH "\xef\x87\x8e" // U+f1ce +#define ICON_FA_CITY "\xef\x99\x8f" // U+f64f +#define ICON_FA_CLINIC_MEDICAL "\xef\x9f\xb2" // U+f7f2 +#define ICON_FA_CLIPBOARD "\xef\x8c\xa8" // U+f328 +#define ICON_FA_CLIPBOARD_CHECK "\xef\x91\xac" // U+f46c +#define ICON_FA_CLIPBOARD_LIST "\xef\x91\xad" // U+f46d +#define ICON_FA_CLOCK "\xef\x80\x97" // U+f017 +#define ICON_FA_CLONE "\xef\x89\x8d" // U+f24d +#define ICON_FA_CLOSED_CAPTIONING "\xef\x88\x8a" // U+f20a +#define ICON_FA_CLOUD "\xef\x83\x82" // U+f0c2 +#define ICON_FA_CLOUD_DOWNLOAD_ALT "\xef\x8e\x81" // U+f381 +#define ICON_FA_CLOUD_MEATBALL "\xef\x9c\xbb" // U+f73b +#define ICON_FA_CLOUD_MOON "\xef\x9b\x83" // U+f6c3 +#define ICON_FA_CLOUD_MOON_RAIN "\xef\x9c\xbc" // U+f73c +#define ICON_FA_CLOUD_RAIN "\xef\x9c\xbd" // U+f73d +#define ICON_FA_CLOUD_SHOWERS_HEAVY "\xef\x9d\x80" // U+f740 +#define ICON_FA_CLOUD_SUN "\xef\x9b\x84" // U+f6c4 +#define ICON_FA_CLOUD_SUN_RAIN "\xef\x9d\x83" // U+f743 +#define ICON_FA_CLOUD_UPLOAD_ALT "\xef\x8e\x82" // U+f382 +#define ICON_FA_COCKTAIL "\xef\x95\xa1" // U+f561 +#define ICON_FA_CODE "\xef\x84\xa1" // U+f121 +#define ICON_FA_CODE_BRANCH "\xef\x84\xa6" // U+f126 +#define ICON_FA_COFFEE "\xef\x83\xb4" // U+f0f4 +#define ICON_FA_COG "\xef\x80\x93" // U+f013 +#define ICON_FA_COGS "\xef\x82\x85" // U+f085 +#define ICON_FA_COINS "\xef\x94\x9e" // U+f51e +#define ICON_FA_COLUMNS "\xef\x83\x9b" // U+f0db +#define ICON_FA_COMMENT "\xef\x81\xb5" // U+f075 +#define ICON_FA_COMMENT_ALT "\xef\x89\xba" // U+f27a +#define ICON_FA_COMMENT_DOLLAR "\xef\x99\x91" // U+f651 +#define ICON_FA_COMMENT_DOTS "\xef\x92\xad" // U+f4ad +#define ICON_FA_COMMENT_MEDICAL "\xef\x9f\xb5" // U+f7f5 +#define ICON_FA_COMMENT_SLASH "\xef\x92\xb3" // U+f4b3 +#define ICON_FA_COMMENTS "\xef\x82\x86" // U+f086 +#define ICON_FA_COMMENTS_DOLLAR "\xef\x99\x93" // U+f653 +#define ICON_FA_COMPACT_DISC "\xef\x94\x9f" // U+f51f +#define ICON_FA_COMPASS "\xef\x85\x8e" // U+f14e +#define ICON_FA_COMPRESS "\xef\x81\xa6" // U+f066 +#define ICON_FA_COMPRESS_ALT "\xef\x90\xa2" // U+f422 +#define ICON_FA_COMPRESS_ARROWS_ALT "\xef\x9e\x8c" // U+f78c +#define ICON_FA_CONCIERGE_BELL "\xef\x95\xa2" // U+f562 +#define ICON_FA_COOKIE "\xef\x95\xa3" // U+f563 +#define ICON_FA_COOKIE_BITE "\xef\x95\xa4" // U+f564 +#define ICON_FA_COPY "\xef\x83\x85" // U+f0c5 +#define ICON_FA_COPYRIGHT "\xef\x87\xb9" // U+f1f9 +#define ICON_FA_COUCH "\xef\x92\xb8" // U+f4b8 +#define ICON_FA_CREDIT_CARD "\xef\x82\x9d" // U+f09d +#define ICON_FA_CROP "\xef\x84\xa5" // U+f125 +#define ICON_FA_CROP_ALT "\xef\x95\xa5" // U+f565 +#define ICON_FA_CROSS "\xef\x99\x94" // U+f654 +#define ICON_FA_CROSSHAIRS "\xef\x81\x9b" // U+f05b +#define ICON_FA_CROW "\xef\x94\xa0" // U+f520 +#define ICON_FA_CROWN "\xef\x94\xa1" // U+f521 +#define ICON_FA_CRUTCH "\xef\x9f\xb7" // U+f7f7 +#define ICON_FA_CUBE "\xef\x86\xb2" // U+f1b2 +#define ICON_FA_CUBES "\xef\x86\xb3" // U+f1b3 +#define ICON_FA_CUT "\xef\x83\x84" // U+f0c4 +#define ICON_FA_DATABASE "\xef\x87\x80" // U+f1c0 +#define ICON_FA_DEAF "\xef\x8a\xa4" // U+f2a4 +#define ICON_FA_DEMOCRAT "\xef\x9d\x87" // U+f747 +#define ICON_FA_DESKTOP "\xef\x84\x88" // U+f108 +#define ICON_FA_DHARMACHAKRA "\xef\x99\x95" // U+f655 +#define ICON_FA_DIAGNOSES "\xef\x91\xb0" // U+f470 +#define ICON_FA_DICE "\xef\x94\xa2" // U+f522 +#define ICON_FA_DICE_D20 "\xef\x9b\x8f" // U+f6cf +#define ICON_FA_DICE_D6 "\xef\x9b\x91" // U+f6d1 +#define ICON_FA_DICE_FIVE "\xef\x94\xa3" // U+f523 +#define ICON_FA_DICE_FOUR "\xef\x94\xa4" // U+f524 +#define ICON_FA_DICE_ONE "\xef\x94\xa5" // U+f525 +#define ICON_FA_DICE_SIX "\xef\x94\xa6" // U+f526 +#define ICON_FA_DICE_THREE "\xef\x94\xa7" // U+f527 +#define ICON_FA_DICE_TWO "\xef\x94\xa8" // U+f528 +#define ICON_FA_DIGITAL_TACHOGRAPH "\xef\x95\xa6" // U+f566 +#define ICON_FA_DIRECTIONS "\xef\x97\xab" // U+f5eb +#define ICON_FA_DISEASE "\xef\x9f\xba" // U+f7fa +#define ICON_FA_DIVIDE "\xef\x94\xa9" // U+f529 +#define ICON_FA_DIZZY "\xef\x95\xa7" // U+f567 +#define ICON_FA_DNA "\xef\x91\xb1" // U+f471 +#define ICON_FA_DOG "\xef\x9b\x93" // U+f6d3 +#define ICON_FA_DOLLAR_SIGN "\xef\x85\x95" // U+f155 +#define ICON_FA_DOLLY "\xef\x91\xb2" // U+f472 +#define ICON_FA_DOLLY_FLATBED "\xef\x91\xb4" // U+f474 +#define ICON_FA_DONATE "\xef\x92\xb9" // U+f4b9 +#define ICON_FA_DOOR_CLOSED "\xef\x94\xaa" // U+f52a +#define ICON_FA_DOOR_OPEN "\xef\x94\xab" // U+f52b +#define ICON_FA_DOT_CIRCLE "\xef\x86\x92" // U+f192 +#define ICON_FA_DOVE "\xef\x92\xba" // U+f4ba +#define ICON_FA_DOWNLOAD "\xef\x80\x99" // U+f019 +#define ICON_FA_DRAFTING_COMPASS "\xef\x95\xa8" // U+f568 +#define ICON_FA_DRAGON "\xef\x9b\x95" // U+f6d5 +#define ICON_FA_DRAW_POLYGON "\xef\x97\xae" // U+f5ee +#define ICON_FA_DRUM "\xef\x95\xa9" // U+f569 +#define ICON_FA_DRUM_STEELPAN "\xef\x95\xaa" // U+f56a +#define ICON_FA_DRUMSTICK_BITE "\xef\x9b\x97" // U+f6d7 +#define ICON_FA_DUMBBELL "\xef\x91\x8b" // U+f44b +#define ICON_FA_DUMPSTER "\xef\x9e\x93" // U+f793 +#define ICON_FA_DUMPSTER_FIRE "\xef\x9e\x94" // U+f794 +#define ICON_FA_DUNGEON "\xef\x9b\x99" // U+f6d9 +#define ICON_FA_EDIT "\xef\x81\x84" // U+f044 +#define ICON_FA_EGG "\xef\x9f\xbb" // U+f7fb +#define ICON_FA_EJECT "\xef\x81\x92" // U+f052 +#define ICON_FA_ELLIPSIS_H "\xef\x85\x81" // U+f141 +#define ICON_FA_ELLIPSIS_V "\xef\x85\x82" // U+f142 +#define ICON_FA_ENVELOPE "\xef\x83\xa0" // U+f0e0 +#define ICON_FA_ENVELOPE_OPEN "\xef\x8a\xb6" // U+f2b6 +#define ICON_FA_ENVELOPE_OPEN_TEXT "\xef\x99\x98" // U+f658 +#define ICON_FA_ENVELOPE_SQUARE "\xef\x86\x99" // U+f199 +#define ICON_FA_EQUALS "\xef\x94\xac" // U+f52c +#define ICON_FA_ERASER "\xef\x84\xad" // U+f12d +#define ICON_FA_ETHERNET "\xef\x9e\x96" // U+f796 +#define ICON_FA_EURO_SIGN "\xef\x85\x93" // U+f153 +#define ICON_FA_EXCHANGE_ALT "\xef\x8d\xa2" // U+f362 +#define ICON_FA_EXCLAMATION "\xef\x84\xaa" // U+f12a +#define ICON_FA_EXCLAMATION_CIRCLE "\xef\x81\xaa" // U+f06a +#define ICON_FA_EXCLAMATION_TRIANGLE "\xef\x81\xb1" // U+f071 +#define ICON_FA_EXPAND "\xef\x81\xa5" // U+f065 +#define ICON_FA_EXPAND_ALT "\xef\x90\xa4" // U+f424 +#define ICON_FA_EXPAND_ARROWS_ALT "\xef\x8c\x9e" // U+f31e +#define ICON_FA_EXTERNAL_LINK_ALT "\xef\x8d\x9d" // U+f35d +#define ICON_FA_EXTERNAL_LINK_SQUARE_ALT "\xef\x8d\xa0" // U+f360 +#define ICON_FA_EYE "\xef\x81\xae" // U+f06e +#define ICON_FA_EYE_DROPPER "\xef\x87\xbb" // U+f1fb +#define ICON_FA_EYE_SLASH "\xef\x81\xb0" // U+f070 +#define ICON_FA_FAN "\xef\xa1\xa3" // U+f863 +#define ICON_FA_FAST_BACKWARD "\xef\x81\x89" // U+f049 +#define ICON_FA_FAST_FORWARD "\xef\x81\x90" // U+f050 +#define ICON_FA_FAUCET "\xee\x80\x85" // U+e005 +#define ICON_FA_FAX "\xef\x86\xac" // U+f1ac +#define ICON_FA_FEATHER "\xef\x94\xad" // U+f52d +#define ICON_FA_FEATHER_ALT "\xef\x95\xab" // U+f56b +#define ICON_FA_FEMALE "\xef\x86\x82" // U+f182 +#define ICON_FA_FIGHTER_JET "\xef\x83\xbb" // U+f0fb +#define ICON_FA_FILE "\xef\x85\x9b" // U+f15b +#define ICON_FA_FILE_ALT "\xef\x85\x9c" // U+f15c +#define ICON_FA_FILE_ARCHIVE "\xef\x87\x86" // U+f1c6 +#define ICON_FA_FILE_AUDIO "\xef\x87\x87" // U+f1c7 +#define ICON_FA_FILE_CODE "\xef\x87\x89" // U+f1c9 +#define ICON_FA_FILE_CONTRACT "\xef\x95\xac" // U+f56c +#define ICON_FA_FILE_CSV "\xef\x9b\x9d" // U+f6dd +#define ICON_FA_FILE_DOWNLOAD "\xef\x95\xad" // U+f56d +#define ICON_FA_FILE_EXCEL "\xef\x87\x83" // U+f1c3 +#define ICON_FA_FILE_EXPORT "\xef\x95\xae" // U+f56e +#define ICON_FA_FILE_IMAGE "\xef\x87\x85" // U+f1c5 +#define ICON_FA_FILE_IMPORT "\xef\x95\xaf" // U+f56f +#define ICON_FA_FILE_INVOICE "\xef\x95\xb0" // U+f570 +#define ICON_FA_FILE_INVOICE_DOLLAR "\xef\x95\xb1" // U+f571 +#define ICON_FA_FILE_MEDICAL "\xef\x91\xb7" // U+f477 +#define ICON_FA_FILE_MEDICAL_ALT "\xef\x91\xb8" // U+f478 +#define ICON_FA_FILE_PDF "\xef\x87\x81" // U+f1c1 +#define ICON_FA_FILE_POWERPOINT "\xef\x87\x84" // U+f1c4 +#define ICON_FA_FILE_PRESCRIPTION "\xef\x95\xb2" // U+f572 +#define ICON_FA_FILE_SIGNATURE "\xef\x95\xb3" // U+f573 +#define ICON_FA_FILE_UPLOAD "\xef\x95\xb4" // U+f574 +#define ICON_FA_FILE_VIDEO "\xef\x87\x88" // U+f1c8 +#define ICON_FA_FILE_WORD "\xef\x87\x82" // U+f1c2 +#define ICON_FA_FILL "\xef\x95\xb5" // U+f575 +#define ICON_FA_FILL_DRIP "\xef\x95\xb6" // U+f576 +#define ICON_FA_FILM "\xef\x80\x88" // U+f008 +#define ICON_FA_FILTER "\xef\x82\xb0" // U+f0b0 +#define ICON_FA_FINGERPRINT "\xef\x95\xb7" // U+f577 +#define ICON_FA_FIRE "\xef\x81\xad" // U+f06d +#define ICON_FA_FIRE_ALT "\xef\x9f\xa4" // U+f7e4 +#define ICON_FA_FIRE_EXTINGUISHER "\xef\x84\xb4" // U+f134 +#define ICON_FA_FIRST_AID "\xef\x91\xb9" // U+f479 +#define ICON_FA_FISH "\xef\x95\xb8" // U+f578 +#define ICON_FA_FIST_RAISED "\xef\x9b\x9e" // U+f6de +#define ICON_FA_FLAG "\xef\x80\xa4" // U+f024 +#define ICON_FA_FLAG_CHECKERED "\xef\x84\x9e" // U+f11e +#define ICON_FA_FLAG_USA "\xef\x9d\x8d" // U+f74d +#define ICON_FA_FLASK "\xef\x83\x83" // U+f0c3 +#define ICON_FA_FLUSHED "\xef\x95\xb9" // U+f579 +#define ICON_FA_FOLDER "\xef\x81\xbb" // U+f07b +#define ICON_FA_FOLDER_MINUS "\xef\x99\x9d" // U+f65d +#define ICON_FA_FOLDER_OPEN "\xef\x81\xbc" // U+f07c +#define ICON_FA_FOLDER_PLUS "\xef\x99\x9e" // U+f65e +#define ICON_FA_FONT "\xef\x80\xb1" // U+f031 +#define ICON_FA_FONT_AWESOME_LOGO_FULL "\xef\x93\xa6" // U+f4e6 +#define ICON_FA_FOOTBALL_BALL "\xef\x91\x8e" // U+f44e +#define ICON_FA_FORWARD "\xef\x81\x8e" // U+f04e +#define ICON_FA_FROG "\xef\x94\xae" // U+f52e +#define ICON_FA_FROWN "\xef\x84\x99" // U+f119 +#define ICON_FA_FROWN_OPEN "\xef\x95\xba" // U+f57a +#define ICON_FA_FUNNEL_DOLLAR "\xef\x99\xa2" // U+f662 +#define ICON_FA_FUTBOL "\xef\x87\xa3" // U+f1e3 +#define ICON_FA_GAMEPAD "\xef\x84\x9b" // U+f11b +#define ICON_FA_GAS_PUMP "\xef\x94\xaf" // U+f52f +#define ICON_FA_GAVEL "\xef\x83\xa3" // U+f0e3 +#define ICON_FA_GEM "\xef\x8e\xa5" // U+f3a5 +#define ICON_FA_GENDERLESS "\xef\x88\xad" // U+f22d +#define ICON_FA_GHOST "\xef\x9b\xa2" // U+f6e2 +#define ICON_FA_GIFT "\xef\x81\xab" // U+f06b +#define ICON_FA_GIFTS "\xef\x9e\x9c" // U+f79c +#define ICON_FA_GLASS_CHEERS "\xef\x9e\x9f" // U+f79f +#define ICON_FA_GLASS_MARTINI "\xef\x80\x80" // U+f000 +#define ICON_FA_GLASS_MARTINI_ALT "\xef\x95\xbb" // U+f57b +#define ICON_FA_GLASS_WHISKEY "\xef\x9e\xa0" // U+f7a0 +#define ICON_FA_GLASSES "\xef\x94\xb0" // U+f530 +#define ICON_FA_GLOBE "\xef\x82\xac" // U+f0ac +#define ICON_FA_GLOBE_AFRICA "\xef\x95\xbc" // U+f57c +#define ICON_FA_GLOBE_AMERICAS "\xef\x95\xbd" // U+f57d +#define ICON_FA_GLOBE_ASIA "\xef\x95\xbe" // U+f57e +#define ICON_FA_GLOBE_EUROPE "\xef\x9e\xa2" // U+f7a2 +#define ICON_FA_GOLF_BALL "\xef\x91\x90" // U+f450 +#define ICON_FA_GOPURAM "\xef\x99\xa4" // U+f664 +#define ICON_FA_GRADUATION_CAP "\xef\x86\x9d" // U+f19d +#define ICON_FA_GREATER_THAN "\xef\x94\xb1" // U+f531 +#define ICON_FA_GREATER_THAN_EQUAL "\xef\x94\xb2" // U+f532 +#define ICON_FA_GRIMACE "\xef\x95\xbf" // U+f57f +#define ICON_FA_GRIN "\xef\x96\x80" // U+f580 +#define ICON_FA_GRIN_ALT "\xef\x96\x81" // U+f581 +#define ICON_FA_GRIN_BEAM "\xef\x96\x82" // U+f582 +#define ICON_FA_GRIN_BEAM_SWEAT "\xef\x96\x83" // U+f583 +#define ICON_FA_GRIN_HEARTS "\xef\x96\x84" // U+f584 +#define ICON_FA_GRIN_SQUINT "\xef\x96\x85" // U+f585 +#define ICON_FA_GRIN_SQUINT_TEARS "\xef\x96\x86" // U+f586 +#define ICON_FA_GRIN_STARS "\xef\x96\x87" // U+f587 +#define ICON_FA_GRIN_TEARS "\xef\x96\x88" // U+f588 +#define ICON_FA_GRIN_TONGUE "\xef\x96\x89" // U+f589 +#define ICON_FA_GRIN_TONGUE_SQUINT "\xef\x96\x8a" // U+f58a +#define ICON_FA_GRIN_TONGUE_WINK "\xef\x96\x8b" // U+f58b +#define ICON_FA_GRIN_WINK "\xef\x96\x8c" // U+f58c +#define ICON_FA_GRIP_HORIZONTAL "\xef\x96\x8d" // U+f58d +#define ICON_FA_GRIP_LINES "\xef\x9e\xa4" // U+f7a4 +#define ICON_FA_GRIP_LINES_VERTICAL "\xef\x9e\xa5" // U+f7a5 +#define ICON_FA_GRIP_VERTICAL "\xef\x96\x8e" // U+f58e +#define ICON_FA_GUITAR "\xef\x9e\xa6" // U+f7a6 +#define ICON_FA_H_SQUARE "\xef\x83\xbd" // U+f0fd +#define ICON_FA_HAMBURGER "\xef\xa0\x85" // U+f805 +#define ICON_FA_HAMMER "\xef\x9b\xa3" // U+f6e3 +#define ICON_FA_HAMSA "\xef\x99\xa5" // U+f665 +#define ICON_FA_HAND_HOLDING "\xef\x92\xbd" // U+f4bd +#define ICON_FA_HAND_HOLDING_HEART "\xef\x92\xbe" // U+f4be +#define ICON_FA_HAND_HOLDING_MEDICAL "\xee\x81\x9c" // U+e05c +#define ICON_FA_HAND_HOLDING_USD "\xef\x93\x80" // U+f4c0 +#define ICON_FA_HAND_HOLDING_WATER "\xef\x93\x81" // U+f4c1 +#define ICON_FA_HAND_LIZARD "\xef\x89\x98" // U+f258 +#define ICON_FA_HAND_MIDDLE_FINGER "\xef\xa0\x86" // U+f806 +#define ICON_FA_HAND_PAPER "\xef\x89\x96" // U+f256 +#define ICON_FA_HAND_PEACE "\xef\x89\x9b" // U+f25b +#define ICON_FA_HAND_POINT_DOWN "\xef\x82\xa7" // U+f0a7 +#define ICON_FA_HAND_POINT_LEFT "\xef\x82\xa5" // U+f0a5 +#define ICON_FA_HAND_POINT_RIGHT "\xef\x82\xa4" // U+f0a4 +#define ICON_FA_HAND_POINT_UP "\xef\x82\xa6" // U+f0a6 +#define ICON_FA_HAND_POINTER "\xef\x89\x9a" // U+f25a +#define ICON_FA_HAND_ROCK "\xef\x89\x95" // U+f255 +#define ICON_FA_HAND_SCISSORS "\xef\x89\x97" // U+f257 +#define ICON_FA_HAND_SPARKLES "\xee\x81\x9d" // U+e05d +#define ICON_FA_HAND_SPOCK "\xef\x89\x99" // U+f259 +#define ICON_FA_HANDS "\xef\x93\x82" // U+f4c2 +#define ICON_FA_HANDS_HELPING "\xef\x93\x84" // U+f4c4 +#define ICON_FA_HANDS_WASH "\xee\x81\x9e" // U+e05e +#define ICON_FA_HANDSHAKE "\xef\x8a\xb5" // U+f2b5 +#define ICON_FA_HANDSHAKE_ALT_SLASH "\xee\x81\x9f" // U+e05f +#define ICON_FA_HANDSHAKE_SLASH "\xee\x81\xa0" // U+e060 +#define ICON_FA_HANUKIAH "\xef\x9b\xa6" // U+f6e6 +#define ICON_FA_HARD_HAT "\xef\xa0\x87" // U+f807 +#define ICON_FA_HASHTAG "\xef\x8a\x92" // U+f292 +#define ICON_FA_HAT_COWBOY "\xef\xa3\x80" // U+f8c0 +#define ICON_FA_HAT_COWBOY_SIDE "\xef\xa3\x81" // U+f8c1 +#define ICON_FA_HAT_WIZARD "\xef\x9b\xa8" // U+f6e8 +#define ICON_FA_HDD "\xef\x82\xa0" // U+f0a0 +#define ICON_FA_HEAD_SIDE_COUGH "\xee\x81\xa1" // U+e061 +#define ICON_FA_HEAD_SIDE_COUGH_SLASH "\xee\x81\xa2" // U+e062 +#define ICON_FA_HEAD_SIDE_MASK "\xee\x81\xa3" // U+e063 +#define ICON_FA_HEAD_SIDE_VIRUS "\xee\x81\xa4" // U+e064 +#define ICON_FA_HEADING "\xef\x87\x9c" // U+f1dc +#define ICON_FA_HEADPHONES "\xef\x80\xa5" // U+f025 +#define ICON_FA_HEADPHONES_ALT "\xef\x96\x8f" // U+f58f +#define ICON_FA_HEADSET "\xef\x96\x90" // U+f590 +#define ICON_FA_HEART "\xef\x80\x84" // U+f004 +#define ICON_FA_HEART_BROKEN "\xef\x9e\xa9" // U+f7a9 +#define ICON_FA_HEARTBEAT "\xef\x88\x9e" // U+f21e +#define ICON_FA_HELICOPTER "\xef\x94\xb3" // U+f533 +#define ICON_FA_HIGHLIGHTER "\xef\x96\x91" // U+f591 +#define ICON_FA_HIKING "\xef\x9b\xac" // U+f6ec +#define ICON_FA_HIPPO "\xef\x9b\xad" // U+f6ed +#define ICON_FA_HISTORY "\xef\x87\x9a" // U+f1da +#define ICON_FA_HOCKEY_PUCK "\xef\x91\x93" // U+f453 +#define ICON_FA_HOLLY_BERRY "\xef\x9e\xaa" // U+f7aa +#define ICON_FA_HOME "\xef\x80\x95" // U+f015 +#define ICON_FA_HORSE "\xef\x9b\xb0" // U+f6f0 +#define ICON_FA_HORSE_HEAD "\xef\x9e\xab" // U+f7ab +#define ICON_FA_HOSPITAL "\xef\x83\xb8" // U+f0f8 +#define ICON_FA_HOSPITAL_ALT "\xef\x91\xbd" // U+f47d +#define ICON_FA_HOSPITAL_SYMBOL "\xef\x91\xbe" // U+f47e +#define ICON_FA_HOSPITAL_USER "\xef\xa0\x8d" // U+f80d +#define ICON_FA_HOT_TUB "\xef\x96\x93" // U+f593 +#define ICON_FA_HOTDOG "\xef\xa0\x8f" // U+f80f +#define ICON_FA_HOTEL "\xef\x96\x94" // U+f594 +#define ICON_FA_HOURGLASS "\xef\x89\x94" // U+f254 +#define ICON_FA_HOURGLASS_END "\xef\x89\x93" // U+f253 +#define ICON_FA_HOURGLASS_HALF "\xef\x89\x92" // U+f252 +#define ICON_FA_HOURGLASS_START "\xef\x89\x91" // U+f251 +#define ICON_FA_HOUSE_DAMAGE "\xef\x9b\xb1" // U+f6f1 +#define ICON_FA_HOUSE_USER "\xee\x81\xa5" // U+e065 +#define ICON_FA_HRYVNIA "\xef\x9b\xb2" // U+f6f2 +#define ICON_FA_I_CURSOR "\xef\x89\x86" // U+f246 +#define ICON_FA_ICE_CREAM "\xef\xa0\x90" // U+f810 +#define ICON_FA_ICICLES "\xef\x9e\xad" // U+f7ad +#define ICON_FA_ICONS "\xef\xa1\xad" // U+f86d +#define ICON_FA_ID_BADGE "\xef\x8b\x81" // U+f2c1 +#define ICON_FA_ID_CARD "\xef\x8b\x82" // U+f2c2 +#define ICON_FA_ID_CARD_ALT "\xef\x91\xbf" // U+f47f +#define ICON_FA_IGLOO "\xef\x9e\xae" // U+f7ae +#define ICON_FA_IMAGE "\xef\x80\xbe" // U+f03e +#define ICON_FA_IMAGES "\xef\x8c\x82" // U+f302 +#define ICON_FA_INBOX "\xef\x80\x9c" // U+f01c +#define ICON_FA_INDENT "\xef\x80\xbc" // U+f03c +#define ICON_FA_INDUSTRY "\xef\x89\xb5" // U+f275 +#define ICON_FA_INFINITY "\xef\x94\xb4" // U+f534 +#define ICON_FA_INFO "\xef\x84\xa9" // U+f129 +#define ICON_FA_INFO_CIRCLE "\xef\x81\x9a" // U+f05a +#define ICON_FA_ITALIC "\xef\x80\xb3" // U+f033 +#define ICON_FA_JEDI "\xef\x99\xa9" // U+f669 +#define ICON_FA_JOINT "\xef\x96\x95" // U+f595 +#define ICON_FA_JOURNAL_WHILLS "\xef\x99\xaa" // U+f66a +#define ICON_FA_KAABA "\xef\x99\xab" // U+f66b +#define ICON_FA_KEY "\xef\x82\x84" // U+f084 +#define ICON_FA_KEYBOARD "\xef\x84\x9c" // U+f11c +#define ICON_FA_KHANDA "\xef\x99\xad" // U+f66d +#define ICON_FA_KISS "\xef\x96\x96" // U+f596 +#define ICON_FA_KISS_BEAM "\xef\x96\x97" // U+f597 +#define ICON_FA_KISS_WINK_HEART "\xef\x96\x98" // U+f598 +#define ICON_FA_KIWI_BIRD "\xef\x94\xb5" // U+f535 +#define ICON_FA_LANDMARK "\xef\x99\xaf" // U+f66f +#define ICON_FA_LANGUAGE "\xef\x86\xab" // U+f1ab +#define ICON_FA_LAPTOP "\xef\x84\x89" // U+f109 +#define ICON_FA_LAPTOP_CODE "\xef\x97\xbc" // U+f5fc +#define ICON_FA_LAPTOP_HOUSE "\xee\x81\xa6" // U+e066 +#define ICON_FA_LAPTOP_MEDICAL "\xef\xa0\x92" // U+f812 +#define ICON_FA_LAUGH "\xef\x96\x99" // U+f599 +#define ICON_FA_LAUGH_BEAM "\xef\x96\x9a" // U+f59a +#define ICON_FA_LAUGH_SQUINT "\xef\x96\x9b" // U+f59b +#define ICON_FA_LAUGH_WINK "\xef\x96\x9c" // U+f59c +#define ICON_FA_LAYER_GROUP "\xef\x97\xbd" // U+f5fd +#define ICON_FA_LEAF "\xef\x81\xac" // U+f06c +#define ICON_FA_LEMON "\xef\x82\x94" // U+f094 +#define ICON_FA_LESS_THAN "\xef\x94\xb6" // U+f536 +#define ICON_FA_LESS_THAN_EQUAL "\xef\x94\xb7" // U+f537 +#define ICON_FA_LEVEL_DOWN_ALT "\xef\x8e\xbe" // U+f3be +#define ICON_FA_LEVEL_UP_ALT "\xef\x8e\xbf" // U+f3bf +#define ICON_FA_LIFE_RING "\xef\x87\x8d" // U+f1cd +#define ICON_FA_LIGHTBULB "\xef\x83\xab" // U+f0eb +#define ICON_FA_LINK "\xef\x83\x81" // U+f0c1 +#define ICON_FA_LIRA_SIGN "\xef\x86\x95" // U+f195 +#define ICON_FA_LIST "\xef\x80\xba" // U+f03a +#define ICON_FA_LIST_ALT "\xef\x80\xa2" // U+f022 +#define ICON_FA_LIST_OL "\xef\x83\x8b" // U+f0cb +#define ICON_FA_LIST_UL "\xef\x83\x8a" // U+f0ca +#define ICON_FA_LOCATION_ARROW "\xef\x84\xa4" // U+f124 +#define ICON_FA_LOCK "\xef\x80\xa3" // U+f023 +#define ICON_FA_LOCK_OPEN "\xef\x8f\x81" // U+f3c1 +#define ICON_FA_LONG_ARROW_ALT_DOWN "\xef\x8c\x89" // U+f309 +#define ICON_FA_LONG_ARROW_ALT_LEFT "\xef\x8c\x8a" // U+f30a +#define ICON_FA_LONG_ARROW_ALT_RIGHT "\xef\x8c\x8b" // U+f30b +#define ICON_FA_LONG_ARROW_ALT_UP "\xef\x8c\x8c" // U+f30c +#define ICON_FA_LOW_VISION "\xef\x8a\xa8" // U+f2a8 +#define ICON_FA_LUGGAGE_CART "\xef\x96\x9d" // U+f59d +#define ICON_FA_LUNGS "\xef\x98\x84" // U+f604 +#define ICON_FA_LUNGS_VIRUS "\xee\x81\xa7" // U+e067 +#define ICON_FA_MAGIC "\xef\x83\x90" // U+f0d0 +#define ICON_FA_MAGNET "\xef\x81\xb6" // U+f076 +#define ICON_FA_MAIL_BULK "\xef\x99\xb4" // U+f674 +#define ICON_FA_MALE "\xef\x86\x83" // U+f183 +#define ICON_FA_MAP "\xef\x89\xb9" // U+f279 +#define ICON_FA_MAP_MARKED "\xef\x96\x9f" // U+f59f +#define ICON_FA_MAP_MARKED_ALT "\xef\x96\xa0" // U+f5a0 +#define ICON_FA_MAP_MARKER "\xef\x81\x81" // U+f041 +#define ICON_FA_MAP_MARKER_ALT "\xef\x8f\x85" // U+f3c5 +#define ICON_FA_MAP_PIN "\xef\x89\xb6" // U+f276 +#define ICON_FA_MAP_SIGNS "\xef\x89\xb7" // U+f277 +#define ICON_FA_MARKER "\xef\x96\xa1" // U+f5a1 +#define ICON_FA_MARS "\xef\x88\xa2" // U+f222 +#define ICON_FA_MARS_DOUBLE "\xef\x88\xa7" // U+f227 +#define ICON_FA_MARS_STROKE "\xef\x88\xa9" // U+f229 +#define ICON_FA_MARS_STROKE_H "\xef\x88\xab" // U+f22b +#define ICON_FA_MARS_STROKE_V "\xef\x88\xaa" // U+f22a +#define ICON_FA_MASK "\xef\x9b\xba" // U+f6fa +#define ICON_FA_MEDAL "\xef\x96\xa2" // U+f5a2 +#define ICON_FA_MEDKIT "\xef\x83\xba" // U+f0fa +#define ICON_FA_MEH "\xef\x84\x9a" // U+f11a +#define ICON_FA_MEH_BLANK "\xef\x96\xa4" // U+f5a4 +#define ICON_FA_MEH_ROLLING_EYES "\xef\x96\xa5" // U+f5a5 +#define ICON_FA_MEMORY "\xef\x94\xb8" // U+f538 +#define ICON_FA_MENORAH "\xef\x99\xb6" // U+f676 +#define ICON_FA_MERCURY "\xef\x88\xa3" // U+f223 +#define ICON_FA_METEOR "\xef\x9d\x93" // U+f753 +#define ICON_FA_MICROCHIP "\xef\x8b\x9b" // U+f2db +#define ICON_FA_MICROPHONE "\xef\x84\xb0" // U+f130 +#define ICON_FA_MICROPHONE_ALT "\xef\x8f\x89" // U+f3c9 +#define ICON_FA_MICROPHONE_ALT_SLASH "\xef\x94\xb9" // U+f539 +#define ICON_FA_MICROPHONE_SLASH "\xef\x84\xb1" // U+f131 +#define ICON_FA_MICROSCOPE "\xef\x98\x90" // U+f610 +#define ICON_FA_MINUS "\xef\x81\xa8" // U+f068 +#define ICON_FA_MINUS_CIRCLE "\xef\x81\x96" // U+f056 +#define ICON_FA_MINUS_SQUARE "\xef\x85\x86" // U+f146 +#define ICON_FA_MITTEN "\xef\x9e\xb5" // U+f7b5 +#define ICON_FA_MOBILE "\xef\x84\x8b" // U+f10b +#define ICON_FA_MOBILE_ALT "\xef\x8f\x8d" // U+f3cd +#define ICON_FA_MONEY_BILL "\xef\x83\x96" // U+f0d6 +#define ICON_FA_MONEY_BILL_ALT "\xef\x8f\x91" // U+f3d1 +#define ICON_FA_MONEY_BILL_WAVE "\xef\x94\xba" // U+f53a +#define ICON_FA_MONEY_BILL_WAVE_ALT "\xef\x94\xbb" // U+f53b +#define ICON_FA_MONEY_CHECK "\xef\x94\xbc" // U+f53c +#define ICON_FA_MONEY_CHECK_ALT "\xef\x94\xbd" // U+f53d +#define ICON_FA_MONUMENT "\xef\x96\xa6" // U+f5a6 +#define ICON_FA_MOON "\xef\x86\x86" // U+f186 +#define ICON_FA_MORTAR_PESTLE "\xef\x96\xa7" // U+f5a7 +#define ICON_FA_MOSQUE "\xef\x99\xb8" // U+f678 +#define ICON_FA_MOTORCYCLE "\xef\x88\x9c" // U+f21c +#define ICON_FA_MOUNTAIN "\xef\x9b\xbc" // U+f6fc +#define ICON_FA_MOUSE "\xef\xa3\x8c" // U+f8cc +#define ICON_FA_MOUSE_POINTER "\xef\x89\x85" // U+f245 +#define ICON_FA_MUG_HOT "\xef\x9e\xb6" // U+f7b6 +#define ICON_FA_MUSIC "\xef\x80\x81" // U+f001 +#define ICON_FA_NETWORK_WIRED "\xef\x9b\xbf" // U+f6ff +#define ICON_FA_NEUTER "\xef\x88\xac" // U+f22c +#define ICON_FA_NEWSPAPER "\xef\x87\xaa" // U+f1ea +#define ICON_FA_NOT_EQUAL "\xef\x94\xbe" // U+f53e +#define ICON_FA_NOTES_MEDICAL "\xef\x92\x81" // U+f481 +#define ICON_FA_OBJECT_GROUP "\xef\x89\x87" // U+f247 +#define ICON_FA_OBJECT_UNGROUP "\xef\x89\x88" // U+f248 +#define ICON_FA_OIL_CAN "\xef\x98\x93" // U+f613 +#define ICON_FA_OM "\xef\x99\xb9" // U+f679 +#define ICON_FA_OTTER "\xef\x9c\x80" // U+f700 +#define ICON_FA_OUTDENT "\xef\x80\xbb" // U+f03b +#define ICON_FA_PAGER "\xef\xa0\x95" // U+f815 +#define ICON_FA_PAINT_BRUSH "\xef\x87\xbc" // U+f1fc +#define ICON_FA_PAINT_ROLLER "\xef\x96\xaa" // U+f5aa +#define ICON_FA_PALETTE "\xef\x94\xbf" // U+f53f +#define ICON_FA_PALLET "\xef\x92\x82" // U+f482 +#define ICON_FA_PAPER_PLANE "\xef\x87\x98" // U+f1d8 +#define ICON_FA_PAPERCLIP "\xef\x83\x86" // U+f0c6 +#define ICON_FA_PARACHUTE_BOX "\xef\x93\x8d" // U+f4cd +#define ICON_FA_PARAGRAPH "\xef\x87\x9d" // U+f1dd +#define ICON_FA_PARKING "\xef\x95\x80" // U+f540 +#define ICON_FA_PASSPORT "\xef\x96\xab" // U+f5ab +#define ICON_FA_PASTAFARIANISM "\xef\x99\xbb" // U+f67b +#define ICON_FA_PASTE "\xef\x83\xaa" // U+f0ea +#define ICON_FA_PAUSE "\xef\x81\x8c" // U+f04c +#define ICON_FA_PAUSE_CIRCLE "\xef\x8a\x8b" // U+f28b +#define ICON_FA_PAW "\xef\x86\xb0" // U+f1b0 +#define ICON_FA_PEACE "\xef\x99\xbc" // U+f67c +#define ICON_FA_PEN "\xef\x8c\x84" // U+f304 +#define ICON_FA_PEN_ALT "\xef\x8c\x85" // U+f305 +#define ICON_FA_PEN_FANCY "\xef\x96\xac" // U+f5ac +#define ICON_FA_PEN_NIB "\xef\x96\xad" // U+f5ad +#define ICON_FA_PEN_SQUARE "\xef\x85\x8b" // U+f14b +#define ICON_FA_PENCIL_ALT "\xef\x8c\x83" // U+f303 +#define ICON_FA_PENCIL_RULER "\xef\x96\xae" // U+f5ae +#define ICON_FA_PEOPLE_ARROWS "\xee\x81\xa8" // U+e068 +#define ICON_FA_PEOPLE_CARRY "\xef\x93\x8e" // U+f4ce +#define ICON_FA_PEPPER_HOT "\xef\xa0\x96" // U+f816 +#define ICON_FA_PERCENT "\xef\x8a\x95" // U+f295 +#define ICON_FA_PERCENTAGE "\xef\x95\x81" // U+f541 +#define ICON_FA_PERSON_BOOTH "\xef\x9d\x96" // U+f756 +#define ICON_FA_PHONE "\xef\x82\x95" // U+f095 +#define ICON_FA_PHONE_ALT "\xef\xa1\xb9" // U+f879 +#define ICON_FA_PHONE_SLASH "\xef\x8f\x9d" // U+f3dd +#define ICON_FA_PHONE_SQUARE "\xef\x82\x98" // U+f098 +#define ICON_FA_PHONE_SQUARE_ALT "\xef\xa1\xbb" // U+f87b +#define ICON_FA_PHONE_VOLUME "\xef\x8a\xa0" // U+f2a0 +#define ICON_FA_PHOTO_VIDEO "\xef\xa1\xbc" // U+f87c +#define ICON_FA_PIGGY_BANK "\xef\x93\x93" // U+f4d3 +#define ICON_FA_PILLS "\xef\x92\x84" // U+f484 +#define ICON_FA_PIZZA_SLICE "\xef\xa0\x98" // U+f818 +#define ICON_FA_PLACE_OF_WORSHIP "\xef\x99\xbf" // U+f67f +#define ICON_FA_PLANE "\xef\x81\xb2" // U+f072 +#define ICON_FA_PLANE_ARRIVAL "\xef\x96\xaf" // U+f5af +#define ICON_FA_PLANE_DEPARTURE "\xef\x96\xb0" // U+f5b0 +#define ICON_FA_PLANE_SLASH "\xee\x81\xa9" // U+e069 +#define ICON_FA_PLAY "\xef\x81\x8b" // U+f04b +#define ICON_FA_PLAY_CIRCLE "\xef\x85\x84" // U+f144 +#define ICON_FA_PLUG "\xef\x87\xa6" // U+f1e6 +#define ICON_FA_PLUS "\xef\x81\xa7" // U+f067 +#define ICON_FA_PLUS_CIRCLE "\xef\x81\x95" // U+f055 +#define ICON_FA_PLUS_SQUARE "\xef\x83\xbe" // U+f0fe +#define ICON_FA_PODCAST "\xef\x8b\x8e" // U+f2ce +#define ICON_FA_POLL "\xef\x9a\x81" // U+f681 +#define ICON_FA_POLL_H "\xef\x9a\x82" // U+f682 +#define ICON_FA_POO "\xef\x8b\xbe" // U+f2fe +#define ICON_FA_POO_STORM "\xef\x9d\x9a" // U+f75a +#define ICON_FA_POOP "\xef\x98\x99" // U+f619 +#define ICON_FA_PORTRAIT "\xef\x8f\xa0" // U+f3e0 +#define ICON_FA_POUND_SIGN "\xef\x85\x94" // U+f154 +#define ICON_FA_POWER_OFF "\xef\x80\x91" // U+f011 +#define ICON_FA_PRAY "\xef\x9a\x83" // U+f683 +#define ICON_FA_PRAYING_HANDS "\xef\x9a\x84" // U+f684 +#define ICON_FA_PRESCRIPTION "\xef\x96\xb1" // U+f5b1 +#define ICON_FA_PRESCRIPTION_BOTTLE "\xef\x92\x85" // U+f485 +#define ICON_FA_PRESCRIPTION_BOTTLE_ALT "\xef\x92\x86" // U+f486 +#define ICON_FA_PRINT "\xef\x80\xaf" // U+f02f +#define ICON_FA_PROCEDURES "\xef\x92\x87" // U+f487 +#define ICON_FA_PROJECT_DIAGRAM "\xef\x95\x82" // U+f542 +#define ICON_FA_PUMP_MEDICAL "\xee\x81\xaa" // U+e06a +#define ICON_FA_PUMP_SOAP "\xee\x81\xab" // U+e06b +#define ICON_FA_PUZZLE_PIECE "\xef\x84\xae" // U+f12e +#define ICON_FA_QRCODE "\xef\x80\xa9" // U+f029 +#define ICON_FA_QUESTION "\xef\x84\xa8" // U+f128 +#define ICON_FA_QUESTION_CIRCLE "\xef\x81\x99" // U+f059 +#define ICON_FA_QUIDDITCH "\xef\x91\x98" // U+f458 +#define ICON_FA_QUOTE_LEFT "\xef\x84\x8d" // U+f10d +#define ICON_FA_QUOTE_RIGHT "\xef\x84\x8e" // U+f10e +#define ICON_FA_QURAN "\xef\x9a\x87" // U+f687 +#define ICON_FA_RADIATION "\xef\x9e\xb9" // U+f7b9 +#define ICON_FA_RADIATION_ALT "\xef\x9e\xba" // U+f7ba +#define ICON_FA_RAINBOW "\xef\x9d\x9b" // U+f75b +#define ICON_FA_RANDOM "\xef\x81\xb4" // U+f074 +#define ICON_FA_RECEIPT "\xef\x95\x83" // U+f543 +#define ICON_FA_RECORD_VINYL "\xef\xa3\x99" // U+f8d9 +#define ICON_FA_RECYCLE "\xef\x86\xb8" // U+f1b8 +#define ICON_FA_REDO "\xef\x80\x9e" // U+f01e +#define ICON_FA_REDO_ALT "\xef\x8b\xb9" // U+f2f9 +#define ICON_FA_REGISTERED "\xef\x89\x9d" // U+f25d +#define ICON_FA_REMOVE_FORMAT "\xef\xa1\xbd" // U+f87d +#define ICON_FA_REPLY "\xef\x8f\xa5" // U+f3e5 +#define ICON_FA_REPLY_ALL "\xef\x84\xa2" // U+f122 +#define ICON_FA_REPUBLICAN "\xef\x9d\x9e" // U+f75e +#define ICON_FA_RESTROOM "\xef\x9e\xbd" // U+f7bd +#define ICON_FA_RETWEET "\xef\x81\xb9" // U+f079 +#define ICON_FA_RIBBON "\xef\x93\x96" // U+f4d6 +#define ICON_FA_RING "\xef\x9c\x8b" // U+f70b +#define ICON_FA_ROAD "\xef\x80\x98" // U+f018 +#define ICON_FA_ROBOT "\xef\x95\x84" // U+f544 +#define ICON_FA_ROCKET "\xef\x84\xb5" // U+f135 +#define ICON_FA_ROUTE "\xef\x93\x97" // U+f4d7 +#define ICON_FA_RSS "\xef\x82\x9e" // U+f09e +#define ICON_FA_RSS_SQUARE "\xef\x85\x83" // U+f143 +#define ICON_FA_RUBLE_SIGN "\xef\x85\x98" // U+f158 +#define ICON_FA_RULER "\xef\x95\x85" // U+f545 +#define ICON_FA_RULER_COMBINED "\xef\x95\x86" // U+f546 +#define ICON_FA_RULER_HORIZONTAL "\xef\x95\x87" // U+f547 +#define ICON_FA_RULER_VERTICAL "\xef\x95\x88" // U+f548 +#define ICON_FA_RUNNING "\xef\x9c\x8c" // U+f70c +#define ICON_FA_RUPEE_SIGN "\xef\x85\x96" // U+f156 +#define ICON_FA_SAD_CRY "\xef\x96\xb3" // U+f5b3 +#define ICON_FA_SAD_TEAR "\xef\x96\xb4" // U+f5b4 +#define ICON_FA_SATELLITE "\xef\x9e\xbf" // U+f7bf +#define ICON_FA_SATELLITE_DISH "\xef\x9f\x80" // U+f7c0 +#define ICON_FA_SAVE "\xef\x83\x87" // U+f0c7 +#define ICON_FA_SCHOOL "\xef\x95\x89" // U+f549 +#define ICON_FA_SCREWDRIVER "\xef\x95\x8a" // U+f54a +#define ICON_FA_SCROLL "\xef\x9c\x8e" // U+f70e +#define ICON_FA_SD_CARD "\xef\x9f\x82" // U+f7c2 +#define ICON_FA_SEARCH "\xef\x80\x82" // U+f002 +#define ICON_FA_SEARCH_DOLLAR "\xef\x9a\x88" // U+f688 +#define ICON_FA_SEARCH_LOCATION "\xef\x9a\x89" // U+f689 +#define ICON_FA_SEARCH_MINUS "\xef\x80\x90" // U+f010 +#define ICON_FA_SEARCH_PLUS "\xef\x80\x8e" // U+f00e +#define ICON_FA_SEEDLING "\xef\x93\x98" // U+f4d8 +#define ICON_FA_SERVER "\xef\x88\xb3" // U+f233 +#define ICON_FA_SHAPES "\xef\x98\x9f" // U+f61f +#define ICON_FA_SHARE "\xef\x81\xa4" // U+f064 +#define ICON_FA_SHARE_ALT "\xef\x87\xa0" // U+f1e0 +#define ICON_FA_SHARE_ALT_SQUARE "\xef\x87\xa1" // U+f1e1 +#define ICON_FA_SHARE_SQUARE "\xef\x85\x8d" // U+f14d +#define ICON_FA_SHEKEL_SIGN "\xef\x88\x8b" // U+f20b +#define ICON_FA_SHIELD_ALT "\xef\x8f\xad" // U+f3ed +#define ICON_FA_SHIELD_VIRUS "\xee\x81\xac" // U+e06c +#define ICON_FA_SHIP "\xef\x88\x9a" // U+f21a +#define ICON_FA_SHIPPING_FAST "\xef\x92\x8b" // U+f48b +#define ICON_FA_SHOE_PRINTS "\xef\x95\x8b" // U+f54b +#define ICON_FA_SHOPPING_BAG "\xef\x8a\x90" // U+f290 +#define ICON_FA_SHOPPING_BASKET "\xef\x8a\x91" // U+f291 +#define ICON_FA_SHOPPING_CART "\xef\x81\xba" // U+f07a +#define ICON_FA_SHOWER "\xef\x8b\x8c" // U+f2cc +#define ICON_FA_SHUTTLE_VAN "\xef\x96\xb6" // U+f5b6 +#define ICON_FA_SIGN "\xef\x93\x99" // U+f4d9 +#define ICON_FA_SIGN_IN_ALT "\xef\x8b\xb6" // U+f2f6 +#define ICON_FA_SIGN_LANGUAGE "\xef\x8a\xa7" // U+f2a7 +#define ICON_FA_SIGN_OUT_ALT "\xef\x8b\xb5" // U+f2f5 +#define ICON_FA_SIGNAL "\xef\x80\x92" // U+f012 +#define ICON_FA_SIGNATURE "\xef\x96\xb7" // U+f5b7 +#define ICON_FA_SIM_CARD "\xef\x9f\x84" // U+f7c4 +#define ICON_FA_SINK "\xee\x81\xad" // U+e06d +#define ICON_FA_SITEMAP "\xef\x83\xa8" // U+f0e8 +#define ICON_FA_SKATING "\xef\x9f\x85" // U+f7c5 +#define ICON_FA_SKIING "\xef\x9f\x89" // U+f7c9 +#define ICON_FA_SKIING_NORDIC "\xef\x9f\x8a" // U+f7ca +#define ICON_FA_SKULL "\xef\x95\x8c" // U+f54c +#define ICON_FA_SKULL_CROSSBONES "\xef\x9c\x94" // U+f714 +#define ICON_FA_SLASH "\xef\x9c\x95" // U+f715 +#define ICON_FA_SLEIGH "\xef\x9f\x8c" // U+f7cc +#define ICON_FA_SLIDERS_H "\xef\x87\x9e" // U+f1de +#define ICON_FA_SMILE "\xef\x84\x98" // U+f118 +#define ICON_FA_SMILE_BEAM "\xef\x96\xb8" // U+f5b8 +#define ICON_FA_SMILE_WINK "\xef\x93\x9a" // U+f4da +#define ICON_FA_SMOG "\xef\x9d\x9f" // U+f75f +#define ICON_FA_SMOKING "\xef\x92\x8d" // U+f48d +#define ICON_FA_SMOKING_BAN "\xef\x95\x8d" // U+f54d +#define ICON_FA_SMS "\xef\x9f\x8d" // U+f7cd +#define ICON_FA_SNOWBOARDING "\xef\x9f\x8e" // U+f7ce +#define ICON_FA_SNOWFLAKE "\xef\x8b\x9c" // U+f2dc +#define ICON_FA_SNOWMAN "\xef\x9f\x90" // U+f7d0 +#define ICON_FA_SNOWPLOW "\xef\x9f\x92" // U+f7d2 +#define ICON_FA_SOAP "\xee\x81\xae" // U+e06e +#define ICON_FA_SOCKS "\xef\x9a\x96" // U+f696 +#define ICON_FA_SOLAR_PANEL "\xef\x96\xba" // U+f5ba +#define ICON_FA_SORT "\xef\x83\x9c" // U+f0dc +#define ICON_FA_SORT_ALPHA_DOWN "\xef\x85\x9d" // U+f15d +#define ICON_FA_SORT_ALPHA_DOWN_ALT "\xef\xa2\x81" // U+f881 +#define ICON_FA_SORT_ALPHA_UP "\xef\x85\x9e" // U+f15e +#define ICON_FA_SORT_ALPHA_UP_ALT "\xef\xa2\x82" // U+f882 +#define ICON_FA_SORT_AMOUNT_DOWN "\xef\x85\xa0" // U+f160 +#define ICON_FA_SORT_AMOUNT_DOWN_ALT "\xef\xa2\x84" // U+f884 +#define ICON_FA_SORT_AMOUNT_UP "\xef\x85\xa1" // U+f161 +#define ICON_FA_SORT_AMOUNT_UP_ALT "\xef\xa2\x85" // U+f885 +#define ICON_FA_SORT_DOWN "\xef\x83\x9d" // U+f0dd +#define ICON_FA_SORT_NUMERIC_DOWN "\xef\x85\xa2" // U+f162 +#define ICON_FA_SORT_NUMERIC_DOWN_ALT "\xef\xa2\x86" // U+f886 +#define ICON_FA_SORT_NUMERIC_UP "\xef\x85\xa3" // U+f163 +#define ICON_FA_SORT_NUMERIC_UP_ALT "\xef\xa2\x87" // U+f887 +#define ICON_FA_SORT_UP "\xef\x83\x9e" // U+f0de +#define ICON_FA_SPA "\xef\x96\xbb" // U+f5bb +#define ICON_FA_SPACE_SHUTTLE "\xef\x86\x97" // U+f197 +#define ICON_FA_SPELL_CHECK "\xef\xa2\x91" // U+f891 +#define ICON_FA_SPIDER "\xef\x9c\x97" // U+f717 +#define ICON_FA_SPINNER "\xef\x84\x90" // U+f110 +#define ICON_FA_SPLOTCH "\xef\x96\xbc" // U+f5bc +#define ICON_FA_SPRAY_CAN "\xef\x96\xbd" // U+f5bd +#define ICON_FA_SQUARE "\xef\x83\x88" // U+f0c8 +#define ICON_FA_SQUARE_FULL "\xef\x91\x9c" // U+f45c +#define ICON_FA_SQUARE_ROOT_ALT "\xef\x9a\x98" // U+f698 +#define ICON_FA_STAMP "\xef\x96\xbf" // U+f5bf +#define ICON_FA_STAR "\xef\x80\x85" // U+f005 +#define ICON_FA_STAR_AND_CRESCENT "\xef\x9a\x99" // U+f699 +#define ICON_FA_STAR_HALF "\xef\x82\x89" // U+f089 +#define ICON_FA_STAR_HALF_ALT "\xef\x97\x80" // U+f5c0 +#define ICON_FA_STAR_OF_DAVID "\xef\x9a\x9a" // U+f69a +#define ICON_FA_STAR_OF_LIFE "\xef\x98\xa1" // U+f621 +#define ICON_FA_STEP_BACKWARD "\xef\x81\x88" // U+f048 +#define ICON_FA_STEP_FORWARD "\xef\x81\x91" // U+f051 +#define ICON_FA_STETHOSCOPE "\xef\x83\xb1" // U+f0f1 +#define ICON_FA_STICKY_NOTE "\xef\x89\x89" // U+f249 +#define ICON_FA_STOP "\xef\x81\x8d" // U+f04d +#define ICON_FA_STOP_CIRCLE "\xef\x8a\x8d" // U+f28d +#define ICON_FA_STOPWATCH "\xef\x8b\xb2" // U+f2f2 +#define ICON_FA_STOPWATCH_20 "\xee\x81\xaf" // U+e06f +#define ICON_FA_STORE "\xef\x95\x8e" // U+f54e +#define ICON_FA_STORE_ALT "\xef\x95\x8f" // U+f54f +#define ICON_FA_STORE_ALT_SLASH "\xee\x81\xb0" // U+e070 +#define ICON_FA_STORE_SLASH "\xee\x81\xb1" // U+e071 +#define ICON_FA_STREAM "\xef\x95\x90" // U+f550 +#define ICON_FA_STREET_VIEW "\xef\x88\x9d" // U+f21d +#define ICON_FA_STRIKETHROUGH "\xef\x83\x8c" // U+f0cc +#define ICON_FA_STROOPWAFEL "\xef\x95\x91" // U+f551 +#define ICON_FA_SUBSCRIPT "\xef\x84\xac" // U+f12c +#define ICON_FA_SUBWAY "\xef\x88\xb9" // U+f239 +#define ICON_FA_SUITCASE "\xef\x83\xb2" // U+f0f2 +#define ICON_FA_SUITCASE_ROLLING "\xef\x97\x81" // U+f5c1 +#define ICON_FA_SUN "\xef\x86\x85" // U+f185 +#define ICON_FA_SUPERSCRIPT "\xef\x84\xab" // U+f12b +#define ICON_FA_SURPRISE "\xef\x97\x82" // U+f5c2 +#define ICON_FA_SWATCHBOOK "\xef\x97\x83" // U+f5c3 +#define ICON_FA_SWIMMER "\xef\x97\x84" // U+f5c4 +#define ICON_FA_SWIMMING_POOL "\xef\x97\x85" // U+f5c5 +#define ICON_FA_SYNAGOGUE "\xef\x9a\x9b" // U+f69b +#define ICON_FA_SYNC "\xef\x80\xa1" // U+f021 +#define ICON_FA_SYNC_ALT "\xef\x8b\xb1" // U+f2f1 +#define ICON_FA_SYRINGE "\xef\x92\x8e" // U+f48e +#define ICON_FA_TABLE "\xef\x83\x8e" // U+f0ce +#define ICON_FA_TABLE_TENNIS "\xef\x91\x9d" // U+f45d +#define ICON_FA_TABLET "\xef\x84\x8a" // U+f10a +#define ICON_FA_TABLET_ALT "\xef\x8f\xba" // U+f3fa +#define ICON_FA_TABLETS "\xef\x92\x90" // U+f490 +#define ICON_FA_TACHOMETER_ALT "\xef\x8f\xbd" // U+f3fd +#define ICON_FA_TAG "\xef\x80\xab" // U+f02b +#define ICON_FA_TAGS "\xef\x80\xac" // U+f02c +#define ICON_FA_TAPE "\xef\x93\x9b" // U+f4db +#define ICON_FA_TASKS "\xef\x82\xae" // U+f0ae +#define ICON_FA_TAXI "\xef\x86\xba" // U+f1ba +#define ICON_FA_TEETH "\xef\x98\xae" // U+f62e +#define ICON_FA_TEETH_OPEN "\xef\x98\xaf" // U+f62f +#define ICON_FA_TEMPERATURE_HIGH "\xef\x9d\xa9" // U+f769 +#define ICON_FA_TEMPERATURE_LOW "\xef\x9d\xab" // U+f76b +#define ICON_FA_TENGE "\xef\x9f\x97" // U+f7d7 +#define ICON_FA_TERMINAL "\xef\x84\xa0" // U+f120 +#define ICON_FA_TEXT_HEIGHT "\xef\x80\xb4" // U+f034 +#define ICON_FA_TEXT_WIDTH "\xef\x80\xb5" // U+f035 +#define ICON_FA_TH "\xef\x80\x8a" // U+f00a +#define ICON_FA_TH_LARGE "\xef\x80\x89" // U+f009 +#define ICON_FA_TH_LIST "\xef\x80\x8b" // U+f00b +#define ICON_FA_THEATER_MASKS "\xef\x98\xb0" // U+f630 +#define ICON_FA_THERMOMETER "\xef\x92\x91" // U+f491 +#define ICON_FA_THERMOMETER_EMPTY "\xef\x8b\x8b" // U+f2cb +#define ICON_FA_THERMOMETER_FULL "\xef\x8b\x87" // U+f2c7 +#define ICON_FA_THERMOMETER_HALF "\xef\x8b\x89" // U+f2c9 +#define ICON_FA_THERMOMETER_QUARTER "\xef\x8b\x8a" // U+f2ca +#define ICON_FA_THERMOMETER_THREE_QUARTERS "\xef\x8b\x88" // U+f2c8 +#define ICON_FA_THUMBS_DOWN "\xef\x85\xa5" // U+f165 +#define ICON_FA_THUMBS_UP "\xef\x85\xa4" // U+f164 +#define ICON_FA_THUMBTACK "\xef\x82\x8d" // U+f08d +#define ICON_FA_TICKET_ALT "\xef\x8f\xbf" // U+f3ff +#define ICON_FA_TIMES "\xef\x80\x8d" // U+f00d +#define ICON_FA_TIMES_CIRCLE "\xef\x81\x97" // U+f057 +#define ICON_FA_TINT "\xef\x81\x83" // U+f043 +#define ICON_FA_TINT_SLASH "\xef\x97\x87" // U+f5c7 +#define ICON_FA_TIRED "\xef\x97\x88" // U+f5c8 +#define ICON_FA_TOGGLE_OFF "\xef\x88\x84" // U+f204 +#define ICON_FA_TOGGLE_ON "\xef\x88\x85" // U+f205 +#define ICON_FA_TOILET "\xef\x9f\x98" // U+f7d8 +#define ICON_FA_TOILET_PAPER "\xef\x9c\x9e" // U+f71e +#define ICON_FA_TOILET_PAPER_SLASH "\xee\x81\xb2" // U+e072 +#define ICON_FA_TOOLBOX "\xef\x95\x92" // U+f552 +#define ICON_FA_TOOLS "\xef\x9f\x99" // U+f7d9 +#define ICON_FA_TOOTH "\xef\x97\x89" // U+f5c9 +#define ICON_FA_TORAH "\xef\x9a\xa0" // U+f6a0 +#define ICON_FA_TORII_GATE "\xef\x9a\xa1" // U+f6a1 +#define ICON_FA_TRACTOR "\xef\x9c\xa2" // U+f722 +#define ICON_FA_TRADEMARK "\xef\x89\x9c" // U+f25c +#define ICON_FA_TRAFFIC_LIGHT "\xef\x98\xb7" // U+f637 +#define ICON_FA_TRAILER "\xee\x81\x81" // U+e041 +#define ICON_FA_TRAIN "\xef\x88\xb8" // U+f238 +#define ICON_FA_TRAM "\xef\x9f\x9a" // U+f7da +#define ICON_FA_TRANSGENDER "\xef\x88\xa4" // U+f224 +#define ICON_FA_TRANSGENDER_ALT "\xef\x88\xa5" // U+f225 +#define ICON_FA_TRASH "\xef\x87\xb8" // U+f1f8 +#define ICON_FA_TRASH_ALT "\xef\x8b\xad" // U+f2ed +#define ICON_FA_TRASH_RESTORE "\xef\xa0\xa9" // U+f829 +#define ICON_FA_TRASH_RESTORE_ALT "\xef\xa0\xaa" // U+f82a +#define ICON_FA_TREE "\xef\x86\xbb" // U+f1bb +#define ICON_FA_TROPHY "\xef\x82\x91" // U+f091 +#define ICON_FA_TRUCK "\xef\x83\x91" // U+f0d1 +#define ICON_FA_TRUCK_LOADING "\xef\x93\x9e" // U+f4de +#define ICON_FA_TRUCK_MONSTER "\xef\x98\xbb" // U+f63b +#define ICON_FA_TRUCK_MOVING "\xef\x93\x9f" // U+f4df +#define ICON_FA_TRUCK_PICKUP "\xef\x98\xbc" // U+f63c +#define ICON_FA_TSHIRT "\xef\x95\x93" // U+f553 +#define ICON_FA_TTY "\xef\x87\xa4" // U+f1e4 +#define ICON_FA_TV "\xef\x89\xac" // U+f26c +#define ICON_FA_UMBRELLA "\xef\x83\xa9" // U+f0e9 +#define ICON_FA_UMBRELLA_BEACH "\xef\x97\x8a" // U+f5ca +#define ICON_FA_UNDERLINE "\xef\x83\x8d" // U+f0cd +#define ICON_FA_UNDO "\xef\x83\xa2" // U+f0e2 +#define ICON_FA_UNDO_ALT "\xef\x8b\xaa" // U+f2ea +#define ICON_FA_UNIVERSAL_ACCESS "\xef\x8a\x9a" // U+f29a +#define ICON_FA_UNIVERSITY "\xef\x86\x9c" // U+f19c +#define ICON_FA_UNLINK "\xef\x84\xa7" // U+f127 +#define ICON_FA_UNLOCK "\xef\x82\x9c" // U+f09c +#define ICON_FA_UNLOCK_ALT "\xef\x84\xbe" // U+f13e +#define ICON_FA_UPLOAD "\xef\x82\x93" // U+f093 +#define ICON_FA_USER "\xef\x80\x87" // U+f007 +#define ICON_FA_USER_ALT "\xef\x90\x86" // U+f406 +#define ICON_FA_USER_ALT_SLASH "\xef\x93\xba" // U+f4fa +#define ICON_FA_USER_ASTRONAUT "\xef\x93\xbb" // U+f4fb +#define ICON_FA_USER_CHECK "\xef\x93\xbc" // U+f4fc +#define ICON_FA_USER_CIRCLE "\xef\x8a\xbd" // U+f2bd +#define ICON_FA_USER_CLOCK "\xef\x93\xbd" // U+f4fd +#define ICON_FA_USER_COG "\xef\x93\xbe" // U+f4fe +#define ICON_FA_USER_EDIT "\xef\x93\xbf" // U+f4ff +#define ICON_FA_USER_FRIENDS "\xef\x94\x80" // U+f500 +#define ICON_FA_USER_GRADUATE "\xef\x94\x81" // U+f501 +#define ICON_FA_USER_INJURED "\xef\x9c\xa8" // U+f728 +#define ICON_FA_USER_LOCK "\xef\x94\x82" // U+f502 +#define ICON_FA_USER_MD "\xef\x83\xb0" // U+f0f0 +#define ICON_FA_USER_MINUS "\xef\x94\x83" // U+f503 +#define ICON_FA_USER_NINJA "\xef\x94\x84" // U+f504 +#define ICON_FA_USER_NURSE "\xef\xa0\xaf" // U+f82f +#define ICON_FA_USER_PLUS "\xef\x88\xb4" // U+f234 +#define ICON_FA_USER_SECRET "\xef\x88\x9b" // U+f21b +#define ICON_FA_USER_SHIELD "\xef\x94\x85" // U+f505 +#define ICON_FA_USER_SLASH "\xef\x94\x86" // U+f506 +#define ICON_FA_USER_TAG "\xef\x94\x87" // U+f507 +#define ICON_FA_USER_TIE "\xef\x94\x88" // U+f508 +#define ICON_FA_USER_TIMES "\xef\x88\xb5" // U+f235 +#define ICON_FA_USERS "\xef\x83\x80" // U+f0c0 +#define ICON_FA_USERS_COG "\xef\x94\x89" // U+f509 +#define ICON_FA_USERS_SLASH "\xee\x81\xb3" // U+e073 +#define ICON_FA_UTENSIL_SPOON "\xef\x8b\xa5" // U+f2e5 +#define ICON_FA_UTENSILS "\xef\x8b\xa7" // U+f2e7 +#define ICON_FA_VECTOR_SQUARE "\xef\x97\x8b" // U+f5cb +#define ICON_FA_VENUS "\xef\x88\xa1" // U+f221 +#define ICON_FA_VENUS_DOUBLE "\xef\x88\xa6" // U+f226 +#define ICON_FA_VENUS_MARS "\xef\x88\xa8" // U+f228 +#define ICON_FA_VEST "\xee\x82\x85" // U+e085 +#define ICON_FA_VEST_PATCHES "\xee\x82\x86" // U+e086 +#define ICON_FA_VIAL "\xef\x92\x92" // U+f492 +#define ICON_FA_VIALS "\xef\x92\x93" // U+f493 +#define ICON_FA_VIDEO "\xef\x80\xbd" // U+f03d +#define ICON_FA_VIDEO_SLASH "\xef\x93\xa2" // U+f4e2 +#define ICON_FA_VIHARA "\xef\x9a\xa7" // U+f6a7 +#define ICON_FA_VIRUS "\xee\x81\xb4" // U+e074 +#define ICON_FA_VIRUS_SLASH "\xee\x81\xb5" // U+e075 +#define ICON_FA_VIRUSES "\xee\x81\xb6" // U+e076 +#define ICON_FA_VOICEMAIL "\xef\xa2\x97" // U+f897 +#define ICON_FA_VOLLEYBALL_BALL "\xef\x91\x9f" // U+f45f +#define ICON_FA_VOLUME_DOWN "\xef\x80\xa7" // U+f027 +#define ICON_FA_VOLUME_MUTE "\xef\x9a\xa9" // U+f6a9 +#define ICON_FA_VOLUME_OFF "\xef\x80\xa6" // U+f026 +#define ICON_FA_VOLUME_UP "\xef\x80\xa8" // U+f028 +#define ICON_FA_VOTE_YEA "\xef\x9d\xb2" // U+f772 +#define ICON_FA_VR_CARDBOARD "\xef\x9c\xa9" // U+f729 +#define ICON_FA_WALKING "\xef\x95\x94" // U+f554 +#define ICON_FA_WALLET "\xef\x95\x95" // U+f555 +#define ICON_FA_WAREHOUSE "\xef\x92\x94" // U+f494 +#define ICON_FA_WATER "\xef\x9d\xb3" // U+f773 +#define ICON_FA_WAVE_SQUARE "\xef\xa0\xbe" // U+f83e +#define ICON_FA_WEIGHT "\xef\x92\x96" // U+f496 +#define ICON_FA_WEIGHT_HANGING "\xef\x97\x8d" // U+f5cd +#define ICON_FA_WHEELCHAIR "\xef\x86\x93" // U+f193 +#define ICON_FA_WIFI "\xef\x87\xab" // U+f1eb +#define ICON_FA_WIND "\xef\x9c\xae" // U+f72e +#define ICON_FA_WINDOW_CLOSE "\xef\x90\x90" // U+f410 +#define ICON_FA_WINDOW_MAXIMIZE "\xef\x8b\x90" // U+f2d0 +#define ICON_FA_WINDOW_MINIMIZE "\xef\x8b\x91" // U+f2d1 +#define ICON_FA_WINDOW_RESTORE "\xef\x8b\x92" // U+f2d2 +#define ICON_FA_WINE_BOTTLE "\xef\x9c\xaf" // U+f72f +#define ICON_FA_WINE_GLASS "\xef\x93\xa3" // U+f4e3 +#define ICON_FA_WINE_GLASS_ALT "\xef\x97\x8e" // U+f5ce +#define ICON_FA_WON_SIGN "\xef\x85\x99" // U+f159 +#define ICON_FA_WRENCH "\xef\x82\xad" // U+f0ad +#define ICON_FA_X_RAY "\xef\x92\x97" // U+f497 +#define ICON_FA_YEN_SIGN "\xef\x85\x97" // U+f157 +#define ICON_FA_YIN_YANG "\xef\x9a\xad" // U+f6ad diff --git a/src/FontAwesome/IconsFontAwesome5Brands.h b/src/FontAwesome/IconsFontAwesome5Brands.h new file mode 100644 index 0000000..b4778a8 --- /dev/null +++ b/src/FontAwesome/IconsFontAwesome5Brands.h @@ -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 diff --git a/src/Maps/LastMissionId.h b/src/Maps/LastMissionId.h new file mode 100644 index 0000000..e96fc8d --- /dev/null +++ b/src/Maps/LastMissionId.h @@ -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 . + +#include + +#include + +static const std::map 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"} +}}; diff --git a/src/Maps/StoryProgress.h b/src/Maps/StoryProgress.h new file mode 100644 index 0000000..b4fc7f3 --- /dev/null +++ b/src/Maps/StoryProgress.h @@ -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 . + +#include + +#include + +struct StoryProgressPoint { + std::int32_t id; + const char* chapter; + const char* point; + const char* after = ""; +}; + +static const Corrade::Containers::Array 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"}, + } +}; diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp new file mode 100644 index 0000000..7f6f67f --- /dev/null +++ b/src/Mass/Mass.cpp @@ -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 . + +#include + +#include + +#include +#include + +#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; +} diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h new file mode 100644 index 0000000..eed4138 --- /dev/null +++ b/src/Mass/Mass.h @@ -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 . + +#include + +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; +}; diff --git a/src/MassBuilderManager/MassBuilderManager.cpp b/src/MassBuilderManager/MassBuilderManager.cpp new file mode 100644 index 0000000..2cbc9ee --- /dev/null +++ b/src/MassBuilderManager/MassBuilderManager.cpp @@ -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 . + +#include "MassBuilderManager.h" + +#include +#include +#include + +#include +#include + +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; +} diff --git a/src/MassBuilderManager/MassBuilderManager.h b/src/MassBuilderManager/MassBuilderManager.h new file mode 100644 index 0000000..8a32c6d --- /dev/null +++ b/src/MassBuilderManager/MassBuilderManager.h @@ -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 . + +#include + +#include + +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; +}; diff --git a/src/MassManager/MassManager.cpp b/src/MassManager/MassManager.cpp new file mode 100644 index 0000000..b6b3b00 --- /dev/null +++ b/src/MassManager/MassManager.cpp @@ -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 . + +#include + +#include +#include +#include + +#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 const& { + return _stagedMasses; +} + +void MassManager::refreshStagedMasses() { + _stagedMasses.clear(); + + using Utility::Directory::Flag; + std::vector 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; +} diff --git a/src/MassManager/MassManager.h b/src/MassManager/MassManager.h new file mode 100644 index 0000000..222ef07 --- /dev/null +++ b/src/MassManager/MassManager.h @@ -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 . + +#include +#include + +#include + +#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 const&; + void refreshStagedMasses(); + auto deleteStagedMass(const std::string& filename) -> bool; + + private: + std::string _saveDirectory; + std::string _steamId; + bool _demo; + + std::string _lastError = ""; + + Containers::Array _hangars; + + const std::string _stagingAreaDirectory; + + std::map _stagedMasses; +}; diff --git a/src/Profile/Locators.h b/src/Profile/Locators.h new file mode 100644 index 0000000..8fa63c0 --- /dev/null +++ b/src/Profile/Locators.h @@ -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 . + +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"; diff --git a/src/Profile/Profile.cpp b/src/Profile/Profile.cpp new file mode 100644 index 0000000..7249b40 --- /dev/null +++ b/src/Profile/Profile.cpp @@ -0,0 +1,1003 @@ +// 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 . + +#include + +#include +#include +#include +#include + +#include "Profile.h" + +#include "Locators.h" + +using namespace Corrade; + +Profile::Profile(const std::string& path) { + auto map = Utility::Directory::mapRead(path); + + if(!map) { + _lastError = "Couldn't memory-map " + Utility::Directory::filename(path); + return; + } + + _profileDirectory = Utility::Directory::path(path); + _filename = Utility::Directory::filename(path); + + if(Utility::String::beginsWith(_filename, "Demo")) { + _type = ProfileType::Demo; + } + else { + _type = ProfileType::FullGame; + } + + _steamId = Utility::String::ltrim(Utility::String::rtrim(_filename, ".sav"), (_type == ProfileType::Demo ? "Demo" : "") + std::string{"Profile"}); + + auto it = std::search(map.begin(), map.end(), &company_name_locator[0], &company_name_locator[27]); + + if(it == map.end()) { + _lastError = "Couldn't find a company name in " + _filename; + return; + } + + _companyName = std::string{it + 41}; + + _valid = true; +} + +auto Profile::valid() const -> bool { + return _valid; +} + +auto Profile::lastError() const -> std::string const& { + return _lastError; +} + +auto Profile::filename() const -> std::string const& { + return _filename; +} + +auto Profile::type() const -> ProfileType { + return _type; +} + +auto Profile::steamId() const -> std::string const& { + return _steamId; +} + +void Profile::refreshValues() { + getCompanyName(); + getActiveFrameSlot(); + getCredits(); + getStoryProgress(); + getLastMissionId(); + + getVerseSteel(); + getUndinium(); + getNecriumAlloy(); + getLunarite(); + getAsterite(); + + getEdnil(); + getNuflalt(); + getAurelene(); + getSoldus(); + getSynthesizedN(); + + getAlcarbonite(); + getKeriphene(); + getNitinolCM(); + getQuarkium(); + getAlterene(); + + getMixedComposition(); + getVoidResidue(); + getMuscularConstruction(); + getMineralExoskeletology(); + getCarbonizedSkin(); +} + +auto Profile::companyName() const -> std::string const& { + return _companyName; +} + +auto Profile::getCompanyName() -> std::string const& { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto it = std::search(mmap.begin(), mmap.end(), &company_name_locator[0], &company_name_locator[27]); + + if(it == mmap.end()) { + _lastError = "Couldn't find a company name in " + _filename; + _companyName = ""; + } + else { + _companyName = std::string{it + 41}; + } + + return _companyName; +} + +auto Profile::renameCompany(const std::string& new_name) -> bool { + char length_difference = static_cast(_companyName.length() - new_name.length()); + + std::string profile_data = Utility::Directory::readString(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(profile_data.begin(), profile_data.end(), &company_name_locator[0], &company_name_locator[27]); + + if(iter != profile_data.end()) { + + *(iter + 0x1C) = *(iter + 0x1C) - length_difference; + *(iter + 0x25) = *(iter + 0x25) - length_difference; + + while(*(iter + 0x29) != '\0') { + profile_data.erase(iter + 0x29); + } + + profile_data.insert(iter + 0x29, new_name.cbegin(), new_name.cend()); + + if(!Utility::Directory::writeString(Utility::Directory::join(_profileDirectory, _filename), profile_data)) { + _lastError = "The file" + _filename + " couldn't be written to."; + return false; + } + + _companyName = new_name; + + return true; + } + else { + _lastError = "Couldn't find the company name in " + _filename; + + return false; + } +} + +auto Profile::activeFrameSlot() const -> std::int8_t { + return _activeFrameSlot; +} + +auto Profile::getActiveFrameSlot() -> std::int8_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &active_slot_locator[0], &active_slot_locator[31]); + + if(iter == mmap.end()) { + if(std::search(mmap.begin(), mmap.end(), &credits_locator[0], &credits_locator[22]) != mmap.end()) { + _activeFrameSlot = 0; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _activeFrameSlot = -1; + } + } + else { + _activeFrameSlot = *(iter + 41); + } + + return _activeFrameSlot; +} + +auto Profile::credits() const -> std::int32_t { + return _credits; +} + +auto Profile::getCredits() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &credits_locator[0], &credits_locator[22]); + + if(iter != mmap.end()) { + _credits = *reinterpret_cast(iter + 0x20); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _credits = -1; + } + + return _credits; +} + +auto Profile::setCredits(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &credits_locator[0], &credits_locator[22]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x20) = amount; + _credits = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::storyProgress() const -> std::int32_t { + return _storyProgress; +} + +auto Profile::getStoryProgress() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &story_progress_locator[0], &story_progress_locator[29]); + + if(iter != mmap.end()) { + _storyProgress = *reinterpret_cast(iter + 0x27); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _storyProgress = -1; + } + + return _storyProgress; +} + +auto Profile::setStoryProgress(std::int32_t progress) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &story_progress_locator[0], &story_progress_locator[29]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x27) = progress; + _storyProgress = progress; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::lastMissionId() const -> std::int32_t { + return _lastMissionId; +} + +auto Profile::getLastMissionId() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &last_mission_id_locator[0], &last_mission_id_locator[29]); + + if(iter != mmap.end()) { + _lastMissionId = *reinterpret_cast(iter + 0x27); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _lastMissionId = -1; + } + + return _lastMissionId; +} + +auto Profile::verseSteel() const -> std::int32_t { + return _verseSteel; +} + +auto Profile::getVerseSteel() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &verse_steel_locator[0], &verse_steel_locator[129]); + + if(iter != mmap.end()) { + _verseSteel = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _verseSteel = -1; + } + + return _verseSteel; +} + +auto Profile::setVerseSteel(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &verse_steel_locator[0], &verse_steel_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _verseSteel = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::undinium() const -> std::int32_t { + return _undinium; +} + +auto Profile::getUndinium() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &undinium_locator[0], &undinium_locator[129]); + + if(iter != mmap.end()) { + _undinium = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _undinium = -1; + } + + return _undinium; +} + +auto Profile::setUndinium(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &undinium_locator[0], &undinium_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _undinium = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::necriumAlloy() const -> std::int32_t { + return _necriumAlloy; +} + +auto Profile::getNecriumAlloy() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &necrium_alloy_locator[0], &necrium_alloy_locator[129]); + + if(iter != mmap.end()) { + _necriumAlloy = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _necriumAlloy = -1; + } + + return _necriumAlloy; +} + +auto Profile::setNecriumAlloy(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &necrium_alloy_locator[0], &necrium_alloy_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _necriumAlloy = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::lunarite() const -> std::int32_t { + return _lunarite; +} + +auto Profile::getLunarite() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &lunarite_locator[0], &lunarite_locator[129]); + + if(iter != mmap.end()) { + _lunarite = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _lunarite = -1; + } + + return _lunarite; +} + +auto Profile::setLunarite(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &lunarite_locator[0], &lunarite_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _lunarite = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::asterite() const -> std::int32_t { + return _asterite; +} + +auto Profile::getAsterite() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &asterite_locator[0], &asterite_locator[129]); + + if(iter != mmap.end()) { + _asterite = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _asterite = -1; + } + + return _asterite; +} + +auto Profile::setAsterite(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &asterite_locator[0], &asterite_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _asterite = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::ednil() const -> std::int32_t { + return _ednil; +} + +auto Profile::getEdnil() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &ednil_locator[0], &ednil_locator[129]); + + if(iter != mmap.end()) { + _ednil = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _ednil = -1; + } + + return _ednil; +} + +auto Profile::setEdnil(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &ednil_locator[0], &ednil_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _ednil = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::nuflalt() const -> std::int32_t { + return _nuflalt; +} + +auto Profile::getNuflalt() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &nuflalt_locator[0], &nuflalt_locator[129]); + + if(iter != mmap.end()) { + _nuflalt = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _nuflalt = -1; + } + + return _nuflalt; +} + +auto Profile::setNuflalt(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &nuflalt_locator[0], &nuflalt_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _nuflalt = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::aurelene() const -> std::int32_t { + return _aurelene; +} + +auto Profile::getAurelene() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &aurelene_locator[0], &aurelene_locator[129]); + + if(iter != mmap.end()) { + _aurelene = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _aurelene = -1; + } + + return _aurelene; +} + +auto Profile::setAurelene(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &aurelene_locator[0], &aurelene_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _aurelene = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::soldus() const -> std::int32_t { + return _soldus; +} + +auto Profile::getSoldus() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &soldus_locator[0], &soldus_locator[129]); + + if(iter != mmap.end()) { + _soldus = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _soldus = -1; + } + + return _soldus; +} + +auto Profile::setSoldus(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &soldus_locator[0], &soldus_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _soldus = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::synthesizedN() const -> std::int32_t { + return _synthesizedN; +} + +auto Profile::getSynthesizedN() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &synthesized_n_locator[0], &synthesized_n_locator[129]); + + if(iter != mmap.end()) { + _synthesizedN = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _synthesizedN = -1; + } + + return _synthesizedN; +} + +auto Profile::setSynthesizedN(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &synthesized_n_locator[0], &synthesized_n_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _synthesizedN = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::alcarbonite() const -> std::int32_t { + return _alcarbonite; +} + +auto Profile::getAlcarbonite() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &alcarbonite_locator[0], &alcarbonite_locator[129]); + + if(iter != mmap.end()) { + _alcarbonite = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _alcarbonite = -1; + } + + return _alcarbonite; +} + +auto Profile::setAlcarbonite(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &alcarbonite_locator[0], &alcarbonite_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _alcarbonite = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::keriphene() const -> std::int32_t { + return _keriphene; +} + +auto Profile::getKeriphene() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &keriphene_locator[0], &keriphene_locator[129]); + + if(iter != mmap.end()) { + _keriphene = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _keriphene= -1; + } + + return _keriphene; +} + +auto Profile::setKeriphene(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &keriphene_locator[0], &keriphene_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _keriphene = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::nitinolCM() const -> std::int32_t { + return _nitinolCM; +} + +auto Profile::getNitinolCM() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &nitinol_cm_locator[0], &nitinol_cm_locator[129]); + + if(iter != mmap.end()) { + _nitinolCM = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _nitinolCM = -1; + } + + return _nitinolCM; +} + +auto Profile::setNitinolCM(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &nitinol_cm_locator[0], &nitinol_cm_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _nitinolCM = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::quarkium() const -> std::int32_t { + return _quarkium; +} + +auto Profile::getQuarkium() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &quarkium_locator[0], &quarkium_locator[129]); + + if(iter != mmap.end()) { + _quarkium = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _quarkium = -1; + } + + return _quarkium; +} + +auto Profile::setQuarkium(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &quarkium_locator[0], &quarkium_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _quarkium = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::alterene() const -> std::int32_t { + return _alterene; +} + +auto Profile::getAlterene() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &alterene_locator[0], &alterene_locator[129]); + + if(iter != mmap.end()) { + _alterene = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _alterene = -1; + } + + return _alterene; +} + +auto Profile::setAlterene(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &alterene_locator[0], &alterene_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _alterene = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::mixedComposition() const -> std::int32_t { + return _mixedComposition; +} + +auto Profile::getMixedComposition() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &mixed_composition_locator[0], &mixed_composition_locator[129]); + + if(iter != mmap.end()) { + _mixedComposition = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _mixedComposition = -1; + } + + return _mixedComposition; +} + +auto Profile::setMixedComposition(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &mixed_composition_locator[0], &mixed_composition_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _mixedComposition = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::voidResidue() const -> std::int32_t { + return _voidResidue; +} + +auto Profile::getVoidResidue() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &void_residue_locator[0], &void_residue_locator[129]); + + if(iter != mmap.end()) { + _voidResidue = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _voidResidue = -1; + } + + return _voidResidue; +} + +auto Profile::setVoidResidue(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &void_residue_locator[0], &void_residue_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _voidResidue = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::muscularConstruction() const -> std::int32_t { + return _muscularConstruction; +} + +auto Profile::getMuscularConstruction() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &muscular_construction_locator[0], &muscular_construction_locator[129]); + + if(iter != mmap.end()) { + _muscularConstruction = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _muscularConstruction = -1; + } + + return _muscularConstruction; +} + +auto Profile::setMuscularConstruction(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &muscular_construction_locator[0], &muscular_construction_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _muscularConstruction = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::mineralExoskeletology() const -> std::int32_t { + return _mineralExoskeletology; +} + +auto Profile::getMineralExoskeletology() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &mineral_exoskeletology_locator[0], &mineral_exoskeletology_locator[129]); + + if(iter != mmap.end()) { + _mineralExoskeletology = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _mineralExoskeletology = -1; + } + + return _mineralExoskeletology; +} + +auto Profile::setMineralExoskeletology(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &mineral_exoskeletology_locator[0], &mineral_exoskeletology_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _mineralExoskeletology = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} + +auto Profile::carbonizedSkin() const -> std::int32_t { + return _carbonizedSkin; +} + +auto Profile::getCarbonizedSkin() -> std::int32_t { + auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &carbonized_skin_locator[0], &carbonized_skin_locator[129]); + + if(iter != mmap.end()) { + _carbonizedSkin = *reinterpret_cast(iter + 0x8C); + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + _carbonizedSkin = -1; + } + + return _carbonizedSkin; +} + +auto Profile::setCarbonizedSkin(std::int32_t amount) -> bool { + auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + + auto iter = std::search(mmap.begin(), mmap.end(), &carbonized_skin_locator[0], &carbonized_skin_locator[129]); + + if(iter != mmap.end()) { + *reinterpret_cast(iter + 0x8C) = amount; + _carbonizedSkin = amount; + return true; + } + else { + _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + return false; + } +} diff --git a/src/Profile/Profile.h b/src/Profile/Profile.h new file mode 100644 index 0000000..21f6c62 --- /dev/null +++ b/src/Profile/Profile.h @@ -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 . + +#include + +#include + +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; +}; diff --git a/src/ProfileManager/ProfileManager.cpp b/src/ProfileManager/ProfileManager.cpp new file mode 100644 index 0000000..9305684 --- /dev/null +++ b/src/ProfileManager/ProfileManager.cpp @@ -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 . + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#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 const& { + return _profiles; +} + +auto ProfileManager::refreshProfiles() -> bool { + _profiles.clear(); + + using Utility::Directory::Flag; + std::vector 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(×tamp); + + 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 const& { + return _backups; +} + +void ProfileManager::refreshBackups() { + _backups.clear(); + + using Utility::Directory::Flag; + std::vector 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(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; +} diff --git a/src/ProfileManager/ProfileManager.h b/src/ProfileManager/ProfileManager.h new file mode 100644 index 0000000..7e2f21d --- /dev/null +++ b/src/ProfileManager/ProfileManager.h @@ -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 . + +#include +#include + +#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 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 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 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 _profiles; + std::vector _backups; +}; diff --git a/src/SaveTool/SaveTool.cpp b/src/SaveTool/SaveTool.cpp new file mode 100644 index 0000000..9bc42e7 --- /dev/null +++ b/src/SaveTool/SaveTool.cpp @@ -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 . + +#include "SaveTool.h" + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "../FontAwesome/IconsFontAwesome5.h" +#include "../FontAwesome/IconsFontAwesome5Brands.h" + +extern const ImVec2 center_pivot = {0.5f, 0.5f}; + +#ifdef MANAGER_DEBUG_BUILD +#include + +#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(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(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(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(brand_font.data()), brand_font.size(), 16.0f, &icon_config, brand_range); + + auto mono_font = _rs.getRaw("SourceCodePro-Regular.ttf"); + ImVector range; + ImFontGlyphRangesBuilder builder; + builder.AddRanges(io.Fonts->GetGlyphRangesDefault()); + builder.AddChar(u'š'); // This allows displaying Vladimír Vondruš' name in Corrade's and Magnum's licences. + builder.BuildRanges(&range); + io.Fonts->AddFontFromMemoryTTF(const_cast(mono_font.data()), 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); +} diff --git a/src/SaveTool/SaveTool.h b/src/SaveTool/SaveTool.h new file mode 100644 index 0000000..f1004ac --- /dev/null +++ b/src/SaveTool/SaveTool.h @@ -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 . + +#include + +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +#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 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 + 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)...); + + 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 _mbManager; + SDL_TimerID _gameCheckTimerId; + + Containers::Pointer _profileManager; + Profile* _currentProfile{nullptr}; + + Containers::Pointer _massManager; + + Containers::Pointer _fileWatcher; + enum watchID { + SaveDir = 0, + StagingDir = 1 + }; + Containers::StaticArray<2, efsw::WatchID> _watchIDs; + + bool _unsafeMode{false}; +}; diff --git a/src/SaveTool/SaveTool_MainManager.cpp b/src/SaveTool/SaveTool_MainManager.cpp new file mode 100644 index 0000000..2032cad --- /dev/null +++ b/src/SaveTool/SaveTool_MainManager.cpp @@ -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 . + +#include "SaveTool.h" + +#include + +#include +#include +#include + +#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 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(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(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(""); + break; + case MassState::Invalid: + ImGui::TextUnformatted(""); + 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 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{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(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; +} diff --git a/src/SaveTool/SaveTool_ProfileManager.cpp b/src/SaveTool/SaveTool_ProfileManager.cpp new file mode 100644 index 0000000..b9e09cc --- /dev/null +++ b/src/SaveTool/SaveTool_ProfileManager.cpp @@ -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 . + +#include "SaveTool.h" + +#include + +#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; +} diff --git a/src/SaveTool/SaveTool_drawAbout.cpp b/src/SaveTool/SaveTool_drawAbout.cpp new file mode 100644 index 0000000..83601bb --- /dev/null +++ b/src/SaveTool/SaveTool_drawAbout.cpp @@ -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 . + +#include "SaveTool.h" + +#include + +#include +#include +#include + +#include + +#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 ()", 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(); +} diff --git a/src/SaveTool/SaveTool_drawMainMenu.cpp b/src/SaveTool/SaveTool_drawMainMenu.cpp new file mode 100644 index 0000000..663f3ea --- /dev/null +++ b/src/SaveTool/SaveTool_drawMainMenu.cpp @@ -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 . + +#include "SaveTool.h" + +#include + +#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(); + } +} diff --git a/src/assets.conf b/src/assets.conf new file mode 100644 index 0000000..544ef09 --- /dev/null +++ b/src/assets.conf @@ -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 diff --git a/src/assets/SourceCodePro-Regular.ttf b/src/assets/SourceCodePro-Regular.ttf new file mode 100644 index 0000000..3563e73 Binary files /dev/null and b/src/assets/SourceCodePro-Regular.ttf differ diff --git a/src/assets/SourceSansPro-Regular.ttf b/src/assets/SourceSansPro-Regular.ttf new file mode 100644 index 0000000..b422bf4 Binary files /dev/null and b/src/assets/SourceSansPro-Regular.ttf differ diff --git a/src/assets/fa-brands-400.ttf b/src/assets/fa-brands-400.ttf new file mode 100644 index 0000000..fbdcc77 Binary files /dev/null and b/src/assets/fa-brands-400.ttf differ diff --git a/src/assets/fa-solid-900.ttf b/src/assets/fa-solid-900.ttf new file mode 100644 index 0000000..b820448 Binary files /dev/null and b/src/assets/fa-solid-900.ttf differ diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..292b457 --- /dev/null +++ b/src/main.cpp @@ -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 . + +#include "SaveTool/SaveTool.h" + +MAGNUM_SDL2APPLICATION_MAIN(SaveTool) diff --git a/src/mbst.ico b/src/mbst.ico new file mode 100644 index 0000000..e87cc99 Binary files /dev/null and b/src/mbst.ico differ diff --git a/src/mbst.png b/src/mbst.png new file mode 100644 index 0000000..a9e2141 Binary files /dev/null and b/src/mbst.png differ diff --git a/src/resource.rc b/src/resource.rc new file mode 100644 index 0000000..7491807 --- /dev/null +++ b/src/resource.rc @@ -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 . + +MAINICON ICON "mbst.ico" + +1 24 "Application.manifest" diff --git a/third-party/SDL b/third-party/SDL new file mode 160000 index 0000000..d956636 --- /dev/null +++ b/third-party/SDL @@ -0,0 +1 @@ +Subproject commit d956636c85aafc055d37153d52370e9b1c2c5929 diff --git a/third-party/corrade b/third-party/corrade new file mode 160000 index 0000000..76d5492 --- /dev/null +++ b/third-party/corrade @@ -0,0 +1 @@ +Subproject commit 76d54922ba22e6a1abfffb5c9dc1012dfbae149e diff --git a/third-party/efsw b/third-party/efsw new file mode 160000 index 0000000..6fb0c9c --- /dev/null +++ b/third-party/efsw @@ -0,0 +1 @@ +Subproject commit 6fb0c9ccd284445330723914249b7be46798ee76 diff --git a/third-party/imgui b/third-party/imgui new file mode 160000 index 0000000..94b680e --- /dev/null +++ b/third-party/imgui @@ -0,0 +1 @@ +Subproject commit 94b680e83052b9ff2e877360309020b72db057f2 diff --git a/third-party/libzip b/third-party/libzip new file mode 160000 index 0000000..821cbfe --- /dev/null +++ b/third-party/libzip @@ -0,0 +1 @@ +Subproject commit 821cbfe1b260193563b04d3db1bb7eb4a4ed0d8a diff --git a/third-party/magnum b/third-party/magnum new file mode 160000 index 0000000..dd3ce93 --- /dev/null +++ b/third-party/magnum @@ -0,0 +1 @@ +Subproject commit dd3ce93888ada70074d63d72c5998d950fa2eba6 diff --git a/third-party/magnum-integration b/third-party/magnum-integration new file mode 160000 index 0000000..73018a6 --- /dev/null +++ b/third-party/magnum-integration @@ -0,0 +1 @@ +Subproject commit 73018a6d5daf04ef69449e82fa757c3acf797b1b