Build viewer/editor #13

Manually merged
williamjcm merged 128 commits from mass-viewer into master 2022-03-02 14:50:10 +01: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