Compare commits

...

128 commits

Author SHA1 Message Date
f522d20dd4 SaveTool: fragment SaveTool_MassViewer.cpp.
This will make maintenance easier. I hope.
2022-03-02 14:43:02 +01:00
6208825aa6 Mass: fix a bug that prevented global styles from being read. 2022-03-02 14:10:13 +01:00
572585e648 SaveTool: optimise code readability.
...mostly for Clang/CLion. :D
2022-03-02 11:46:31 +01:00
de2ba9ce7f Mass(Manager),SaveTool: improve error handling. 2022-02-26 14:48:45 +01:00
d0ddc73852 Profile: fix a compile error. 2022-02-25 21:00:32 +01:00
a1c17b7138 Profile(Manager),SaveTool: improve error handling and fix bugs. 2022-02-24 14:00:47 +01:00
350ad59f8e SaveTool: add a convenience wrapper over ImGui stuff. 2022-02-23 21:59:00 +01:00
883d5d3f41 Mass,SaveTool: improve error handling. 2022-02-23 15:47:34 +01:00
77d7eaefad SaveTool: fix a condition. 2022-02-23 10:18:58 +01:00
82170b3078 Mass,SaveTool: optimise the effect colour mode. 2022-02-17 20:22:33 +01:00
88abf91047 Mass,SaveTool: add some future-proofing. 2022-02-17 20:01:59 +01:00
955ec010b8 Crc32: make the polynomial literal explicitly unsigned. 2022-02-16 11:48:39 +01:00
7cb9ea28b2 BinaryReader: add a way to read arbitrary types. 2022-02-16 11:47:49 +01:00
975f471a68 BinaryReader: add a way to seek into the file. 2022-02-14 09:31:20 +01:00
76210e147a BinaryWriter: add a way to access the temp array.
That way, I'll be able to easily compute the CRC32 of it.
2022-02-13 15:09:07 +01:00
5e06c48492 BinaryWriter: fix an issue with writeValueToArray().
The view needs to be of type T, not U (which can potentially be T&).
2022-02-13 15:03:45 +01:00
2ff32c4c78 Add a CRC32 algorithm. 2022-02-13 15:02:08 +01:00
4000421a8c Mass,SaveTool: refactor even more code. 2022-02-13 10:31:55 +01:00
8f1e3668a3 BinaryWriter: allow writing string literals. 2022-02-12 11:21:23 +01:00
a6c0614979 BinaryWriter: make non-copyable. 2022-02-11 19:44:16 +01:00
2cabe6a3ba MassManager: adapt to Mass changes.
Should have caught it earlier. Ugh.
2022-02-11 18:31:45 +01:00
afc163f344 Mass: rename a member.
SteamIDs aren't used anymore in 0.8+ save files.
2022-02-09 20:16:14 +01:00
353a71d8ab Weapon: remove an unneeded blank line. 2022-02-09 18:28:23 +01:00
bbc40d7c93 Weapon: update formatting. 2022-02-09 18:12:11 +01:00
28db82c8a9 Weapon,Mass,SaveTool: refactor some more code. 2022-02-09 14:17:05 +01:00
9f324c30fd Delete WeaponTypes.h.
It's not needed anymore.
2022-02-09 13:54:44 +01:00
41cd92352d Mass: refactor and optimise some parts. 2022-02-09 13:41:55 +01:00
940fe3feee SaveTool: implement weapon copying. 2022-01-30 14:04:22 +01:00
d74a7bc219 Mass: make Weapon copyable.
This is necessary to add weapon copying.
2022-01-30 14:02:30 +01:00
51faed7210 Update copyright years.
I should have done that earlier... and of course I forgot a few files...
2022-01-30 11:38:22 +01:00
8fb837bfc0 SaveTool: finish implementing weapon reordering. 2022-01-30 09:48:14 +01:00
0ac1e759ca Profile(Manager),SaveTool: prepare for legacy/normal distinction. 2022-01-20 19:42:27 +01:00
a4045e8e9b Add a few IDs to maps. 2022-01-20 11:39:36 +01:00
1ec4522baf SaveTool: make the clickthrough hint have priority. 2022-01-15 13:26:12 +01:00
13d09e4aa0 SaveTool: prepare for the better import/export system. 2022-01-15 11:31:33 +01:00
ed0c4a73bb SaveTool: mark profiles as "legacy" in the manager.
I'll probably have to redesign that whole part of the UI anyway, so...
2022-01-15 11:31:06 +01:00
8102d1d83a SaveTool: improve the ShellExecuteW call. 2022-01-15 11:30:12 +01:00
9a9c08391a SaveTool: change how story progress is handled. 2022-01-15 11:01:11 +01:00
51602c713a SaveTool: update layout. 2022-01-14 13:42:09 +01:00
1621a4dbd5 SaveTool: move M.A.S.S. viewer state tracking.
That way, it's easier to reset. Some states, such as the current tab or
which headers are open/collapsed, are internal to ImGui, though, so I
can't do much there. Well, I could use the internal ImGui API, but, it's
still a pain in the ass.
2022-01-14 13:22:51 +01:00
7fb269f862 SaveTool: change viewer window ID. 2022-01-07 09:35:32 +01:00
1378676bbc SaveTool: fix old code. 2022-01-03 12:46:13 +01:00
96768c1aab SaveTool: add a conversion operator. 2022-01-03 12:10:20 +01:00
bd05a98820 SaveTool: make pre-releases considered up-to-date if they're more recent than the latest stable. 2022-01-03 11:56:34 +01:00
7059295cb3 Strip the release executable.
For *some* reason, there's debug info in one of the MinGW-w64 libs,
which account for roughly half the bloat of the release exe.
2021-12-03 00:14:22 +01:00
065e63f27c SaveTool: skip prereleases in update check. 2021-12-02 20:37:47 +01:00
a05f3eeed0 Prepare the pre-release. 2021-12-02 20:12:54 +01:00
321e8feed0 SaveTool: change how versions are evaluated.
This allows pre-releases and beta versions to be considered out of date
once complete versions are released.
2021-12-02 19:52:26 +01:00
c6de9c1940 SaveTool: finish most of the M.A.S.S. viewer.
Some parts are very unfinished, but do work.
2021-12-02 19:23:28 +01:00
fe0db983ce Mass: add tuning reading support.
Writing support SOON™.
2021-12-02 15:27:00 +01:00
bd8ff47f1e Mass: fix data ordering issue with joint sliders. 2021-11-01 11:19:34 +01:00
83fa5822bf MassManager: fix bugs in path handling. 2021-11-01 09:40:33 +01:00
8d87cdd619 Mass: fix a check. 2021-10-29 14:41:52 +02:00
0900f92b9f Mass: finish implementing the saving feature.
Oh, and also remove a blank line, but that's not important.
2021-10-29 10:23:34 +02:00
af71806e13 Mass: finish getWeaponType(). 2021-10-18 14:54:28 +02:00
247578a386 Mass: add getDecals() and getAccessories(). 2021-10-18 14:54:04 +02:00
4ca6f62d9b Mass: remove as many hardcoded values as possible. 2021-10-17 15:29:16 +02:00
e461d5a505 Mass: add getCustomStyles(); 2021-10-17 15:28:54 +02:00
d79debe69f Mass: change how setCustomStyle() works.
This'll allow usage with weapon styles.
2021-10-17 10:52:57 +02:00
83fe02a8dc Mass: improve readability and reliability. 2021-10-17 09:24:44 +02:00
7e452db3a4 Mass: improve readability. 2021-10-17 08:44:23 +02:00
80bb85c0d8 Mass: add weapon reading.
Writing will come SOON™.
2021-10-17 08:37:12 +02:00
4df90efd67 Mass: rename a field. 2021-10-16 11:36:31 +02:00
b92c37e4b6 Mass: fix another bug with importing. 2021-10-14 15:06:03 +02:00
dd460b4313 MassManager: fix a bug with importing. 2021-10-13 14:49:31 +02:00
05a2b1cfb0 SaveTool: change a drag widget to a slider. 2021-10-12 16:53:19 +02:00
8fedbdd4e5 Add armour sets and slots maps. 2021-10-12 16:52:40 +02:00
3ac5288f12 Mass: prepare decals for edition. 2021-10-12 16:35:49 +02:00
8bae723018 Mass: finish implementing armour part support. 2021-10-11 17:17:14 +02:00
3714162b50 UnrealPropertyBase: initialise valueLength. 2021-10-05 10:31:06 +02:00
a22aa6f7ae Mass: update CustomStyle default values to match game. 2021-10-05 10:10:18 +02:00
d03e75a8e9 Mass: not all parts can have 8 decals in the demo.
For now, at least. Just like the code that treats a lack of global
styles as a demo thing, I'll remove it in due time.
2021-10-05 10:03:52 +02:00
4429e581f3 Mass: reorganise a whole chunk of code. 2021-10-04 18:18:53 +02:00
2d0d5817f2 SaveTool: add a basic guide to the help menu. 2021-10-04 18:18:20 +02:00
bfe9a2c3a8 Profile: fix a crash that happens when restoring a backup. 2021-10-04 18:17:51 +02:00
8c81b7811b Mass: add support for reading armour parts. 2021-10-03 16:32:47 +02:00
0904384e0d SaveTool: fixed a condition for drag and dropping builds. 2021-10-03 00:14:07 +02:00
6f3da0b4a7 Mass: add some sanity checks. 2021-10-02 19:23:35 +02:00
19c00a3ce3 Mass: rename Armour to ArmourPart.
This is more consistent with WeaponPart, which designates a part of the
full weapon.
2021-10-02 19:22:52 +02:00
79762e176e Mass: add (partial) support for custom style edition. 2021-10-02 14:52:48 +02:00
b5b5b3b38c Mass: rename some members. 2021-10-02 14:51:39 +02:00
e4cfd3834a Mass: update CustomStyle. 2021-09-29 11:07:10 +02:00
e77cce5b42 Mass: add missing value to CustomStyle. 2021-09-27 21:55:05 +02:00
18aa7f659e SaveTool: move the tw macro so all SaveTool files can use it. 2021-09-27 20:51:48 +02:00
1612e4372b SaveTool: make the file watcher less aggressive. 2021-09-27 20:50:37 +02:00
b377e0de6c Mass: add a missing variable to CustomStyle. 2021-09-27 20:50:03 +02:00
0a438a4d72 MassManager: adapt to Mass changes. 2021-09-27 17:54:42 +02:00
e839d1c19b SaveTool: adapt to Mass changes. 2021-09-27 17:54:30 +02:00
5689ec6c1a SaveTool: adapt to Profile changes. 2021-09-27 17:53:56 +02:00
c2d0fbd941 Mass: adapt to UESaveFile. 2021-09-27 17:52:47 +02:00
2b2320ae0a UESaveFile: clear properties when reloading data. 2021-09-27 16:21:39 +02:00
10368e09db Profile: add default values for some members. 2021-09-27 16:18:03 +02:00
bd255ef8d5 Profile: ensure (in)validity. 2021-09-27 16:17:32 +02:00
911e18fc0a UESaveFile: add a few sanity things. 2021-09-27 16:16:47 +02:00
0c257bcfa6 GenericStructProperty: remove a redundant function. 2021-09-25 15:46:01 +02:00
9bc4aaf66b Profile: add data caching.
Querying the properties each frame isn't performant because of all the
casts and pointer indirections.
2021-09-24 21:51:06 +02:00
79e3193309 ArrayProperty: update at() to cast as well. 2021-09-23 21:54:59 +02:00
76e36791d7 Profile: remove Locators.h.
It's not needed anymore.
2021-09-23 19:11:08 +02:00
50a7b1d7f0 Profile: adapt to UESaveFile.
Also change ProfileManager to use growable arrays instead of vectors.
2021-09-23 19:01:42 +02:00
1caa472833 UESaveFile: add more error messages. 2021-09-23 18:25:28 +02:00
d3d065c945 StructSerialiser: fix serialisation of array'd structs.
Not all of them are generic structs, after all.
2021-09-23 18:24:55 +02:00
b8b156a724 Add a serialiser for struct sttResourceItemValue. 2021-09-23 15:09:18 +02:00
9c1aeb753e UESaveFile: add API to append a property. 2021-09-23 15:08:20 +02:00
0006c90a21 UESaveFile: add sanity check when reading files. 2021-09-23 15:07:29 +02:00
0826d4aede UESaveFile: close the file after we're done writing to it. 2021-09-22 21:50:39 +02:00
b3220ca8e1 MapPropertySerialiser: fix serialisation of demo saves. 2021-09-22 21:50:08 +02:00
48210c7186 UESaveFile: prevent reloading data on save. 2021-09-22 19:46:41 +02:00
f500e982e6 MapPropertySerialiser: add support for demo props. 2021-09-22 19:35:16 +02:00
2e1949ed5d BytePropertySerialiser: add support for demo props. 2021-09-22 19:16:33 +02:00
10becfdc31 PropertySerialiser: add an explicit cast. 2021-09-22 18:25:15 +02:00
f286ec0633 UESaveFile,GenericStructProperty: update at(). 2021-09-22 18:23:16 +02:00
ce29d6174c UESaveFile: allow the class to be moved. 2021-09-22 18:22:48 +02:00
de07b760d0 Add UESaveFile. 2021-09-22 17:37:50 +02:00
66d96bd893 SaveTool: update formatting. 2021-09-22 10:47:23 +02:00
083b60aac4 Update dependencies. 2021-09-19 13:09:04 +02:00
918b26ab5e SaveTool: add some frame info display. 2021-09-10 16:16:21 +02:00
32bc179120 SaveTool: change an include.
The old one works on my setup, but might not work on others.
2021-09-10 16:15:27 +02:00
c64684b34c StyleNames: add placeholders for custom/global style names. 2021-09-10 16:14:31 +02:00
40840e3128 Mass: add support for reading joint sliders. 2021-09-10 16:13:29 +02:00
c7c379c419 SaveTool: adapt main manager to Mass changes. 2021-08-29 19:39:29 +02:00
a9a5bfb2af SaveTool: add basic skeleton for the M.A.S.S. viewer. 2021-08-28 21:03:06 +02:00
5f4576a2bc SaveTool: improve readability of the header. 2021-08-28 20:57:06 +02:00
f3318e0ed1 SaveTool: add safety measures in drawMassViewer(). 2021-08-28 20:22:04 +02:00
7fcf8b518e Mass: add functions to read/write frame styles. 2021-08-28 20:21:13 +02:00
69021eacdf Mass: change how the name is obtained, and move the state enum. 2021-08-28 20:20:09 +02:00
bd6e55826d Add StyleNames.h. 2021-08-28 20:16:19 +02:00
597e9dfe98 SaveTool: initial work for the viewer UI. 2021-08-19 20:35:00 +02:00
4cdd1b35ec Mass(Manager): rework to prepare for the viewer. 2021-08-19 20:34:37 +02:00
125 changed files with 8816 additions and 1332 deletions

View file

@ -1,5 +1,5 @@
# MassBuilderSaveTool # MassBuilderSaveTool
# Copyright (C) 2021 Guillaume Jacquemin # Copyright (C) 2021-2022 Guillaume Jacquemin
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View file

@ -1,5 +1,5 @@
# MassBuilderSaveTool # MassBuilderSaveTool
# Copyright (C) 2021 Guillaume Jacquemin # Copyright (C) 2021-2022 Guillaume Jacquemin
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -18,7 +18,7 @@ set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)
set(SAVETOOL_PROJECT_VERSION 1.2.0) set(SAVETOOL_PROJECT_VERSION 1.3.0-pre)
find_package(Corrade REQUIRED Main Containers Utility Interconnect) find_package(Corrade REQUIRED Main Containers Utility Interconnect)
find_package(Magnum REQUIRED GL Sdl2Application) find_package(Magnum REQUIRED GL Sdl2Application)
@ -28,6 +28,86 @@ set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON)
corrade_add_resource(Assets assets.conf) corrade_add_resource(Assets assets.conf)
add_library(UESaveFile STATIC EXCLUDE_FROM_ALL
UESaveFile/Serialisers/AbstractUnrealCollectionPropertySerialiser.h
UESaveFile/Serialisers/AbstractUnrealPropertySerialiser.h
UESaveFile/Serialisers/AbstractUnrealStructSerialiser.h
UESaveFile/Serialisers/ArrayPropertySerialiser.h
UESaveFile/Serialisers/ArrayPropertySerialiser.cpp
UESaveFile/Serialisers/BoolPropertySerialiser.h
UESaveFile/Serialisers/BoolPropertySerialiser.cpp
UESaveFile/Serialisers/BytePropertySerialiser.h
UESaveFile/Serialisers/BytePropertySerialiser.cpp
UESaveFile/Serialisers/ColourPropertySerialiser.h
UESaveFile/Serialisers/ColourPropertySerialiser.cpp
UESaveFile/Serialisers/DateTimePropertySerialiser.h
UESaveFile/Serialisers/DateTimePropertySerialiser.cpp
UESaveFile/Serialisers/EnumPropertySerialiser.h
UESaveFile/Serialisers/EnumPropertySerialiser.cpp
UESaveFile/Serialisers/FloatPropertySerialiser.h
UESaveFile/Serialisers/FloatPropertySerialiser.cpp
UESaveFile/Serialisers/GuidPropertySerialiser.h
UESaveFile/Serialisers/GuidPropertySerialiser.cpp
UESaveFile/Serialisers/IntPropertySerialiser.h
UESaveFile/Serialisers/IntPropertySerialiser.cpp
UESaveFile/Serialisers/MapPropertySerialiser.h
UESaveFile/Serialisers/MapPropertySerialiser.cpp
UESaveFile/Serialisers/ResourcePropertySerialiser.h
UESaveFile/Serialisers/ResourcePropertySerialiser.cpp
UESaveFile/Serialisers/RotatorPropertySerialiser.h
UESaveFile/Serialisers/RotatorPropertySerialiser.cpp
UESaveFile/Serialisers/StringPropertySerialiser.h
UESaveFile/Serialisers/StringPropertySerialiser.cpp
UESaveFile/Serialisers/SetPropertySerialiser.h
UESaveFile/Serialisers/SetPropertySerialiser.cpp
UESaveFile/Serialisers/StructSerialiser.h
UESaveFile/Serialisers/StructSerialiser.cpp
UESaveFile/Serialisers/TextPropertySerialiser.h
UESaveFile/Serialisers/TextPropertySerialiser.cpp
UESaveFile/Serialisers/UnrealPropertySerialiser.h
UESaveFile/Serialisers/VectorPropertySerialiser.h
UESaveFile/Serialisers/VectorPropertySerialiser.cpp
UESaveFile/Serialisers/Vector2DPropertySerialiser.h
UESaveFile/Serialisers/Vector2DPropertySerialiser.cpp
UESaveFile/Types/ArrayProperty.h
UESaveFile/Types/BoolProperty.h
UESaveFile/Types/ByteProperty.h
UESaveFile/Types/ColourStructProperty.h
UESaveFile/Types/DateTimeStructProperty.h
UESaveFile/Types/EnumProperty.h
UESaveFile/Types/FloatProperty.h
UESaveFile/Types/GenericStructProperty.h
UESaveFile/Types/GuidStructProperty.h
UESaveFile/Types/IntProperty.h
UESaveFile/Types/MapProperty.h
UESaveFile/Types/NoneProperty.h
UESaveFile/Types/RotatorStructProperty.h
UESaveFile/Types/SetProperty.h
UESaveFile/Types/StringProperty.h
UESaveFile/Types/StructProperty.h
UESaveFile/Types/ResourceItemValue.h
UESaveFile/Types/TextProperty.h
UESaveFile/Types/UnrealProperty.h
UESaveFile/Types/UnrealPropertyBase.h
UESaveFile/Types/VectorStructProperty.h
UESaveFile/Debug.h
UESaveFile/Debug.cpp
UESaveFile/UESaveFile.h
UESaveFile/UESaveFile.cpp
UESaveFile/BinaryReader.h
UESaveFile/BinaryReader.cpp
UESaveFile/BinaryWriter.h
UESaveFile/BinaryWriter.cpp
UESaveFile/PropertySerialiser.h
UESaveFile/PropertySerialiser.cpp)
target_link_libraries(UESaveFile PRIVATE
Corrade::Containers
Corrade::Utility
Magnum::Magnum)
add_executable(MassBuilderSaveTool WIN32 add_executable(MassBuilderSaveTool WIN32
main.cpp main.cpp
SaveTool/SaveTool.h SaveTool/SaveTool.h
@ -35,19 +115,40 @@ add_executable(MassBuilderSaveTool WIN32
SaveTool/SaveTool_drawAbout.cpp SaveTool/SaveTool_drawAbout.cpp
SaveTool/SaveTool_drawMainMenu.cpp SaveTool/SaveTool_drawMainMenu.cpp
SaveTool/SaveTool_MainManager.cpp SaveTool/SaveTool_MainManager.cpp
SaveTool/SaveTool_MassViewer.cpp
SaveTool/SaveTool_MassViewer_Frame.cpp
SaveTool/SaveTool_MassViewer_Armour.cpp
SaveTool/SaveTool_MassViewer_Weapons.cpp
SaveTool/SaveTool_ProfileManager.cpp SaveTool/SaveTool_ProfileManager.cpp
ProfileManager/ProfileManager.h ProfileManager/ProfileManager.h
ProfileManager/ProfileManager.cpp ProfileManager/ProfileManager.cpp
Profile/Profile.h Profile/Profile.h
Profile/Profile.cpp Profile/Profile.cpp
Profile/ResourceIDs.h
MassManager/MassManager.h MassManager/MassManager.h
MassManager/MassManager.cpp MassManager/MassManager.cpp
Mass/Accessory.h
Mass/ArmourPart.h
Mass/CustomStyle.h
Mass/Decal.h
Mass/Joints.h
Mass/Mass.h Mass/Mass.h
Mass/Mass.cpp Mass/Mass.cpp
Mass/Weapon.h
Mass/Weapon.cpp
Mass/WeaponPart.h
Maps/Accessories.h
Maps/ArmourSets.h
Maps/ArmourSlots.hpp
Maps/DamageTypes.hpp
Maps/EffectColourModes.hpp
Maps/LastMissionId.h Maps/LastMissionId.h
Maps/StoryProgress.h Maps/StoryProgress.h
Maps/StyleNames.h
Maps/WeaponTypes.hpp
ToastQueue/ToastQueue.h ToastQueue/ToastQueue.h
ToastQueue/ToastQueue.cpp ToastQueue/ToastQueue.cpp
Utilities/Crc32.h
FontAwesome/IconsFontAwesome5.h FontAwesome/IconsFontAwesome5.h
FontAwesome/IconsFontAwesome5Brands.h FontAwesome/IconsFontAwesome5Brands.h
resource.rc resource.rc
@ -57,7 +158,7 @@ if(CMAKE_BUILD_TYPE STREQUAL Debug)
add_compile_definitions(SAVETOOL_DEBUG_BUILD) add_compile_definitions(SAVETOOL_DEBUG_BUILD)
endif() endif()
add_compile_definitions(SAVETOOL_VERSION="${SAVETOOL_PROJECT_VERSION}" add_compile_definitions(SAVETOOL_VERSION="${SAVETOOL_PROJECT_VERSION}"
SAVETOOL_CODENAME="Cute Quindolia" SAVETOOL_CODENAME="Dickish Cyclops"
SUPPORTED_GAME_VERSION="0.7.6") SUPPORTED_GAME_VERSION="0.7.6")
if(CMAKE_BUILD_TYPE STREQUAL Release) if(CMAKE_BUILD_TYPE STREQUAL Release)
@ -66,6 +167,10 @@ endif()
target_link_options(MassBuilderSaveTool PRIVATE -static -static-libgcc -static-libstdc++) target_link_options(MassBuilderSaveTool PRIVATE -static -static-libgcc -static-libstdc++)
if(CMAKE_BUILD_TYPE STREQUAL Release)
target_link_options(MassBuilderSaveTool PRIVATE -Wl,-S)
endif()
target_link_libraries(MassBuilderSaveTool PRIVATE target_link_libraries(MassBuilderSaveTool PRIVATE
Corrade::Containers Corrade::Containers
Corrade::Utility Corrade::Utility
@ -75,6 +180,7 @@ target_link_libraries(MassBuilderSaveTool PRIVATE
Magnum::GL Magnum::GL
Magnum::Sdl2Application Magnum::Sdl2Application
MagnumIntegration::ImGui MagnumIntegration::ImGui
UESaveFile
efsw efsw
zip zip
cpr::cpr cpr::cpr

74
src/Maps/Accessories.h Normal file
View file

@ -0,0 +1,74 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <map>
#include <Magnum/Types.h>
using namespace Magnum;
static const std::map<Int, const char*> accessories {
// Primitives
{1, "Cube (S)"},
{2, "Pentagon (S)"},
{3, "Hexagon (S)"},
{4, "Cylinder (S)"},
{5, "Sphere (S)"},
{6, "TriPyramid (S)"},
{7, "SquPyramid (S)"},
{8, "PenPyramid (S)"},
{9, "HexPyramid (S)"},
{10, "Cone (S)"},
{11, "SquStick (S)"},
{12, "PenStick (S)"},
{13, "HexStick (S)"},
{14, "CycStick (S)"},
{15, "Capsule (S)"},
{16, "Decal Pad 01 (S)"},
{17, "Decal Pad 02 (S)"},
{18, "Decal Pad 03 (S)"},
{19, "Decal Pad 04 (S)"},
{20, "Decal Pad 05 (S)"},
{51, "SquBevel (S)"},
{52, "TriBevel (S)"},
{53, "PenBevel (S)"},
{54, "HexBevel (S)"},
{55, "CycBevel (S)"},
{56, "RecBevel (S)"},
{57, "DaiBevel (S)"},
{58, "MonBevel (S)"},
{59, "CofBevel (S)"},
{60, "JevBevel (S)"},
{61, "SquEmboss (S)"},
{62, "TriEmboss (S)"},
{63, "PenEmboss (S)"},
{64, "HexEmboss (S)"},
{65, "CycEmboss (S)"},
{66, "RecEmboss (S)"},
{67, "DaiEmboss (S)"},
{68, "MonEmboss (S)"},
{69, "CofEmboss (S)"},
{70, "JevEmboss (S)"},
// Armours
// Components
// Connectors
};

51
src/Maps/ArmourSets.h Normal file
View file

@ -0,0 +1,51 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <map>
#include <Magnum/Types.h>
using namespace Magnum;
struct ArmourSet {
const char* name;
bool neck_compatible;
};
static const std::map<Int, ArmourSet> armour_sets {
{-1, {"<unequipped>", true}},
{0, {"Vanguard", true}},
{1, {"Assault Mk.I", true}},
{2, {"Assault Mk.II", false}},
{3, {"Assault Mk.III", false}},
{7, {"Titan 001", true}},
{8, {"Titan 002", false}},
{9, {"Titan 003", false}},
{13, {"Blitz X", true}},
{14, {"Blitz EX", false}},
{15, {"Blitz EXS", false}},
{16, {"Kaiser S-R0", true}},
{17, {"Kaiser S-R1", false}},
{18, {"Kaiser S-R2", false}},
{19, {"Hammerfall MG-A", true}},
{20, {"Hammerfall MG-S", false}},
{21, {"Hammerfall MG-X", false}},
{22, {"Panzer S-UC", true}},
{23, {"Panzer L-UC", false}},
{24, {"Panzer H-UC", false}},
};

56
src/Maps/ArmourSlots.hpp Normal file
View file

@ -0,0 +1,56 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#ifdef c
c(Face, "enuArmorSlots::NewEnumerator0", "Face")
c(UpperHead, "enuArmorSlots::NewEnumerator1", "Upper head")
c(LowerHead, "enuArmorSlots::NewEnumerator2", "Lower head")
c(Neck, "enuArmorSlots::NewEnumerator3", "Neck")
c(UpperBody, "enuArmorSlots::NewEnumerator4", "Upper body")
c(MiddleBody, "enuArmorSlots::NewEnumerator5", "Middle body")
c(LowerBody, "enuArmorSlots::NewEnumerator6", "Lower body")
c(FrontWaist, "enuArmorSlots::NewEnumerator7", "Front waist")
c(LeftFrontSkirt, "enuArmorSlots::NewEnumerator8", "Left front skirt")
c(RightFrontSkirt, "enuArmorSlots::NewEnumerator9", "Right front skirt")
c(LeftSideSkirt, "enuArmorSlots::NewEnumerator10", "Left side skirt")
c(RightSideSkirt, "enuArmorSlots::NewEnumerator11", "Right side skirt")
c(LeftBackSkirt, "enuArmorSlots::NewEnumerator12", "Left back skirt")
c(RightBackSkirt, "enuArmorSlots::NewEnumerator13", "Right back skirt")
c(BackWaist, "enuArmorSlots::NewEnumerator14", "Back waist")
c(LeftShoulder, "enuArmorSlots::NewEnumerator15", "Left shoulder")
c(RightShoulder, "enuArmorSlots::NewEnumerator16", "Right shoulder")
c(LeftUpperArm, "enuArmorSlots::NewEnumerator17", "Left upper arm")
c(RightUpperArm, "enuArmorSlots::NewEnumerator18", "Right upper arm")
c(LeftElbow, "enuArmorSlots::NewEnumerator19", "Left elbow")
c(RightElbow, "enuArmorSlots::NewEnumerator20", "Right elbow")
c(LeftLowerArm, "enuArmorSlots::NewEnumerator21", "Left lower arm")
c(RightLowerArm, "enuArmorSlots::NewEnumerator22", "Right lower arm")
c(Backpack, "enuArmorSlots::NewEnumerator23", "Backpack")
c(LeftHand, "enuArmorSlots::NewEnumerator24", "Left hand")
c(RightHand, "enuArmorSlots::NewEnumerator25", "Right hand")
c(LeftUpperLeg, "enuArmorSlots::NewEnumerator26", "Left upper leg")
c(RightUpperLeg, "enuArmorSlots::NewEnumerator27", "Right upper leg")
c(LeftKnee, "enuArmorSlots::NewEnumerator28", "Left knee")
c(RightKnee, "enuArmorSlots::NewEnumerator29", "Right knee")
c(LeftLowerLeg, "enuArmorSlots::NewEnumerator30", "Left lower leg")
c(RightLowerLeg, "enuArmorSlots::NewEnumerator31", "Right lower leg")
c(LeftAnkle, "enuArmorSlots::NewEnumerator32", "Left ankle")
c(RightAnkle, "enuArmorSlots::NewEnumerator33", "Right ankle")
c(LeftHeel, "enuArmorSlots::NewEnumerator34", "Left heel")
c(RightHeel, "enuArmorSlots::NewEnumerator35", "Right heel")
c(LeftFoot, "enuArmorSlots::NewEnumerator36", "Left foot")
c(RightFoot, "enuArmorSlots::NewEnumerator37", "Right foot")
#endif

24
src/Maps/DamageTypes.hpp Normal file
View file

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

View file

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

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
// MassBuilderSaveTool // MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin // Copyright (C) 2021-2022 Guillaume Jacquemin
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -33,6 +33,7 @@ static const std::map<Int, const char*> mission_id_map {{
{0x006E, "Mission 11 - Tempestuous Sector"}, {0x006E, "Mission 11 - Tempestuous Sector"},
{0x006F, "Mission 12 - Clashes of Metal"}, {0x006F, "Mission 12 - Clashes of Metal"},
{0x0070, "Mission 13 - The Sandstorm Glutton"}, {0x0070, "Mission 13 - The Sandstorm Glutton"},
{0x0071, "Mission 14 - An Icy Investigation"},
// Hunting grounds // Hunting grounds
{0x00C8, "Hunt 1 - Desert Pathway Safety"}, {0x00C8, "Hunt 1 - Desert Pathway Safety"},

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
// MassBuilderSaveTool // MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin // Copyright (C) 2021-2022 Guillaume Jacquemin
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -22,7 +22,7 @@ struct StoryProgressPoint {
Int id; Int id;
const char* chapter; const char* chapter;
const char* point; const char* point;
const char* after = ""; const char* after = nullptr;
}; };
static const Corrade::Containers::Array<StoryProgressPoint> story_progress static const Corrade::Containers::Array<StoryProgressPoint> story_progress
@ -88,5 +88,7 @@ static const Corrade::Containers::Array<StoryProgressPoint> story_progress
{0x0579, "Chapter 2", "Returned to 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"}, {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"}, {0x057B, "Chapter 2", "Got briefing for challenges 1, 2, and 3", "After mission 13"},
{0x057C, "Chapter 2", "Talked with Reina about device", "After mission 13"},
{0x057D, "Chapter 2", "Got mission 14 briefing", "After mission 13"},
} }
}; };

192
src/Maps/StyleNames.h Normal file
View file

@ -0,0 +1,192 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <map>
#include <Magnum/Magnum.h>
using namespace Magnum;
extern const std::map<Int, const char*> style_names
#ifdef STYLENAMES_DEFINITION
{
{0, "Custom Style 1"},
{1, "Custom Style 2"},
{2, "Custom Style 3"},
{3, "Custom Style 4"},
{4, "Custom Style 5"},
{5, "Custom Style 6"},
{6, "Custom Style 7"},
{7, "Custom Style 8"},
{8, "Custom Style 9"},
{9, "Custom Style 10"},
{10, "Custom Style 11"},
{11, "Custom Style 12"},
{12, "Custom Style 13"},
{13, "Custom Style 14"},
{14, "Custom Style 15"},
{15, "Custom Style 16"},
{50, "Global Style 1"},
{51, "Global Style 2"},
{52, "Global Style 3"},
{53, "Global Style 4"},
{54, "Global Style 5"},
{55, "Global Style 6"},
{56, "Global Style 7"},
{57, "Global Style 8"},
{58, "Global Style 9"},
{59, "Global Style 10"},
{60, "Global Style 11"},
{61, "Global Style 12"},
{62, "Global Style 13"},
{63, "Global Style 14"},
{64, "Global Style 15"},
{65, "Global Style 16"},
{100, "Iron"},
{101, "Silver"},
{102, "Gold"},
{103, "Bronze"},
{104, "Copper"},
{105, "Nickel"},
{106, "Cobalt"},
{107, "Aluminium"},
{108, "Titanium"},
{109, "Platinum"},
{110, "Gun Metal"},
{111, "White"},
{112, "White Metal"},
{113, "White Gloss"},
{114, "Grey"},
{115, "Grey Metal"},
{116, "Grey Gloss"},
{117, "Dark Grey"},
{118, "Dark Grey Metal"},
{119, "Dark Grey Gloss"},
{120, "Black"},
{121, "Black Metal"},
{122, "Black Gloss"},
{123, "Red"},
{124, "Red Metal"},
{125, "Red Gloss"},
{126, "Dark Red"},
{127, "Dark Red Metal"},
{128, "Dark Red Gloss"},
{129, "Orange"},
{130, "Orange Metal"},
{131, "Orange Gloss"},
{132, "Dark Orange"},
{133, "Dark Orange Metal"},
{134, "Dark Orange Gloss"},
{135, "Yellow"},
{136, "Yellow Metal"},
{137, "Yellow Gloss"},
{138, "Brown"},
{139, "Brown Metal"},
{140, "Brown Gloss"},
{141, "Dark Brown"},
{142, "Dark Brown Metal"},
{143, "Dark Brown Gloss"},
{144, "Leafgreen"},
{145, "Leafgreen Metal"},
{146, "Leafgreen Gloss"},
{147, "Military Green"},
{148, "Military Green Metal"},
{149, "Military Green Gloss"},
{150, "Green"},
{151, "Green Metal"},
{152, "Green Gloss"},
{153, "Dark Green"},
{154, "Dark Green Metal"},
{155, "Dark Green Gloss"},
{156, "Teal"},
{157, "Teal Metal"},
{158, "Teal Gloss"},
{159, "Cyan"},
{160, "Cyan Metal"},
{161, "Cyan Gloss"},
{162, "Blue"},
{163, "Blue Metal"},
{164, "Blue Gloss"},
{165, "Blue Sky"},
{166, "Blue Sky Metal"},
{167, "Blue Sky Gloss"},
{168, "Dark Blue"},
{169, "Dark Blue Metal"},
{170, "Dark Blue Gloss"},
{171, "Purple"},
{172, "Purple Metal"},
{173, "Purple Gloss"},
{174, "Dark Purple"},
{175, "Dark Purple Metal"},
{176, "Dark Purple Gloss"},
{177, "Pink"},
{178, "Pink Metal"},
{179, "Pink Gloss"},
{180, "Rosy Brown"},
{181, "Rosy Brown Metal"},
{182, "Rosy Brown Gloss"},
{183, "Ivory"},
{184, "Ivory Metal"},
{185, "Ivory Gloss"},
{186, "Slate Brown"},
{187, "Slate Brown Metal"},
{188, "Slate Brown Gloss"},
{189, "Slate Green"},
{190, "Slate Green Metal"},
{191, "Slate Green Gloss"},
{192, "Slate Blue"},
{193, "Slate Blue Metal"},
{194, "Slate Blue Gloss"},
{195, "Slate Purple"},
{196, "Slate Purple Metal"},
{197, "Slate Purple Gloss"},
{198, "White Glow"},
{199, "White Radiance"},
{200, "Red Glow"},
{201, "Red Radiance"},
{202, "Orange Glow"},
{203, "Orange Radiance"},
{204, "Yellow Glow"},
{205, "Yellow Radiance"},
{206, "Leafgreen Glow"},
{207, "Leafgreen Radiance"},
{208, "Green Glow"},
{209, "Green Radiance"},
{210, "Teal Glow"},
{211, "Teal Radiance"},
{212, "Cyan Glow"},
{213, "Cyan Radiance"},
{214, "Blue Glow"},
{215, "Blue Radiance"},
{216, "Purple Glow"},
{217, "Purple Radiance"},
{218, "Pink Glow"},
{219, "Pink Radiance"},
{220, "Grey Camo"},
{221, "Dark Grey Camo"},
{222, "Green Camo"},
{223, "Dark Green Camo"},
{224, "Brown Camo"},
{225, "Dark Brown Camo"},
{226, "Blue Camo"},
{227, "Dark Blue Camo"},
}
#endif
;

24
src/Maps/WeaponTypes.hpp Normal file
View file

@ -0,0 +1,24 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#ifdef c
c(Melee, "enuWeaponTypes::NewEnumerator0", "Melee weapon")
c(BulletShooter, "enuWeaponTypes::NewEnumerator1", "Bullet shooter")
c(EnergyShooter, "enuWeaponTypes::NewEnumerator2", "Energy shooter")
c(BulletLauncher, "enuWeaponTypes::NewEnumerator3", "Bullet launcher")
c(EnergyLauncher, "enuWeaponTypes::NewEnumerator4", "Energy launcher")
c(Shield, "enuWeaponTypes::NewEnumerator5", "Shield")
#endif

36
src/Mass/Accessory.h Normal file
View file

@ -0,0 +1,36 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <Corrade/Containers/StaticArray.h>
#include <Magnum/Magnum.h>
#include <Magnum/Math/Vector3.h>
using namespace Corrade;
using namespace Magnum;
struct Accessory {
Int attachIndex = -1;
Int id = -1;
Containers::StaticArray<2, Int> styles{ValueInit};
Vector3 relativePosition{0.0f};
Vector3 relativePositionOffset{0.0f};
Vector3 relativeRotation{0.0f};
Vector3 relativeRotationOffset{0.0f};
Vector3 localScale{1.0f};
};

79
src/Mass/ArmourPart.h Normal file
View file

@ -0,0 +1,79 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <string>
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/StaticArray.h>
#include <Magnum/Types.h>
#include "Decal.h"
#include "Accessory.h"
using namespace Corrade;
using namespace Magnum;
enum class ArmourSlot {
Face = 0,
UpperHead = 1,
LowerHead = 2,
Neck = 3,
UpperBody = 4,
MiddleBody = 5,
LowerBody = 6,
FrontWaist = 7,
LeftFrontSkirt = 8,
RightFrontSkirt = 9,
LeftSideSkirt = 10,
RightSideSkirt = 11,
LeftBackSkirt = 12,
RightBackSkirt = 13,
BackWaist = 14,
LeftShoulder = 15,
RightShoulder = 16,
LeftUpperArm = 17,
RightUpperArm = 18,
LeftElbow = 19,
RightElbow = 20,
LeftLowerArm = 21,
RightLowerArm = 22,
Backpack = 23,
LeftHand = 24,
RightHand = 25,
LeftUpperLeg = 26,
RightUpperLeg = 27,
LeftKnee = 28,
RightKnee = 29,
LeftLowerLeg = 30,
RightLowerLeg = 31,
LeftAnkle = 32,
RightAnkle = 33,
LeftHeel = 34,
RightHeel = 35,
LeftFoot = 36,
RightFoot = 37,
};
struct ArmourPart {
ArmourSlot slot = ArmourSlot::Face;
Int id = 0;
Containers::StaticArray<4, Int> styles{ValueInit};
Containers::Array<Decal> decals;
Containers::Array<Accessory> accessories;
};

39
src/Mass/CustomStyle.h Normal file
View file

@ -0,0 +1,39 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <string>
#include <Magnum/Magnum.h>
#include <Magnum/Math/Color.h>
#include <Magnum/Math/Vector2.h>
using namespace Magnum;
struct CustomStyle {
std::string name;
Color4 colour{0.0f};
Float metallic = 0.5f;
Float gloss = 0.5f;
bool glow = false;
Int patternId = 0;
Float opacity = 0.5f;
Vector2 offset{0.5f};
Float rotation = 0.0f;
Float scale = 0.5f;
};

38
src/Mass/Decal.h Normal file
View file

@ -0,0 +1,38 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <string>
#include <Magnum/Magnum.h>
#include <Magnum/Math/Color.h>
#include <Magnum/Math/Vector2.h>
using namespace Magnum;
struct Decal {
Int id = -1;
Color4 colour{0.0f};
Vector3 position{0.0f};
Vector3 uAxis{0.0f};
Vector3 vAxis{0.0f};
Vector2 offset{0.5f};
Float scale = 0.5f;
Float rotation = 0.0f;
bool flip = false;
bool wrap = false;
};

32
src/Mass/Joints.h Normal file
View file

@ -0,0 +1,32 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <Magnum/Types.h>
using namespace Magnum;
struct Joints {
Float neck = 0.0f;
Float body = 0.0f;
Float shoulders = 0.0f;
Float hips = 0.0f;
Float upperArms = 0.0f;
Float lowerArms = 0.0f;
Float upperLegs = 0.0f;
Float lowerLegs = 0.0f;
};

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
// MassBuilderSaveTool // MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin // Copyright (C) 2021-2022 Guillaume Jacquemin
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -18,17 +18,37 @@
#include <string> #include <string>
#include <Magnum/Magnum.h> #include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/Pointer.h>
#include <Corrade/Containers/StaticArray.h>
#include <Magnum/Magnum.h>
#include <Magnum/Math/Color.h>
#include <Magnum/Math/Vector2.h>
#include <Magnum/Math/Vector3.h>
#include "Joints.h"
#include "CustomStyle.h"
#include "Decal.h"
#include "Accessory.h"
#include "ArmourPart.h"
#include "WeaponPart.h"
#include "Weapon.h"
#include "../UESaveFile/UESaveFile.h"
using namespace Corrade;
using namespace Magnum; using namespace Magnum;
enum class MassState : UnsignedByte { struct ArrayProperty;
Empty, Invalid, Valid
};
class Mass { class Mass {
public: public:
explicit Mass(const std::string& filename); enum class State : UnsignedByte {
Empty, Invalid, Valid
};
explicit Mass(const std::string& path);
Mass(const Mass&) = delete; Mass(const Mass&) = delete;
Mass& operator=(const Mass&) = delete; Mass& operator=(const Mass&) = delete;
@ -36,23 +56,153 @@ class Mass {
Mass(Mass&&) = default; Mass(Mass&&) = default;
Mass& operator=(Mass&&) = default; Mass& operator=(Mass&&) = default;
static auto lastError() -> std::string const&; auto lastError() -> std::string const&;
static auto getNameFromFile(const std::string& filename) -> std::string; static auto getNameFromFile(const std::string& path) -> Containers::Optional<std::string>;
void refreshValues();
auto filename() -> std::string const&; auto filename() -> std::string const&;
auto name() -> std::string const&; auto name() -> Containers::Optional<std::string> const&;
auto getName() -> std::string const&; auto setName(std::string new_name) -> bool;
auto state() -> MassState; auto state() -> State;
auto updateSteamId(const std::string& steam_id) -> bool; auto dirty() const -> bool;
void setDirty(bool dirty = true);
auto jointSliders() -> Joints&;
void getJointSliders();
auto writeJointSliders() -> bool;
auto frameStyles() -> Containers::ArrayView<Int>;
void getFrameStyles();
auto writeFrameStyles() -> bool;
auto eyeFlareColour() -> Color4&;
void getEyeFlareColour();
auto writeEyeFlareColour() -> bool;
auto frameCustomStyles() -> Containers::ArrayView<CustomStyle>;
void getFrameCustomStyles();
auto writeFrameCustomStyle(UnsignedLong index) -> bool;
auto armourParts() -> Containers::ArrayView<ArmourPart>;
void getArmourParts();
auto writeArmourPart(ArmourSlot slot) -> bool;
auto armourCustomStyles() -> Containers::ArrayView<CustomStyle>;
void getArmourCustomStyles();
auto writeArmourCustomStyle(UnsignedLong index) -> bool;
auto meleeWeapons() -> Containers::ArrayView<Weapon>;
void getMeleeWeapons();
auto writeMeleeWeapons() -> bool;
auto shields() -> Containers::ArrayView<Weapon>;
void getShields();
auto writeShields() -> bool;
auto bulletShooters() -> Containers::ArrayView<Weapon>;
void getBulletShooters();
auto writeBulletShooters() -> bool;
auto energyShooters() -> Containers::ArrayView<Weapon>;
void getEnergyShooters();
auto writeEnergyShooters() -> bool;
auto bulletLaunchers() -> Containers::ArrayView<Weapon>;
void getBulletLaunchers();
auto writeBulletLaunchers() -> bool;
auto energyLaunchers() -> Containers::ArrayView<Weapon>;
void getEnergyLaunchers();
auto writeEnergyLaunchers() -> bool;
auto globalStyles() -> Containers::ArrayView<CustomStyle>;
void getGlobalStyles();
auto writeGlobalStyle(UnsignedLong index) -> bool;
void getTuning();
auto engine() -> Int&;
auto gears() -> Containers::ArrayView<Int>;
auto os() -> Int&;
auto modules() -> Containers::ArrayView<Int>;
auto architecture() -> Int&;
auto techs() -> Containers::ArrayView<Int>;
auto account() -> std::string const&;
auto updateAccount(const std::string& new_account) -> bool;
private: private:
static std::string _lastError; void getCustomStyles(Containers::ArrayView<CustomStyle> styles, ArrayProperty* style_array);
auto setCustomStyle(const CustomStyle& style, UnsignedLong index, ArrayProperty* style_array) -> bool;
std::string _filename = ""; void getDecals(Containers::ArrayView<Decal> decals, ArrayProperty* decal_array);
std::string _name = ""; void writeDecals(Containers::ArrayView<Decal> decals, ArrayProperty* decal_array);
MassState _state = MassState::Empty;
void getAccessories(Containers::ArrayView<Accessory> accessories, ArrayProperty* accessory_array);
void writeAccessories(Containers::ArrayView<Accessory> accessories, ArrayProperty* accs_array);
void getWeaponType(const char* prop_name, Containers::ArrayView<Weapon> weapon_array);
auto writeWeaponType(const char* prop_name, Containers::ArrayView<Weapon> weapon_array) -> bool;
void getTuningCategory(const char* big_node_prop_name, Int& big_node_id,
const char* small_nodes_prop_name, Containers::ArrayView<Int> small_nodes_ids);
Containers::Optional<UESaveFile> _mass;
std::string _lastError;
std::string _folder;
std::string _filename;
State _state = State::Empty;
bool _dirty = false;
Containers::Optional<std::string> _name = Containers::NullOpt;
struct {
Joints joints{};
Containers::StaticArray<4, Int> styles{ValueInit};
Color4 eyeFlare{0.0f};
Containers::StaticArray<16, CustomStyle> customStyles;
} _frame;
struct {
Containers::StaticArray<38, ArmourPart> parts;
Containers::StaticArray<16, CustomStyle> customStyles;
} _armour;
struct {
Containers::StaticArray<8, Weapon> melee;
Containers::StaticArray<1, Weapon> shields;
Containers::StaticArray<4, Weapon> bulletShooters;
Containers::StaticArray<4, Weapon> energyShooters;
Containers::StaticArray<4, Weapon> bulletLaunchers;
Containers::StaticArray<4, Weapon> energyLaunchers;
} _weapons;
Containers::Array<CustomStyle> _globalStyles;
struct {
Int engineId;
Containers::StaticArray<7, Int> gearIds;
Int osId;
Containers::StaticArray<7, Int> moduleIds;
Int archId;
Containers::StaticArray<7, Int> techIds;
} _tuning;
std::string _account;
}; };

49
src/Mass/Weapon.cpp Normal file
View file

@ -0,0 +1,49 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Weapon.h"
Weapon::Weapon(const Weapon& other) {
name = other.name;
type = other.type;
parts = Containers::Array<WeaponPart>{other.parts.size()};
for(UnsignedInt i = 0; i < parts.size(); i++) {
parts[i] = other.parts[i];
}
customStyles = other.customStyles;
attached = other.attached;
damageType = other.damageType;
dualWield = other.dualWield;
effectColourMode = other.effectColourMode;
effectColour = other.effectColour;
}
Weapon& Weapon::operator=(const Weapon& other) {
name = other.name;
type = other.type;
parts = Containers::Array<WeaponPart>{other.parts.size()};
for(UnsignedInt i = 0; i < parts.size(); i++) {
parts[i] = other.parts[i];
}
customStyles = other.customStyles;
attached = other.attached;
damageType = other.damageType;
dualWield = other.dualWield;
effectColourMode = other.effectColourMode;
effectColour = other.effectColour;
return *this;
}

74
src/Mass/Weapon.h Normal file
View file

@ -0,0 +1,74 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <string>
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/StaticArray.h>
#include <Magnum/Magnum.h>
#include <Magnum/Math/Color.h>
#include "WeaponPart.h"
#include "CustomStyle.h"
using namespace Corrade;
using namespace Magnum;
enum class WeaponType {
Melee = 0,
Shield = 5,
BulletShooter = 1,
EnergyShooter = 2,
BulletLauncher = 3,
EnergyLauncher = 4,
};
enum class DamageType {
Physical = 0,
Piercing = 1,
Plasma = 5,
Heat = 2,
Freeze = 3,
Shock = 4,
};
enum class EffectColourMode {
Default = 0,
Custom = 1,
};
struct Weapon {
Weapon() = default;
Weapon(const Weapon& other);
Weapon& operator=(const Weapon& other);
Weapon(Weapon&& other) = default;
Weapon& operator=(Weapon&& other) = default;
std::string name;
WeaponType type = WeaponType::Melee;
Containers::Array<WeaponPart> parts;
Containers::StaticArray<16, CustomStyle> customStyles{ValueInit};
bool attached = false;
DamageType damageType = DamageType::Physical;
bool dualWield = false;
EffectColourMode effectColourMode = EffectColourMode::Default;
Color4 effectColour{0.0f};
};

66
src/Mass/WeaponPart.h Normal file
View file

@ -0,0 +1,66 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/StaticArray.h>
#include <Magnum/Types.h>
#include "Decal.h"
#include "Accessory.h"
using namespace Corrade;
using namespace Magnum;
struct WeaponPart {
WeaponPart() = default;
WeaponPart(const WeaponPart& other) {
id = other.id;
styles = other.styles;
decals = Containers::Array<Decal>{other.decals.size()};
for(UnsignedInt i = 0; i < decals.size(); i++) {
decals[i] = other.decals[i];
}
accessories = Containers::Array<Accessory>{other.accessories.size()};
for(UnsignedInt i = 0; i < accessories.size(); i++) {
accessories[i] = other.accessories[i];
}
}
WeaponPart& operator=(const WeaponPart& other) {
id = other.id;
styles = other.styles;
decals = Containers::Array<Decal>{other.decals.size()};
for(UnsignedInt i = 0; i < decals.size(); i++) {
decals[i] = other.decals[i];
}
accessories = Containers::Array<Accessory>{other.accessories.size()};
for(UnsignedInt i = 0; i < accessories.size(); i++) {
accessories[i] = other.accessories[i];
}
return *this;
}
WeaponPart(WeaponPart&& other) = default;
WeaponPart& operator=(WeaponPart&& other) = default;
Int id = 0;
Containers::StaticArray<4, Int> styles{ValueInit};
Containers::Array<Decal> decals{};
Containers::Array<Accessory> accessories{};
};

View file

@ -1,5 +1,5 @@
// MassBuilderSaveTool // MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin // Copyright (C) 2021-2022 Guillaume Jacquemin
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -49,23 +49,11 @@ auto MassManager::lastError() -> std::string const& {
return _lastError; return _lastError;
} }
auto MassManager::massName(int hangar) -> std::string const& { auto MassManager::hangar(Int hangar) -> Mass& {
if(hangar < 0 || hangar >= 32) { return _hangars[hangar];
return empty_string;
}
return _hangars[hangar].name();
} }
auto MassManager::massState(int hangar) -> MassState { void MassManager::refreshHangar(Int hangar) {
if(hangar < 0 || hangar >= 32) {
return MassState::Empty;
}
return _hangars[hangar].state();
}
void MassManager::refreshHangar(int hangar) {
if(hangar < 0 || hangar >= 32) { if(hangar < 0 || hangar >= 32) {
return; return;
} }
@ -75,7 +63,7 @@ void MassManager::refreshHangar(int hangar) {
_hangars[hangar] = Mass{mass_filename}; _hangars[hangar] = Mass{mass_filename};
} }
auto MassManager::importMass(const std::string& staged_fn, int hangar) -> bool { auto MassManager::importMass(const std::string& staged_fn, Int hangar) -> bool {
if(hangar < 0 || hangar >= 32) { if(hangar < 0 || hangar >= 32) {
_lastError = "Hangar out of range in MassManager::importMass()"; _lastError = "Hangar out of range in MassManager::importMass()";
return false; return false;
@ -91,18 +79,22 @@ auto MassManager::importMass(const std::string& staged_fn, int hangar) -> bool {
std::string source = Utility::Directory::join(_stagingAreaDirectory, staged_fn); std::string source = Utility::Directory::join(_stagingAreaDirectory, staged_fn);
Utility::Directory::copy(source, source + ".tmp"); 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."; Mass mass{source + ".tmp"};
Utility::Directory::rm(source + ".tmp"); if(!mass.updateAccount(_steamId)) {
return false; _lastError = mass.lastError();
Utility::Directory::rm(source + ".tmp");
return false;
}
} }
if(Utility::Directory::exists(_hangars[hangar].filename())) { std::string dest = Utility::Directory::join(_saveDirectory, _hangars[hangar].filename());
Utility::Directory::rm(_hangars[hangar].filename());
if(Utility::Directory::exists(dest)) {
Utility::Directory::rm(dest);
} }
if(!Utility::Directory::move(source + ".tmp", _hangars[hangar].filename())) { if(!Utility::Directory::move(source + ".tmp", dest)) {
_lastError = Utility::formatString("Couldn't move {} to hangar {:.2d}", staged_fn, hangar + 1); _lastError = Utility::formatString("Couldn't move {} to hangar {:.2d}", staged_fn, hangar + 1);
return false; return false;
} }
@ -110,20 +102,20 @@ auto MassManager::importMass(const std::string& staged_fn, int hangar) -> bool {
return true; return true;
} }
auto MassManager::exportMass(int hangar) -> bool { auto MassManager::exportMass(Int hangar) -> bool {
if(hangar < 0 || hangar >= 32) { if(hangar < 0 || hangar >= 32) {
_lastError = "Hangar out of range in MassManager::exportMass()"; _lastError = "Hangar out of range in MassManager::exportMass()";
return false; return false;
} }
if(_hangars[hangar].state() != MassState::Valid) { if(_hangars[hangar].state() != Mass::State::Valid) {
_lastError = Utility::formatString("There is no valid data to export in hangar {:.2d}", hangar + 1); _lastError = Utility::formatString("There is no valid data to export in hangar {:.2d}", hangar + 1);
return false; return false;
} }
std::string source = Utility::Directory::join(_saveDirectory, _hangars[hangar].filename()); std::string source = Utility::Directory::join(_saveDirectory, _hangars[hangar].filename());
std::string dest = Utility::Directory::join(_stagingAreaDirectory, std::string dest = Utility::Directory::join(_stagingAreaDirectory,
Utility::formatString("{}_{}.sav", _hangars[hangar].name(), _steamId)); Utility::formatString("{}_{}.sav", *_hangars[hangar].name(), _steamId));
if(!Utility::Directory::copy(source, dest)) { if(!Utility::Directory::copy(source, dest)) {
_lastError = Utility::formatString("Couldn't export data from hangar {:.2d} to {}", hangar, dest); _lastError = Utility::formatString("Couldn't export data from hangar {:.2d} to {}", hangar, dest);
@ -133,7 +125,7 @@ auto MassManager::exportMass(int hangar) -> bool {
return true; return true;
} }
auto MassManager::moveMass(int source, int destination) -> bool { auto MassManager::moveMass(Int source, Int destination) -> bool {
if(source < 0 || source >= 32) { if(source < 0 || source >= 32) {
_lastError = "Source hangar out of range."; _lastError = "Source hangar out of range.";
return false; return false;
@ -144,38 +136,38 @@ auto MassManager::moveMass(int source, int destination) -> bool {
return false; return false;
} }
std::string source_file = _hangars[source].filename(); std::string source_file = Utility::Directory::join(_saveDirectory, _hangars[source].filename());
std::string dest_file = _hangars[destination].filename(); std::string dest_file = Utility::Directory::join(_saveDirectory, _hangars[destination].filename());
MassState dest_state = _hangars[destination].state(); Mass::State dest_state = _hangars[destination].state();
switch(dest_state) { switch(dest_state) {
case MassState::Empty: case Mass::State::Empty:
break; break;
case MassState::Invalid: case Mass::State::Invalid:
Utility::Directory::rm(dest_file); Utility::Directory::rm(dest_file);
break; break;
case MassState::Valid: case Mass::State::Valid:
Utility::Directory::move(dest_file, dest_file + ".tmp"); Utility::Directory::move(dest_file, dest_file + ".tmp");
break; break;
} }
Utility::Directory::move(source_file, dest_file); Utility::Directory::move(source_file, dest_file);
if(dest_state == MassState::Valid) { if(dest_state == Mass::State::Valid) {
Utility::Directory::move(dest_file + ".tmp", source_file); Utility::Directory::move(dest_file + ".tmp", source_file);
} }
return true; return true;
} }
auto MassManager::deleteMass(int hangar) -> bool { auto MassManager::deleteMass(Int hangar) -> bool {
if(hangar < 0 || hangar >= 32) { if(hangar < 0 || hangar >= 32) {
_lastError = "Hangar out of bounds"; _lastError = "Hangar out of range.";
return false; return false;
} }
if(!Utility::Directory::rm(_hangars[hangar].filename())) { if(!Utility::Directory::rm(Utility::Directory::join(_saveDirectory, _hangars[hangar].filename()))) {
_lastError = "Deletion failed. Maybe the file was already deleted, or it's locked by another application."; _lastError = Utility::formatString("Deletion failed: {}", std::strerror(errno));
return false; return false;
} }
@ -199,7 +191,7 @@ void MassManager::refreshStagedMasses() {
file_list.erase(iter, file_list.end()); file_list.erase(iter, file_list.end());
for(const std::string& file : file_list) { for(const std::string& file : file_list) {
std::string name = Mass::getNameFromFile(Utility::Directory::join(_stagingAreaDirectory, file)); std::string name = *Mass::getNameFromFile(Utility::Directory::join(_stagingAreaDirectory, file));
if(!name.empty()) { if(!name.empty()) {
_stagedMasses[file] = name; _stagedMasses[file] = name;
@ -214,7 +206,7 @@ auto MassManager::deleteStagedMass(const std::string& filename) -> bool {
} }
if(!Utility::Directory::rm(Utility::Directory::join(_stagingAreaDirectory, filename))) { if(!Utility::Directory::rm(Utility::Directory::join(_stagingAreaDirectory, filename))) {
_lastError = "The file " + filename + " couldn't be deleted for unknown reasons."; _lastError = Utility::formatString("{} couldn't be deleted: {}", filename, std::strerror(errno));
return false; return false;
} }

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
// MassBuilderSaveTool // MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin // Copyright (C) 2021-2022 Guillaume Jacquemin
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -31,8 +31,7 @@ class MassManager {
auto lastError() -> std::string const&; auto lastError() -> std::string const&;
auto massName(int hangar) -> std::string const&; auto hangar(int hangar) -> Mass&;
auto massState(int hangar) -> MassState;
void refreshHangar(int hangar); void refreshHangar(int hangar);

View file

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

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
// MassBuilderSaveTool // MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin // Copyright (C) 2021-2022 Guillaume Jacquemin
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -20,6 +20,10 @@
#include <Magnum/Magnum.h> #include <Magnum/Magnum.h>
#include "../UESaveFile/UESaveFile.h"
#include "ResourceIDs.h"
using namespace Magnum; using namespace Magnum;
enum class ProfileType : UnsignedByte { enum class ProfileType : UnsignedByte {
@ -27,6 +31,11 @@ enum class ProfileType : UnsignedByte {
FullGame FullGame
}; };
enum class ProfileVersion : UnsignedByte {
Legacy, // pre-0.8
Normal // 0.8 and later
};
class Profile { class Profile {
public: public:
explicit Profile(const std::string& path); explicit Profile(const std::string& path);
@ -39,148 +48,128 @@ class Profile {
auto type() const -> ProfileType; auto type() const -> ProfileType;
auto steamId() const -> std::string const&; auto version() const -> ProfileVersion;
auto account() const -> std::string const&;
void refreshValues(); void refreshValues();
auto companyName() const -> std::string const&; auto companyName() const -> std::string const&;
auto getCompanyName() -> std::string const&;
auto renameCompany(const std::string& new_name) -> bool; auto renameCompany(const std::string& new_name) -> bool;
auto activeFrameSlot() const -> Int; auto activeFrameSlot() const -> Int;
auto getActiveFrameSlot() -> Int;
auto credits() const -> Int; auto credits() const -> Int;
auto getCredits() -> Int;
auto setCredits(Int credits) -> bool; auto setCredits(Int credits) -> bool;
auto storyProgress() const -> Int; auto storyProgress() const -> Int;
auto getStoryProgress() -> Int;
auto setStoryProgress(Int progress) -> bool; auto setStoryProgress(Int progress) -> bool;
auto lastMissionId() const -> Int; auto lastMissionId() const -> Int;
auto getLastMissionId() -> Int;
auto verseSteel() const -> Int; auto verseSteel() const -> Int;
auto getVerseSteel() -> Int;
auto setVerseSteel(Int amount) -> bool; auto setVerseSteel(Int amount) -> bool;
auto undinium() const -> Int; auto undinium() const -> Int;
auto getUndinium() -> Int;
auto setUndinium(Int amount) -> bool; auto setUndinium(Int amount) -> bool;
auto necriumAlloy() const -> Int; auto necriumAlloy() const -> Int;
auto getNecriumAlloy() -> Int;
auto setNecriumAlloy(Int amount) -> bool; auto setNecriumAlloy(Int amount) -> bool;
auto lunarite() const -> Int; auto lunarite() const -> Int;
auto getLunarite() -> Int;
auto setLunarite(Int amount) -> bool; auto setLunarite(Int amount) -> bool;
auto asterite() const -> Int; auto asterite() const -> Int;
auto getAsterite() -> Int;
auto setAsterite(Int amount) -> bool; auto setAsterite(Int amount) -> bool;
auto ednil() const -> Int; auto ednil() const -> Int;
auto getEdnil() -> Int;
auto setEdnil(Int amount) -> bool; auto setEdnil(Int amount) -> bool;
auto nuflalt() const -> Int; auto nuflalt() const -> Int;
auto getNuflalt() -> Int;
auto setNuflalt(Int amount) -> bool; auto setNuflalt(Int amount) -> bool;
auto aurelene() const -> Int; auto aurelene() const -> Int;
auto getAurelene() -> Int;
auto setAurelene(Int amount) -> bool; auto setAurelene(Int amount) -> bool;
auto soldus() const -> Int; auto soldus() const -> Int;
auto getSoldus() -> Int;
auto setSoldus(Int amount) -> bool; auto setSoldus(Int amount) -> bool;
auto synthesizedN() const -> Int; auto synthesisedN() const -> Int;
auto getSynthesizedN() -> Int; auto setSynthesisedN(Int amount) -> bool;
auto setSynthesizedN(Int amount) -> bool;
auto alcarbonite() const -> Int; auto alcarbonite() const -> Int;
auto getAlcarbonite() -> Int;
auto setAlcarbonite(Int amount) -> bool; auto setAlcarbonite(Int amount) -> bool;
auto keriphene() const -> Int; auto keriphene() const -> Int;
auto getKeriphene() -> Int;
auto setKeriphene(Int amount) -> bool; auto setKeriphene(Int amount) -> bool;
auto nitinolCM() const -> Int; auto nitinolCM() const -> Int;
auto getNitinolCM() -> Int;
auto setNitinolCM(Int amount) -> bool; auto setNitinolCM(Int amount) -> bool;
auto quarkium() const -> Int; auto quarkium() const -> Int;
auto getQuarkium() -> Int;
auto setQuarkium(Int amount) -> bool; auto setQuarkium(Int amount) -> bool;
auto alterene() const -> Int; auto alterene() const -> Int;
auto getAlterene() -> Int;
auto setAlterene(Int amount) -> bool; auto setAlterene(Int amount) -> bool;
auto mixedComposition() const -> Int; auto mixedComposition() const -> Int;
auto getMixedComposition() -> Int;
auto setMixedComposition(Int amount) -> bool; auto setMixedComposition(Int amount) -> bool;
auto voidResidue() const -> Int; auto voidResidue() const -> Int;
auto getVoidResidue() -> Int;
auto setVoidResidue(Int amount) -> bool; auto setVoidResidue(Int amount) -> bool;
auto muscularConstruction() const -> Int; auto muscularConstruction() const -> Int;
auto getMuscularConstruction() -> Int;
auto setMuscularConstruction(Int amount) -> bool; auto setMuscularConstruction(Int amount) -> bool;
auto mineralExoskeletology() const -> Int; auto mineralExoskeletology() const -> Int;
auto getMineralExoskeletology() -> Int;
auto setMineralExoskeletology(Int amount) -> bool; auto setMineralExoskeletology(Int amount) -> bool;
auto carbonizedSkin() const -> Int; auto carbonisedSkin() const -> Int;
auto getCarbonizedSkin() -> Int; auto setCarbonisedSkin(Int amount) -> bool;
auto setCarbonizedSkin(Int amount) -> bool;
private: private:
std::string _profileDirectory; auto getResource(const char* container, MaterialID id) -> Int;
auto setResource(const char* container, MaterialID id, Int amount) -> bool;
std::string _filename; std::string _filename;
ProfileType _type; ProfileType _type;
ProfileVersion _version;
std::string _steamId; UESaveFile _profile;
std::string _name;
Int _activeFrameSlot = 0;
Int _credits = 0;
Int _storyProgress = 0;
Int _lastMissionId = 0;
Int _verseSteel = 0;
Int _undinium = 0;
Int _necriumAlloy = 0;
Int _lunarite = 0;
Int _asterite = 0;
Int _ednil = 0;
Int _nuflalt = 0;
Int _aurelene = 0;
Int _soldus = 0;
Int _synthesisedN = 0;
Int _alcarbonite = 0;
Int _keriphene = 0;
Int _nitinolCM = 0;
Int _quarkium = 0;
Int _alterene = 0;
Int _mixedComposition = 0;
Int _voidResidue = 0;
Int _muscularConstruction = 0;
Int _mineralExoskeletology = 0;
Int _carbonisedSkin = 0;
std::string _account;
bool _valid = false; bool _valid = false;
std::string _lastError; std::string _lastError;
std::string _companyName;
Int _activeFrameSlot = 0;
Int _credits;
Int _storyProgress;
Int _lastMissionId;
Int _verseSteel;
Int _undinium;
Int _necriumAlloy;
Int _lunarite;
Int _asterite;
Int _ednil;
Int _nuflalt;
Int _aurelene;
Int _soldus;
Int _synthesizedN;
Int _alcarbonite;
Int _keriphene;
Int _nitinolCM;
Int _quarkium;
Int _alterene;
Int _mixedComposition;
Int _voidResidue;
Int _muscularConstruction;
Int _mineralExoskeletology;
Int _carbonizedSkin;
}; };

47
src/Profile/ResourceIDs.h Normal file
View file

@ -0,0 +1,47 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <Magnum/Types.h>
using namespace Magnum;
enum MaterialID : Int {
VerseSteel = 0xC3500,
Undinium = 0xC3501,
NecriumAlloy = 0xC3502,
Lunarite = 0xC3503,
Asterite = 0xC3504,
Ednil = 0xC350A,
Nuflalt = 0xC350B,
Aurelene = 0xC350C,
Soldus = 0xC350D,
SynthesisedN = 0xC350E,
Alcarbonite = 0xC3514,
Keriphene = 0xC3515,
NitinolCM = 0xC3516,
Quarkium = 0xC3517,
Alterene = 0xC3518,
MixedComposition = 0xDBBA0,
VoidResidue = 0xDBBA1,
MuscularConstruction = 0xDBBA2,
MineralExoskeletology = 0xDBBA3,
CarbonisedSkin = 0xDBBA4
};

View file

@ -1,5 +1,5 @@
// MassBuilderSaveTool // MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin // Copyright (C) 2021-2022 Guillaume Jacquemin
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -31,8 +31,6 @@
#include "ProfileManager.h" #include "ProfileManager.h"
using namespace Corrade;
ProfileManager::ProfileManager(const std::string& save_dir, const std::string& backup_dir): ProfileManager::ProfileManager(const std::string& save_dir, const std::string& backup_dir):
_saveDirectory{save_dir}, _saveDirectory{save_dir},
_backupsDirectory{backup_dir} _backupsDirectory{backup_dir}
@ -48,12 +46,12 @@ auto ProfileManager::lastError() -> std::string const& {
return _lastError; return _lastError;
} }
auto ProfileManager::profiles() -> std::vector<Profile> const& { auto ProfileManager::profiles() -> Containers::ArrayView<Profile> {
return _profiles; return _profiles;
} }
auto ProfileManager::refreshProfiles() -> bool { auto ProfileManager::refreshProfiles() -> bool {
_profiles.clear(); _profiles = Containers::Array<Profile>{};
using Utility::Directory::Flag; using Utility::Directory::Flag;
std::vector<std::string> files = Utility::Directory::list(_saveDirectory, Flag::SkipSpecial|Flag::SkipDirectories|Flag::SkipDotAndDotDot); std::vector<std::string> files = Utility::Directory::list(_saveDirectory, Flag::SkipSpecial|Flag::SkipDirectories|Flag::SkipDotAndDotDot);
@ -74,11 +72,11 @@ auto ProfileManager::refreshProfiles() -> bool {
continue; continue;
} }
_profiles.push_back(std::move(profile)); arrayAppend(_profiles, std::move(profile));
} }
if(_profiles.empty()) { if(_profiles.empty()) {
_lastError = "No profiles were found."; _lastError = "No valid profiles were found.";
return false; return false;
} }
@ -86,14 +84,14 @@ auto ProfileManager::refreshProfiles() -> bool {
} }
auto ProfileManager::getProfile(std::size_t index) -> Profile* { auto ProfileManager::getProfile(std::size_t index) -> Profile* {
return &(_profiles.at(index)); return index <= _profiles.size() ? &(_profiles[index]) : nullptr;
} }
auto ProfileManager::deleteProfile(std::size_t index, bool delete_builds) -> bool { auto ProfileManager::deleteProfile(std::size_t index, bool delete_builds) -> bool {
if(!Utility::Directory::rm(Utility::Directory::join(_saveDirectory, _profiles.at(index).filename()))) { if(!Utility::Directory::rm(Utility::Directory::join(_saveDirectory, _profiles[index].filename()))) {
_lastError = Utility::formatString("Couldn't delete {} (filename: {}).", _lastError = Utility::formatString("Couldn't delete {} (filename: {}).",
_profiles.at(index).companyName(), _profiles[index].companyName(),
_profiles.at(index).filename()); _profiles[index].filename());
refreshProfiles(); refreshProfiles();
return false; return false;
} }
@ -101,13 +99,18 @@ auto ProfileManager::deleteProfile(std::size_t index, bool delete_builds) -> boo
if(delete_builds) { if(delete_builds) {
for(UnsignedByte i = 0; i < 32; ++i) { for(UnsignedByte i = 0; i < 32; ++i) {
std::string filename = Utility::formatString("{}Unit{:.2d}{}.sav", std::string filename = Utility::formatString("{}Unit{:.2d}{}.sav",
_profiles.at(index).type() == ProfileType::Demo ? "Demo": "", _profiles[index].type() == ProfileType::Demo ? "Demo": "",
i, _profiles.at(index).steamId()); i, _profiles[index].account());
Utility::Directory::rm(Utility::Directory::join(_saveDirectory, filename)); Utility::Directory::rm(Utility::Directory::join(_saveDirectory, filename));
} }
} }
_profiles.erase(_profiles.cbegin() + index); std::string file = _profiles[index].filename();
auto it = std::remove_if(_profiles.begin(), _profiles.end(), [&file](Profile& profile){return profile.filename() == file;});
if(it != _profiles.end()) {
arrayRemoveSuffix(_profiles, 1);
}
return true; return true;
} }
@ -117,7 +120,7 @@ auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> boo
std::tm* time = std::localtime(&timestamp); std::tm* time = std::localtime(&timestamp);
std::string filename = Utility::formatString("{}_{}{:.2d}{:.2d}_{:.2d}{:.2d}{:.2d}.mbprofbackup", std::string filename = Utility::formatString("{}_{}{:.2d}{:.2d}_{:.2d}{:.2d}{:.2d}.mbprofbackup",
Utility::String::replaceAll(_profiles.at(index).companyName(), " ", "_"), Utility::String::replaceAll(_profiles[index].companyName(), " ", "_"),
time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
time->tm_hour, time->tm_min, time->tm_sec); time->tm_hour, time->tm_min, time->tm_sec);
@ -130,21 +133,21 @@ auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> boo
return false; return false;
} }
zip_source_t* profile_source = zip_source_file(zip, Utility::Directory::toNativeSeparators(Utility::Directory::join(_saveDirectory, _profiles.at(index).filename())).c_str(), 0, 0); zip_source_t* profile_source = zip_source_file(zip, Utility::Directory::toNativeSeparators(Utility::Directory::join(_saveDirectory, _profiles[index].filename())).c_str(), 0, 0);
if(profile_source == nullptr) { if(profile_source == nullptr) {
_lastError = zip_strerror(zip); _lastError = zip_strerror(zip);
zip_source_free(profile_source); zip_source_free(profile_source);
return false; return false;
} }
if(zip_file_add(zip, _profiles.at(index).filename().c_str(), profile_source, ZIP_FL_ENC_UTF_8) == -1) { if(zip_file_add(zip, _profiles[index].filename().c_str(), profile_source, ZIP_FL_ENC_UTF_8) == -1) {
_lastError = zip_strerror(zip); _lastError = zip_strerror(zip);
zip_source_free(profile_source); zip_source_free(profile_source);
return false; return false;
} }
std::string comment = Utility::String::join({_profiles.at(index).companyName(), std::string comment = Utility::String::join({_profiles[index].companyName(),
_profiles.at(index).type() == ProfileType::Demo ? "demo" : "full", _profiles[index].type() == ProfileType::Demo ? "demo" : "full",
Utility::formatString("{}-{:.2d}-{:.2d}-{:.2d}-{:.2d}-{:.2d}", Utility::formatString("{}-{:.2d}-{:.2d}-{:.2d}-{:.2d}-{:.2d}",
time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
time->tm_hour, time->tm_min, time->tm_sec) time->tm_hour, time->tm_min, time->tm_sec)
@ -154,8 +157,8 @@ auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> boo
if(backup_builds) { if(backup_builds) {
for(UnsignedByte i = 0; i < 32; ++i) { for(UnsignedByte i = 0; i < 32; ++i) {
std::string build_filename = Utility::formatString("{}Unit{:.2d}{}.sav", std::string build_filename = Utility::formatString("{}Unit{:.2d}{}.sav",
_profiles.at(index).type() == ProfileType::Demo ? "Demo": "", _profiles[index].type() == ProfileType::Demo ? "Demo": "",
i, _profiles.at(index).steamId()); i, _profiles[index].account());
if(!Utility::Directory::exists(Utility::Directory::join(_saveDirectory, build_filename))) { if(!Utility::Directory::exists(Utility::Directory::join(_saveDirectory, build_filename))) {
continue; continue;
@ -184,12 +187,12 @@ auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> boo
return true; return true;
} }
auto ProfileManager::backups() -> std::vector<Backup> const& { auto ProfileManager::backups() -> Containers::ArrayView<Backup> {
return _backups; return _backups;
} }
void ProfileManager::refreshBackups() { void ProfileManager::refreshBackups() {
_backups.clear(); _backups = Containers::Array<Backup>{};
using Utility::Directory::Flag; using Utility::Directory::Flag;
std::vector<std::string> files = Utility::Directory::list(_backupsDirectory, Flag::SkipSpecial|Flag::SkipDirectories|Flag::SkipDotAndDotDot); std::vector<std::string> files = Utility::Directory::list(_backupsDirectory, Flag::SkipSpecial|Flag::SkipDirectories|Flag::SkipDotAndDotDot);
@ -255,29 +258,34 @@ void ProfileManager::refreshBackups() {
continue; continue;
} }
backup.includedFiles.reserve(num_entries); arrayReserve(backup.includedFiles, num_entries);
for(Long i = 0; i < num_entries; i++) { for(Long i = 0; i < num_entries; i++) {
backup.includedFiles.emplace_back(zip_get_name(zip, i, ZIP_FL_UNCHANGED)); arrayAppend(backup.includedFiles, InPlaceInit, zip_get_name(zip, i, ZIP_FL_UNCHANGED));
} }
_backups.push_back(std::move(backup)); arrayAppend(_backups, std::move(backup));
} }
} }
auto ProfileManager::deleteBackup(std::size_t index) -> bool { auto ProfileManager::deleteBackup(std::size_t index) -> bool {
if(!Utility::Directory::rm(Utility::Directory::join(_backupsDirectory, _backups.at(index).filename))) { if(!Utility::Directory::rm(Utility::Directory::join(_backupsDirectory, _backups[index].filename))) {
_lastError = "Couldn't delete " + _backups.at(index).filename; _lastError = "Couldn't delete " + _backups[index].filename;
return false; return false;
} }
_backups.erase(_backups.begin() + index); std::string file = _backups[index].filename;
auto it = std::remove_if(_backups.begin(), _backups.end(), [&file](Backup& backup){return backup.filename == file;});
if(it != _backups.end()) {
arrayRemoveSuffix(_backups, 1);
}
return true; return true;
} }
auto ProfileManager::restoreBackup(std::size_t index) -> bool { auto ProfileManager::restoreBackup(std::size_t index) -> bool {
const Backup& backup = _backups.at(index); const Backup& backup = _backups[index];
static const char* error_format = "Extraction of file {} failed: {}"; static const char* error_format = "Extraction of file {} failed: {}";

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
// MassBuilderSaveTool // MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin // Copyright (C) 2021-2022 Guillaume Jacquemin
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -17,10 +17,13 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <string> #include <string>
#include <vector>
#include <Corrade/Containers/Array.h>
#include "../Profile/Profile.h" #include "../Profile/Profile.h"
using namespace Corrade;
struct Backup { struct Backup {
std::string filename; std::string filename;
std::string company; std::string company;
@ -33,7 +36,7 @@ struct Backup {
int minute; int minute;
int second; int second;
} timestamp; } timestamp;
std::vector<std::string> includedFiles; Containers::Array<std::string> includedFiles;
}; };
class ProfileManager { class ProfileManager {
@ -43,14 +46,14 @@ class ProfileManager {
auto ready() const -> bool; auto ready() const -> bool;
auto lastError() -> std::string const&; auto lastError() -> std::string const&;
auto profiles() -> std::vector<Profile> const&; auto profiles() -> Containers::ArrayView<Profile>;
auto refreshProfiles() -> bool; auto refreshProfiles() -> bool;
auto getProfile(std::size_t index) -> Profile*; auto getProfile(std::size_t index) -> Profile*;
auto deleteProfile(std::size_t index, bool delete_builds) -> bool; auto deleteProfile(std::size_t index, bool delete_builds) -> bool;
auto backupProfile(std::size_t index, bool backup_builds) -> bool; auto backupProfile(std::size_t index, bool backup_builds) -> bool;
auto backups() -> std::vector<Backup> const&; auto backups() -> Containers::ArrayView<Backup>;
void refreshBackups(); void refreshBackups();
auto deleteBackup(std::size_t index) -> bool; auto deleteBackup(std::size_t index) -> bool;
@ -63,6 +66,6 @@ class ProfileManager {
const std::string& _saveDirectory; const std::string& _saveDirectory;
const std::string& _backupsDirectory; const std::string& _backupsDirectory;
std::vector<Profile> _profiles; Containers::Array<Profile> _profiles;
std::vector<Backup> _backups; Containers::Array<Backup> _backups;
}; };

View file

@ -1,5 +1,5 @@
// MassBuilderSaveTool // MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin // Copyright (C) 2021-2022 Guillaume Jacquemin
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -49,9 +49,6 @@
extern const ImVec2 center_pivot = {0.5f, 0.5f}; extern const ImVec2 center_pivot = {0.5f, 0.5f};
#ifdef SAVETOOL_DEBUG_BUILD #ifdef SAVETOOL_DEBUG_BUILD
#include <Corrade/Utility/Tweakable.h>
#define tw CORRADE_TWEAKABLE
Utility::Tweakable tweak; Utility::Tweakable tweak;
#endif #endif
@ -65,7 +62,7 @@ SaveTool::SaveTool(const Arguments& arguments):
#endif #endif
if(SDL_VERSION_ATLEAST(2, 0, 5)) { if(SDL_VERSION_ATLEAST(2, 0, 5)) {
if(SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1") == SDL_TRUE) { if(SDL_SetHintWithPriority(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1", SDL_HINT_OVERRIDE) == SDL_TRUE) {
Utility::Debug{} << "Clickthrough is available."; Utility::Debug{} << "Clickthrough is available.";
} }
else { else {
@ -98,6 +95,10 @@ SaveTool::SaveTool(const Arguments& arguments):
_backupsDir = Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "backups"); _backupsDir = Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "backups");
_stagingDir = Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "staging"); _stagingDir = Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "staging");
_armouryDir = Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "armoury");
_armoursDir = Utility::Directory::join(_armouryDir, "armours");
_weaponsDir = Utility::Directory::join(_armouryDir, "weapons");
_stylesDir = Utility::Directory::join(_armouryDir, "styles");
if(!Utility::Directory::exists(_backupsDir)) { if(!Utility::Directory::exists(_backupsDir)) {
Utility::Directory::mkpath(_backupsDir); Utility::Directory::mkpath(_backupsDir);
@ -107,6 +108,22 @@ SaveTool::SaveTool(const Arguments& arguments):
Utility::Directory::mkpath(_stagingDir); Utility::Directory::mkpath(_stagingDir);
} }
if(!Utility::Directory::exists(_armouryDir)) {
Utility::Directory::mkpath(_armouryDir);
}
if(!Utility::Directory::exists(_armoursDir)) {
Utility::Directory::mkpath(_armoursDir);
}
if(!Utility::Directory::exists(_weaponsDir)) {
Utility::Directory::mkpath(_weaponsDir);
}
if(!Utility::Directory::exists(_stylesDir)) {
Utility::Directory::mkpath(_stylesDir);
}
if(!findGameDataDirectory()) { if(!findGameDataDirectory()) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app", _lastError.c_str(), window()); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app", _lastError.c_str(), window());
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -206,22 +223,31 @@ void SaveTool::handleFileAction(efsw::WatchID watch_id,
return; return;
} }
static bool is_moved_after_save = false;
switch(action) { switch(action) {
case efsw::Actions::Add: case efsw::Actions::Add:
if(Utility::String::endsWith(filename, _currentProfile->steamId() + ".sav")) { if(Utility::String::endsWith(filename, _currentProfile->account() + ".sav")) {
if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) { if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) {
int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) + int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) +
(filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30); (filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30);
_massManager->refreshHangar(index); if(!_currentMass || _currentMass != &(_massManager->hangar(index))) {
_massManager->refreshHangar(index);
}
else {
_currentMass->setDirty();
}
} }
} }
break; break;
case efsw::Actions::Delete: case efsw::Actions::Delete:
if(Utility::String::endsWith(filename, _currentProfile->steamId() + ".sav")) { if(Utility::String::endsWith(filename, _currentProfile->account() + ".sav")) {
if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) { if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) {
int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) + int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) +
(filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30); (filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30);
_massManager->refreshHangar(index); if(!_currentMass || _currentMass != &(_massManager->hangar(index))) {
_massManager->refreshHangar(index);
}
} }
} }
break; break;
@ -229,20 +255,37 @@ void SaveTool::handleFileAction(efsw::WatchID watch_id,
if(filename == _currentProfile->filename()) { if(filename == _currentProfile->filename()) {
_currentProfile->refreshValues(); _currentProfile->refreshValues();
} }
else if(Utility::String::endsWith(filename, _currentProfile->steamId() + ".sav")) { else if(Utility::String::endsWith(filename, _currentProfile->account() + ".sav")) {
if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) { if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) {
int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) + int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) +
(filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30); (filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30);
_massManager->refreshHangar(index);
if(!_currentMass || _currentMass != &(_massManager->hangar(index))) {
_massManager->refreshHangar(index);
}
else {
if(!is_moved_after_save) {
is_moved_after_save = false;
_currentMass->setDirty();
}
}
} }
} }
break; break;
case efsw::Actions::Moved: case efsw::Actions::Moved:
if(Utility::String::endsWith(filename, _currentProfile->steamId() + ".sav")) { if(Utility::String::endsWith(filename, _currentProfile->account() + ".sav")) {
if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) { if(Utility::String::endsWith(old_filename, ".tmp")) {
is_moved_after_save = true;
return;
}
if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : "")) &&
Utility::String::endsWith(old_filename, ".sav"))
{
int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) + int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) +
(filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30); (filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30);
_massManager->refreshHangar(index); _massManager->refreshHangar(index);
int old_index = ((old_filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) + int old_index = ((old_filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) +
(old_filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30); (old_filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30);
_massManager->refreshHangar(old_index); _massManager->refreshHangar(old_index);
@ -376,24 +419,36 @@ void SaveTool::updateCheckEvent(SDL_Event& event) {
return ( major * 10000 + minor * 100 + patch) > return ( major * 10000 + minor * 100 + patch) >
(other.major * 10000 + other.minor * 100 + other.patch); (other.major * 10000 + other.minor * 100 + other.patch);
} }
operator std::string() const {
return Utility::formatString("{}.{}.{}", major, minor, patch);
}
}; };
static const Version current_ver{SAVETOOL_VERSION}; static const Version current_ver{SAVETOOL_VERSION};
Version latest_ver{response[0]["tag_name"]};
if(latest_ver == current_ver) { for(auto& release : response) {
_queue.addToast(Toast::Type::Success, "The application is already up to date."); if(release["prerelease"] == true) {
} continue;
else if(latest_ver > current_ver) { }
_queue.addToast(Toast::Type::Warning, "Your version is out of date.\nCheck the settings for more information.",
std::chrono::milliseconds{5000}); Version latest_ver{release["tag_name"]};
_updateAvailable = true;
_latestVersion = Utility::formatString("{}.{}.{}", latest_ver.major, latest_ver.minor, latest_ver.patch); if(latest_ver > current_ver || (latest_ver == current_ver && Utility::String::endsWith(SAVETOOL_VERSION, "-pre"))) {
_releaseLink = response[0]["html_url"]; _queue.addToast(Toast::Type::Warning, "Your version is out of date.\nCheck the settings for more information.",
_downloadLink = response[0]["assets"][0]["browser_download_url"]; std::chrono::milliseconds{5000});
} _updateAvailable = true;
else if(current_ver > latest_ver) { _latestVersion = latest_ver;
_queue.addToast(Toast::Type::Warning, "Your version is more recent than the latest one in the repo. How???"); _releaseLink = release["html_url"];
_downloadLink = release["assets"][0]["browser_download_url"];
}
else if(latest_ver == current_ver || (current_ver > latest_ver && Utility::String::endsWith(SAVETOOL_VERSION, "-pre"))) {
_queue.addToast(Toast::Type::Success, "The application is already up to date.");
}
else if(current_ver > latest_ver) {
_queue.addToast(Toast::Type::Warning, "Your version is more recent than the latest one in the repo. How???");
}
break;
} }
} }
@ -526,10 +581,8 @@ auto SaveTool::findGameDataDirectory() -> bool {
} }
void SaveTool::initialiseMassManager() { void SaveTool::initialiseMassManager() {
_currentProfile->refreshValues();
_massManager.emplace(_saveDir, _massManager.emplace(_saveDir,
_currentProfile->steamId(), _currentProfile->account(),
_currentProfile->type() == ProfileType::Demo, _currentProfile->type() == ProfileType::Demo,
_stagingDir); _stagingDir);
@ -576,6 +629,9 @@ void SaveTool::drawGui() {
case UiState::MainManager: case UiState::MainManager:
drawManager(); drawManager();
break; break;
case UiState::MassViewer:
drawMassViewer();
break;
} }
if(_aboutPopup) { if(_aboutPopup) {
@ -712,7 +768,7 @@ void SaveTool::drawTooltip(const char* text, Float wrap_pos) {
} }
void SaveTool::openUri(const std::string& uri) { void SaveTool::openUri(const std::string& uri) {
ShellExecuteW(nullptr, nullptr, Utility::Unicode::widen(uri).c_str(), nullptr, nullptr, SW_SHOW); ShellExecuteW(nullptr, nullptr, Utility::Unicode::widen(uri).c_str(), nullptr, nullptr, SW_SHOWDEFAULT);
} }
void SaveTool::checkGameState() { void SaveTool::checkGameState() {
@ -738,8 +794,7 @@ void SaveTool::checkGameState() {
} }
void SaveTool::checkForUpdates() { void SaveTool::checkForUpdates() {
cpr::Response r = cpr::Get(cpr::Url{"https://williamjcm.ovh/git/api/v1/repos/williamjcm/MassBuilderSaveTool/releases"}, cpr::Response r = cpr::Get(cpr::Url{"https://williamjcm.ovh/git/api/v1/repos/williamjcm/MassBuilderSaveTool/releases"}, cpr::Timeout{10000});
cpr::Parameters{{"limit", "1"}}, cpr::Timeout{10000});
SDL_Event event; SDL_Event event;
SDL_zero(event); SDL_zero(event);

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
// MassBuilderSaveTool // MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin // Copyright (C) 2021-2022 Guillaume Jacquemin
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -25,7 +25,7 @@
#include <Magnum/Platform/Sdl2Application.h> #include <Magnum/Platform/Sdl2Application.h>
#include <Magnum/ImGuiIntegration/Context.h> #include <Magnum/ImGuiIntegration/Context.h>
#include <SDL2/SDL.h> #include <SDL.h>
#include <imgui.h> #include <imgui.h>
#include <imgui_internal.h> #include <imgui_internal.h>
@ -36,6 +36,12 @@
#include "../MassManager/MassManager.h" #include "../MassManager/MassManager.h"
#include "../ToastQueue/ToastQueue.h" #include "../ToastQueue/ToastQueue.h"
#ifdef SAVETOOL_DEBUG_BUILD
#include <Corrade/Utility/Tweakable.h>
#define tw CORRADE_TWEAKABLE
#endif
using namespace Corrade; using namespace Corrade;
using namespace Magnum; using namespace Magnum;
@ -88,18 +94,48 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
void drawMainMenu(); void drawMainMenu();
void drawDisclaimer(); void drawDisclaimer();
void drawInitialisation(); void drawInitialisation();
void drawProfileManager(); void drawProfileManager();
auto drawBackupListPopup() -> ImGuiID; auto drawBackupListPopup() -> ImGuiID;
auto drawBackupProfilePopup(std::size_t profile_index) -> ImGuiID; auto drawBackupProfilePopup(std::size_t profile_index) -> ImGuiID;
auto drawDeleteProfilePopup(std::size_t profile_index) -> ImGuiID; auto drawDeleteProfilePopup(std::size_t profile_index) -> ImGuiID;
void drawManager(); void drawManager();
auto drawIntEditPopup(int* value_to_edit, int max) -> bool; auto drawIntEditPopup(int* value_to_edit, int max) -> bool;
auto drawRenamePopup(Containers::ArrayView<char> name_view) -> bool; auto drawRenamePopup(Containers::ArrayView<char> name_view) -> bool;
void drawGeneralInfo(); void drawGeneralInfo();
void drawResearchInventory(); void drawResearchInventory();
template<typename Getter, typename Setter>
void drawMaterialRow(const char* name, Int tier, Getter getter, Setter setter);
void drawUnavailableMaterialRow(const char* name, Int tier);
void drawMassManager(); void drawMassManager();
auto drawDeleteMassPopup(int mass_index) -> ImGuiID; auto drawDeleteMassPopup(int mass_index) -> ImGuiID;
auto drawDeleteStagedMassPopup(const std::string& filename) -> ImGuiID; auto drawDeleteStagedMassPopup(const std::string& filename) -> ImGuiID;
void drawMassViewer();
void drawFrameInfo();
void drawJointSliders();
void drawFrameStyles();
void drawEyeColourPicker();
void drawCustomFrameStyles();
void drawArmour();
void drawCustomArmourStyles();
void drawWeapons();
void drawWeaponCategory(const char* name, Containers::ArrayView<Weapon> weapons_view, bool& dirty, const char* payload_type, const char* payload_tooltip);
void drawWeaponEditor(Weapon& weapon);
void drawGlobalStyles();
void drawTuning();
void drawDecalEditor(Decal& decal);
void drawAccessoryEditor(Accessory& accessory, Containers::ArrayView<CustomStyle> style_view);
auto getStyleName(Int id, Containers::ArrayView<CustomStyle> view) -> const char*;
enum DCSResult {
DCS_Fail,
DCS_ResetStyle,
DCS_Save
};
auto drawCustomStyle(CustomStyle& style) -> DCSResult;
void drawAbout(); void drawAbout();
void drawGameState(); void drawGameState();
@ -136,6 +172,12 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
} }
} }
template<typename... Args>
void drawAlignedText(const char* text, Args... args) {
ImGui::AlignTextToFramePadding();
ImGui::Text(text, std::forward<Args>(args)...);
}
void openUri(const std::string& uri); void openUri(const std::string& uri);
void checkGameState(); void checkGameState();
@ -152,7 +194,8 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
Disclaimer, Disclaimer,
Initialising, Initialising,
ProfileManager, ProfileManager,
MainManager MainManager,
MassViewer
} _uiState{UiState::Disclaimer}; } _uiState{UiState::Disclaimer};
bool _aboutPopup{false}; bool _aboutPopup{false};
@ -179,6 +222,10 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
std::string _backupsDir; std::string _backupsDir;
std::string _stagingDir; std::string _stagingDir;
std::string _armouryDir;
std::string _armoursDir;
std::string _weaponsDir;
std::string _stylesDir;
enum class GameState : UnsignedByte { enum class GameState : UnsignedByte {
Unknown, NotRunning, Running Unknown, NotRunning, Running
@ -190,6 +237,9 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
Profile* _currentProfile{nullptr}; Profile* _currentProfile{nullptr};
Containers::Pointer<MassManager> _massManager; Containers::Pointer<MassManager> _massManager;
Mass* _currentMass{nullptr};
Weapon* _currentWeapon = nullptr;
Containers::Pointer<efsw::FileWatcher> _fileWatcher; Containers::Pointer<efsw::FileWatcher> _fileWatcher;
enum watchID { enum watchID {
@ -214,5 +264,20 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
std::string _releaseLink; std::string _releaseLink;
std::string _downloadLink; std::string _downloadLink;
bool _jointsDirty{false};
bool _stylesDirty{false};
bool _eyeFlareDirty{false};
Containers::StaticArray<38, Int> _selectedArmourDecals{ValueInit};
Containers::StaticArray<38, Int> _selectedArmourAccessories{ValueInit};
Int _selectedWeaponPart{0};
Int _selectedWeaponDecal{0};
Int _selectedWeaponAccessory{0};
bool _meleeDirty{false};
bool _shieldsDirty{false};
bool _bShootersDirty{false};
bool _eShootersDirty{false};
bool _bLaunchersDirty{false};
bool _eLaunchersDirty{false};
bool _cheatMode{false}; bool _cheatMode{false};
}; };

View file

@ -1,5 +1,5 @@
// MassBuilderSaveTool // MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin // Copyright (C) 2021-2022 Guillaume Jacquemin
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -42,10 +42,9 @@ void SaveTool::drawManager() {
return; return;
} }
ImGui::AlignTextToFramePadding(); drawAlignedText("Current profile: %s (%s)",
ImGui::Text("Current profile: %s (%s)", _currentProfile->companyName().c_str(),
_currentProfile->companyName().c_str(), _currentProfile->type() == ProfileType::Demo ? "demo" : "full game");
_currentProfile->type() == ProfileType::Demo ? "demo" : "full game");
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button(ICON_FA_ARROW_LEFT " Back to profile manager")) { if(ImGui::Button(ICON_FA_ARROW_LEFT " Back to profile manager")) {
_currentProfile = nullptr; _currentProfile = nullptr;
@ -107,8 +106,8 @@ auto SaveTool::drawIntEditPopup(int* value_to_edit, int max) -> bool {
drawHelpMarker("You can either drag the widget left or right to change the value,\n" 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."); "or click on it while holding Ctrl to edit the value directly.");
ImGui::SameLine(); ImGui::SameLine();
drawUnsafeWidget([](auto... args){ return ImGui::DragInt("", args...); }, drawUnsafeWidget([](auto... args){ return ImGui::SliderInt("", args...); },
value_to_edit, 1.0f, 0, max, "%d", ImGuiSliderFlags_AlwaysClamp); value_to_edit, 0, max, "%d", ImGuiSliderFlags_AlwaysClamp);
ImGui::SameLine(); ImGui::SameLine();
if(drawUnsafeWidget([]{ return ImGui::Button("Apply"); })) { if(drawUnsafeWidget([]{ return ImGui::Button("Apply"); })) {
apply = true; apply = true;
@ -188,7 +187,7 @@ void SaveTool::drawGeneralInfo() {
{ {
ImGui::TextUnformatted("Story progress:"); ImGui::TextUnformatted("Story progress:");
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.x / 4.0f); ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.x / 4.0f);
if(std::strcmp(it->after, "") == 0) { if(!it->after) {
ImGui::TextWrapped("%s - %s", it->chapter, it->point); ImGui::TextWrapped("%s - %s", it->chapter, it->point);
} }
else { else {
@ -226,8 +225,7 @@ void SaveTool::drawGeneralInfo() {
} }
if(drawRenamePopup(name_buf)) { if(drawRenamePopup(name_buf)) {
if(!_currentProfile->renameCompany(name_buf.data())) { if(!_currentProfile->renameCompany(name_buf.data())) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", _queue.addToast(Toast::Type::Error, _currentProfile->lastError());
_currentProfile->lastError().c_str(), window());
} }
} }
@ -244,8 +242,7 @@ void SaveTool::drawGeneralInfo() {
} }
if(drawIntEditPopup(&credits, 20000000)) { if(drawIntEditPopup(&credits, 20000000)) {
if(!_currentProfile->setCredits(credits)) { if(!_currentProfile->setCredits(credits)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", _queue.addToast(Toast::Type::Error, _currentProfile->lastError());
_currentProfile->lastError().c_str(), window());
} }
} }
@ -261,11 +258,10 @@ void SaveTool::drawGeneralInfo() {
} }
for(const auto& sp : story_progress) { for(const auto& sp : story_progress) {
if(ImGui::BeginMenu(sp.chapter)) { if(ImGui::BeginMenu(sp.chapter)) {
if(std::strcmp(sp.after, "") == 0) { if(!sp.after) {
if(ImGui::MenuItem(sp.point)) { if(ImGui::MenuItem(sp.point)) {
if(!_currentProfile->setStoryProgress(sp.id)) { if(!_currentProfile->setStoryProgress(sp.id)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", _queue.addToast(Toast::Type::Error, _currentProfile->lastError());
_currentProfile->lastError().c_str(), window());
} }
} }
} }
@ -273,8 +269,7 @@ void SaveTool::drawGeneralInfo() {
if(ImGui::BeginMenu(sp.after)) { if(ImGui::BeginMenu(sp.after)) {
if(ImGui::MenuItem(sp.point)) { if(ImGui::MenuItem(sp.point)) {
if(!_currentProfile->setStoryProgress(sp.id)) { if(!_currentProfile->setStoryProgress(sp.id)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", _queue.addToast(Toast::Type::Error, _currentProfile->lastError());
_currentProfile->lastError().c_str(), window());
} }
} }
ImGui::EndMenu(); ImGui::EndMenu();
@ -292,45 +287,6 @@ void SaveTool::drawResearchInventory() {
return; return;
} }
#define unavRow(name, tier) \
ImGui::TableNextRow(); \
ImGui::TableSetColumnIndex(0); \
ImGui::TextUnformatted("T" #tier); \
ImGui::TableSetColumnIndex(1); \
ImGui::TextUnformatted(name); \
ImGui::TableSetColumnIndex(2); \
ImGui::TextDisabled("Unavailable as of game version " SUPPORTED_GAME_VERSION);
#define matRow(name, tier, var, getter, setter) \
ImGui::TableNextRow(); \
ImGui::TableSetColumnIndex(0); \
ImGui::TextUnformatted("T" #tier); \
ImGui::TableSetColumnIndex(1); \
ImGui::TextUnformatted(name); \
ImGui::TableSetColumnIndex(2); \
if(_currentProfile->getter() != -1) { \
ImGui::Text("%i", _currentProfile->getter()); \
if(_cheatMode) { \
ImGui::TableSetColumnIndex(3); \
ImGui::PushID(#setter); \
static Int var = _currentProfile->getter(); \
if(drawUnsafeWidget([]{ return ImGui::SmallButton(ICON_FA_EDIT); })) { \
(var) = _currentProfile->getter(); \
ImGui::OpenPopup("int_edit"); \
} \
if(drawIntEditPopup(&(var), 9999)) { \
if(!_currentProfile->set##setter((var))) { \
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", \
_currentProfile->lastError().c_str(), window()); \
} \
} \
ImGui::PopID(); \
} \
} \
else { \
ImGui::TextDisabled("Not found in the save file"); \
}
if(ImGui::BeginTable("##ResearchInventoryTable", 4, if(ImGui::BeginTable("##ResearchInventoryTable", 4,
ImGuiTableFlags_BordersOuter|ImGuiTableFlags_ScrollY|ImGuiTableFlags_BordersInnerH)) ImGuiTableFlags_BordersOuter|ImGuiTableFlags_ScrollY|ImGuiTableFlags_BordersInnerH))
{ {
@ -343,55 +299,136 @@ void SaveTool::drawResearchInventory() {
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
ImGui::Text("Engine materials"); ImGui::Text("Engine materials");
matRow("Verse steel", 1, verse_steel, verseSteel, VerseSteel) drawMaterialRow("Verse steel", 1,
matRow("Undinium", 2, undinium, undinium, Undinium) [this]{ return _currentProfile->verseSteel(); },
matRow("Necrium alloy", 3, necrium_alloy, necriumAlloy, NecriumAlloy) [this](Int amount){ return _currentProfile->setVerseSteel(amount); });
matRow("Lunarite", 4, lunarite, lunarite, Lunarite) drawMaterialRow("Undinium", 2,
matRow("Asterite", 5, asterite, asterite, Asterite) [this]{ return _currentProfile->undinium(); },
unavRow("Hallite fragma", 6) [this](Int amount){ return _currentProfile->setUndinium(amount); });
unavRow("Unnoctinium", 7) drawMaterialRow("Necrium alloy", 3,
[this]{ return _currentProfile->necriumAlloy(); },
[this](Int amount){ return _currentProfile->setNecriumAlloy(amount); });
drawMaterialRow("Lunarite", 4,
[this]{ return _currentProfile->lunarite(); },
[this](Int amount){ return _currentProfile->setLunarite(amount); });
drawMaterialRow("Asterite", 5,
[this]{ return _currentProfile->asterite(); },
[this](Int amount){ return _currentProfile->setAsterite(amount); });
drawUnavailableMaterialRow("Hallite fragma", 6);
drawUnavailableMaterialRow("Unnoctinium", 7);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
ImGui::Text("OS materials"); ImGui::Text("OS materials");
matRow("Ednil", 1, ednil, ednil, Ednil) drawMaterialRow("Ednil", 1,
matRow("Nuflalt", 2, nuflalt, nuflalt, Nuflalt) [this]{ return _currentProfile->ednil(); },
matRow("Aurelene", 3, aurelene, aurelene, Aurelene) [this](Int amount){ return _currentProfile->setEdnil(amount); });
matRow("Soldus", 4, soldus, soldus, Soldus) drawMaterialRow("Nuflalt", 2,
matRow("Synthesized N", 5, synthesized_n, synthesizedN, SynthesizedN) [this]{ return _currentProfile->nuflalt(); },
unavRow("Nanoc", 6) [this](Int amount){ return _currentProfile->setNuflalt(amount); });
unavRow("Abyssillite", 7) drawMaterialRow("Aurelene", 3,
[this]{ return _currentProfile->aurelene(); },
[this](Int amount){ return _currentProfile->setAurelene(amount); });
drawMaterialRow("Soldus", 4,
[this]{ return _currentProfile->soldus(); },
[this](Int amount){ return _currentProfile->setSoldus(amount); });
drawMaterialRow("Synthesized N", 5,
[this]{ return _currentProfile->synthesisedN(); },
[this](Int amount){ return _currentProfile->setSynthesisedN(amount); });
drawUnavailableMaterialRow("Nanoc", 6);
drawUnavailableMaterialRow("Abyssillite", 7);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
ImGui::Text("Architect materials"); ImGui::Text("Architect materials");
matRow("Alcarbonite", 1, alcarbonite, alcarbonite, Alcarbonite) drawMaterialRow("Alcarbonite", 1,
matRow("Keriphene", 2, keriphene, keriphene, Keriphene) [this]{ return _currentProfile->alcarbonite(); },
matRow("Nitinol-CM", 3, nitinol_cm, nitinolCM, NitinolCM) [this](Int amount){ return _currentProfile->setAlcarbonite(amount); });
matRow("Quarkium", 4, quarkium, quarkium, Quarkium) drawMaterialRow("Keripehene", 2,
matRow("Alterene", 5, alterene, alterene, Alterene) [this]{ return _currentProfile->keriphene(); },
unavRow("Cosmium", 6) [this](Int amount){ return _currentProfile->setKeriphene(amount); });
unavRow("Purified quarkium", 7) drawMaterialRow("Nitinol-CM", 3,
[this]{ return _currentProfile->nitinolCM(); },
[this](Int amount){ return _currentProfile->setNitinolCM(amount); });
drawMaterialRow("Quarkium", 4,
[this]{ return _currentProfile->quarkium(); },
[this](Int amount){ return _currentProfile->setQuarkium(amount); });
drawMaterialRow("Alterene", 5,
[this]{ return _currentProfile->alterene(); },
[this](Int amount){ return _currentProfile->setAlterene(amount); });
drawUnavailableMaterialRow("Cosmium", 6);
drawUnavailableMaterialRow("Purified quarkium", 7);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
ImGui::Text("Quark data"); ImGui::Text("Quark data");
matRow("Mixed composition", 1, mixed_composition, mixedComposition, MixedComposition) drawMaterialRow("Mixed composition", 1,
matRow("Void residue", 2, void_residue, voidResidue, VoidResidue) [this]{ return _currentProfile->mixedComposition(); },
matRow("Muscular construction", 3, muscular_construction, muscularConstruction, MuscularConstruction) [this](Int amount){ return _currentProfile->setMixedComposition(amount); });
matRow("Mineral exoskeletology", 4, mineral_exoskeletology, mineralExoskeletology, MineralExoskeletology) drawMaterialRow("Void residue", 2,
matRow("Carbonized skin", 5, carbonized_skin, carbonizedSkin, CarbonizedSkin) [this]{ return _currentProfile->voidResidue(); },
unavRow("Isolated void particle", 6) [this](Int amount){ return _currentProfile->setVoidResidue(amount); });
unavRow("Weaponised physiology", 7) drawMaterialRow("Muscular construction", 3,
[this]{ return _currentProfile->muscularConstruction(); },
[this](Int amount){ return _currentProfile->setMuscularConstruction(amount); });
drawMaterialRow("Mineral exoskeletology", 4,
[this]{ return _currentProfile->mineralExoskeletology(); },
[this](Int amount){ return _currentProfile->setMineralExoskeletology(amount); });
drawMaterialRow("Carbonized skin", 5,
[this]{ return _currentProfile->carbonisedSkin(); },
[this](Int amount){ return _currentProfile->setCarbonisedSkin(amount); });
drawUnavailableMaterialRow("Isolated void particle", 6);
drawUnavailableMaterialRow("Weaponised physiology", 7);
ImGui::EndTable(); ImGui::EndTable();
} }
}
#undef unavRow template<typename Getter, typename Setter>
#undef matRow void SaveTool::drawMaterialRow(const char* name, Int tier, Getter getter, Setter setter) {
static_assert(std::is_same<decltype(getter()), Int>::value, "getter doesn't return an Int, and/or doesn't take zero arguments.");
static_assert(std::is_same<decltype(setter(0)), bool>::value, "setter doesn't return a bool, and/or doesn't take a single Int as an argument.");
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::Text("T%i", tier);
ImGui::TableSetColumnIndex(1);
ImGui::TextUnformatted(name);
ImGui::TableSetColumnIndex(2);
if(getter() != -1) {
ImGui::Text("%i", getter());
if(_cheatMode) {
ImGui::TableSetColumnIndex(3);
ImGui::PushID(name);
static Int var = 0;
if(drawUnsafeWidget(ImGui::SmallButton, ICON_FA_EDIT)) {
(var) = getter();
ImGui::OpenPopup("int_edit");
}
if(drawIntEditPopup(&(var), 9999)) {
if(!setter(var)) {
_queue.addToast(Toast::Type::Error, _currentProfile->lastError());
}
}
ImGui::PopID();
}
}
else {
ImGui::TextDisabled("Not found in the save file");
}
}
void SaveTool::drawUnavailableMaterialRow(const char* name, Int tier) {
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::Text("T%i", tier);
ImGui::TableSetColumnIndex(1);
ImGui::TextUnformatted(name);
ImGui::TableSetColumnIndex(2);
ImGui::TextDisabled("Unavailable as of game version " SUPPORTED_GAME_VERSION);
} }
void SaveTool::drawMassManager() { void SaveTool::drawMassManager() {
@ -409,7 +446,7 @@ void SaveTool::drawMassManager() {
ImGui::TableSetupColumn("##Hangar", ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("##Hangar", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("##MASSName", ImGuiTableColumnFlags_WidthStretch); ImGui::TableSetupColumn("##MASSName", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##Active", ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("##Active", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("##DeleteButton", ImGuiTableColumnFlags_WidthFixed); ImGui::TableSetupColumn("##Buttons", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupScrollFreeze(0, 1); ImGui::TableSetupScrollFreeze(0, 1);
@ -429,17 +466,17 @@ void SaveTool::drawMassManager() {
ImGui::TableSetColumnIndex(0); ImGui::TableSetColumnIndex(0);
ImGui::Selectable(Utility::formatString("{:.2d}", i + 1).c_str(), ImGui::Selectable(Utility::formatString("{:.2d}", i + 1).c_str(),
false, ImGuiSelectableFlags_SpanAllColumns|ImGuiSelectableFlags_AllowItemOverlap); false, ImGuiSelectableFlags_SpanAllColumns|ImGuiSelectableFlags_AllowItemOverlap);
if(_massManager->massState(i) == MassState::Valid && if(_massManager->hangar(i).state() == Mass::State::Valid &&
ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoHoldToOpenOthers))
{ {
drag_drop_index = i; drag_drop_index = i;
ImGui::SetDragDropPayload("Mass", &drag_drop_index, sizeof(int)); ImGui::SetDragDropPayload("Mass", &drag_drop_index, sizeof(int));
ImGui::Text("%s - Hangar %.2d", _massManager->massName(i).c_str(), i + 1); ImGui::Text("%s - Hangar %.2d", (*_massManager->hangar(i).name()).c_str(), i + 1);
ImGui::EndDragDropSource(); ImGui::EndDragDropSource();
} }
if((!_unsafeMode && _gameState == GameState::NotRunning) && ImGui::BeginDragDropTarget()) { if((_unsafeMode || _gameState == GameState::NotRunning) && ImGui::BeginDragDropTarget()) {
if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("StagedMass")) { if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("StagedMass")) {
if(payload->DataSize != sizeof(std::string)) { if(payload->DataSize != sizeof(std::string)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error", SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error",
@ -451,11 +488,10 @@ void SaveTool::drawMassManager() {
std::string file = *(static_cast<std::string*>(payload->Data)); std::string file = *(static_cast<std::string*>(payload->Data));
if(!_massManager->importMass(file, i)) { if(!_massManager->importMass(file, i)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error importing M.A.S.S.", _queue.addToast(Toast::Type::Error, _massManager->lastError());
_massManager->lastError().c_str(), window());
} }
} }
else if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("Mass")) { else if((payload = ImGui::AcceptDragDropPayload("Mass"))) {
if(payload->DataSize != sizeof(int)) { if(payload->DataSize != sizeof(int)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error", SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error",
"payload->DataSize != sizeof(int) in SaveTool::drawMassManager()", "payload->DataSize != sizeof(int) in SaveTool::drawMassManager()",
@ -466,9 +502,7 @@ void SaveTool::drawMassManager() {
int index = *(static_cast<int*>(payload->Data)); int index = *(static_cast<int*>(payload->Data));
if(!_massManager->moveMass(index, i)) { if(!_massManager->moveMass(index, i)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", _queue.addToast(Toast::Type::Error, _massManager->lastError());
_massManager->lastError().c_str(),
window());
} }
} }
@ -476,15 +510,15 @@ void SaveTool::drawMassManager() {
} }
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
switch(_massManager->massState(i)) { switch(_massManager->hangar(i).state()) {
case MassState::Empty: case Mass::State::Empty:
ImGui::TextDisabled("<empty>"); ImGui::TextDisabled("<empty>");
break; break;
case MassState::Invalid: case Mass::State::Invalid:
ImGui::TextDisabled("<invalid>"); ImGui::TextDisabled("<invalid>");
break; break;
case MassState::Valid: case Mass::State::Valid:
ImGui::TextUnformatted(_massManager->massName(i).c_str()); ImGui::TextUnformatted((*_massManager->hangar(i).name()).c_str());
break; break;
} }
@ -494,9 +528,21 @@ void SaveTool::drawMassManager() {
drawTooltip("This is the currently active frame slot."); drawTooltip("This is the currently active frame slot.");
} }
if(_massManager->massState(i) != MassState::Empty) { if(_massManager->hangar(i).state() != Mass::State::Empty) {
ImGui::TableSetColumnIndex(3); ImGui::TableSetColumnIndex(3);
ImGui::PushID(i); ImGui::PushID(i);
if(_massManager->hangar(i).state() == Mass::State::Valid) {
if(ImGui::SmallButton(ICON_FA_SEARCH)) {
_currentMass = &_massManager->hangar(i);
_uiState = UiState::MassViewer;
}
ImGui::SameLine(0.0f, 2.0f);
}
else{
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.0f);
ImGui::SmallButton(ICON_FA_SEARCH);
ImGui::PopStyleVar();
}
if(drawUnsafeWidget(ImGui::SmallButton, ICON_FA_TRASH_ALT)) { if(drawUnsafeWidget(ImGui::SmallButton, ICON_FA_TRASH_ALT)) {
mass_to_delete = i; mass_to_delete = i;
ImGui::OpenPopup(mass_deletion_popup_ID); ImGui::OpenPopup(mass_deletion_popup_ID);
@ -569,9 +615,7 @@ void SaveTool::drawMassManager() {
int index = *(static_cast<int*>(payload->Data)); int index = *(static_cast<int*>(payload->Data));
if(!_massManager->exportMass(index)) { if(!_massManager->exportMass(index)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", _queue.addToast(Toast::Type::Error, _massManager->lastError());
_massManager->lastError().c_str(),
window());
} }
} }
@ -588,7 +632,7 @@ auto SaveTool::drawDeleteMassPopup(int mass_index) -> ImGuiID {
return ImGui::GetID("Confirmation##DeleteMassConfirmation"); return ImGui::GetID("Confirmation##DeleteMassConfirmation");
} }
if(_massManager->massState(mass_index) == MassState::Empty) { if(_massManager->hangar(mass_index).state() == Mass::State::Empty) {
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
ImGui::EndPopup(); ImGui::EndPopup();
return 0; return 0;
@ -601,13 +645,13 @@ auto SaveTool::drawDeleteMassPopup(int mass_index) -> ImGuiID {
} }
ImGui::PushTextWrapPos(windowSize().x() * 0.40f); ImGui::PushTextWrapPos(windowSize().x() * 0.40f);
if(_massManager->massState(mass_index) == MassState::Invalid) { if(_massManager->hangar(mass_index).state() == Mass::State::Invalid) {
ImGui::Text("Are you sure you want to delete the invalid M.A.S.S. data in hangar %.2i ? This operation is irreversible.", 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); mass_index + 1);
} }
else { else {
ImGui::Text("Are you sure you want to delete the M.A.S.S. named %s in hangar %.2i ? This operation is irreversible.", ImGui::Text("Are you sure you want to delete the M.A.S.S. named %s in hangar %.2i ? This operation is irreversible.",
_massManager->massName(mass_index).c_str(), mass_index + 1); (*_massManager->hangar(mass_index).name()).c_str(), mass_index + 1);
} }
ImGui::PopTextWrapPos(); ImGui::PopTextWrapPos();
@ -620,8 +664,7 @@ auto SaveTool::drawDeleteMassPopup(int mass_index) -> ImGuiID {
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
if(ImGui::Button("Yes")) { if(ImGui::Button("Yes")) {
if(!_massManager->deleteMass(mass_index)) { if(!_massManager->deleteMass(mass_index)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when deleting M.A.S.S.", _queue.addToast(Toast::Type::Error, _massManager->lastError());
_massManager->lastError().c_str(), window());
} }
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
@ -659,8 +702,7 @@ auto SaveTool::drawDeleteStagedMassPopup(const std::string& filename) -> ImGuiID
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
if(ImGui::Button("Yes")) { if(ImGui::Button("Yes")) {
if(!_massManager->deleteStagedMass(filename)) { if(!_massManager->deleteStagedMass(filename)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when deleting M.A.S.S.", _queue.addToast(Toast::Type::Error, _massManager->lastError());
_massManager->lastError().c_str(), window());
} }
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }

View file

@ -0,0 +1,607 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <Corrade/Containers/ScopeGuard.h>
#include <Magnum/ImGuiIntegration/Integration.h>
#include "../Maps/Accessories.h"
#define STYLENAMES_DEFINITION
#include "../Maps/StyleNames.h"
#include "../FontAwesome/IconsFontAwesome5.h"
#include "SaveTool.h"
void SaveTool::drawMassViewer() {
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
_currentMass = nullptr;
_currentWeapon = nullptr;
_uiState = UiState::MainManager;
_queue.addToast(Toast::Type::Error, "The selected M.A.S.S. isn't valid anymore.");
return;
}
ImGui::SetNextWindowPos({0.0f, ImGui::GetItemRectSize().y}, ImGuiCond_Always);
ImGui::SetNextWindowSize({Float(windowSize().x()), Float(windowSize().y()) - ImGui::GetItemRectSize().y},
ImGuiCond_Always);
if(!ImGui::Begin("##MassViewer", nullptr,
ImGuiWindowFlags_NoDecoration|ImGuiWindowFlags_NoMove|
ImGuiWindowFlags_NoBackground|ImGuiWindowFlags_NoBringToFrontOnFocus))
{
ImGui::End();
return;
}
if(ImGui::BeginChild("##MassInfo",
{0.0f, 0.0f},
true, ImGuiWindowFlags_MenuBar))
{
if(ImGui::BeginMenuBar()) {
if(ImGui::BeginTable("##MassViewerMenuTable", 4)) {
ImGui::TableSetupColumn("##MassName");
ImGui::TableSetupColumn("##Spacer", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##Updates");
ImGui::TableSetupColumn("##Close", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
ImGui::Text("M.A.S.S.: %s", (*_currentMass->name()).c_str());
drawTooltip(_currentMass->filename().c_str());
ImGui::TableSetColumnIndex(2);
if(_currentMass->dirty()) {
ImGui::TextUnformatted("External changes detected");
ImGui::SameLine();
if(ImGui::SmallButton(ICON_FA_SYNC_ALT " Refresh")) {
_currentMass->refreshValues();
_currentMass->setDirty(false);
_jointsDirty = false;
_stylesDirty = false;
_eyeFlareDirty = false;
}
}
ImGui::TableSetColumnIndex(3);
if(ImGui::SmallButton(ICON_FA_TIMES)) {
_currentWeapon = nullptr;
_currentMass = nullptr;
_uiState = UiState::MainManager;
_jointsDirty = false;
_stylesDirty = false;
_eyeFlareDirty = false;
_selectedArmourDecals = Containers::StaticArray<38, Int>{ValueInit};
_selectedArmourAccessories = Containers::StaticArray<38, Int>{ValueInit};
_selectedWeaponPart = 0;
_selectedWeaponDecal = 0;
_selectedWeaponAccessory = 0;
};
ImGui::EndTable();
}
ImGui::EndMenuBar();
}
ImGui::TextColored(ImColor(255, 255, 0), ICON_FA_EXCLAMATION_TRIANGLE);
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::TextWrapped("WARNING: Colours in this app may look different from in-game colours, due to unavoidable differences in the rendering pipeline.");
ImGui::TextColored(ImColor(255, 255, 0), ICON_FA_EXCLAMATION_TRIANGLE);
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::TextWrapped("Real-time updates are disabled on this screen.");
if(_currentMass) {
if(ImGui::BeginTabBar("##MassTabBar")) {
if(ImGui::BeginTabItem("Frame")) {
drawFrameInfo();
ImGui::EndTabItem();
}
if(ImGui::BeginTabItem("Custom frame styles")) {
drawCustomFrameStyles();
ImGui::EndTabItem();
}
if(ImGui::BeginTabItem("Armour parts")) {
drawArmour();
ImGui::EndTabItem();
}
if(ImGui::BeginTabItem("Custom armour styles")) {
drawCustomArmourStyles();
ImGui::EndTabItem();
}
if(ImGui::BeginTabItem("Weapons (WIP)")) {
drawWeapons();
ImGui::EndTabItem();
}
if(_currentMass->globalStyles().size() != 0 && ImGui::BeginTabItem("Global styles")) {
drawGlobalStyles();
ImGui::EndTabItem();
}
if(ImGui::BeginTabItem("Tuning (WIP)")) {
drawTuning();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
}
}
ImGui::EndChild();
ImGui::End();
}
void SaveTool::drawGlobalStyles() {
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
return;
}
if(!ImGui::BeginChild("##GlobalStyles")) {
ImGui::EndChild();
return;
}
ImGui::TextWrapped("In-game values are multiplied by 100. For example, 0.500 here is equal to 50 in-game.");
for(UnsignedInt i = 0; i < _currentMass->globalStyles().size(); i++) {
ImGui::PushID(i);
DCSResult result;
result = drawCustomStyle(_currentMass->globalStyles()[i]);
switch(result) {
case DCS_ResetStyle:
_currentMass->getGlobalStyles();
break;
case DCS_Save:
if(!_currentMass->writeGlobalStyle(i)) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
break;
default:
break;
}
ImGui::PopID();
}
ImGui::EndChild();
}
void SaveTool::drawTuning() {
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
return;
}
if(!ImGui::BeginTable("##TuningTable", 3)) {
return;
}
ImGui::TableSetupColumn("##EngineColumn");
ImGui::TableSetupColumn("##OSColumn");
ImGui::TableSetupColumn("##ArchitectureColumn");
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
if(ImGui::BeginTable("##EngineTable", 1, ImGuiTableFlags_Borders)) {
ImGui::TableSetupColumn("##Engine");
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::TextUnformatted("Engine");
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%i", _currentMass->engine());
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::TextUnformatted("Gears");
for(UnsignedInt i = 0; i < _currentMass->gears().size(); i++) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%i", _currentMass->gears()[i]);
}
ImGui::EndTable();
}
ImGui::TableSetColumnIndex(1);
if(ImGui::BeginTable("##OSTable", 1, ImGuiTableFlags_Borders)) {
ImGui::TableSetupColumn("##OS");
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::TextUnformatted("OS");
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%i", _currentMass->os());
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::TextUnformatted("Modules");
for(UnsignedInt i = 0; i < _currentMass->modules().size(); i++) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%i", _currentMass->modules()[i]);
}
ImGui::EndTable();
}
ImGui::TableSetColumnIndex(2);
if(ImGui::BeginTable("##ArchTable", 1, ImGuiTableFlags_Borders)) {
ImGui::TableSetupColumn("##Arch");
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::TextUnformatted("Architecture");
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%i", _currentMass->architecture());
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::TextUnformatted("Techs");
for(UnsignedInt i = 0; i < _currentMass->techs().size(); i++) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%i", _currentMass->techs()[i]);
}
ImGui::EndTable();
}
ImGui::EndTable();
}
auto SaveTool::drawCustomStyle(CustomStyle& style) -> DCSResult {
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
return DCS_Fail;
}
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {8.0f, 0.0f});
Containers::ScopeGuard guard{[]{ ImGui::PopStyleVar(); }};
DCSResult return_value = DCS_Fail;
if(!ImGui::BeginChild("##CustomStyle", {0.0f, 244.0f}, true, ImGuiWindowFlags_MenuBar)) {
ImGui::EndChild();
return DCS_Fail;
}
if(ImGui::BeginMenuBar()) {
ImGui::TextUnformatted(style.name.c_str());
static Containers::StaticArray<33, char> name_buf{ValueInit};
if(ImGui::SmallButton(ICON_FA_EDIT " Rename")) {
for(auto& c : name_buf) {
c = '\0';
}
std::strncpy(name_buf.data(), style.name.c_str(), 32);
ImGui::OpenPopup("name_edit");
}
if(drawRenamePopup(name_buf)) {
style.name = name_buf.data();
}
ImGui::EndMenuBar();
}
if(ImGui::BeginTable("##StyleTable", 2, ImGuiTableFlags_BordersInnerV)) {
ImGui::TableSetupColumn("##Colour", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##Pattern", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::BeginGroup();
drawAlignedText("Colour:");
drawAlignedText("Metallic:");
drawAlignedText("Gloss:");
drawAlignedText("Glow:");
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::ColorEdit3("##Picker", &style.colour.r());
ImGui::SameLine();
drawHelpMarker("Right-click for more option, click the coloured square for the full picker.");
ImGui::SetNextItemWidth(-FLT_MIN);
ImGui::SliderFloat("##SliderMetallic", &style.metallic, 0.0f, 1.0f);
ImGui::SetNextItemWidth(-FLT_MIN);
ImGui::SliderFloat("##SliderGloss", &style.gloss,0.0f, 1.0f);
ImGui::Checkbox("##Glow", &style.glow);
ImGui::EndGroup();
ImGui::TableNextColumn();
ImGui::BeginGroup();
drawAlignedText("Pattern:");
drawAlignedText("Opacity:");
drawAlignedText("X offset:");
drawAlignedText("Y offset:");
drawAlignedText("Rotation:");
drawAlignedText("Scale:");
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
drawAlignedText("%i", style.patternId);
ImGui::PushItemWidth(-FLT_MIN);
ImGui::SliderFloat("##SliderOpacity", &style.opacity, 0.0f, 1.0f);
ImGui::SliderFloat("##SliderOffsetX", &style.offset.x(), 0.0f, 1.0f);
ImGui::SliderFloat("##SliderOffsetY", &style.offset.y(), 0.0f, 1.0f);
ImGui::SliderFloat("##SliderRotation", &style.rotation, 0.0f, 1.0f);
ImGui::SliderFloat("##SliderScale", &style.scale, 0.0f, 1.0f);
ImGui::PopItemWidth();
ImGui::EndGroup();
ImGui::EndTable();
}
if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) {
return_value = DCS_Save;
}
ImGui::SameLine();
if(ImGui::Button(ICON_FA_UNDO " Reset")) {
return_value = DCS_ResetStyle;
}
ImGui::EndChild();
return return_value;
}
void SaveTool::drawDecalEditor(Decal& decal) {
ImGui::Text("ID: %i", decal.id);
if(ImGui::BeginTable("##DecalTable", 2, ImGuiTableFlags_BordersInnerV)) {
ImGui::TableSetupColumn("##Normal", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##Advanced", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::BeginGroup();
drawAlignedText("Colour:");
drawAlignedText("Offset:");
drawAlignedText("Rotation:");
drawAlignedText("Scale:");
drawAlignedText("Flip X:");
drawAlignedText("Surface wrap:");
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::ColorEdit3("##Picker", &decal.colour.r());
ImGui::SameLine();
drawHelpMarker("Right-click for more option, click the coloured square for the full picker.");
ImGui::PushMultiItemsWidths(2, ImGui::CalcItemWidth());
ImGui::SliderFloat("##OffsetX", &decal.offset.x(), 0.0f, 1.0f, "X: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::SliderFloat("##OffsetY", &decal.offset.y(), 0.0f, 1.0f, "Y: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine();
drawHelpMarker("0.0 = -100 in-game\n1.0 = 100 in-game");
ImGui::SliderFloat("##Rotation", &decal.rotation, 0.0f, 1.0f);
ImGui::SameLine();
drawHelpMarker("0.0 = 0 in-game\n1.0 = 360 in-game");
ImGui::SliderFloat("##Scale", &decal.scale, 0.0f, 1.0f);
ImGui::SameLine();
drawHelpMarker("0.0 = 1 in-game\n1.0 = 100 in-game");
ImGui::Checkbox("##Flip", &decal.flip);
ImGui::Checkbox("##Wrap", &decal.wrap);
ImGui::EndGroup();
ImGui::TableNextColumn();
ImGui::TextColored(ImColor(255, 255, 0), ICON_FA_EXCLAMATION_TRIANGLE);
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::TextUnformatted("Advanced settings. Touch these at your own risk.");
ImGui::BeginGroup();
drawAlignedText("Position:");
drawAlignedText("U axis:");
drawAlignedText("V axis:");
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
ImGui::DragFloat("##PosX", &decal.position.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::DragFloat("##PosY", &decal.position.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::DragFloat("##PosZ", &decal.position.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f");
ImGui::PopItemWidth();
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
ImGui::DragFloat("##UX", &decal.uAxis.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::DragFloat("##UY", &decal.uAxis.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::DragFloat("##UZ", &decal.uAxis.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f");
ImGui::PopItemWidth();
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
ImGui::DragFloat("##VX", &decal.vAxis.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::DragFloat("##VY", &decal.vAxis.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::DragFloat("##VZ", &decal.vAxis.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f");
ImGui::PopItemWidth();
ImGui::EndGroup();
ImGui::EndTable();
}
}
void SaveTool::drawAccessoryEditor(Accessory& accessory, Containers::ArrayView<CustomStyle> style_view) {
if(accessory.id < 1) {
ImGui::TextUnformatted("Accessory: <none>");
}
else if(accessories.find(accessory.id) != accessories.cend()) {
ImGui::Text("Accessory #%i - %s", accessory.id, accessories.at(accessory.id));
}
else {
ImGui::Text("Accessory #%i", accessory.id);
drawTooltip("WARNING: accessory mapping is a WIP.");
}
#ifdef SAVETOOL_DEBUG_BUILD
ImGui::SameLine(0.0f, ImGui::GetStyle().FramePadding.x * 5.0f);
ImGui::Text("Attach index: %i", accessory.attachIndex);
#endif
ImGui::BeginGroup();
drawAlignedText("Styles:");
drawAlignedText("Base position:");
drawAlignedText("Position offset:");
drawAlignedText("Base rotation:");
drawAlignedText("Rotation offset:");
drawAlignedText("Scale:");
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::PushMultiItemsWidths(2, ImGui::CalcItemWidth());
if(ImGui::BeginCombo("##Style1", getStyleName(accessory.styles[0], style_view))) {
for(const auto& style : style_names) {
if(ImGui::Selectable(getStyleName(style.first, style_view), accessory.styles[0] == style.first)) {
accessory.styles[0] = style.first;
}
}
ImGui::EndCombo();
}
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
if(ImGui::BeginCombo("##Style2", getStyleName(accessory.styles[1], style_view))) {
for(const auto& style : style_names) {
if(ImGui::Selectable(getStyleName(style.first, style_view), accessory.styles[1] == style.first)) {
accessory.styles[1] = style.first;
}
}
ImGui::EndCombo();
}
ImGui::PopItemWidth();
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
ImGui::DragFloat("##PosX", &accessory.relativePosition.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::DragFloat("##PosY", &accessory.relativePosition.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::DragFloat("##PosZ", &accessory.relativePosition.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f");
ImGui::PopItemWidth();
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
ImGui::SliderFloat("##PosOffsetX", &accessory.relativePositionOffset.x(), -500.0f, +500.0f, "X: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::SliderFloat("##PosOffsetY", &accessory.relativePositionOffset.y(), -500.0f, +500.0f, "Y: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::SliderFloat("##PosOffsetZ", &accessory.relativePositionOffset.z(), -500.0f, +500.0f, "Z: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine();
drawHelpMarker("+/-500.0 = +/-250 in-game");
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
ImGui::DragFloat("##RotX", &accessory.relativeRotation.x(), 1.0f, -FLT_MAX, +FLT_MAX, "Roll: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::DragFloat("##RotY", &accessory.relativeRotation.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Yaw: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::DragFloat("##RotZ", &accessory.relativeRotation.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Pitch: %.3f");
ImGui::PopItemWidth();
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
ImGui::SliderFloat("##RotOffsetX", &accessory.relativeRotationOffset.x(), -180.0f, +180.0f, "Roll: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::SliderFloat("##RotOffsetY", &accessory.relativeRotationOffset.y(), -180.0f, +180.0f, "Yaw: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::SliderFloat("##RotOffsetZ", &accessory.relativeRotationOffset.z(), -180.0f, +180.0f, "Pitch: %.3f");
ImGui::PopItemWidth();
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
ImGui::SliderFloat("##ScaleX", &accessory.localScale.x(), -3.0f, +3.0f, "X: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::SliderFloat("##ScaleY", &accessory.localScale.y(), -3.0f, +3.0f, "Y: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x);
ImGui::SliderFloat("##ScaleZ", &accessory.localScale.z(), -3.0f, +3.0f, "Z: %.3f");
ImGui::PopItemWidth();
ImGui::SameLine();
drawHelpMarker("+/-3.0 = +/-150 in-game");
ImGui::EndGroup();
}
auto SaveTool::getStyleName(Int id, Containers::ArrayView<CustomStyle> view) -> const char* {
if(id >= 0 && id <= 15) {
return view[id].name.c_str();
}
else if(id >= 50 && id <= 65) {
return _currentMass->globalStyles()[id - 50].name.c_str();
}
else {
return style_names.at(id);
}
}

View file

@ -0,0 +1,198 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../FontAwesome/IconsFontAwesome5.h"
#include "../Maps/ArmourSets.h"
#include "../Maps/StyleNames.h"
#include "SaveTool.h"
void SaveTool::drawArmour() {
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
return;
}
if(ImGui::Button(ICON_FA_UNDO_ALT " Reset all")) {
_currentMass->getArmourParts();
}
if(!ImGui::BeginChild("##ArmourParts")) {
ImGui::EndChild();
return;
}
static const char* slot_labels[] = {
#define c(enumerator, strenum, name) name,
#include "../Maps/ArmourSlots.hpp"
#undef c
};
for(UnsignedInt i = 0; i < _currentMass->armourParts().size(); i++) {
ImGui::PushID(i);
auto& part = _currentMass->armourParts()[i];
static char header[129] = {'\0'};
std::memset(header, '\0', 129);
if(armour_sets.find(part.id) != armour_sets.cend()) {
std::snprintf(header, 128, "%s: %s###%u", slot_labels[UnsignedInt(part.slot)], armour_sets.at(part.id).name, UnsignedInt(part.slot));
}
else {
std::snprintf(header, 128, "%s: %i###%u", slot_labels[UnsignedInt(part.slot)], part.id, UnsignedInt(part.slot));
}
if(ImGui::CollapsingHeader(header)) {
ImGui::BeginGroup();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth() * 0.491f);
if(ImGui::BeginListBox("##ChangePart")) {
if(std::strncmp("Neck", slot_labels[UnsignedInt(part.slot)], 4) != 0) {
for(auto& set : armour_sets) {
if(ImGui::Selectable(set.second.name, set.first == part.id, ImGuiSelectableFlags_SpanAvailWidth)) {
part.id = set.first;
}
}
}
else {
for(auto& set : armour_sets) {
if(!set.second.neck_compatible) {
continue;
}
if(ImGui::Selectable(set.second.name, set.first == part.id, ImGuiSelectableFlags_SpanAvailWidth)) {
part.id = set.first;
}
}
}
ImGui::EndListBox();
}
ImGui::EndGroup();
ImGui::SameLine();
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::TextUnformatted("Styles:");
for(Int j = 0; j < 4; j++) {
drawAlignedText("Slot %d:", j + 1);
ImGui::SameLine();
ImGui::PushID(j);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth() - 2.0f);
if(ImGui::BeginCombo("##Style", getStyleName(part.styles[j], _currentMass->armourCustomStyles()))) {
for(const auto& style : style_names) {
if(ImGui::Selectable(getStyleName(style.first, _currentMass->armourCustomStyles()), part.styles[j] == style.first)) {
part.styles[j] = style.first;
}
}
ImGui::EndCombo();
}
ImGui::PopID();
}
ImGui::EndGroup();
ImGui::Separator();
ImGui::PushID("Decal");
drawAlignedText("Showing/editing decal");
for(UnsignedInt j = 0; j < part.decals.size(); j++) {
ImGui::SameLine();
ImGui::RadioButton(std::to_string(j + 1).c_str(), &_selectedArmourDecals[i], j);
}
drawDecalEditor(part.decals[_selectedArmourDecals[i]]);
ImGui::PopID();
if(!part.accessories.size()) {
ImGui::Separator();
ImGui::PushID("Accessory");
drawAlignedText("Showing/editing accessory");
for(UnsignedInt j = 0; j < part.accessories.size(); j++) {
ImGui::SameLine();
ImGui::RadioButton(std::string{char(65 + j)}.c_str(), &_selectedArmourAccessories[i], j);
}
drawAccessoryEditor(part.accessories[_selectedArmourAccessories[i]], _currentMass->armourCustomStyles());
ImGui::PopID();
}
ImGui::Separator();
if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) {
if(!_currentMass->writeArmourPart(part.slot)) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
}
}
ImGui::PopID();
}
ImGui::EndChild();
}
void SaveTool::drawCustomArmourStyles() {
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
return;
}
if(!ImGui::BeginChild("##ArmourStyles")) {
ImGui::EndChild();
return;
}
ImGui::TextWrapped("In-game values are multiplied by 100. For example, 0.500 here is equal to 50 in-game.");
for(UnsignedInt i = 0; i < _currentMass->armourCustomStyles().size(); i++) {
ImGui::PushID(i);
DCSResult result;
result = drawCustomStyle(_currentMass->armourCustomStyles()[i]);
switch(result) {
case DCS_ResetStyle:
_currentMass->getArmourCustomStyles();
break;
case DCS_Save:
if(_currentMass->writeArmourCustomStyle(i)) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
break;
default:
break;
}
ImGui::PopID();
}
ImGui::EndChild();
}

View file

@ -0,0 +1,307 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../FontAwesome/IconsFontAwesome5.h"
#include "../Maps/StyleNames.h"
#include "SaveTool.h"
void SaveTool::drawFrameInfo() {
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
return;
}
if(!ImGui::BeginChild("##FrameInfo")) {
ImGui::EndChild();
return;
}
ImGui::BeginGroup();
if(ImGui::BeginChild("##JointSliders", {(ImGui::GetContentRegionAvailWidth() / 2.0f) - (ImGui::GetStyle().WindowPadding.x / 2.0f), 300.0f}, true, ImGuiWindowFlags_MenuBar)) {
if(ImGui::BeginMenuBar()) {
ImGui::TextUnformatted("Joint sliders");
ImGui::EndMenuBar();
}
drawJointSliders();
}
ImGui::EndChild();
if(ImGui::BeginChild("##FrameStyles", {(ImGui::GetContentRegionAvailWidth() / 2.0f) - (ImGui::GetStyle().WindowPadding.x / 2.0f), 0.0f}, true, ImGuiWindowFlags_MenuBar)) {
if(ImGui::BeginMenuBar()) {
ImGui::TextUnformatted("Frame styles");
ImGui::EndMenuBar();
}
drawFrameStyles();
}
ImGui::EndChild();
ImGui::EndGroup();
ImGui::SameLine();
if(ImGui::BeginChild("##EyeFlare", {0.0f, 0.0f}, true, ImGuiWindowFlags_MenuBar)) {
if(ImGui::BeginMenuBar()) {
ImGui::TextUnformatted("Eye flare colour");
drawHelpMarker("Right-click the picker for more options.", 250.0f);
ImGui::EndMenuBar();
}
drawEyeColourPicker();
}
ImGui::EndChild();
ImGui::EndChild();
}
void SaveTool::drawJointSliders() {
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
return;
}
ImGui::TextWrapped("In-game values are multiplied by 100.\nFor example, 0.500 here is equal to 50 in-game.");
if(ImGui::BeginTable("##JointSliderTable", 2, ImGuiTableFlags_Borders)) {
ImGui::TableSetupColumn("##SliderLabel", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("##Sliders", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
drawAlignedText("Neck");
ImGui::TableSetColumnIndex(1);
ImGui::SetNextItemWidth(-1.0f);
if(ImGui::SliderFloat("##NeckSlider", &_currentMass->jointSliders().neck, 0.0f, 1.0f)) {
_jointsDirty = true;
}
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
drawAlignedText("Body");
ImGui::TableSetColumnIndex(1);
ImGui::SetNextItemWidth(-1.0f);
if(ImGui::SliderFloat("##BodySlider", &_currentMass->jointSliders().body, 0.0f, 1.0f)) {
_jointsDirty = true;
}
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
drawAlignedText("Shoulders");
ImGui::TableSetColumnIndex(1);
ImGui::SetNextItemWidth(-1.0f);
if(ImGui::SliderFloat("##ShouldersSlider", &_currentMass->jointSliders().shoulders, 0.0f, 1.0f)) {
_jointsDirty = true;
}
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
drawAlignedText("Hips");
ImGui::TableSetColumnIndex(1);
ImGui::SetNextItemWidth(-1.0f);
if(ImGui::SliderFloat("##HipsSlider", &_currentMass->jointSliders().hips, 0.0f, 1.0f)) {
_jointsDirty = true;
}
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
drawAlignedText("Arms");
ImGui::TableSetColumnIndex(1);
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2{2.0f, 1.0f});
if(ImGui::BeginTable("##UpperLowerArmsLayoutTable", 2)) {
ImGui::TableSetupColumn("##UpperArms", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##LowerArms", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(-1.0f);
if(ImGui::SliderFloat("##UpperArmsSlider", &_currentMass->jointSliders().upperArms, 0.0f, 1.0f, "Upper: %.3f")) {
_jointsDirty = true;
}
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(-1.0f);
if(ImGui::SliderFloat("##LowerArmsSlider", &_currentMass->jointSliders().lowerArms, 0.0f, 1.0f, "Lower: %.3f")) {
_jointsDirty = true;
}
ImGui::EndTable();
}
ImGui::PopStyleVar();
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0);
drawAlignedText("Legs");
ImGui::TableSetColumnIndex(1);
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2{2.0f, 1.0f});
if(ImGui::BeginTable("##UpperLowerLegsLayoutTable", 2)) {
ImGui::TableSetupColumn("##UpperLegs", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##LowerLegs", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(-1.0f);
if(ImGui::SliderFloat("##UpperLegsSlider", &_currentMass->jointSliders().upperLegs, 0.0f, 1.0f, "Upper: %.3f")) {
_jointsDirty = true;
}
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(-1.0f);
if(ImGui::SliderFloat("##LowerLegsSlider", &_currentMass->jointSliders().lowerLegs, 0.0f, 1.0f, "Lower: %.3f")) {
_jointsDirty = true;
}
ImGui::EndTable();
}
ImGui::PopStyleVar();
ImGui::EndTable();
}
if(!_jointsDirty) {
ImGui::BeginDisabled();
ImGui::Button(ICON_FA_SAVE " Save");
ImGui::SameLine();
ImGui::Button(ICON_FA_UNDO " Reset");
ImGui::EndDisabled();
}
else {
if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) {
if(!_currentMass->writeJointSliders()) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
_jointsDirty = false;
}
ImGui::SameLine();
if(ImGui::Button(ICON_FA_UNDO " Reset")) {
_currentMass->getJointSliders();
_jointsDirty = false;
}
}
}
void SaveTool::drawFrameStyles() {
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
return;
}
for(Int i = 0; i < 4; i++) {
drawAlignedText("Slot %d:", i + 1);
ImGui::SameLine();
ImGui::PushID(i);
if(ImGui::BeginCombo("##Style", getStyleName(_currentMass->frameStyles()[i], _currentMass->frameCustomStyles()))) {
for(const auto& style : style_names) {
if(ImGui::Selectable(getStyleName(style.first, _currentMass->frameCustomStyles()), _currentMass->frameStyles()[i] == style.first)) {
_currentMass->frameStyles()[i] = style.first;
_stylesDirty = true;
}
}
ImGui::EndCombo();
}
ImGui::PopID();
}
if(!_stylesDirty) {
ImGui::BeginDisabled();
ImGui::Button(ICON_FA_SAVE " Save");
ImGui::SameLine();
ImGui::Button(ICON_FA_UNDO " Reset");
ImGui::EndDisabled();
}
else {
if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) {
if(!_currentMass->writeFrameStyles()) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
_stylesDirty = false;
}
ImGui::SameLine();
if(ImGui::Button(ICON_FA_UNDO " Reset")) {
_currentMass->getFrameStyles();
_stylesDirty = false;
}
}
}
void SaveTool::drawEyeColourPicker() {
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
return;
}
if(ImGui::ColorPicker3("##EyeFlarePicker", &_currentMass->eyeFlareColour().x())) {
_eyeFlareDirty = true;
}
if(!_eyeFlareDirty) {
ImGui::BeginDisabled();
ImGui::Button(ICON_FA_SAVE " Save");
ImGui::SameLine();
ImGui::Button(ICON_FA_UNDO " Reset");
ImGui::EndDisabled();
}
else {
if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) {
if(!_currentMass->writeEyeFlareColour()) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
_eyeFlareDirty = false;
}
ImGui::SameLine();
if(ImGui::Button(ICON_FA_UNDO " Reset")) {
_currentMass->getEyeFlareColour();
_eyeFlareDirty = false;
}
}
}
void SaveTool::drawCustomFrameStyles() {
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
return;
}
if(!ImGui::BeginChild("##FrameStyles")) {
ImGui::EndChild();
return;
}
ImGui::TextWrapped("In-game values are multiplied by 100. For example, 0.500 here is equal to 50 in-game.");
for(UnsignedInt i = 0; i < _currentMass->frameCustomStyles().size(); i++) {
ImGui::PushID(i);
DCSResult result;
result = drawCustomStyle(_currentMass->frameCustomStyles()[i]);
switch(result) {
case DCS_ResetStyle:
_currentMass->getFrameCustomStyles();
break;
case DCS_Save:
if(!_currentMass->writeFrameCustomStyle(i)) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
break;
default:
break;
}
ImGui::PopID();
}
ImGui::EndChild();
}

View file

@ -0,0 +1,475 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../FontAwesome/IconsFontAwesome5.h"
#include "../Maps/StyleNames.h"
#include "SaveTool.h"
void SaveTool::drawWeapons() {
if(!_currentMass || _currentMass->state() != Mass::State::Valid) {
_currentWeapon = nullptr;
return;
}
const Float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
ImGui::BeginGroup();
if(!ImGui::BeginTable("##WeaponsList", 1,
ImGuiTableFlags_ScrollY|ImGuiTableFlags_BordersOuter|ImGuiTableFlags_BordersInnerH,
{ImGui::GetContentRegionAvailWidth() * 0.2f, -footer_height_to_reserve}))
{
ImGui::EndGroup();
return;
}
ImGui::TableSetupColumn("Weapon");
drawWeaponCategory("Melee weapons", _currentMass->meleeWeapons(), _meleeDirty, "MeleeWeapon", "Melee weapon");
drawWeaponCategory("Shield", _currentMass->shields(), _shieldsDirty, "Shield", "Shield");
drawWeaponCategory("Bullet shooters", _currentMass->bulletShooters(), _bShootersDirty, "BShooter", "Bullet shooter");
drawWeaponCategory("Energy shooters", _currentMass->energyShooters(), _eShootersDirty, "EShooter", "Energy shooter");
drawWeaponCategory("Bullet launchers", _currentMass->bulletLaunchers(), _bLaunchersDirty, "BLauncher", "Bullet launcher");
drawWeaponCategory("Energy launchers", _currentMass->energyLaunchers(), _eLaunchersDirty, "ELauncher", "Energy launcher");
ImGui::EndTable();
bool dirty = _meleeDirty || _shieldsDirty || _bShootersDirty || _eShootersDirty || _bLaunchersDirty || _eLaunchersDirty;
if(!dirty) {
ImGui::BeginDisabled();
}
if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) {
if(_meleeDirty) {
if(!_currentMass->writeMeleeWeapons()) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
else {
_meleeDirty = false;
}
}
if(_shieldsDirty) {
if(!_currentMass->writeShields()) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
else {
_shieldsDirty = false;
}
}
if(_bShootersDirty) {
if(!_currentMass->writeBulletShooters()) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
else {
_bShootersDirty = false;
}
}
if(_eShootersDirty) {
if(_currentMass->writeEnergyShooters()) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
else {
_eShootersDirty = false;
}
}
if(_bLaunchersDirty) {
if(_currentMass->writeBulletLaunchers()) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
else {
_bLaunchersDirty = false;
}
}
if(_eLaunchersDirty) {
if(_currentMass->writeEnergyLaunchers()) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
else {
_eLaunchersDirty = false;
}
}
}
ImGui::SameLine();
if(ImGui::Button(ICON_FA_UNDO_ALT " Reset")) {
if(_meleeDirty) {
_currentMass->getMeleeWeapons();
_meleeDirty = false;
}
if(_shieldsDirty) {
_currentMass->getShields();
_shieldsDirty = false;
}
if(_bShootersDirty) {
_currentMass->getBulletShooters();
_bShootersDirty = false;
}
if(_eShootersDirty) {
_currentMass->getEnergyShooters();
_eShootersDirty = false;
}
if(_bLaunchersDirty) {
_currentMass->getBulletLaunchers();
_bLaunchersDirty = false;
}
if(_eLaunchersDirty) {
_currentMass->getEnergyLaunchers();
_eLaunchersDirty = false;
}
}
if(!dirty) {
ImGui::EndDisabled();
}
ImGui::EndGroup();
ImGui::SameLine();
if(!_currentWeapon) {
ImGui::TextUnformatted("No weapon selected.");
return;
}
ImGui::BeginGroup();
if(!ImGui::BeginChild("##WeaponChild", {0.0f, -footer_height_to_reserve})) {
ImGui::EndChild();
return;
}
drawWeaponEditor(*_currentWeapon);
ImGui::EndChild();
ImGui::Separator();
if(drawUnsafeWidget([](){ return ImGui::Button(ICON_FA_SAVE " Save changes to weapon category"); })) {
switch(_currentWeapon->type) {
case WeaponType::Melee:
if(!_currentMass->writeMeleeWeapons()) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
break;
case WeaponType::Shield:
if(!_currentMass->writeShields()) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
break;
case WeaponType::BulletShooter:
if(!_currentMass->writeBulletShooters()) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
break;
case WeaponType::EnergyShooter:
if(!_currentMass->writeEnergyShooters()) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
break;
case WeaponType::BulletLauncher:
if(!_currentMass->writeBulletLaunchers()) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
break;
case WeaponType::EnergyLauncher:
if(!_currentMass->writeEnergyLaunchers()) {
_queue.addToast(Toast::Type::Error, _currentMass->lastError());
}
break;
default:
_queue.addToast(Toast::Type::Error, "Unknown weapon type");
}
}
ImGui::SameLine();
if(ImGui::Button(ICON_FA_UNDO_ALT " Reset weapon category")) {
switch(_currentWeapon->type) {
case WeaponType::Melee:
_currentMass->getMeleeWeapons();
break;
case WeaponType::Shield:
_currentMass->getShields();
break;
case WeaponType::BulletShooter:
_currentMass->getBulletShooters();
break;
case WeaponType::EnergyShooter:
_currentMass->getEnergyShooters();
break;
case WeaponType::BulletLauncher:
_currentMass->getBulletLaunchers();
break;
case WeaponType::EnergyLauncher:
_currentMass->getEnergyLaunchers();
break;
default:
_queue.addToast(Toast::Type::Error, "Unknown weapon type");
}
}
ImGui::EndGroup();
}
void SaveTool::drawWeaponCategory(const char* name, Containers::ArrayView<Weapon> weapons_view, bool& dirty,
const char* payload_type, const char* payload_tooltip)
{
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::TextUnformatted(name);
ImGui::PushID(payload_type);
for(UnsignedInt i = 0; i < weapons_view.size(); i++) {
auto& weapon = weapons_view[i];
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::PushID(i);
if(ImGui::Selectable(weapon.name.c_str(), _currentWeapon == &weapon)) {
_currentWeapon = &weapon;
}
if(ImGui::BeginDragDropSource()) {
ImGui::SetDragDropPayload(payload_type, &i, sizeof(UnsignedInt));
if(ImGui::GetIO().KeyCtrl) {
ImGui::Text("%s %i - %s (copy)", payload_tooltip, i + 1, weapon.name.c_str());
}
else {
ImGui::Text("%s %i - %s", payload_tooltip, i + 1, weapon.name.c_str());
}
ImGui::EndDragDropSource();
}
if(ImGui::BeginDragDropTarget()) {
if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(payload_type)) {
int index = *static_cast<int*>(payload->Data);
if(!ImGui::GetIO().KeyCtrl) {
if(_currentWeapon == &weapons_view[index]) {
_currentWeapon = &weapons_view[i];
}
else if (_currentWeapon == &weapons_view[i]) {
_currentWeapon = &weapons_view[index];
}
std::swap(weapons_view[index], weapons_view[i]);
}
else {
weapons_view[i] = weapons_view[index];
}
dirty = true;
}
ImGui::EndDragDropTarget();
}
ImGui::PopID();
if(weapon.attached == true) {
ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, 0x1F008CFFu);
}
}
ImGui::PopID();
}
void SaveTool::drawWeaponEditor(Weapon& weapon) {
if(!_currentMass || _currentMass->state() != Mass::State::Valid || !_currentWeapon) {
return;
}
static const char* labels[] {
#define c(enumerator, strenum, name) name,
#include "../Maps/WeaponTypes.hpp"
#undef c
};
drawAlignedText("%s: %s", labels[UnsignedInt(weapon.type)], weapon.name.c_str());
ImGui::SameLine();
static Containers::StaticArray<33, char> name_buf{ValueInit};
if(ImGui::Button(ICON_FA_EDIT " Rename")) {
for(auto& c : name_buf) {
c = '\0';
}
std::strncpy(name_buf.data(), weapon.name.c_str(), 32);
ImGui::OpenPopup("name_edit");
}
if(drawRenamePopup(name_buf)) {
weapon.name = name_buf.data();
}
ImGui::BeginGroup();
drawAlignedText("Equipped:");
if(weapon.type != WeaponType::Shield) {
drawAlignedText("Damage type:");
}
if(weapon.type == WeaponType::Melee) {
drawAlignedText("Dual-wield:");
drawAlignedText("Custom effect mode:");
drawAlignedText("Custom effect colour:");
}
ImGui::EndGroup();
ImGui::SameLine();
ImGui::BeginGroup();
ImGui::Checkbox("##EquippedCheckbox", &weapon.attached);
if(weapon.type != WeaponType::Shield) {
if(weapon.type == WeaponType::Melee &&
ImGui::RadioButton("Physical##NoElement", weapon.damageType == DamageType::Physical))
{
weapon.damageType = DamageType::Physical;
}
else if((weapon.type == WeaponType::BulletShooter || weapon.type == WeaponType::BulletLauncher) &&
ImGui::RadioButton("Piercing##NoElement", weapon.damageType == DamageType::Piercing))
{
weapon.damageType = DamageType::Piercing;
}
else if((weapon.type == WeaponType::EnergyShooter || weapon.type == WeaponType::EnergyLauncher) &&
ImGui::RadioButton("Plasma##NoElement", weapon.damageType == DamageType::Plasma))
{
weapon.damageType = DamageType::Plasma;
}
ImGui::SameLine();
if(ImGui::RadioButton("Heat##Heat", weapon.damageType == DamageType::Heat)) {
weapon.damageType = DamageType::Heat;
}
ImGui::SameLine();
if(ImGui::RadioButton("Freeze##Freeze", weapon.damageType == DamageType::Freeze)) {
weapon.damageType = DamageType::Freeze;
}
ImGui::SameLine();
if(ImGui::RadioButton("Shock##Shock", weapon.damageType == DamageType::Freeze)) {
weapon.damageType = DamageType::Freeze;
}
}
if(weapon.type == WeaponType::Melee) {
ImGui::Checkbox("##DualWield", &weapon.dualWield);
if(ImGui::RadioButton("Default##Default", weapon.effectColourMode == EffectColourMode::Default)) {
weapon.effectColourMode = EffectColourMode::Default;
}
ImGui::SameLine();
if(ImGui::RadioButton("Custom##Custom", weapon.effectColourMode == EffectColourMode::Custom)) {
weapon.effectColourMode = EffectColourMode::Custom;
}
bool custom_effect = (weapon.effectColourMode == EffectColourMode::Custom);
if(!custom_effect) {
ImGui::BeginDisabled();
}
ImGui::ColorEdit3("##CustomEffectColourPicker", &weapon.effectColour.x(), ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_Float);
if(!custom_effect) {
ImGui::EndDisabled();
}
}
ImGui::EndGroup();
ImGui::Separator();
if(ImGui::CollapsingHeader("Weapon parts")) {
drawAlignedText("Viewing/editing part:");
for(Int i = 0; UnsignedLong(i) < weapon.parts.size(); i++) {
if(UnsignedLong(_selectedWeaponPart) >= weapon.parts.size()) {
_selectedWeaponPart = 0;
}
ImGui::SameLine();
ImGui::RadioButton(std::to_string(i + 1).c_str(), &_selectedWeaponPart, i);
}
auto& part = weapon.parts[_selectedWeaponPart];
ImGui::Text("ID: %i", part.id);
if(ImGui::BeginChild("##PartDetails", {0.0f, 0.0f}, true)) {
ImGui::TextUnformatted("Styles:");
for(Int i = 0; i < 4; i++) {
drawAlignedText("Slot %d:", i + 1);
ImGui::SameLine();
ImGui::PushID(i);
if(ImGui::BeginCombo("##Style", getStyleName(part.styles[i], weapon.customStyles))) {
for(const auto& style: style_names) {
if(ImGui::Selectable(getStyleName(style.first, weapon.customStyles),
part.styles[i] == style.first)) {
part.styles[i] = style.first;
}
}
ImGui::EndCombo();
}
ImGui::PopID();
}
ImGui::Separator();
ImGui::PushID("Decal");
drawAlignedText("Showing/editing decal");
for(UnsignedLong i = 0; i < part.decals.size(); i++) {
ImGui::SameLine();
ImGui::RadioButton(std::to_string(i + 1).c_str(), &_selectedWeaponDecal, i);
}
drawDecalEditor(part.decals[_selectedWeaponDecal]);
ImGui::PopID();
if(part.accessories.size() != 0) {
ImGui::Separator();
ImGui::PushID("Accessory");
drawAlignedText("Showing/editing accessory");
for(UnsignedLong i = 0; i < part.accessories.size(); i++) {
ImGui::SameLine();
ImGui::RadioButton(std::string{char(65 + i)}.c_str(), &_selectedWeaponAccessory, i);
}
drawAccessoryEditor(part.accessories[_selectedWeaponAccessory], weapon.customStyles);
ImGui::PopID();
}
}
ImGui::EndChild();
}
}

View file

@ -1,5 +1,5 @@
// MassBuilderSaveTool // MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin // Copyright (C) 2021-2022 Guillaume Jacquemin
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -81,7 +81,7 @@ void SaveTool::drawProfileManager() {
ImGui::TableSetColumnIndex(0); ImGui::TableSetColumnIndex(0);
ImGui::PushID(i); ImGui::PushID(i);
if(ImGui::Selectable(_profileManager->profiles().at(i).companyName().c_str(), false, if(ImGui::Selectable(_profileManager->profiles()[i].companyName().c_str(), false,
ImGuiSelectableFlags_SpanAllColumns|ImGuiSelectableFlags_AllowItemOverlap)) ImGuiSelectableFlags_SpanAllColumns|ImGuiSelectableFlags_AllowItemOverlap))
{ {
_currentProfile = _profileManager->getProfile(i); _currentProfile = _profileManager->getProfile(i);
@ -90,7 +90,7 @@ void SaveTool::drawProfileManager() {
} }
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
ImGui::TextUnformatted(_profileManager->profiles().at(i).type() == ProfileType::Demo ? "Demo" : "Full"); ImGui::TextUnformatted(_profileManager->profiles()[i].type() == ProfileType::Demo ? "Demo (legacy)" : "Full (legacy)");
ImGui::TableSetColumnIndex(2); ImGui::TableSetColumnIndex(2);
if(ImGui::SmallButton(ICON_FA_FILE_ARCHIVE)) { if(ImGui::SmallButton(ICON_FA_FILE_ARCHIVE)) {
@ -132,13 +132,13 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
{ {
ImGui::PushTextWrapPos(windowSize().x() * 0.40f); 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.", 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()[backup_index].company.c_str(),
_profileManager->backups().at(backup_index).timestamp.year, _profileManager->backups()[backup_index].timestamp.year,
_profileManager->backups().at(backup_index).timestamp.month, _profileManager->backups()[backup_index].timestamp.month,
_profileManager->backups().at(backup_index).timestamp.day, _profileManager->backups()[backup_index].timestamp.day,
_profileManager->backups().at(backup_index).timestamp.hour, _profileManager->backups()[backup_index].timestamp.hour,
_profileManager->backups().at(backup_index).timestamp.minute, _profileManager->backups()[backup_index].timestamp.minute,
_profileManager->backups().at(backup_index).timestamp.second); _profileManager->backups()[backup_index].timestamp.second);
ImGui::PopTextWrapPos(); ImGui::PopTextWrapPos();
if(ImGui::BeginTable("##RestoreBackupLayout", 2)) { if(ImGui::BeginTable("##RestoreBackupLayout", 2)) {
@ -150,8 +150,7 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
if(ImGui::Button("Yes")) { if(ImGui::Button("Yes")) {
if(!_profileManager->restoreBackup(backup_index)) { if(!_profileManager->restoreBackup(backup_index)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when restoring backup", _queue.addToast(Toast::Type::Error, _profileManager->lastError());
_profileManager->lastError().c_str(), window());
} }
_profileManager->refreshProfiles(); _profileManager->refreshProfiles();
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
@ -172,13 +171,13 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
{ {
ImGui::PushTextWrapPos(windowSize().x() * 0.40f); 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.", 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()[backup_index].company.c_str(),
_profileManager->backups().at(backup_index).timestamp.year, _profileManager->backups()[backup_index].timestamp.year,
_profileManager->backups().at(backup_index).timestamp.month, _profileManager->backups()[backup_index].timestamp.month,
_profileManager->backups().at(backup_index).timestamp.day, _profileManager->backups()[backup_index].timestamp.day,
_profileManager->backups().at(backup_index).timestamp.hour, _profileManager->backups()[backup_index].timestamp.hour,
_profileManager->backups().at(backup_index).timestamp.minute, _profileManager->backups()[backup_index].timestamp.minute,
_profileManager->backups().at(backup_index).timestamp.second); _profileManager->backups()[backup_index].timestamp.second);
ImGui::PopTextWrapPos(); ImGui::PopTextWrapPos();
if(ImGui::BeginTable("##DeleteBackupLayout", 2)) { if(ImGui::BeginTable("##DeleteBackupLayout", 2)) {
@ -190,8 +189,7 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
if(ImGui::Button("Yes")) { if(ImGui::Button("Yes")) {
if(!_profileManager->deleteBackup(backup_index)) { if(!_profileManager->deleteBackup(backup_index)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when deleting backup", _queue.addToast(Toast::Type::Error, _profileManager->lastError());
_profileManager->lastError().c_str(), window());
} }
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
@ -251,10 +249,10 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableSetColumnIndex(0); ImGui::TableSetColumnIndex(0);
ImGui::TextUnformatted(_profileManager->backups().at(i).company.c_str()); ImGui::TextUnformatted(_profileManager->backups()[i].company.c_str());
if(ImGui::IsItemHovered()) { if(ImGui::IsItemHovered()) {
ImGui::BeginTooltip(); ImGui::BeginTooltip();
for(const auto& file : _profileManager->backups().at(i).includedFiles) { for(const auto& file : _profileManager->backups()[i].includedFiles) {
ImGui::TextUnformatted(file.c_str()); ImGui::TextUnformatted(file.c_str());
} }
ImGui::EndTooltip(); ImGui::EndTooltip();
@ -262,15 +260,15 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
ImGui::Text("%.4i-%.2i-%.2i %.2i:%.2i:%.2i", ImGui::Text("%.4i-%.2i-%.2i %.2i:%.2i:%.2i",
_profileManager->backups().at(i).timestamp.year, _profileManager->backups()[i].timestamp.year,
_profileManager->backups().at(i).timestamp.month, _profileManager->backups()[i].timestamp.month,
_profileManager->backups().at(i).timestamp.day, _profileManager->backups()[i].timestamp.day,
_profileManager->backups().at(i).timestamp.hour, _profileManager->backups()[i].timestamp.hour,
_profileManager->backups().at(i).timestamp.minute, _profileManager->backups()[i].timestamp.minute,
_profileManager->backups().at(i).timestamp.second); _profileManager->backups()[i].timestamp.second);
ImGui::TableSetColumnIndex(2); ImGui::TableSetColumnIndex(2);
ImGui::TextUnformatted(_profileManager->backups().at(i).type == ProfileType::Demo ? "Demo" : "Full"); ImGui::TextUnformatted(_profileManager->backups()[i].type == ProfileType::Demo ? "Demo" : "Full");
ImGui::TableSetColumnIndex(3); ImGui::TableSetColumnIndex(3);
ImGui::PushID(i); ImGui::PushID(i);
@ -363,8 +361,8 @@ auto SaveTool::drawDeleteProfilePopup(std::size_t profile_index) -> ImGuiID {
ImGui::PushTextWrapPos(windowSize().x() * 0.40f); ImGui::PushTextWrapPos(windowSize().x() * 0.40f);
ImGui::Text("Are you sure you want to delete the %s %s profile ? This operation is irreversible.", 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()[profile_index].companyName().c_str(),
_profileManager->profiles().at(profile_index).type() == ProfileType::Demo ? "demo" : "full game"); _profileManager->profiles()[profile_index].type() == ProfileType::Demo ? "demo" : "full game");
ImGui::PopTextWrapPos(); ImGui::PopTextWrapPos();
if(ImGui::BeginTable("##DeleteProfileLayout", 2)) { if(ImGui::BeginTable("##DeleteProfileLayout", 2)) {
@ -378,8 +376,7 @@ auto SaveTool::drawDeleteProfilePopup(std::size_t profile_index) -> ImGuiID {
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
if(ImGui::Button("Yes")) { if(ImGui::Button("Yes")) {
if(!_profileManager->deleteProfile(profile_index, delete_builds)) { if(!_profileManager->deleteProfile(profile_index, delete_builds)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when deleting profile", _queue.addToast(Toast::Type::Error, _profileManager->lastError());
_profileManager->lastError().c_str(), window());
} }
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }

View file

@ -1,5 +1,5 @@
// MassBuilderSaveTool // MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin // Copyright (C) 2021-2022 Guillaume Jacquemin
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -57,9 +57,8 @@ void SaveTool::drawAbout() {
ImGui::TextWrapped("This application, made for the M.A.S.S. Builder community by Guillaume Jacquemin (aka William JCM), " 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)."); "is a rewrite of the wxWidgets-powered M.A.S.S. Builder Save Tool (formerly known as wxMASSManager).");
ImGui::AlignTextToFramePadding();
const char* repo = "https://williamjcm.ovh/git/williamjcm/MassBuilderSaveTool"; const char* repo = "https://williamjcm.ovh/git/williamjcm/MassBuilderSaveTool";
ImGui::Text(ICON_FA_GIT_ALT " %s", repo); drawAlignedText(ICON_FA_GIT_ALT " %s", repo);
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) { if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(repo); ImGui::SetClipboardText(repo);
@ -90,9 +89,8 @@ void SaveTool::drawAbout() {
if(ImGui::TreeNodeEx("Corrade", ImGuiTreeNodeFlags_SpanAvailWidth)) { if(ImGui::TreeNodeEx("Corrade", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::Text("Version used: %s", CORRADE_VERSION_STRING); ImGui::Text("Version used: %s", CORRADE_VERSION_STRING);
ImGui::AlignTextToFramePadding();
const char* corrade_website = "https://magnum.graphics/corrade"; const char* corrade_website = "https://magnum.graphics/corrade";
ImGui::Text(ICON_FA_GLOBE " %s", corrade_website); drawAlignedText(ICON_FA_GLOBE " %s", corrade_website);
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) { if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(corrade_website); ImGui::SetClipboardText(corrade_website);
@ -119,9 +117,8 @@ void SaveTool::drawAbout() {
ImGui::TextUnformatted("Versions used:"); ImGui::TextUnformatted("Versions used:");
ImGui::BulletText("Magnum: %s", MAGNUM_VERSION_STRING); ImGui::BulletText("Magnum: %s", MAGNUM_VERSION_STRING);
ImGui::BulletText("Integration: %s", MAGNUMINTEGRATION_VERSION_STRING); ImGui::BulletText("Integration: %s", MAGNUMINTEGRATION_VERSION_STRING);
ImGui::AlignTextToFramePadding();
const char* magnum_website = "https://magnum.graphics"; const char* magnum_website = "https://magnum.graphics";
ImGui::Text(ICON_FA_GLOBE " %s", magnum_website); drawAlignedText(ICON_FA_GLOBE " %s", magnum_website);
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) { if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(magnum_website); ImGui::SetClipboardText(magnum_website);
@ -146,9 +143,8 @@ void SaveTool::drawAbout() {
if(ImGui::TreeNodeEx("Dear ImGui", ImGuiTreeNodeFlags_SpanAvailWidth)) { if(ImGui::TreeNodeEx("Dear ImGui", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::Text("Version used: %s", IMGUI_VERSION); ImGui::Text("Version used: %s", IMGUI_VERSION);
ImGui::AlignTextToFramePadding();
const char* imgui_repo = "https://github.com/ocornut/imgui"; const char* imgui_repo = "https://github.com/ocornut/imgui";
ImGui::Text(ICON_FA_GITHUB " %s", imgui_repo); drawAlignedText(ICON_FA_GITHUB " %s", imgui_repo);
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) { if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(imgui_repo); ImGui::SetClipboardText(imgui_repo);
@ -173,9 +169,8 @@ void SaveTool::drawAbout() {
if(ImGui::TreeNodeEx("Simple DirectMedia Layer (SDL) 2", ImGuiTreeNodeFlags_SpanAvailWidth)) { 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::Text("Version used: %i.%i.%i", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
ImGui::AlignTextToFramePadding();
const char* sdl_website = "https://www.libsdl.org/"; const char* sdl_website = "https://www.libsdl.org/";
ImGui::Text(ICON_FA_GLOBE " %s", sdl_website); drawAlignedText(ICON_FA_GLOBE " %s", sdl_website);
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) { if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(sdl_website); ImGui::SetClipboardText(sdl_website);
@ -200,9 +195,8 @@ void SaveTool::drawAbout() {
if(ImGui::TreeNodeEx("libzip", ImGuiTreeNodeFlags_SpanAvailWidth)) { if(ImGui::TreeNodeEx("libzip", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::Text("Version used: %s", LIBZIP_VERSION); ImGui::Text("Version used: %s", LIBZIP_VERSION);
ImGui::AlignTextToFramePadding();
const char* libzip_website = "https://libzip.org/"; const char* libzip_website = "https://libzip.org/";
ImGui::Text(ICON_FA_GLOBE " %s", libzip_website); drawAlignedText(ICON_FA_GLOBE " %s", libzip_website);
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) { if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(libzip_website); ImGui::SetClipboardText(libzip_website);
@ -226,9 +220,8 @@ void SaveTool::drawAbout() {
} }
if(ImGui::TreeNodeEx("Entropia File System Watcher (efsw)", ImGuiTreeNodeFlags_SpanAvailWidth)) { if(ImGui::TreeNodeEx("Entropia File System Watcher (efsw)", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::AlignTextToFramePadding();
const char* efsw_repo = "https://github.com/SpartanJ/efsw"; const char* efsw_repo = "https://github.com/SpartanJ/efsw";
ImGui::Text(ICON_FA_GITHUB " %s", efsw_repo); drawAlignedText(ICON_FA_GITHUB " %s", efsw_repo);
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) { if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(efsw_repo); ImGui::SetClipboardText(efsw_repo);
@ -252,9 +245,8 @@ void SaveTool::drawAbout() {
} }
if(ImGui::TreeNodeEx("C++ Requests (cpr)", ImGuiTreeNodeFlags_SpanAvailWidth)) { if(ImGui::TreeNodeEx("C++ Requests (cpr)", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::AlignTextToFramePadding();
const char* cpr_website = "https://whoshuu.github.io/cpr/"; const char* cpr_website = "https://whoshuu.github.io/cpr/";
ImGui::Text(ICON_FA_GLOBE " %s", cpr_website); drawAlignedText(ICON_FA_GLOBE " %s", cpr_website);
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) { if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(cpr_website); ImGui::SetClipboardText(cpr_website);
@ -278,9 +270,8 @@ void SaveTool::drawAbout() {
} }
if(ImGui::TreeNodeEx("JSON for Modern C++ (aka json.hpp)", ImGuiTreeNodeFlags_SpanAvailWidth)) { if(ImGui::TreeNodeEx("JSON for Modern C++ (aka json.hpp)", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::AlignTextToFramePadding();
const char* json_website = "https://json.nlohmann.me/"; const char* json_website = "https://json.nlohmann.me/";
ImGui::Text(ICON_FA_GLOBE " %s", json_website); drawAlignedText(ICON_FA_GLOBE " %s", json_website);
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) { if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(json_website); ImGui::SetClipboardText(json_website);
@ -305,9 +296,8 @@ void SaveTool::drawAbout() {
if(ImGui::TreeNodeEx("Font Awesome", ImGuiTreeNodeFlags_SpanAvailWidth)) { if(ImGui::TreeNodeEx("Font Awesome", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::TextUnformatted("Version used: 5.15.3"); ImGui::TextUnformatted("Version used: 5.15.3");
ImGui::AlignTextToFramePadding();
const char* fa_website = "https://fontawesome.com/"; const char* fa_website = "https://fontawesome.com/";
ImGui::Text(ICON_FA_GLOBE " %s", fa_website); drawAlignedText(ICON_FA_GLOBE " %s", fa_website);
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) { if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(fa_website); ImGui::SetClipboardText(fa_website);
@ -323,9 +313,8 @@ void SaveTool::drawAbout() {
} }
if(ImGui::TreeNodeEx("IconFontCppHeaders", ImGuiTreeNodeFlags_SpanAvailWidth)) { if(ImGui::TreeNodeEx("IconFontCppHeaders", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::AlignTextToFramePadding();
const char* icon_repo = "https://github.com/juliettef/IconFontCppHeaders"; const char* icon_repo = "https://github.com/juliettef/IconFontCppHeaders";
ImGui::Text(ICON_FA_GITHUB " %s", icon_repo); drawAlignedText(ICON_FA_GITHUB " %s", icon_repo);
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) { if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(icon_repo); ImGui::SetClipboardText(icon_repo);

View file

@ -1,5 +1,5 @@
// MassBuilderSaveTool // MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin // Copyright (C) 2021-2022 Guillaume Jacquemin
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@ -55,8 +55,7 @@ void SaveTool::drawMainMenu() {
ImGui::Separator(); ImGui::Separator();
if(ImGui::BeginMenu(ICON_FA_COG " Settings")) { if(ImGui::BeginMenu(ICON_FA_COG " Settings")) {
ImGui::AlignTextToFramePadding(); drawAlignedText("Frame limiter:");
ImGui::TextUnformatted("Frame limiter:");
ImGui::SameLine(); ImGui::SameLine();
static UnsignedByte selection = static_cast<UnsignedByte>(_framelimit); static UnsignedByte selection = static_cast<UnsignedByte>(_framelimit);
@ -118,8 +117,7 @@ void SaveTool::drawMainMenu() {
} }
if(_updateAvailable) { if(_updateAvailable) {
ImGui::AlignTextToFramePadding(); drawAlignedText("Version %s is available.", _latestVersion.c_str());
ImGui::Text("Version %s is available.", _latestVersion.c_str());
if(ImGui::Button(ICON_FA_FILE_SIGNATURE " Release notes")) { if(ImGui::Button(ICON_FA_FILE_SIGNATURE " Release notes")) {
openUri(_releaseLink); openUri(_releaseLink);
} }
@ -181,6 +179,20 @@ void SaveTool::drawMainMenu() {
#endif #endif
if(ImGui::BeginMenu("Help")) { if(ImGui::BeginMenu("Help")) {
if(ImGui::BeginMenu(ICON_FA_BOOK " ImGui user guide")) {
ImGui::BulletText("CTRL+Click on a slider or drag box to input value as text.");
ImGui::BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields.");
ImGui::BulletText("While inputing text:\n");
ImGui::Indent();
ImGui::BulletText("CTRL+Left/Right to word jump.");
ImGui::BulletText("CTRL+A or double-click to select all.");
ImGui::BulletText("CTRL+X/C/V to use clipboard cut/copy/paste.");
ImGui::BulletText("CTRL+Z,CTRL+Y to undo/redo.");
ImGui::BulletText("ESCAPE to revert.");
ImGui::BulletText("You can apply arithmetic operators +,*,/ on numerical values.\nUse +- to subtract.");
ImGui::Unindent();
ImGui::EndMenu();
}
ImGui::MenuItem(ICON_FA_INFO_CIRCLE " About", nullptr, &_aboutPopup); ImGui::MenuItem(ICON_FA_INFO_CIRCLE " About", nullptr, &_aboutPopup);
ImGui::EndMenu(); ImGui::EndMenu();

View file

@ -1,5 +1,5 @@
// MassBuilderSaveTool // MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin // Copyright (C) 2021-2022 Guillaume Jacquemin
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
// MassBuilderSaveTool // MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin // Copyright (C) 2021-2022 Guillaume Jacquemin
// //
// This program is free software: you can redistribute it and/or modify // This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by

View file

@ -0,0 +1,126 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cstring>
#include <string>
#include "BinaryReader.h"
BinaryReader::BinaryReader(const std::string& filename) {
_file = std::fopen(filename.c_str(), "rb");
if(!_file) {
Utility::Error{} << "Couldn't open" << filename.c_str() << "for reading:\n"
<< std::strerror(errno);
}
}
BinaryReader::~BinaryReader() {
closeFile();
}
auto BinaryReader::open() -> bool {
return _file;
}
auto BinaryReader::eof() -> bool {
return std::feof(_file) != 0;
}
auto BinaryReader::position() -> Long {
return _ftelli64(_file);
}
auto BinaryReader::seek(Long position) -> bool {
return _fseeki64(_file, position, SEEK_SET) == 0;
}
void BinaryReader::closeFile() {
std::fclose(_file);
_file = nullptr;
}
auto BinaryReader::readChar(char& value) -> bool {
return std::fread(&value, sizeof(char), 1, _file) == 1;
}
auto BinaryReader::readByte(Byte& value) -> bool {
return std::fread(&value, sizeof(Byte), 1, _file) == 1;
}
auto BinaryReader::readUnsignedByte(UnsignedByte& value) -> bool {
return std::fread(&value, sizeof(UnsignedByte), 1, _file) == 1;
}
auto BinaryReader::readShort(Short& value) -> bool {
return std::fread(&value, sizeof(Short), 1, _file) == 1;
}
auto BinaryReader::readUnsignedShort(UnsignedShort& value) -> bool {
return std::fread(&value, sizeof(UnsignedShort), 1, _file) == 1;
}
auto BinaryReader::readInt(Int& value) -> bool {
return std::fread(&value, sizeof(Int), 1, _file) == 1;
}
auto BinaryReader::readUnsignedInt(UnsignedInt& value) -> bool {
return std::fread(&value, sizeof(UnsignedInt), 1, _file) == 1;
}
auto BinaryReader::readLong(Long& value) -> bool {
return std::fread(&value, sizeof(Long), 1, _file) == 1;
}
auto BinaryReader::readUnsignedLong(UnsignedLong& value) -> bool {
return std::fread(&value, sizeof(UnsignedLong), 1, _file) == 1;
}
auto BinaryReader::readFloat(Float& value) -> bool {
return std::fread(&value, sizeof(Float), 1, _file) == 1;
}
auto BinaryReader::readDouble(Double& value) -> bool {
return std::fread(&value, sizeof(Double), 1, _file) == 1;
}
auto BinaryReader::readArray(Containers::Array<char>& array, std::size_t count) -> bool {
if(array.size() < count) {
array = Containers::Array<char>{ValueInit, count};
}
return std::fread(array.data(), sizeof(char), count, _file) == count;
}
auto BinaryReader::readUEString(std::string& str) -> bool {
UnsignedInt length = 0;
if(!readUnsignedInt(length) || length == 0) {
return false;
}
str = std::string{};
str.resize(length - 1);
return std::fread(&str[0], sizeof(char), length, _file) == length;
}
auto BinaryReader::peekChar() -> Int {
Int c;
c = std::fgetc(_file);
std::ungetc(c, _file);
return c;
}

View file

@ -0,0 +1,72 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cstdio>
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/StaticArray.h>
#include <Corrade/Utility/StlForwardString.h>
#include <Magnum/Types.h>
using namespace Corrade;
using namespace Magnum;
class BinaryReader {
public:
explicit BinaryReader(const std::string& filename);
~BinaryReader();
auto open() -> bool;
auto eof() -> bool;
auto position() -> Long;
auto seek(Long position) -> bool;
void closeFile();
auto readChar(char& value) -> bool;
auto readByte(Byte& value) -> bool;
auto readUnsignedByte(UnsignedByte& value) -> bool;
auto readShort(Short& value) -> bool;
auto readUnsignedShort(UnsignedShort& value) -> bool;
auto readInt(Int& value) -> bool;
auto readUnsignedInt(UnsignedInt& value) -> bool;
auto readLong(Long& value) -> bool;
auto readUnsignedLong(UnsignedLong& value) -> bool;
auto readFloat(Float& value) -> bool;
auto readDouble(Double& value) -> bool;
auto readArray(Containers::Array<char>& array, std::size_t count) -> bool;
template<typename T>
auto readValue(T& value) -> bool {
return fread(&value, sizeof(T), 1, _file) == sizeof(T);
}
template<std::size_t S>
auto readStaticArray(Containers::StaticArray<S, char>& array) -> bool {
return std::fread(array.data(), sizeof(char), S, _file) == S;
}
auto readUEString(std::string& str) -> bool;
auto peekChar() -> Int;
private:
std::FILE* _file = nullptr;
};

View file

@ -0,0 +1,137 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cstring>
#include <string>
#include "BinaryWriter.h"
BinaryWriter::BinaryWriter(const std::string& filename) {
_file = std::fopen(filename.c_str(), "wb");
if(!_file) {
Utility::Error{} << "Couldn't open" << filename.c_str() << "for reading:\n"
<< std::strerror(errno);
}
}
BinaryWriter::~BinaryWriter() {
closeFile();
}
auto BinaryWriter::open() -> bool {
return _file;
}
void BinaryWriter::closeFile() {
std::fflush(_file);
std::fclose(_file);
_file = nullptr;
}
auto BinaryWriter::position() -> Long {
return _ftelli64(_file);
}
auto BinaryWriter::array() const -> Containers::ArrayView<const char> {
return _data;
}
auto BinaryWriter::arrayPosition() const -> UnsignedLong {
return _index;
}
auto BinaryWriter::flushToFile() -> bool {
bool ret = writeArray(_data);
std::fflush(_file);
_data = Containers::Array<char>{};
_index = 0;
return ret;
}
auto BinaryWriter::writeChar(char value) -> bool {
return std::fwrite(&value, sizeof(char), 1, _file) == 1;
}
auto BinaryWriter::writeByte(Byte value) -> bool {
return std::fwrite(&value, sizeof(Byte), 1, _file) == 1;
}
auto BinaryWriter::writeUnsignedByte(UnsignedByte value) -> bool {
return std::fwrite(&value, sizeof(UnsignedByte), 1, _file) == 1;
}
auto BinaryWriter::writeShort(Short value) -> bool {
return std::fwrite(&value, sizeof(Short), 1, _file) == 1;
}
auto BinaryWriter::writeUnsignedShort(UnsignedShort value) -> bool {
return std::fwrite(&value, sizeof(UnsignedShort), 1, _file) == 1;
}
auto BinaryWriter::writeInt(Int value) -> bool {
return std::fwrite(&value, sizeof(Int), 1, _file) == 1;
}
auto BinaryWriter::writeUnsignedInt(UnsignedInt value) -> bool {
return std::fwrite(&value, sizeof(UnsignedInt), 1, _file) == 1;
}
auto BinaryWriter::writeLong(Long value) -> bool {
return std::fwrite(&value, sizeof(Long), 1, _file) == 1;
}
auto BinaryWriter::writeUnsignedLong(UnsignedLong value) -> bool {
return std::fwrite(&value, sizeof(UnsignedLong), 1, _file) == 1;
}
auto BinaryWriter::writeFloat(Float value) -> bool {
return std::fwrite(&value, sizeof(Float), 1, _file) == 1;
}
auto BinaryWriter::writeDouble(Double value) -> bool {
return std::fwrite(&value, sizeof(Double), 1, _file) == 1;
}
auto BinaryWriter::writeArray(Containers::ArrayView<const char> array) -> bool {
if(array.size() == 0) {
return false;
}
return std::fwrite(array.data(), sizeof(char), array.size(), _file) == array.size();
}
auto BinaryWriter::writeUEString(const std::string& str) -> bool {
if(str.length() > UINT32_MAX) {
Utility::Error{} << "BinaryWriter::writeUEString(): string is too big.";
return false;
}
writeUnsignedInt(static_cast<UnsignedInt>(str.length()) + 1);
if(str.length() > 0) {
std::size_t count = std::fwrite(&str[0], sizeof(char), str.length(), _file);
if(count != str.length()) {
return false;
}
}
return writeChar('\0');
}
auto BinaryWriter::writeUEStringToArray(const std::string& value) -> UnsignedLong {
Containers::ArrayView<const char> view{value.c_str(), value.length()};
return writeValueToArray<UnsignedInt>(UnsignedInt(value.length()) + 1u) + writeDataToArray(view) + writeValueToArray<char>('\0');
}

View file

@ -0,0 +1,110 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cstdio>
#include <Corrade/Containers/ArrayView.h>
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/StaticArray.h>
#include <Corrade/Utility/StlForwardString.h>
#include <Magnum/Types.h>
using namespace Corrade;
using namespace Magnum;
class BinaryWriter {
public:
explicit BinaryWriter(const std::string& filename);
~BinaryWriter();
BinaryWriter(const BinaryWriter& other) = delete;
BinaryWriter& operator=(const BinaryWriter& other) = delete;
BinaryWriter(BinaryWriter&& other) = default;
BinaryWriter& operator=(BinaryWriter&& other) = default;
auto open() -> bool;
void closeFile();
auto position() -> Long;
auto array() const -> Containers::ArrayView<const char>;
auto arrayPosition() const -> UnsignedLong;
auto flushToFile() -> bool;
auto writeByte(Byte value) -> bool;
auto writeChar(char value) -> bool;
auto writeUnsignedByte(UnsignedByte value) -> bool;
auto writeShort(Short value) -> bool;
auto writeUnsignedShort(UnsignedShort value) -> bool;
auto writeInt(Int value) -> bool;
auto writeUnsignedInt(UnsignedInt value) -> bool;
auto writeLong(Long value) -> bool;
auto writeUnsignedLong(UnsignedLong value) -> bool;
auto writeFloat(Float value) -> bool;
auto writeDouble(Double value) -> bool;
auto writeArray(Containers::ArrayView<const char> array) -> bool;
template<std::size_t size>
auto writeString(const char(&str)[size]) -> bool {
return writeArray({str, size - 1});
}
template<std::size_t S>
auto writeStaticArray(Containers::StaticArrayView<S, const char> array) -> bool {
return std::fwrite(array.data(), sizeof(char), S, _file) == S;
}
auto writeUEString(const std::string& str) -> bool;
template<typename T, typename U = std::conditional_t<std::is_trivially_copyable<T>::value, T, T&>>
auto writeValueToArray(U value) -> UnsignedLong {
Containers::ArrayView<T> view{&value, 1};
return writeDataToArray(view);
}
auto writeUEStringToArray(const std::string& value) -> UnsignedLong;
template<typename T>
void writeValueToArrayAt(T& value, UnsignedLong position) {
Containers::ArrayView<T> view{&value, 1};
writeDataToArrayAt(view, position);
}
template<typename T>
auto writeDataToArray(Containers::ArrayView<T> view) -> UnsignedLong {
arrayAppend(_data, Containers::arrayCast<const char>(view));
_index += sizeof(T) * view.size();
return sizeof(T) * view.size();
}
template<typename T>
void writeDataToArrayAt(Containers::ArrayView<T> view, UnsignedLong position) {
auto casted_view = Containers::arrayCast<const char>(view);
for(UnsignedLong i = 0; i < casted_view.size(); i++) {
_data[position + i] = casted_view[i];
}
}
private:
FILE* _file = nullptr;
Containers::Array<char> _data;
UnsignedLong _index = 0;
};

76
src/UESaveFile/Debug.cpp Normal file
View file

@ -0,0 +1,76 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "Types/UnrealPropertyBase.h"
#include "Types/ArrayProperty.h"
#include "Types/SetProperty.h"
#include "Types/StructProperty.h"
#include "Types/GenericStructProperty.h"
#include "Debug.h"
Utility::Debug& operator<<(Utility::Debug& debug, const ArrayProperty* prop) {
return debug << (*prop->name).c_str() << Utility::Debug::nospace << ":" <<
prop->propertyType.c_str() << "of" << prop->items.size() << prop->itemType.c_str();
}
Utility::Debug& operator<<(Utility::Debug& debug, const SetProperty* prop) {
return debug << (*prop->name).c_str() << Utility::Debug::nospace << ":" <<
prop->propertyType.c_str() << "of" << prop->items.size() << prop->itemType.c_str();
}
Utility::Debug& operator<<(Utility::Debug& debug, const GenericStructProperty* prop) {
debug << (*prop->name).c_str() << Utility::Debug::nospace << ":" <<
prop->structType.c_str() << "(" << Utility::Debug::nospace << prop->propertyType.c_str() << Utility::Debug::nospace <<
") Contents:";
for(const auto& item : prop->properties) {
debug << "\n " << Utility::Debug::nospace << item.get();
}
return debug;
}
Utility::Debug& operator<<(Utility::Debug& debug, const StructProperty* prop) {
auto cast = dynamic_cast<const GenericStructProperty*>(prop);
if(cast) {
return debug << cast;
}
return debug << (*prop->name).c_str() << Utility::Debug::nospace << ":" <<
prop->structType.c_str() << "(" << Utility::Debug::nospace << prop->propertyType.c_str() << Utility::Debug::nospace << ")";
}
Utility::Debug& operator<<(Utility::Debug& debug, const UnrealPropertyBase* prop) {
if(prop->propertyType == "ArrayProperty") {
auto array_prop = dynamic_cast<const ArrayProperty*>(prop);
if(array_prop) {
return debug << array_prop;
}
}
else if(prop->propertyType == "SetProperty") {
auto set_prop = dynamic_cast<const SetProperty*>(prop);
if(set_prop) {
return debug << set_prop;
}
}
else if(prop->propertyType == "StructProperty") {
auto struct_prop = dynamic_cast<const StructProperty*>(prop);
if(struct_prop) {
return debug << struct_prop;
}
}
return debug << (*prop->name).c_str() << Utility::Debug::nospace << ":" << prop->propertyType.c_str();
}

33
src/UESaveFile/Debug.h Normal file
View file

@ -0,0 +1,33 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <Corrade/Utility/Debug.h>
struct ArrayProperty;
struct SetProperty;
struct GenericStructProperty;
struct StructProperty;
struct UnrealPropertyBase;
using namespace Corrade;
Utility::Debug& operator<<(Utility::Debug& debug, const ArrayProperty* prop);
Utility::Debug& operator<<(Utility::Debug& debug, const SetProperty* prop);
Utility::Debug& operator<<(Utility::Debug& debug, const GenericStructProperty* prop);
Utility::Debug& operator<<(Utility::Debug& debug, const StructProperty* prop);
Utility::Debug& operator<<(Utility::Debug& debug, const UnrealPropertyBase* prop);

View file

@ -0,0 +1,254 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <algorithm>
#include "Serialisers/ArrayPropertySerialiser.h"
#include "Serialisers/BoolPropertySerialiser.h"
#include "Serialisers/BytePropertySerialiser.h"
#include "Serialisers/ColourPropertySerialiser.h"
#include "Serialisers/DateTimePropertySerialiser.h"
#include "Serialisers/EnumPropertySerialiser.h"
#include "Serialisers/FloatPropertySerialiser.h"
#include "Serialisers/GuidPropertySerialiser.h"
#include "Serialisers/IntPropertySerialiser.h"
#include "Serialisers/MapPropertySerialiser.h"
#include "Serialisers/ResourcePropertySerialiser.h"
#include "Serialisers/RotatorPropertySerialiser.h"
#include "Serialisers/StringPropertySerialiser.h"
#include "Serialisers/SetPropertySerialiser.h"
#include "Serialisers/StructSerialiser.h"
#include "Serialisers/TextPropertySerialiser.h"
#include "Serialisers/VectorPropertySerialiser.h"
#include "Serialisers/Vector2DPropertySerialiser.h"
#include "Types/NoneProperty.h"
#include "BinaryReader.h"
#include "BinaryWriter.h"
#include "PropertySerialiser.h"
PropertySerialiser::PropertySerialiser() {
arrayAppend(_serialisers, Containers::pointer<ArrayPropertySerialiser>());
arrayAppend(_serialisers, Containers::pointer<BoolPropertySerialiser>());
arrayAppend(_serialisers, Containers::pointer<BytePropertySerialiser>());
arrayAppend(_serialisers, Containers::pointer<ColourPropertySerialiser>());
arrayAppend(_serialisers, Containers::pointer<DateTimePropertySerialiser>());
arrayAppend(_serialisers, Containers::pointer<EnumPropertySerialiser>());
arrayAppend(_serialisers, Containers::pointer<FloatPropertySerialiser>());
arrayAppend(_serialisers, Containers::pointer<GuidPropertySerialiser>());
arrayAppend(_serialisers, Containers::pointer<IntPropertySerialiser>());
arrayAppend(_serialisers, Containers::pointer<MapPropertySerialiser>());
arrayAppend(_serialisers, Containers::pointer<ResourcePropertySerialiser>());
arrayAppend(_serialisers, Containers::pointer<RotatorPropertySerialiser>());
arrayAppend(_serialisers, Containers::pointer<StringPropertySerialiser>());
arrayAppend(_serialisers, Containers::pointer<SetPropertySerialiser>());
arrayAppend(_serialisers, Containers::pointer<TextPropertySerialiser>());
arrayAppend(_serialisers, Containers::pointer<VectorPropertySerialiser>());
arrayAppend(_serialisers, Containers::pointer<Vector2DPropertySerialiser>());
arrayAppend(_serialisers, Containers::pointer<StructSerialiser>());
arrayAppend(_collectionSerialisers, Containers::pointer<StructSerialiser>());
}
auto PropertySerialiser::read(BinaryReader& reader) -> UnrealPropertyBase::ptr {
if(reader.peekChar() < 0 || reader.eof()) {
return nullptr;
}
std::string name;
if(!reader.readUEString(name)) {
return nullptr;
}
if(name == "None") {
return Containers::pointer<NoneProperty>();
}
std::string type;
if(!reader.readUEString(type)) {
return nullptr;
}
UnsignedLong value_length;
if(!reader.readUnsignedLong(value_length)) {
return nullptr;
}
return deserialise(std::move(name), std::move(type), value_length, reader);
}
auto PropertySerialiser::readItem(BinaryReader& reader, std::string type, UnsignedLong value_length, std::string name) -> UnrealPropertyBase::ptr {
if(reader.peekChar() < 0 || reader.eof()) {
return nullptr;
}
return deserialise(std::move(name), std::move(type), value_length, reader);
}
auto PropertySerialiser::readSet(BinaryReader& reader, const std::string& item_type, UnsignedInt count) -> Containers::Array<UnrealPropertyBase::ptr> {
if(reader.peekChar() < 0 || reader.eof()) {
return nullptr;
}
auto serialiser = getCollectionSerialiser(item_type);
Containers::Array<UnrealPropertyBase::ptr> array;
if(serialiser) {
std::string name;
if(!reader.readUEString(name)) {
return nullptr;
}
std::string type;
if(!reader.readUEString(type)) {
return nullptr;
}
UnsignedLong value_length;
if(!reader.readUnsignedLong(value_length)) {
return nullptr;
}
array = serialiser->deserialise(name, type, value_length, count, reader, *this);
for(auto& item : array) {
if(item->name == Containers::NullOpt) {
item->name.emplace(name);
}
}
}
else {
for(UnsignedInt i = 0; i < count; i++) {
auto item = readItem(reader, item_type, UnsignedLong(-1), "");
arrayAppend(array, std::move(item));
}
}
return array;
}
auto PropertySerialiser::deserialise(std::string name, std::string type, UnsignedLong value_length,
BinaryReader& reader) -> UnrealPropertyBase::ptr
{
UnrealPropertyBase::ptr prop;
auto serialiser = getSerialiser(type);
if(serialiser == nullptr) {
return nullptr;
}
prop = serialiser->deserialise(name, type, value_length, reader, *this);
if(!prop) {
!Utility::Error{} << "No prop in" << __func__;
return nullptr;
}
prop->name = std::move(name);
prop->propertyType = std::move(type);
return prop;
}
auto PropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, const std::string& item_type, UnsignedLong& bytes_written,
BinaryWriter& writer) -> bool
{
auto serialiser = getSerialiser(item_type);
if(!serialiser) {
return false;
}
return serialiser->serialise(prop, bytes_written, writer, *this);
}
auto PropertySerialiser::write(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer) -> bool {
if(prop->name == "None" && prop->propertyType == "NoneProperty" && dynamic_cast<NoneProperty*>(prop.get())) {
bytes_written += writer.writeUEStringToArray(*prop->name);
return true;
}
bytes_written += writer.writeUEStringToArray(*prop->name);
bytes_written += writer.writeUEStringToArray(prop->propertyType);
UnsignedLong value_length = 0;
UnsignedLong vl_position = writer.arrayPosition();
bytes_written += writer.writeValueToArray<UnsignedLong>(value_length);
bool ret = serialise(prop, prop->propertyType, value_length, writer);
writer.writeValueToArrayAt(value_length, vl_position);
bytes_written += value_length;
return ret;
}
auto PropertySerialiser::writeItem(UnrealPropertyBase::ptr& prop, const std::string& item_type,
UnsignedLong& bytes_written, BinaryWriter& writer) -> bool
{
if(prop->name == "None" && prop->propertyType == "NoneProperty" && dynamic_cast<NoneProperty*>(prop.get())) {
bytes_written += writer.writeUEStringToArray(*prop->name);
return true;
}
return serialise(prop, item_type, bytes_written, writer);
}
auto PropertySerialiser::writeSet(Containers::ArrayView<UnrealPropertyBase::ptr> props, const std::string& item_type,
UnsignedLong& bytes_written, BinaryWriter& writer) -> bool
{
auto serialiser = getCollectionSerialiser(item_type);
if(serialiser) {
return serialiser->serialise(props, item_type, bytes_written, writer, *this);
}
else {
for(auto& prop : props) {
if(!writeItem(prop, item_type, bytes_written, writer)) {
return false;
}
}
return true;
}
}
auto PropertySerialiser::getSerialiser(const std::string& item_type) -> AbstractUnrealPropertySerialiser* {
for(auto& item : _serialisers) {
for(const std::string& serialiser_type : item->types()) {
if(item_type == serialiser_type) {
return item.get();
}
}
}
return nullptr;
}
auto PropertySerialiser::getCollectionSerialiser(const std::string& item_type) -> AbstractUnrealCollectionPropertySerialiser* {
for(auto& item : _collectionSerialisers) {
for(const std::string& serialiser_type : item->types()) {
if(item_type == serialiser_type) {
return item.get();
}
}
}
return nullptr;
}

View file

@ -0,0 +1,51 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <Corrade/Containers/GrowableArray.h>
#include "Serialisers/AbstractUnrealPropertySerialiser.h"
#include "Serialisers/AbstractUnrealCollectionPropertySerialiser.h"
#include "Types/UnrealPropertyBase.h"
using namespace Corrade;
class BinaryReader;
class BinaryWriter;
class PropertySerialiser {
public:
PropertySerialiser();
auto read(BinaryReader& reader) -> UnrealPropertyBase::ptr;
auto readItem(BinaryReader& reader, std::string type, UnsignedLong value_length, std::string name) -> UnrealPropertyBase::ptr;
auto readSet(BinaryReader& reader, const std::string& item_type, UnsignedInt count) -> Containers::Array<UnrealPropertyBase::ptr>;
auto deserialise(std::string name, std::string type, UnsignedLong value_length, BinaryReader& reader) -> UnrealPropertyBase::ptr;
auto serialise(UnrealPropertyBase::ptr& prop, const std::string& item_type, UnsignedLong& bytes_written, BinaryWriter& writer) -> bool;
auto write(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer) -> bool;
auto writeItem(UnrealPropertyBase::ptr& prop, const std::string& item_type, UnsignedLong& bytes_written, BinaryWriter& writer) -> bool;
auto writeSet(Containers::ArrayView<UnrealPropertyBase::ptr> props, const std::string& item_type, UnsignedLong& bytes_written, BinaryWriter& writer) -> bool;
private:
auto getSerialiser(const std::string& item_type) -> AbstractUnrealPropertySerialiser*;
auto getCollectionSerialiser(const std::string& item_type) -> AbstractUnrealCollectionPropertySerialiser*;
Containers::Array<AbstractUnrealPropertySerialiser::ptr> _serialisers;
Containers::Array<AbstractUnrealCollectionPropertySerialiser::ptr> _collectionSerialisers;
};

View file

@ -0,0 +1,47 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <string>
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/ArrayView.h>
#include <Corrade/Containers/Pointer.h>
#include <Magnum/Types.h>
#include "../Types/UnrealPropertyBase.h"
using namespace Corrade;
using namespace Magnum;
class BinaryReader;
class BinaryWriter;
class PropertySerialiser;
class AbstractUnrealCollectionPropertySerialiser {
public:
using ptr = Containers::Pointer<AbstractUnrealCollectionPropertySerialiser>;
virtual ~AbstractUnrealCollectionPropertySerialiser() = default;
virtual auto types() -> Containers::ArrayView<const std::string> = 0;
virtual auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, UnsignedInt count, BinaryReader& reader, PropertySerialiser& serialiser) -> Containers::Array<UnrealPropertyBase::ptr> = 0;
virtual auto serialise(Containers::ArrayView<UnrealPropertyBase::ptr> props, const std::string& item_type, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool = 0;
};

View file

@ -0,0 +1,46 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <string>
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/Pointer.h>
#include <Magnum/Types.h>
#include "../Types/UnrealPropertyBase.h"
using namespace Corrade;
using namespace Magnum;
class BinaryReader;
class BinaryWriter;
class PropertySerialiser;
class AbstractUnrealPropertySerialiser {
public:
using ptr = Containers::Pointer<AbstractUnrealPropertySerialiser>;
virtual ~AbstractUnrealPropertySerialiser() = default;
virtual auto types() -> Containers::ArrayView<const std::string> = 0;
virtual auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr = 0;
virtual auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool = 0;
};

View file

@ -0,0 +1,46 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <string>
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/ArrayView.h>
#include <Corrade/Containers/Pointer.h>
#include <Magnum/Types.h>
#include "../Types/UnrealPropertyBase.h"
using namespace Corrade;
using namespace Magnum;
class BinaryReader;
class BinaryWriter;
class AbstractUnrealStructSerialiser {
public:
using ptr = Containers::Pointer<AbstractUnrealStructSerialiser>;
virtual ~AbstractUnrealStructSerialiser() = default;
virtual auto supportsType(const std::string& type) -> bool = 0;
virtual auto deserialise(BinaryReader& reader) -> UnrealPropertyBase::ptr = 0;
virtual auto serialise(UnrealPropertyBase::ptr& structProp, BinaryWriter& writer, UnsignedLong& bytes_written) -> bool = 0;
};

View file

@ -0,0 +1,66 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../BinaryReader.h"
#include "../BinaryWriter.h"
#include "../PropertySerialiser.h"
#include "ArrayPropertySerialiser.h"
auto ArrayPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
{
std::string item_type;
if(!reader.readUEString(item_type)) {
return nullptr;
}
char terminator;
if(!reader.readChar(terminator) || terminator != '\0') {
return nullptr;
}
UnsignedInt item_count;
if(!reader.readUnsignedInt(item_count)) {
return nullptr;
}
auto prop = Containers::pointer<ArrayProperty>();
prop->itemType = std::move(item_type);
prop->items = serialiser.readSet(reader, prop->itemType, item_count);
return prop;
}
auto ArrayPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto array_prop = dynamic_cast<ArrayProperty*>(prop.get());
if(!array_prop) {
return false;
}
writer.writeUEStringToArray(array_prop->itemType);
writer.writeValueToArray<char>('\0');
bytes_written += writer.writeValueToArray<UnsignedInt>(UnsignedInt(array_prop->items.size()));
UnsignedLong start_pos = writer.arrayPosition();
UnsignedLong dummy_bytes_written = 0;
bool ret = serialiser.writeSet(array_prop->items, array_prop->itemType, dummy_bytes_written, writer);
bytes_written += writer.arrayPosition() - start_pos;
return ret;
}

View file

@ -0,0 +1,30 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "UnrealPropertySerialiser.h"
#include "../Types/ArrayProperty.h"
class ArrayPropertySerialiser : public UnrealPropertySerialiser<ArrayProperty> {
public:
using ptr = Containers::Pointer<ArrayPropertySerialiser>;
private:
auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
};

View file

@ -0,0 +1,61 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../BinaryReader.h"
#include "../BinaryWriter.h"
#include "BoolPropertySerialiser.h"
auto BoolPropertySerialiser::types() -> Containers::ArrayView<const std::string> {
static const Containers::Array<std::string> types{InPlaceInit, {"BoolProperty"}};
return types;
}
auto BoolPropertySerialiser::deserialise(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
{
if(value_length != 0) {
return nullptr;
}
Short value;
if(!reader.readShort(value)) {
return nullptr;
}
if(value > 1 || value < 0) {
return nullptr;
}
auto prop = Containers::pointer<BoolProperty>();
prop->value = value;
return prop;
}
auto BoolPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto bool_prop = dynamic_cast<BoolProperty*>(prop.get());
if(!bool_prop) {
return false;
}
writer.writeValueToArray<Short>(Short(bool_prop->value));
return true;
}

View file

@ -0,0 +1,32 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "AbstractUnrealPropertySerialiser.h"
#include "../Types/BoolProperty.h"
class BoolPropertySerialiser : public AbstractUnrealPropertySerialiser {
public:
using ptr = Containers::Pointer<BoolPropertySerialiser>;
auto types() -> Containers::ArrayView<const std::string> override;
auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
};

View file

@ -0,0 +1,82 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../BinaryReader.h"
#include "../BinaryWriter.h"
#include "BytePropertySerialiser.h"
auto BytePropertySerialiser::types() -> Containers::ArrayView<const std::string> {
static const Containers::Array<std::string> types{InPlaceInit, {"ByteProperty"}};
return types;
}
auto BytePropertySerialiser::deserialise(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
{
auto prop = Containers::pointer<ByteProperty>();
if(value_length != UnsignedLong(-1)) {
if(!reader.readUEString(prop->enumType)) {
return nullptr;
}
char terminator;
if(!reader.readChar(terminator) || terminator != '\0') {
return nullptr;
}
}
if(!reader.readUEString(prop->enumValue)) {
return nullptr;
}
prop->valueLength = value_length;
//UnsignedInt count = 0;
//if(!reader.readUnsignedInt(count)) {
// return nullptr;
//}
//if(!reader.readArray(prop->value, count)) {
// return nullptr;
//}
return prop;
}
auto BytePropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto byte_prop = dynamic_cast<ByteProperty*>(prop.get());
if(!byte_prop) {
return false;
}
//writer.writeValueToArray<char>('\0');
//bytes_written += writer.writeValueToArray<UnsignedInt>(byte_prop->value.size());
//bytes_written += writer.writeDataToArray<char>(byte_prop->value);
if(byte_prop->valueLength != UnsignedLong(-1)) {
writer.writeUEStringToArray(byte_prop->enumType);
writer.writeValueToArray<char>('\0');
}
bytes_written += writer.writeUEStringToArray(byte_prop->enumValue);
return true;
}

View file

@ -0,0 +1,32 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "AbstractUnrealPropertySerialiser.h"
#include "../Types/ByteProperty.h"
class BytePropertySerialiser : public AbstractUnrealPropertySerialiser {
public:
using ptr = Containers::Pointer<BytePropertySerialiser>;
auto types() -> Containers::ArrayView<const std::string> override;
auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
};

View file

@ -0,0 +1,49 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../BinaryReader.h"
#include "../BinaryWriter.h"
#include "ColourPropertySerialiser.h"
auto ColourPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
{
auto prop = Containers::pointer<ColourStructProperty>();
if(!reader.readFloat(prop->r) || !reader.readFloat(prop->g) ||
!reader.readFloat(prop->b) || !reader.readFloat(prop->a))
{
return nullptr;
}
return prop;
}
auto ColourPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto colour_prop = dynamic_cast<ColourStructProperty*>(prop.get());
if(!colour_prop) {
return false;
}
bytes_written += writer.writeValueToArray<Float>(colour_prop->r) + writer.writeValueToArray<Float>(colour_prop->g) +
writer.writeValueToArray<Float>(colour_prop->b) + writer.writeValueToArray<Float>(colour_prop->a);
return true;
}

View file

@ -0,0 +1,30 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "UnrealPropertySerialiser.h"
#include "../Types/ColourStructProperty.h"
class ColourPropertySerialiser : public UnrealPropertySerialiser<ColourStructProperty> {
public:
using ptr = Containers::Pointer<ColourPropertySerialiser>;
private:
auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
};

View file

@ -0,0 +1,46 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../BinaryReader.h"
#include "../BinaryWriter.h"
#include "DateTimePropertySerialiser.h"
auto DateTimePropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
{
auto prop = Containers::pointer<DateTimeStructProperty>();
if(!reader.readUnsignedLong(prop->timestamp)) {
return nullptr;
}
return prop;
}
auto DateTimePropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto dt_prop = dynamic_cast<DateTimeStructProperty*>(prop.get());
if(!dt_prop) {
return false;
}
bytes_written += writer.writeValueToArray<UnsignedLong>(dt_prop->timestamp);
return true;
}

View file

@ -0,0 +1,30 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "UnrealPropertySerialiser.h"
#include "../Types/DateTimeStructProperty.h"
class DateTimePropertySerialiser : public UnrealPropertySerialiser<DateTimeStructProperty> {
public:
using ptr = Containers::Pointer<DateTimePropertySerialiser>;
private:
auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
};

View file

@ -0,0 +1,62 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../BinaryReader.h"
#include "../BinaryWriter.h"
#include "EnumPropertySerialiser.h"
auto EnumPropertySerialiser::types() -> Containers::ArrayView<const std::string> {
static const Containers::Array<std::string> types{InPlaceInit, {"EnumProperty"}};
return types;
}
auto EnumPropertySerialiser::deserialise(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
{
auto prop = Containers::pointer<EnumProperty>();
if(!reader.readUEString(prop->enumType)) {
return nullptr;
}
char terminator;
if(!reader.readChar(terminator) || terminator != '\0') {
return nullptr;
}
if(!reader.readUEString(prop->value)) {
return nullptr;
}
return prop;
}
auto EnumPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto enum_prop = dynamic_cast<EnumProperty*>(prop.get());
if(!enum_prop) {
return false;
}
writer.writeUEStringToArray(enum_prop->enumType);
writer.writeValueToArray<char>('\0');
bytes_written += writer.writeUEStringToArray(enum_prop->value);
return true;
}

View file

@ -0,0 +1,32 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "AbstractUnrealPropertySerialiser.h"
#include "../Types/EnumProperty.h"
class EnumPropertySerialiser : public AbstractUnrealPropertySerialiser {
public:
using ptr = Containers::Pointer<EnumPropertySerialiser>;
auto types() -> Containers::ArrayView<const std::string> override;
auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
};

View file

@ -0,0 +1,57 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../BinaryReader.h"
#include "../BinaryWriter.h"
#include "FloatPropertySerialiser.h"
auto FloatPropertySerialiser::types() -> Containers::ArrayView<const std::string> {
static const Containers::Array<std::string> types{InPlaceInit, {"FloatProperty"}};
return types;
}
auto FloatPropertySerialiser::deserialise(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
{
auto prop = Containers::pointer<FloatProperty>();
char terminator;
if(!reader.readChar(terminator) || terminator != '\0') {
return nullptr;
}
if(!reader.readFloat(prop->value)) {
return nullptr;
}
return prop;
}
auto FloatPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto float_prop = dynamic_cast<FloatProperty*>(prop.get());
if(!float_prop) {
return false;
}
writer.writeValueToArray<char>('\0');
bytes_written += writer.writeValueToArray<Float>(float_prop->value);
return true;
}

View file

@ -0,0 +1,32 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "AbstractUnrealPropertySerialiser.h"
#include "../Types/FloatProperty.h"
class FloatPropertySerialiser : public AbstractUnrealPropertySerialiser {
public:
using ptr = Containers::Pointer<FloatPropertySerialiser>;
auto types() -> Containers::ArrayView<const std::string> override;
auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
};

View file

@ -0,0 +1,47 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../BinaryReader.h"
#include "../BinaryWriter.h"
#include "GuidPropertySerialiser.h"
auto GuidPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
{
auto prop = Containers::pointer<GuidStructProperty>();
if(!reader.readStaticArray(prop->guid)) {
Utility::Error{} << "Couldn't read guid in" << __func__;
return nullptr;
}
return prop;
}
auto GuidPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto guid_prop = dynamic_cast<GuidStructProperty*>(prop.get());
if(!guid_prop) {
return false;
}
bytes_written += writer.writeDataToArray<char>({guid_prop->guid.data(), guid_prop->guid.size()});
return true;
}

View file

@ -0,0 +1,31 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "UnrealPropertySerialiser.h"
#include "../Types/GuidStructProperty.h"
class GuidPropertySerialiser : public UnrealPropertySerialiser<GuidStructProperty> {
public:
using ptr = Containers::Pointer<GuidPropertySerialiser>;
private:
auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
};

View file

@ -0,0 +1,66 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../BinaryReader.h"
#include "../BinaryWriter.h"
#include "IntPropertySerialiser.h"
auto IntPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
{
auto prop = Containers::pointer<IntProperty>();
if(value_length == UnsignedLong(-1)) {
if(!reader.readInt(prop->value)) {
return nullptr;
}
prop->valueLength = UnsignedLong(-1);
return prop;
}
char terminator;
if(!reader.readChar(terminator) || terminator != '\0') {
return nullptr;
}
if(!reader.readInt(prop->value)) {
return nullptr;
}
prop->name.emplace(name);
return prop;
}
auto IntPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto int_prop = dynamic_cast<IntProperty*>(prop.get());
if(!int_prop) {
return false;
}
if(prop->valueLength != UnsignedLong(-1)) {
writer.writeValueToArray<char>('\0');
}
bytes_written += writer.writeValueToArray<Int>(int_prop->value);
return true;
}

View file

@ -0,0 +1,30 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "UnrealPropertySerialiser.h"
#include "../Types/IntProperty.h"
class IntPropertySerialiser : public UnrealPropertySerialiser<IntProperty> {
public:
using ptr = Containers::Pointer<IntPropertySerialiser>;
private:
auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
};

View file

@ -0,0 +1,140 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../BinaryReader.h"
#include "../BinaryWriter.h"
#include "../PropertySerialiser.h"
#include "../Types/NoneProperty.h"
#include "MapPropertySerialiser.h"
auto MapPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
{
auto prop = Containers::pointer<MapProperty>();
if(!reader.readUEString(prop->keyType)) {
return nullptr;
}
if(!reader.readUEString(prop->valueType)) {
return nullptr;
}
char terminator;
if(!reader.readChar(terminator) || terminator != '\0') {
return nullptr;
}
UnsignedInt null;
if(!reader.readUnsignedInt(null) || null != 0u) {
return nullptr;
}
UnsignedInt count;
if(!reader.readUnsignedInt(count)) {
return nullptr;
}
// Begin dirty code because the MapProperty format doesn't seem to match any of the GVAS reading stuff I've found,
// so I'm just gonna write stuff that matches the only MapProperty I can find in MB's save files.
arrayReserve(prop->map, count);
for(UnsignedInt i = 0; i < count; i++) {
MapProperty::KeyValuePair pair;
if(prop->keyType == "IntProperty" || prop->keyType == "StrProperty") {
pair.key = serialiser.readItem(reader, prop->keyType, -1, name);
if(pair.key == nullptr) {
return nullptr;
}
}
else { // Add other branches depending on key type, should more maps appear in the future.
return nullptr;
}
UnrealPropertyBase::ptr value_item;
if(prop->valueType == "StructProperty") {
while((value_item = serialiser.read(reader)) != nullptr) {
arrayAppend(pair.values, std::move(value_item));
if(pair.values.back()->name == "None" &&
pair.values.back()->propertyType == "NoneProperty" &&
dynamic_cast<NoneProperty*>(pair.values.back().get()) != nullptr)
{
break;
}
}
}
else if(prop->valueType == "ByteProperty") {
if((value_item = serialiser.readItem(reader, prop->valueType, -1, name)) == nullptr) {
return nullptr;
}
arrayAppend(pair.values, std::move(value_item));
}
arrayAppend(prop->map, std::move(pair));
}
// End dirty code
return prop;
}
auto MapPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto map_prop = dynamic_cast<MapProperty*>(prop.get());
if(!map_prop) {
return false;
}
writer.writeUEStringToArray(map_prop->keyType);
writer.writeUEStringToArray(map_prop->valueType);
writer.writeValueToArray<char>('\0');
UnsignedLong value_start = writer.arrayPosition();
writer.writeValueToArray<UnsignedInt>(0u);
writer.writeValueToArray<UnsignedInt>(UnsignedInt(map_prop->map.size()));
UnsignedLong dummy_bytes_written = 0;
for(auto& pair : map_prop->map) {
if(!serialiser.writeItem(pair.key, map_prop->keyType, dummy_bytes_written, writer)) {
return false;
}
for(auto& value : pair.values) {
if(map_prop->valueType == "StructProperty") {
if(!serialiser.write(value, dummy_bytes_written, writer)) {
return false;
}
}
else {
if(!serialiser.writeItem(value, map_prop->valueType, dummy_bytes_written, writer)) {
return false;
}
}
}
}
bytes_written += (writer.arrayPosition() - value_start);
return true;
}

View file

@ -0,0 +1,30 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "UnrealPropertySerialiser.h"
#include "../Types/MapProperty.h"
class MapPropertySerialiser : public UnrealPropertySerialiser<MapProperty> {
public:
using ptr = Containers::Pointer<MapPropertySerialiser>;
private:
auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
};

View file

@ -0,0 +1,103 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../BinaryReader.h"
#include "../BinaryWriter.h"
#include "../PropertySerialiser.h"
#include "../Types/IntProperty.h"
#include "../Types/NoneProperty.h"
#include "ResourcePropertySerialiser.h"
auto ResourcePropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
{
auto prop = Containers::pointer<ResourceItemValue>();
std::string str;
if(!reader.readUEString(str) || str != "ID_4_AAE08F17428E229EC7A2209F51081A21") {
return nullptr;
}
if(!reader.readUEString(str) || str != "IntProperty") {
return nullptr;
}
if(!reader.readUnsignedLong(value_length) || value_length != 4ull) {
return nullptr;
}
char terminator;
if(!reader.readChar(terminator) || terminator != '\0') {
return nullptr;
}
if(!reader.readInt(prop->id)) {
return nullptr;
}
if(!reader.readUEString(str) || str != "Quantity_3_560F09B5485C365D3041888910019CE3") {
return nullptr;
}
if(!reader.readUEString(str) || str != "IntProperty") {
return nullptr;
}
if(!reader.readUnsignedLong(value_length) || value_length != 4ull) {
return nullptr;
}
if(!reader.readChar(terminator) || terminator != '\0') {
return nullptr;
}
if(!reader.readInt(prop->quantity)) {
return nullptr;
}
if(!reader.readUEString(str) || str != "None") {
return nullptr;
}
return prop;
}
auto ResourcePropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto res_prop = dynamic_cast<ResourceItemValue*>(prop.get());
if(!res_prop) {
return false;
}
bytes_written += writer.writeUEStringToArray("ID_4_AAE08F17428E229EC7A2209F51081A21") +
writer.writeUEStringToArray("IntProperty") +
writer.writeValueToArray<UnsignedLong>(4ull) +
writer.writeValueToArray<char>('\0') +
writer.writeValueToArray<Int>(res_prop->id);
bytes_written += writer.writeUEStringToArray("Quantity_3_560F09B5485C365D3041888910019CE3") +
writer.writeUEStringToArray("IntProperty") +
writer.writeValueToArray<UnsignedLong>(4ull) +
writer.writeValueToArray<char>('\0') +
writer.writeValueToArray<Int>(res_prop->quantity);
bytes_written += writer.writeUEStringToArray("None");
return true;
}

View file

@ -0,0 +1,30 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "UnrealPropertySerialiser.h"
#include "../Types/ResourceItemValue.h"
class ResourcePropertySerialiser : public UnrealPropertySerialiser<ResourceItemValue> {
public:
using ptr = Containers::Pointer<ResourcePropertySerialiser>;
private:
auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
};

View file

@ -0,0 +1,47 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../BinaryReader.h"
#include "../BinaryWriter.h"
#include "RotatorPropertySerialiser.h"
auto RotatorPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
{
auto prop = Containers::pointer<RotatorStructProperty>();
if(!reader.readFloat(prop->x) || !reader.readFloat(prop->y) || !reader.readFloat(prop->z)) {
return nullptr;
}
return prop;
}
auto RotatorPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto rotator = dynamic_cast<RotatorStructProperty*>(prop.get());
if(!rotator) {
return false;
}
bytes_written += writer.writeValueToArray<Float>(rotator->x) + writer.writeValueToArray<Float>(rotator->y) +
writer.writeValueToArray<Float>(rotator->z);
return true;
}

View file

@ -0,0 +1,30 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "UnrealPropertySerialiser.h"
#include "../Types/RotatorStructProperty.h"
class RotatorPropertySerialiser : public UnrealPropertySerialiser<RotatorStructProperty> {
public:
using ptr = Containers::Pointer<RotatorPropertySerialiser>;
private:
auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
};

View file

@ -0,0 +1,73 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../BinaryReader.h"
#include "../BinaryWriter.h"
#include "../PropertySerialiser.h"
#include "SetPropertySerialiser.h"
auto SetPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
{
std::string item_type;
if(!reader.readUEString(item_type)) {
return nullptr;
}
char terminator;
if(!reader.readChar(terminator) || terminator != '\0') {
return nullptr;
}
UnsignedInt four_bytes;
if(!reader.readUnsignedInt(four_bytes) || four_bytes != 0u) {
return nullptr;
}
UnsignedInt item_count;
if(!reader.readUnsignedInt(item_count)) {
return nullptr;
}
auto prop = Containers::pointer<SetProperty>();
prop->itemType = std::move(item_type);
prop->items = serialiser.readSet(reader, prop->itemType, item_count);
return prop;
}
auto SetPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto set_prop = dynamic_cast<SetProperty*>(prop.get());
if(!set_prop) {
return false;
}
writer.writeUEStringToArray(set_prop->itemType);
writer.writeValueToArray<char>('\0');
bytes_written += writer.writeValueToArray<UnsignedInt>(0u);
bytes_written += writer.writeValueToArray<UnsignedInt>(UnsignedInt(set_prop->items.size()));
UnsignedLong start_pos = writer.arrayPosition();
UnsignedLong dummy_bytes_written = 0;
serialiser.writeSet(set_prop->items, set_prop->itemType, dummy_bytes_written, writer);
bytes_written += writer.arrayPosition() - start_pos;
return true;
}

View file

@ -0,0 +1,30 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "UnrealPropertySerialiser.h"
#include "../Types/SetProperty.h"
class SetPropertySerialiser : public UnrealPropertySerialiser<SetProperty> {
public:
using ptr = Containers::Pointer<SetPropertySerialiser>;
private:
auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
};

View file

@ -0,0 +1,64 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../BinaryReader.h"
#include "../BinaryWriter.h"
#include "StringPropertySerialiser.h"
auto StringPropertySerialiser::types() -> Containers::ArrayView<const std::string> {
static const Containers::Array<std::string> types{InPlaceInit, {"NameProperty", "StrProperty", "SoftObjectProperty", "ObjectProperty"}};
return types;
}
auto StringPropertySerialiser::deserialise(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
{
auto prop = Containers::pointer<StringProperty>(type);
if(value_length != UnsignedLong(-1)) {
char terminator;
if(!reader.readChar(terminator) || terminator != '\0') {
return nullptr;
}
}
if(!reader.readUEString(prop->value)) {
return nullptr;
}
prop->valueLength = value_length;
return prop;
}
auto StringPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto str_prop = dynamic_cast<StringProperty*>(prop.get());
if(!str_prop) {
return false;
}
if(str_prop->valueLength != UnsignedLong(-1)) {
writer.writeValueToArray<char>('\0');
}
bytes_written += writer.writeUEStringToArray(str_prop->value);
return true;
}

View file

@ -0,0 +1,32 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "AbstractUnrealPropertySerialiser.h"
#include "../Types/StringProperty.h"
class StringPropertySerialiser : public AbstractUnrealPropertySerialiser {
public:
using ptr = Containers::Pointer<StringPropertySerialiser>;
auto types() -> Containers::ArrayView<const std::string> override;
auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
};

View file

@ -0,0 +1,218 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../BinaryReader.h"
#include "../BinaryWriter.h"
#include "../PropertySerialiser.h"
#include "../Types/GenericStructProperty.h"
#include "../Types/NoneProperty.h"
#include "StructSerialiser.h"
auto StructSerialiser::types() -> Containers::ArrayView<const std::string> {
static const Containers::Array<std::string> types{InPlaceInit, {"StructProperty"}};
return types;
}
auto StructSerialiser::deserialise(const std::string& name, const std::string& type, UnsignedLong value_length,
UnsignedInt count, BinaryReader& reader, PropertySerialiser& serialiser) -> Containers::Array<UnrealPropertyBase::ptr>
{
std::string item_type;
if(!reader.readUEString(item_type)) {
return nullptr;
}
Containers::StaticArray<16, char> guid{ValueInit};
if(!reader.readStaticArray(guid)) {
return nullptr;
}
char terminator;
if(!reader.readChar(terminator) || terminator != '\0') {
return nullptr;
}
Containers::Array<UnrealPropertyBase::ptr> array;
if(count == 0) {
auto prop = Containers::pointer<GenericStructProperty>();
prop->structType = std::move(item_type);
prop->structGuid = std::move(guid);
}
else {
for(UnsignedInt i = 0; i < count; i++) {
auto prop = Containers::pointer<UnrealPropertyBase>();
prop = serialiser.readItem(reader, item_type, UnsignedLong(-1), name);
if(!prop) {
prop = readStructValue(name, item_type, value_length, reader, serialiser);
}
if(!prop) {
return nullptr;
}
static_cast<StructProperty*>(prop.get())->structGuid = guid;
arrayAppend(array, std::move(prop));
}
}
return array;
}
auto StructSerialiser::deserialise(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
{
std::string item_type;
if(!reader.readUEString(item_type)) {
return nullptr;
}
if(item_type == "None") {
return Containers::pointer<NoneProperty>();
}
Containers::StaticArray<16, char> guid{ValueInit};
if(!reader.readStaticArray(guid)) {
return nullptr;
}
char terminator;
if(!reader.readChar(terminator) || terminator != '\0') {
return nullptr;
}
UnrealPropertyBase::ptr prop = serialiser.readItem(reader, item_type, value_length, name);
if(!prop) {
prop = readStructValue(name, item_type, value_length, reader, serialiser);
if(prop) {
dynamic_cast<GenericStructProperty*>(prop.get())->structGuid = std::move(guid);
}
}
return prop;
}
auto StructSerialiser::serialise(Containers::ArrayView<UnrealPropertyBase::ptr> props, const std::string& item_type,
UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
bytes_written += writer.writeUEStringToArray(*(props.front()->name));
bytes_written += writer.writeUEStringToArray(item_type);
UnsignedLong vl_pos = writer.arrayPosition();
bytes_written += writer.writeValueToArray<UnsignedLong>(0ull);
auto struct_prop = dynamic_cast<StructProperty*>(props.front().get());
if(!struct_prop) {
return false;
}
bytes_written += writer.writeUEStringToArray(struct_prop->structType);
bytes_written += writer.writeDataToArray(arrayView(struct_prop->structGuid));
bytes_written += writer.writeValueToArray<char>('\0');
UnsignedLong vl_start = writer.arrayPosition();
UnsignedLong bytes_written_here = 0;
for(auto& prop : props) {
struct_prop = dynamic_cast<StructProperty*>(prop.get());
if(!struct_prop) {
return false;
}
if(!serialiser.writeItem(prop, struct_prop->structType, bytes_written_here, writer)) {
if(!writeStructValue(struct_prop, bytes_written_here, writer, serialiser)) {
return false;
}
}
}
UnsignedLong vl_stop = writer.arrayPosition() - vl_start;
writer.writeValueToArrayAt(vl_stop, vl_pos);
bytes_written += vl_stop;
return true;
}
auto StructSerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto struct_prop = dynamic_cast<StructProperty*>(prop.get());
if(!struct_prop) {
return false;
}
writer.writeUEStringToArray(struct_prop->structType);
writer.writeDataToArray(arrayView(struct_prop->structGuid));
writer.writeValueToArray<char>('\0');
if(!serialiser.writeItem(prop, struct_prop->structType, bytes_written, writer)) {
UnsignedLong dummy_bytes_written = 0;
UnsignedLong vl_start = writer.arrayPosition();
if(!writeStructValue(struct_prop, dummy_bytes_written, writer, serialiser)) {
return false;
}
bytes_written += writer.arrayPosition() - vl_start;
}
return true;
}
auto StructSerialiser::readStructValue(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> StructProperty::ptr
{
auto st_prop = Containers::pointer<GenericStructProperty>();
st_prop->structType = type;
UnrealPropertyBase::ptr prop;
while((prop = serialiser.read(reader)) != nullptr) {
arrayAppend(st_prop->properties, std::move(prop));
if(st_prop->properties.back()->name == "None" &&
st_prop->properties.back()->propertyType == "NoneProperty" &&
dynamic_cast<NoneProperty*>(st_prop->properties.back().get()) != nullptr)
{
break;
}
}
st_prop->name.emplace(name);
return st_prop;
}
auto StructSerialiser::writeStructValue(StructProperty* prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto struct_prop = dynamic_cast<GenericStructProperty*>(prop);
if(!struct_prop) {
return false;
}
for(auto& item : struct_prop->properties) {
if(!serialiser.write(item, bytes_written, writer)) {
return false;
}
}
return true;
}

View file

@ -0,0 +1,45 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "AbstractUnrealCollectionPropertySerialiser.h"
#include "AbstractUnrealPropertySerialiser.h"
#include "AbstractUnrealStructSerialiser.h"
#include "../Types/StructProperty.h"
class StructSerialiser : public AbstractUnrealPropertySerialiser, public AbstractUnrealCollectionPropertySerialiser {
public:
using ptr = Containers::Pointer<StructSerialiser>;
auto types() -> Containers::ArrayView<const std::string> override;
auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, UnsignedInt count,
BinaryReader& reader, PropertySerialiser& serialiser) -> Containers::Array<UnrealPropertyBase::ptr> override;
auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
auto serialise(Containers::ArrayView<UnrealPropertyBase::ptr> props, const std::string& item_type, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
private:
auto readStructValue(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> StructProperty::ptr;
auto writeStructValue(StructProperty* prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool;
};

View file

@ -0,0 +1,84 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../BinaryReader.h"
#include "../BinaryWriter.h"
#include "TextPropertySerialiser.h"
auto TextPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
{
auto prop = Containers::pointer<TextProperty>();
Long start_position = reader.position();
char terminator;
if(!reader.readChar(terminator) || terminator != '\0') {
return nullptr;
}
if(reader.peekChar() > 0) {
if(!reader.readArray(prop->flags, 8)) {
return nullptr;
}
}
else {
if(!reader.readArray(prop->flags, 4)) {
return nullptr;
}
}
if(!reader.readChar(prop->id)) {
return nullptr;
}
auto interval = reader.position() - start_position;
do {
std::string str;
if(!reader.readUEString(str)) {
return nullptr;
}
arrayAppend(prop->data, std::move(str));
interval = reader.position() - start_position;
} while(UnsignedLong(interval) < value_length);
prop->value = prop->data.back();
return prop;
}
auto TextPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto text_prop = dynamic_cast<TextProperty*>(prop.get());
if(!text_prop) {
return false;
}
writer.writeValueToArray<char>('\0');
bytes_written += writer.writeDataToArray<char>(text_prop->flags);
for(const auto& str : text_prop->data) {
bytes_written += writer.writeUEStringToArray(str);
}
return true;
}

View file

@ -0,0 +1,30 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "UnrealPropertySerialiser.h"
#include "../Types/TextProperty.h"
class TextPropertySerialiser : public UnrealPropertySerialiser<TextProperty> {
public:
using ptr = Containers::Pointer<TextPropertySerialiser>;
private:
auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
};

View file

@ -0,0 +1,58 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <type_traits>
#include "AbstractUnrealPropertySerialiser.h"
#include "../Types/StructProperty.h"
template<typename T>
class UnrealPropertySerialiser : public AbstractUnrealPropertySerialiser {
static_assert(std::is_base_of<UnrealPropertyBase, T>::value, "T must be derived from UnrealPropertyBase.");
public:
using ptr = Containers::Pointer<UnrealPropertySerialiser<T>>;
auto types() -> Containers::ArrayView<const std::string> override {
static const Containers::Array<std::string> types = []{
Containers::Array<std::string> array;
Containers::Pointer<T> p(new T);
if(std::is_base_of<StructProperty, T>::value) {
array = Containers::Array<std::string>{InPlaceInit, {dynamic_cast<StructProperty*>(p.get())->structType}};
}
else {
array = Containers::Array<std::string>{InPlaceInit, {p->propertyType}};
}
return array;
}();
return types;
}
auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override {
return deserialiseProperty(name, type, value_length, reader, serialiser);
}
auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override {
return serialiseProperty(prop, bytes_written, writer, serialiser);
}
private:
virtual auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> typename UnrealPropertyBase::ptr = 0;
virtual auto serialiseProperty(typename UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool = 0;
};

View file

@ -0,0 +1,46 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../BinaryReader.h"
#include "../BinaryWriter.h"
#include "Vector2DPropertySerialiser.h"
auto Vector2DPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
{
auto prop = Containers::pointer<Vector2DStructProperty>();
if(!reader.readFloat(prop->x) || !reader.readFloat(prop->y)) {
return nullptr;
}
return prop;
}
auto Vector2DPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto vector = dynamic_cast<Vector2DStructProperty*>(prop.get());
if(!vector) {
return false;
}
bytes_written += writer.writeValueToArray<Float>(vector->x) + writer.writeValueToArray<Float>(vector->y);
return true;
}

View file

@ -0,0 +1,30 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "UnrealPropertySerialiser.h"
#include "../Types/Vector2DStructProperty.h"
class Vector2DPropertySerialiser : public UnrealPropertySerialiser<Vector2DStructProperty> {
public:
using ptr = Containers::Pointer<Vector2DPropertySerialiser>;
private:
auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
};

View file

@ -0,0 +1,47 @@
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "../BinaryReader.h"
#include "../BinaryWriter.h"
#include "VectorPropertySerialiser.h"
auto VectorPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length,
BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr
{
auto prop = Containers::pointer<VectorStructProperty>();
if(!reader.readFloat(prop->x) || !reader.readFloat(prop->y) || !reader.readFloat(prop->z)) {
return nullptr;
}
return prop;
}
auto VectorPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written,
BinaryWriter& writer, PropertySerialiser& serialiser) -> bool
{
auto vector = dynamic_cast<VectorStructProperty*>(prop.get());
if(!vector) {
return false;
}
bytes_written += writer.writeValueToArray<Float>(vector->x) + writer.writeValueToArray<Float>(vector->y) +
writer.writeValueToArray<Float>(vector->z);
return true;
}

View file

@ -0,0 +1,30 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "UnrealPropertySerialiser.h"
#include "../Types/VectorStructProperty.h"
class VectorPropertySerialiser : public UnrealPropertySerialiser<VectorStructProperty> {
public:
using ptr = Containers::Pointer<VectorPropertySerialiser>;
private:
auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override;
auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override;
};

View file

@ -0,0 +1,42 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <Corrade/Containers/GrowableArray.h>
#include "UnrealPropertyBase.h"
struct ArrayProperty : public UnrealPropertyBase {
using ptr = Containers::Pointer<ArrayProperty>;
ArrayProperty() {
propertyType = "ArrayProperty";
}
template<typename T>
std::enable_if_t<std::is_base_of<UnrealPropertyBase, T>::value, T*>
at(std::size_t index) {
if(index >= items.size()) {
return nullptr;
}
return static_cast<T*>(items[index].get());
}
std::string itemType;
Containers::Array<UnrealPropertyBase::ptr> items;
};

View file

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

View file

@ -0,0 +1,33 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <Corrade/Containers/GrowableArray.h>
#include "UnrealProperty.h"
struct ByteProperty : public UnrealProperty<Containers::Array<char>> {
using ptr = Containers::Pointer<ByteProperty>;
ByteProperty() {
propertyType = "ByteProperty";
}
// For some reason, M.A.S.S. Builder stores EnumProperties as ByteProperties. Ugh...
std::string enumType;
std::string enumValue;
};

View file

@ -0,0 +1,27 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "StructProperty.h"
struct ColourStructProperty : public StructProperty {
using ptr = Containers::Pointer<ColourStructProperty>;
ColourStructProperty() {
structType = "LinearColor";
}
Float r = 0.0f, g = 0.0f, b = 0.0f, a = 0.0f;
};

View file

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

View file

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

View file

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

View file

@ -0,0 +1,51 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <algorithm>
#include <Corrade/Containers/GrowableArray.h>
#include "StructProperty.h"
struct GenericStructProperty : public StructProperty {
using ptr = Containers::Pointer<GenericStructProperty>;
template<typename T>
std::enable_if_t<std::is_base_of<UnrealPropertyBase, T>::value, T*>
at(const std::string& name) {
for(auto& item : properties) {
if(item->name == name) {
return static_cast<T*>(item.get());
}
}
return nullptr;
}
template<typename T>
std::enable_if_t<std::is_base_of<UnrealPropertyBase, T>::value, typename T::ptr>
atMove(const std::string& name) {
for(auto& item : properties) {
if(item && item->name == name) {
return Containers::Pointer<T>{static_cast<T*>(item.release())};
}
}
return nullptr;
}
Containers::Array<UnrealPropertyBase::ptr> properties;
};

View file

@ -0,0 +1,31 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <Corrade/Containers/StaticArray.h>
#include "StructProperty.h"
struct GuidStructProperty : public StructProperty {
using ptr = Containers::Pointer<GuidStructProperty>;
GuidStructProperty() {
structType = "Guid";
}
Containers::StaticArray<16, char> guid{ValueInit};
};

View file

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

View file

@ -0,0 +1,37 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include "UnrealPropertyBase.h"
struct MapProperty : public UnrealPropertyBase {
using ptr = Containers::Pointer<MapProperty>;
MapProperty() {
propertyType = "MapProperty";
}
std::string keyType;
std::string valueType;
struct KeyValuePair {
UnrealPropertyBase::ptr key;
Containers::Array<UnrealPropertyBase::ptr> values;
};
Containers::Array<KeyValuePair> map;
};

Some files were not shown because too many files have changed in this diff Show more