Compare commits

...

45 Commits

Author SHA1 Message Date
Guillaume Jacquemin f522d20dd4 SaveTool: fragment SaveTool_MassViewer.cpp.
This will make maintenance easier. I hope.
2022-03-02 14:43:02 +01:00
Guillaume Jacquemin 6208825aa6 Mass: fix a bug that prevented global styles from being read. 2022-03-02 14:10:13 +01:00
Guillaume Jacquemin 572585e648 SaveTool: optimise code readability.
...mostly for Clang/CLion. :D
2022-03-02 11:46:31 +01:00
Guillaume Jacquemin de2ba9ce7f Mass(Manager),SaveTool: improve error handling. 2022-02-26 14:48:45 +01:00
Guillaume Jacquemin d0ddc73852 Profile: fix a compile error. 2022-02-25 21:00:32 +01:00
Guillaume Jacquemin a1c17b7138 Profile(Manager),SaveTool: improve error handling and fix bugs. 2022-02-24 14:00:47 +01:00
Guillaume Jacquemin 350ad59f8e SaveTool: add a convenience wrapper over ImGui stuff. 2022-02-23 21:59:00 +01:00
Guillaume Jacquemin 883d5d3f41 Mass,SaveTool: improve error handling. 2022-02-23 15:47:34 +01:00
Guillaume Jacquemin 77d7eaefad SaveTool: fix a condition. 2022-02-23 10:18:58 +01:00
Guillaume Jacquemin 82170b3078 Mass,SaveTool: optimise the effect colour mode. 2022-02-17 20:22:33 +01:00
Guillaume Jacquemin 88abf91047 Mass,SaveTool: add some future-proofing. 2022-02-17 20:01:59 +01:00
Guillaume Jacquemin 955ec010b8 Crc32: make the polynomial literal explicitly unsigned. 2022-02-16 11:48:39 +01:00
Guillaume Jacquemin 7cb9ea28b2 BinaryReader: add a way to read arbitrary types. 2022-02-16 11:47:49 +01:00
Guillaume Jacquemin 975f471a68 BinaryReader: add a way to seek into the file. 2022-02-14 09:31:20 +01:00
Guillaume Jacquemin 76210e147a BinaryWriter: add a way to access the temp array.
That way, I'll be able to easily compute the CRC32 of it.
2022-02-13 15:09:07 +01:00
Guillaume Jacquemin 5e06c48492 BinaryWriter: fix an issue with writeValueToArray().
The view needs to be of type T, not U (which can potentially be T&).
2022-02-13 15:03:45 +01:00
Guillaume Jacquemin 2ff32c4c78 Add a CRC32 algorithm. 2022-02-13 15:02:08 +01:00
Guillaume Jacquemin 4000421a8c Mass,SaveTool: refactor even more code. 2022-02-13 10:31:55 +01:00
Guillaume Jacquemin 8f1e3668a3 BinaryWriter: allow writing string literals. 2022-02-12 11:21:23 +01:00
Guillaume Jacquemin a6c0614979 BinaryWriter: make non-copyable. 2022-02-11 19:44:16 +01:00
Guillaume Jacquemin 2cabe6a3ba MassManager: adapt to Mass changes.
Should have caught it earlier. Ugh.
2022-02-11 18:31:45 +01:00
Guillaume Jacquemin afc163f344 Mass: rename a member.
SteamIDs aren't used anymore in 0.8+ save files.
2022-02-09 20:16:14 +01:00
Guillaume Jacquemin 353a71d8ab Weapon: remove an unneeded blank line. 2022-02-09 18:28:23 +01:00
Guillaume Jacquemin bbc40d7c93 Weapon: update formatting. 2022-02-09 18:12:11 +01:00
Guillaume Jacquemin 28db82c8a9 Weapon,Mass,SaveTool: refactor some more code. 2022-02-09 14:17:05 +01:00
Guillaume Jacquemin 9f324c30fd Delete WeaponTypes.h.
It's not needed anymore.
2022-02-09 13:54:44 +01:00
Guillaume Jacquemin 41cd92352d Mass: refactor and optimise some parts. 2022-02-09 13:41:55 +01:00
Guillaume Jacquemin 940fe3feee SaveTool: implement weapon copying. 2022-01-30 14:04:22 +01:00
Guillaume Jacquemin d74a7bc219 Mass: make Weapon copyable.
This is necessary to add weapon copying.
2022-01-30 14:02:30 +01:00
Guillaume Jacquemin 51faed7210 Update copyright years.
I should have done that earlier... and of course I forgot a few files...
2022-01-30 11:38:22 +01:00
Guillaume Jacquemin 8fb837bfc0 SaveTool: finish implementing weapon reordering. 2022-01-30 09:48:14 +01:00
Guillaume Jacquemin 0ac1e759ca Profile(Manager),SaveTool: prepare for legacy/normal distinction. 2022-01-20 19:42:27 +01:00
Guillaume Jacquemin a4045e8e9b Add a few IDs to maps. 2022-01-20 11:39:36 +01:00
Guillaume Jacquemin 1ec4522baf SaveTool: make the clickthrough hint have priority. 2022-01-15 13:26:12 +01:00
Guillaume Jacquemin 13d09e4aa0 SaveTool: prepare for the better import/export system. 2022-01-15 11:31:33 +01:00
Guillaume Jacquemin ed0c4a73bb SaveTool: mark profiles as "legacy" in the manager.
I'll probably have to redesign that whole part of the UI anyway, so...
2022-01-15 11:31:06 +01:00
Guillaume Jacquemin 8102d1d83a SaveTool: improve the ShellExecuteW call. 2022-01-15 11:30:12 +01:00
Guillaume Jacquemin 9a9c08391a SaveTool: change how story progress is handled. 2022-01-15 11:01:11 +01:00
Guillaume Jacquemin 51602c713a SaveTool: update layout. 2022-01-14 13:42:09 +01:00
Guillaume Jacquemin 1621a4dbd5 SaveTool: move M.A.S.S. viewer state tracking.
That way, it's easier to reset. Some states, such as the current tab or
which headers are open/collapsed, are internal to ImGui, though, so I
can't do much there. Well, I could use the internal ImGui API, but, it's
still a pain in the ass.
2022-01-14 13:22:51 +01:00
Guillaume Jacquemin 7fb269f862 SaveTool: change viewer window ID. 2022-01-07 09:35:32 +01:00
Guillaume Jacquemin 1378676bbc SaveTool: fix old code. 2022-01-03 12:46:13 +01:00
Guillaume Jacquemin 96768c1aab SaveTool: add a conversion operator. 2022-01-03 12:10:20 +01:00
Guillaume Jacquemin bd05a98820 SaveTool: make pre-releases considered up-to-date if they're more recent than the latest stable. 2022-01-03 11:56:34 +01:00
Guillaume Jacquemin 7059295cb3 Strip the release executable.
For *some* reason, there's debug info in one of the MinGW-w64 libs,
which account for roughly half the bloat of the release exe.
2021-12-03 00:14:22 +01:00
116 changed files with 2336 additions and 1510 deletions

View File

@ -1,5 +1,5 @@
# MassBuilderSaveTool
# Copyright (C) 2021 Guillaume Jacquemin
# 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

View File

@ -1,5 +1,5 @@
# MassBuilderSaveTool
# Copyright (C) 2021 Guillaume Jacquemin
# 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
@ -116,6 +116,9 @@ add_executable(MassBuilderSaveTool WIN32
SaveTool/SaveTool_drawMainMenu.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
ProfileManager/ProfileManager.h
ProfileManager/ProfileManager.cpp
@ -124,17 +127,28 @@ add_executable(MassBuilderSaveTool WIN32
Profile/ResourceIDs.h
MassManager/MassManager.h
MassManager/MassManager.cpp
Mass/Accessory.h
Mass/ArmourPart.h
Mass/CustomStyle.h
Mass/Decal.h
Mass/Joints.h
Mass/Mass.h
Mass/Mass.cpp
Mass/Weapon.h
Mass/Weapon.cpp
Mass/WeaponPart.h
Maps/Accessories.h
Maps/ArmourSets.h
Maps/ArmourSlots.h
Maps/ArmourSlots.hpp
Maps/DamageTypes.hpp
Maps/EffectColourModes.hpp
Maps/LastMissionId.h
Maps/StoryProgress.h
Maps/StyleNames.h
Maps/WeaponTypes.h
Maps/WeaponTypes.hpp
ToastQueue/ToastQueue.h
ToastQueue/ToastQueue.cpp
Utilities/Crc32.h
FontAwesome/IconsFontAwesome5.h
FontAwesome/IconsFontAwesome5Brands.h
resource.rc
@ -153,6 +167,10 @@ endif()
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
Corrade::Containers
Corrade::Utility

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,61 +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/>.
#include <string>
#include <unordered_map>
static const std::unordered_map<std::string, const char*> armour_slots {
{"enuArmorSlots::NewEnumerator0", "Face"},
{"enuArmorSlots::NewEnumerator1", "Upper head"},
{"enuArmorSlots::NewEnumerator2", "Lower head"},
{"enuArmorSlots::NewEnumerator3", "Neck"},
{"enuArmorSlots::NewEnumerator4", "Upper body"},
{"enuArmorSlots::NewEnumerator5", "Middle body"},
{"enuArmorSlots::NewEnumerator23", "Backpack"},
{"enuArmorSlots::NewEnumerator6", "Lower body"},
{"enuArmorSlots::NewEnumerator7", "Front waist"},
{"enuArmorSlots::NewEnumerator8", "Left front skirt"},
{"enuArmorSlots::NewEnumerator9", "Right front skirt"},
{"enuArmorSlots::NewEnumerator10", "Left side skirt"},
{"enuArmorSlots::NewEnumerator11", "Right side skirt"},
{"enuArmorSlots::NewEnumerator12", "Left back skirt"},
{"enuArmorSlots::NewEnumerator13", "Right side skirt"},
{"enuArmorSlots::NewEnumerator14", "Back waist"},
{"enuArmorSlots::NewEnumerator15", "Left shoulder"},
{"enuArmorSlots::NewEnumerator16", "Right shoulder"},
{"enuArmorSlots::NewEnumerator17", "Left upper arm"},
{"enuArmorSlots::NewEnumerator18", "Right upper arm"},
{"enuArmorSlots::NewEnumerator19", "Left elbow"},
{"enuArmorSlots::NewEnumerator20", "Right elbow"},
{"enuArmorSlots::NewEnumerator21", "Left lower arm"},
{"enuArmorSlots::NewEnumerator22", "Right lower arm"},
{"enuArmorSlots::NewEnumerator24", "Left hand"},
{"enuArmorSlots::NewEnumerator25", "Right hand"},
{"enuArmorSlots::NewEnumerator26", "Left upper leg"},
{"enuArmorSlots::NewEnumerator27", "Right upper leg"},
{"enuArmorSlots::NewEnumerator28", "Left knee"},
{"enuArmorSlots::NewEnumerator29", "Right knee"},
{"enuArmorSlots::NewEnumerator30", "Left lower leg"},
{"enuArmorSlots::NewEnumerator31", "Right lower leg"},
{"enuArmorSlots::NewEnumerator32", "Left ankle"},
{"enuArmorSlots::NewEnumerator33", "Right ankle"},
{"enuArmorSlots::NewEnumerator34", "Left heel"},
{"enuArmorSlots::NewEnumerator35", "Right heel"},
{"enuArmorSlots::NewEnumerator36", "Left foot"},
{"enuArmorSlots::NewEnumerator37", "Right foot"},
};

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
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -33,6 +33,7 @@ static const std::map<Int, const char*> mission_id_map {{
{0x006E, "Mission 11 - Tempestuous Sector"},
{0x006F, "Mission 12 - Clashes of Metal"},
{0x0070, "Mission 13 - The Sandstorm Glutton"},
{0x0071, "Mission 14 - An Icy Investigation"},
// Hunting grounds
{0x00C8, "Hunt 1 - Desert Pathway Safety"},

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -22,7 +22,7 @@ struct StoryProgressPoint {
Int id;
const char* chapter;
const char* point;
const char* after = "";
const char* after = nullptr;
};
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"},
{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"},
{0x057C, "Chapter 2", "Talked with Reina about device", "After mission 13"},
{0x057D, "Chapter 2", "Got mission 14 briefing", "After mission 13"},
}
};

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -22,7 +22,9 @@
using namespace Magnum;
static const std::map<Int, const char*> style_names {
extern const std::map<Int, const char*> style_names
#ifdef STYLENAMES_DEFINITION
{
{0, "Custom Style 1"},
{1, "Custom Style 2"},
{2, "Custom Style 3"},
@ -185,4 +187,6 @@ static const std::map<Int, const char*> style_names {
{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;
};

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -17,13 +17,23 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <string>
#include <unordered_map>
static const std::unordered_map<std::string, const char*> weapon_types{
{"enuWeaponTypes::NewEnumerator0", "Melee weapon"},
{"enuWeaponTypes::NewEnumerator5", "Shield"},
{"enuWeaponTypes::NewEnumerator1", "Bullet shooter"},
{"enuWeaponTypes::NewEnumerator2", "Energy shooter"},
{"enuWeaponTypes::NewEnumerator3", "Bullet launcher"},
{"enuWeaponTypes::NewEnumerator4", "Energy launcher"},
#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;
};

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -14,8 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <cstring>
#include <algorithm>
#include <Corrade/Containers/Array.h>
@ -35,8 +33,6 @@
#include "Mass.h"
std::string Mass::_lastError;
Mass::Mass(const std::string& path) {
_folder = Utility::Directory::path(path);
_filename = Utility::Directory::filename(path);
@ -50,28 +46,28 @@ auto Mass::lastError() -> std::string const& {
auto Mass::getNameFromFile(const std::string& path) -> Containers::Optional<std::string> {
if(!Utility::Directory::exists(path)) {
_lastError = path + " couldn't be found.";
Utility::Error{} << path.c_str() << "couldn't be found.";
return Containers::NullOpt;
}
UESaveFile mass{path};
if(!mass.valid()) {
_lastError = "The unit file seems to be corrupt.";
Utility::Error{} << "The unit file seems to be corrupt.";
return Containers::NullOpt;
}
auto unit_data = mass.at<GenericStructProperty>("UnitData");
if(!unit_data) {
_lastError = "Couldn't find unit data in the file.";
Utility::Error{} << "Couldn't find unit data in the file.";
return Containers::NullOpt;
}
auto name_prop = unit_data->at<StringProperty>("Name_45_A037C5D54E53456407BDF091344529BB");
if(!name_prop) {
_lastError = "Couldn't find the name in the file.";
Utility::Error{} << "Couldn't find the name in the file.";
return Containers::NullOpt;
}
@ -174,12 +170,9 @@ void Mass::refreshValues() {
return;
}
if(!_demo)
{
getGlobalStyles();
if(_state == State::Invalid) {
return;
}
getGlobalStyles();
if(_state == State::Invalid) {
return;
}
getTuning();
@ -193,7 +186,7 @@ void Mass::refreshValues() {
return;
}
_steamId = account_prop->value;
_account = account_prop->value;
_state = State::Valid;
}
@ -232,10 +225,6 @@ auto Mass::state() -> State {
return _state;
}
auto Mass::demo() const -> bool {
return _demo;
}
auto Mass::dirty() const -> bool {
return _dirty;
}
@ -385,7 +374,7 @@ auto Mass::writeJointSliders() -> bool {
frame_prop->properties = std::move(temp);
if(!_mass->saveToFile()) {
_lastError = "Couldn't write data to " + _filename;
_lastError = _mass->lastError();
return false;
}
@ -452,7 +441,7 @@ auto Mass::writeFrameStyles() -> bool {
}
if(!_mass->saveToFile()) {
_lastError = "Couldn't write data to " + _filename;
_lastError = _mass->lastError();
return false;
}
@ -489,18 +478,21 @@ auto Mass::writeEyeFlareColour() -> bool {
auto unit_data = _mass->at<GenericStructProperty>("UnitData");
if(!unit_data) {
_state = State::Invalid;
_lastError = "No unit data in " + _filename;
return false;
}
auto frame = unit_data->at<GenericStructProperty>("Frame_3_F92B0F6A44A15088AF7F41B9FF290653");
if(!frame) {
_state = State::Invalid;
_lastError = "No frame data in " + _filename;
return false;
}
auto eye_flare_prop = frame->at<ColourStructProperty>("EyeFlareColor_36_AF79999C40FCA0E88A2F9A84488A38CA");
if(!eye_flare_prop) {
_state = State::Invalid;
_lastError = "No eye flare property in " + _filename;
return false;
}
@ -509,7 +501,12 @@ auto Mass::writeEyeFlareColour() -> bool {
eye_flare_prop->b = _frame.eyeFlare.b();
eye_flare_prop->a = _frame.eyeFlare.a();
return _mass->saveToFile();
if(!_mass->saveToFile()) {
_lastError = _mass->lastError();
return false;
}
return true;
}
auto Mass::frameCustomStyles() -> Containers::ArrayView<CustomStyle> {
@ -539,18 +536,21 @@ void Mass::getFrameCustomStyles() {
auto Mass::writeFrameCustomStyle(UnsignedLong index) -> bool {
if(index > _frame.customStyles.size()) {
_lastError = "Style index out of range.";
return false;
}
auto unit_data = _mass->at<GenericStructProperty>("UnitData");
if(!unit_data) {
_state = State::Invalid;
_lastError = "No unit data in " + _filename;
return false;
}
auto frame_styles = unit_data->at<ArrayProperty>("FrameStyle_44_04A44C9440363CCEC5443D98BFAF22AA");
if(!frame_styles) {
_state = State::Invalid;
_lastError = "No frame styles in " + _filename;
return false;
}
@ -583,7 +583,15 @@ void Mass::getArmourParts() {
auto part_prop = armour_array->at<GenericStructProperty>(i);
auto& part = _armour.parts[i];
part.slot = part_prop->at<ByteProperty>("Slot_3_408BA56F4C9605C7E805CF91B642249C")->enumValue;
auto& armour_slot = part_prop->at<ByteProperty>("Slot_3_408BA56F4C9605C7E805CF91B642249C")->enumValue;
#define c(enumerator, strenum, name) if(armour_slot == (strenum)) { part.slot = ArmourSlot::enumerator; } else
#include "../Maps/ArmourSlots.hpp"
#undef c
{
_state = State::Invalid;
Utility::Warning{} << "Invalid armour slot enum value in getArmourParts().";
}
part.id = part_prop->at<IntProperty>("ID_5_ACD101864D3481DE96EDACACC09BDD25")->value;
auto part_styles = part_prop->at<ArrayProperty>("Styles_47_3E31870441DFD7DB8BEE5C85C26B365B");
@ -607,44 +615,58 @@ void Mass::getArmourParts() {
return;
}
if(decals_array->items.size() != part.decals.size()) {
part.demoDecals = decals_array->items.size();
_demo = true;
}
part.decals = Containers::Array<Decal>{decals_array->items.size()};
getDecals(part.decals, decals_array);
if(!_demo) {
auto accs_array = part_prop->at<ArrayProperty>("Accessories_52_D902DD4241FA0050C2529596255153F3");
if(!accs_array) {
_demo = true;
continue;
}
if(accs_array->items.size() != part.accessories.size()) {
_state = State::Invalid;
return;
}
getAccessories(part.accessories, accs_array);
auto accs_array = part_prop->at<ArrayProperty>("Accessories_52_D902DD4241FA0050C2529596255153F3");
if(!accs_array) {
part.accessories = Containers::Array<Accessory>{};
continue;
}
if(part.accessories.size() != accs_array->items.size()) {
part.accessories = Containers::Array<Accessory>{accs_array->items.size()};
}
getAccessories(part.accessories, accs_array);
}
}
auto Mass::writeArmourPart(UnsignedLong index) -> bool {
if(index > _armour.parts.size()) {
return false;
}
auto Mass::writeArmourPart(ArmourSlot slot) -> bool {
auto& part = *std::find_if(_armour.parts.begin(), _armour.parts.end(), [&slot](const ArmourPart& part){ return slot == part.slot; });
auto unit_data = _mass->at<GenericStructProperty>("UnitData");
auto armour_array = unit_data->at<ArrayProperty>("Armor_10_12E266C44116DDAF57E99ABB575A4B3C");
auto part_prop = armour_array->at<GenericStructProperty>(index);
const char* slot_str = nullptr;
switch(slot) {
#define c(enumerator, strenum, name) case ArmourSlot::enumerator: \
slot_str = strenum; \
break;
#include "../Maps/ArmourSlots.hpp"
#undef c
}
auto& part = _armour.parts[index];
GenericStructProperty* part_prop = nullptr;
if(part_prop->at<ByteProperty>("Slot_3_408BA56F4C9605C7E805CF91B642249C")->enumValue != part.slot) {
for(UnsignedInt i = 0; i < armour_array->items.size(); i++) {
part_prop = armour_array->at<GenericStructProperty>(i);
if(slot_str != part_prop->at<StringProperty>("Slot_3_408BA56F4C9605C7E805CF91B642249C")->value) {
part_prop = nullptr;
}
}
if(!part_prop) {
_lastError = "Couldn't find the armour part for slot ";
switch(slot) {
#define c(enumerator, strenum, name) case ArmourSlot::enumerator: \
_lastError += "ArmourSlot::" #enumerator "."; \
break;
#include "../Maps/ArmourSlots.hpp"
#undef c
}
return false;
}
@ -656,75 +678,19 @@ auto Mass::writeArmourPart(UnsignedLong index) -> bool {
}
auto decals_array = part_prop->at<ArrayProperty>("Decals_42_F358794A4F18497970F56BA9627D3603");
for(UnsignedInt i = 0; i < decals_array->items.size(); i++) {
auto decal_prop = decals_array->at<GenericStructProperty>(i);
auto& decal = part.decals[i];
writeDecals(part.decals, decals_array);
decal_prop->at<IntProperty>("ID_3_694C0B35404D8A3168AEC89026BC8CF9")->value = decal.id;
auto colour_prop = decal_prop->at<ColourStructProperty>("Color_8_1B0B9D2B43DA6AAB9FA549B374D3E606");
colour_prop->r = decal.colour.r();
colour_prop->g = decal.colour.g();
colour_prop->b = decal.colour.b();
colour_prop->a = decal.colour.a();
auto pos_prop = decal_prop->at<VectorStructProperty>("Position_41_022C8FE84E1AAFE587261E88F2C72250");
pos_prop->x = decal.position.x();
pos_prop->y = decal.position.y();
pos_prop->z = decal.position.z();
auto u_prop = decal_prop->at<VectorStructProperty>("UAxis_37_EBEB715F45491AECACCC07A1AE4646D1");
u_prop->x = decal.uAxis.x();
u_prop->y = decal.uAxis.y();
u_prop->z = decal.uAxis.z();
auto v_prop = decal_prop->at<VectorStructProperty>("VAxis_39_C31EB2664EE202CAECFBBB84100B5E35");
v_prop->x = decal.vAxis.x();
v_prop->y = decal.vAxis.y();
v_prop->z = decal.vAxis.z();
auto offset_prop = decal_prop->at<Vector2DStructProperty>("Offset_29_B02BBBB74FC60F5EDBEBAB8020738020");
offset_prop->x = decal.offset.x();
offset_prop->y = decal.offset.y();
decal_prop->at<FloatProperty>("Scale_32_959D1C2747AFD8D62808468235CBBA40")->value = decal.scale;
decal_prop->at<FloatProperty>("Rotation_27_12D7C314493D203D5C2326A03C5F910F")->value = decal.rotation;
decal_prop->at<BoolProperty>("Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1")->value = decal.flip;
decal_prop->at<BoolProperty>("Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62")->value = decal.wrap;
}
if(!_demo) {
if(part.accessories.size() != 0) {
auto accs_array = part_prop->at<ArrayProperty>("Accessories_52_D902DD4241FA0050C2529596255153F3");
for(UnsignedInt i = 0; i < accs_array->items.size(); i++) {
auto acc_prop = accs_array->at<GenericStructProperty>(i);
auto& accessory = part.accessories[i];
acc_prop->at<IntProperty>("AttachIndex_2_4AFCF6024E4BA7426C6B9F80B8179D20")->value = accessory.attachIndex;
acc_prop->at<IntProperty>("ID_4_5757B32647BAE263266259B8A7DFFFC1")->value = accessory.id;
auto acc_styles = acc_prop->at<ArrayProperty>("Styles_7_91DEB0F24E24D13FC9472882C11D0DFD");
for(UnsignedInt j = 0; j < acc_styles->items.size(); j++) {
acc_styles->at<IntProperty>(j)->value = accessory.styles[j];
}
auto rel_pos_prop = acc_prop->at<VectorStructProperty>("RelativePosition_14_BE8FB2A94074F34B3EDA6683B227D3A1");
rel_pos_prop->x = accessory.relativePosition.x();
rel_pos_prop->y = accessory.relativePosition.y();
rel_pos_prop->z = accessory.relativePosition.z();
auto rel_pos_offset_prop = acc_prop->at<VectorStructProperty>("RelativePositionOffset_15_98FD0CE74E44BBAFC2D46FB4CA4E0ED6");
rel_pos_offset_prop->x = accessory.relativePositionOffset.x();
rel_pos_offset_prop->y = accessory.relativePositionOffset.y();
rel_pos_offset_prop->z = accessory.relativePositionOffset.z();
auto rel_rot_prop = acc_prop->at<RotatorStructProperty>("RelativeRotation_20_C78C73274E6E78E7878F8C98ECA342C0");
rel_rot_prop->x = accessory.relativeRotation.x();
rel_rot_prop->y = accessory.relativeRotation.y();
rel_rot_prop->z = accessory.relativeRotation.z();
auto rel_rot_offset_prop = acc_prop->at<RotatorStructProperty>("RelativeRotationOffset_21_E07FA0EC46728B7BA763C6861249ABAA");
rel_rot_offset_prop->x = accessory.relativeRotationOffset.x();
rel_rot_offset_prop->y = accessory.relativeRotationOffset.y();
rel_rot_offset_prop->z = accessory.relativeRotationOffset.z();
auto local_scale_prop = acc_prop->at<VectorStructProperty>("LocalScale_24_DC2D93A742A41A46E7E61D988F15ED53");
local_scale_prop->x = accessory.localScale.x();
local_scale_prop->y = accessory.localScale.y();
local_scale_prop->z = accessory.localScale.z();
}
writeAccessories(part.accessories, accs_array);
}
return _mass->saveToFile();
if(!_mass->saveToFile()) {
_lastError = _mass->lastError();
return false;
}
return true;
}
auto Mass::armourCustomStyles() -> Containers::ArrayView<CustomStyle> {
@ -754,17 +720,20 @@ void Mass::getArmourCustomStyles() {
auto Mass::writeArmourCustomStyle(UnsignedLong index) -> bool {
if(index > _armour.customStyles.size()) {
_lastError = "Style index out of range.";
return false;
}
auto unit_data = _mass->at<GenericStructProperty>("UnitData");
if(!unit_data) {
_state = State::Invalid;
_lastError = "Couldn't find unit data in " + _filename;
return false;
}
auto armour_styles = unit_data->at<ArrayProperty>("ArmorStyle_42_E2F6AC3647788CB366BD469B3B7E899E");
if(!armour_styles) {
_lastError = "Couldn't find armour custom styles in " + _filename;
_state = State::Invalid;
return false;
}
@ -857,13 +826,12 @@ void Mass::getGlobalStyles() {
auto global_styles = unit_data->at<ArrayProperty>("GlobalStyles_57_6A681C114035241F7BDAAE9B43A8BF1B");
if(!global_styles) {
_state = State::Invalid;
_globalStyles = Containers::Array<CustomStyle>{0};
return;
}
if(global_styles->items.size() != _globalStyles.size()) {
_state = State::Invalid;
return;
_globalStyles = Containers::Array<CustomStyle>{global_styles->items.size()};
}
getCustomStyles(_globalStyles, global_styles);
@ -871,18 +839,21 @@ void Mass::getGlobalStyles() {
auto Mass::writeGlobalStyle(UnsignedLong index) -> bool {
if(index > _globalStyles.size()) {
_lastError = "Global style index out of range";
return false;
}
auto unit_data = _mass->at<GenericStructProperty>("UnitData");
if(!unit_data) {
_state = State::Invalid;
_lastError = "No unit data found in " + _filename;
return false;
}
auto global_styles = unit_data->at<ArrayProperty>("GlobalStyles_57_6A681C114035241F7BDAAE9B43A8BF1B");
if(!global_styles) {
_state = State::Invalid;
_lastError = "No global styles found in " + _filename;
return false;
}
@ -930,18 +901,28 @@ auto Mass::techs() -> Containers::ArrayView<Int> {
return _tuning.techIds;
}
auto Mass::updateSteamId(const std::string& steam_id) -> bool {
_steamId = steam_id;
auto Mass::account() -> const std::string& {
return _account;
}
auto Mass::updateAccount(const std::string& new_account) -> bool {
_account = new_account;
auto account = _mass->at<StringProperty>("Account");
if(!account) {
_state = State::Invalid;
_lastError = "Couldn't find the account property.";
return false;
}
account->value = steam_id;
account->value = new_account;
return _mass->saveToFile();
if(!_mass->saveToFile()) {
_lastError = _mass->lastError();
return false;
}
return true;
}
void Mass::getCustomStyles(Containers::ArrayView<CustomStyle> styles, ArrayProperty* style_array) {
@ -969,11 +950,17 @@ void Mass::getCustomStyles(Containers::ArrayView<CustomStyle> styles, ArrayPrope
auto Mass::setCustomStyle(const CustomStyle& style, UnsignedLong index, ArrayProperty* style_array) -> bool {
if(!style_array) {
_lastError = "Mass::setCustomStyle(): style_array is null.";
return false;
}
auto style_prop = style_array->at<GenericStructProperty>(index);
if(!style_prop) {
_lastError = "Style index is out of range in " + _filename;
return false;
}
style_prop->at<StringProperty>("Name_27_1532115A46EF2B2FA283908DF561A86B")->value = style.name;
auto colour_prop = style_prop->at<ColourStructProperty>("Color_5_F0D383DF40474C9464AE48A0984A212E");
colour_prop->r = style.colour.r();
@ -990,7 +977,12 @@ auto Mass::setCustomStyle(const CustomStyle& style, UnsignedLong index, ArrayPro
style_prop->at<FloatProperty>("Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D")->value = style.rotation;
style_prop->at<FloatProperty>("Scale_26_19DF0708409262183E1247B317137671")->value = style.scale;
return _mass->saveToFile();
if(!_mass->saveToFile()) {
_lastError = _mass->lastError();
return false;
}
return true;
}
void Mass::getDecals(Containers::ArrayView<Decal> decals, ArrayProperty* decal_array) {
@ -1016,6 +1008,39 @@ void Mass::getDecals(Containers::ArrayView<Decal> decals, ArrayProperty* decal_a
}
}
void Mass::writeDecals(Containers::ArrayView<Decal> decals, ArrayProperty* decal_array) {
for(UnsignedInt i = 0; i < decal_array->items.size(); i++) {
auto decal_prop = decal_array->at<GenericStructProperty>(i);
auto& decal = decals[i];
decal_prop->at<IntProperty>("ID_3_694C0B35404D8A3168AEC89026BC8CF9")->value = decal.id;
auto colour_prop = decal_prop->at<ColourStructProperty>("Color_8_1B0B9D2B43DA6AAB9FA549B374D3E606");
colour_prop->r = decal.colour.r();
colour_prop->g = decal.colour.g();
colour_prop->b = decal.colour.b();
colour_prop->a = decal.colour.a();
auto pos_prop = decal_prop->at<VectorStructProperty>("Position_41_022C8FE84E1AAFE587261E88F2C72250");
pos_prop->x = decal.position.x();
pos_prop->y = decal.position.y();
pos_prop->z = decal.position.z();
auto u_prop = decal_prop->at<VectorStructProperty>("UAxis_37_EBEB715F45491AECACCC07A1AE4646D1");
u_prop->x = decal.uAxis.x();
u_prop->y = decal.uAxis.y();
u_prop->z = decal.uAxis.z();
auto v_prop = decal_prop->at<VectorStructProperty>("VAxis_39_C31EB2664EE202CAECFBBB84100B5E35");
v_prop->x = decal.vAxis.x();
v_prop->y = decal.vAxis.y();
v_prop->z = decal.vAxis.z();
auto offset_prop = decal_prop->at<Vector2DStructProperty>("Offset_29_B02BBBB74FC60F5EDBEBAB8020738020");
offset_prop->x = decal.offset.x();
offset_prop->y = decal.offset.y();
decal_prop->at<FloatProperty>("Scale_32_959D1C2747AFD8D62808468235CBBA40")->value = decal.scale;
decal_prop->at<FloatProperty>("Rotation_27_12D7C314493D203D5C2326A03C5F910F")->value = decal.rotation;
decal_prop->at<BoolProperty>("Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1")->value = decal.flip;
decal_prop->at<BoolProperty>("Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62")->value = decal.wrap;
}
}
void Mass::getAccessories(Containers::ArrayView<Accessory> accessories, ArrayProperty* accessory_array) {
for(UnsignedInt i = 0; i < accessory_array->items.size(); i++) {
auto acc_prop = accessory_array->at<GenericStructProperty>(i);
@ -1040,6 +1065,40 @@ void Mass::getAccessories(Containers::ArrayView<Accessory> accessories, ArrayPro
}
}
void Mass::writeAccessories(Containers::ArrayView<Accessory> accessories, ArrayProperty* accs_array) {
for(UnsignedInt i = 0; i < accs_array->items.size(); i++) {
auto acc_prop = accs_array->at<GenericStructProperty>(i);
auto& accessory = accessories[i];
acc_prop->at<IntProperty>("AttachIndex_2_4AFCF6024E4BA7426C6B9F80B8179D20")->value = accessory.attachIndex;
acc_prop->at<IntProperty>("ID_4_5757B32647BAE263266259B8A7DFFFC1")->value = accessory.id;
auto acc_styles = acc_prop->at<ArrayProperty>("Styles_7_91DEB0F24E24D13FC9472882C11D0DFD");
for(UnsignedInt j = 0; j < acc_styles->items.size(); j++) {
acc_styles->at<IntProperty>(j)->value = accessory.styles[j];
}
auto rel_pos_prop = acc_prop->at<VectorStructProperty>("RelativePosition_14_BE8FB2A94074F34B3EDA6683B227D3A1");
rel_pos_prop->x = accessory.relativePosition.x();
rel_pos_prop->y = accessory.relativePosition.y();
rel_pos_prop->z = accessory.relativePosition.z();
auto rel_pos_offset_prop = acc_prop->at<VectorStructProperty>("RelativePositionOffset_15_98FD0CE74E44BBAFC2D46FB4CA4E0ED6");
rel_pos_offset_prop->x = accessory.relativePositionOffset.x();
rel_pos_offset_prop->y = accessory.relativePositionOffset.y();
rel_pos_offset_prop->z = accessory.relativePositionOffset.z();
auto rel_rot_prop = acc_prop->at<RotatorStructProperty>("RelativeRotation_20_C78C73274E6E78E7878F8C98ECA342C0");
rel_rot_prop->x = accessory.relativeRotation.x();
rel_rot_prop->y = accessory.relativeRotation.y();
rel_rot_prop->z = accessory.relativeRotation.z();
auto rel_rot_offset_prop = acc_prop->at<RotatorStructProperty>("RelativeRotationOffset_21_E07FA0EC46728B7BA763C6861249ABAA");
rel_rot_offset_prop->x = accessory.relativeRotationOffset.x();
rel_rot_offset_prop->y = accessory.relativeRotationOffset.y();
rel_rot_offset_prop->z = accessory.relativeRotationOffset.z();
auto local_scale_prop = acc_prop->at<VectorStructProperty>("LocalScale_24_DC2D93A742A41A46E7E61D988F15ED53");
local_scale_prop->x = accessory.localScale.x();
local_scale_prop->y = accessory.localScale.y();
local_scale_prop->z = accessory.localScale.z();
}
}
void Mass::getWeaponType(const char* prop_name, Containers::ArrayView<Weapon> weapon_array) {
auto unit_data = _mass->at<GenericStructProperty>("UnitData");
if(!unit_data) {
@ -1063,7 +1122,14 @@ void Mass::getWeaponType(const char* prop_name, Containers::ArrayView<Weapon> we
auto& weapon = weapon_array[i];
weapon.name = weapon_prop->at<StringProperty>("Name_13_7BF0D31F4E50C50C47231BB36A485D92")->value;
weapon.type = weapon_prop->at<ByteProperty>("Type_2_35ABA8C3406F8D9BBF14A89CD6BCE976")->enumValue;
auto& weapon_type = weapon_prop->at<ByteProperty>("Type_2_35ABA8C3406F8D9BBF14A89CD6BCE976")->enumValue;
#define c(enumerator, strenum, name) if(weapon_type == (strenum)) { weapon.type = WeaponType::enumerator; } else
#include "../Maps/WeaponTypes.hpp"
#undef c
{
_state = State::Invalid;
Utility::Warning{} << "Invalid weapon type enum value in getWeaponType().";
}
auto parts_prop = weapon_prop->at<ArrayProperty>("Element_6_8E4617CC4B2C1F1490435599784EC6E0");
weapon.parts = Containers::Array<WeaponPart>{ValueInit, parts_prop->items.size()};
@ -1081,26 +1147,21 @@ void Mass::getWeaponType(const char* prop_name, Containers::ArrayView<Weapon> we
auto part_decals = part_prop->at<ArrayProperty>("Decals_13_8B81112B453D7230C0CDE982185E14F1");
if(part_decals->items.size() != part.decals.size()) {
_demo = true;
part.demoDecals = part_decals->items.size();
part.decals = Containers::Array<Decal>{part_decals->items.size()};
}
getDecals(part.decals, part_decals);
if(!_demo) {
auto part_accs = part_prop->at<ArrayProperty>("Accessories_21_3878DE8B4ED0EA0DB725E98BCDC20E0C");
if(!part_accs) {
_demo = true;
continue;
}
if(part_accs->items.size() != part.accessories.size()) {
_state = State::Invalid;
return;
}
getAccessories(part.accessories, part_accs);
auto part_accs = part_prop->at<ArrayProperty>("Accessories_21_3878DE8B4ED0EA0DB725E98BCDC20E0C");
if(!part_accs) {
part.accessories = Containers::Array<Accessory>{0};
continue;
}
if(part_accs->items.size() != part.accessories.size()) {
part.accessories = Containers::Array<Accessory>{part_accs->items.size()};
}
getAccessories(part.accessories, part_accs);
}
auto custom_styles = weapon_prop->at<ArrayProperty>("Styles_10_8C3C82444B986AD7A99595AD4985912D");
@ -1117,9 +1178,23 @@ void Mass::getWeaponType(const char* prop_name, Containers::ArrayView<Weapon> we
getCustomStyles(weapon.customStyles, custom_styles);
weapon.attached = weapon_prop->at<BoolProperty>("Attach_15_D00AABBD4AD6A04778D56D81E51927B3")->value;
weapon.damageType = weapon_prop->at<ByteProperty>("DamageType_18_E1FFA53540591A9087EC698117A65C83")->enumValue;
auto& damage_type = weapon_prop->at<ByteProperty>("DamageType_18_E1FFA53540591A9087EC698117A65C83")->enumValue;
#define c(enumerator, strenum) if(damage_type == (strenum)) { weapon.damageType = DamageType::enumerator; } else
#include "../Maps/DamageTypes.hpp"
#undef c
{
_state = State::Invalid;
Utility::Warning{} << "Invalid damage type enum value in getWeaponType().";
}
weapon.dualWield = weapon_prop->at<BoolProperty>("DualWield_20_B2EB2CEA4A6A233DC7575996B6DD1222")->value;
weapon.effectColourMode = weapon_prop->at<ByteProperty>("ColorEfxMode_24_D254BCF943E852BF9ADB8AAA8FD80014")->enumValue;
auto& effect_colour_mode = weapon_prop->at<ByteProperty>("ColorEfxMode_24_D254BCF943E852BF9ADB8AAA8FD80014")->enumValue;
#define c(enumerator, strenum) if(effect_colour_mode == (strenum)) { weapon.effectColourMode = EffectColourMode::enumerator; } else
#include "../Maps/EffectColourModes.hpp"
#undef c
{
_state = State::Invalid;
Utility::Warning{} << "Invalid effect colour mode in getWeaponType().";
}
auto effect_colour = weapon_prop->at<ColourStructProperty>("ColorEfx_26_D921B62946C493E487455A831F4520AC");
weapon.effectColour = Color4{effect_colour->r, effect_colour->g, effect_colour->b, effect_colour->a};
}
@ -1129,17 +1204,20 @@ auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView<Weapon>
auto unit_data = _mass->at<GenericStructProperty>("UnitData");
if(!unit_data) {
_state = State::Invalid;
_lastError = "No unit data in " + _filename;
return false;
}
auto prop = unit_data->at<ArrayProperty>(prop_name);
if(!prop) {
_state = State::Invalid;
_lastError = std::string{prop_name} + " not found in " + _filename;
return false;
}
if(prop->items.size() != weapon_array.size()) {
_state = State::Invalid;
_lastError = "Weapon type array size mismatch.";
return false;
}
@ -1148,11 +1226,18 @@ auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView<Weapon>
auto& weapon = weapon_array[i];
weapon_prop->at<StringProperty>("Name_13_7BF0D31F4E50C50C47231BB36A485D92")->value = weapon.name;
weapon_prop->at<ByteProperty>("Type_2_35ABA8C3406F8D9BBF14A89CD6BCE976")->enumValue = weapon.type;
switch(weapon.type) {
#define c(enumerator, strenum, name) case WeaponType::enumerator: weapon_prop->at<ByteProperty>("Type_2_35ABA8C3406F8D9BBF14A89CD6BCE976")->enumValue = strenum; break;
#include "../Maps/WeaponTypes.hpp"
#undef c
default:
Utility::Warning{} << "Invalid weapon type enum value in writeWeaponType().";
}
auto parts_prop = weapon_prop->at<ArrayProperty>("Element_6_8E4617CC4B2C1F1490435599784EC6E0");
if(parts_prop->items.size() != weapon.parts.size()) {
_state = State::Invalid;
_lastError = "Weapon parts array size mismatch.";
return false;
}
@ -1168,61 +1253,32 @@ auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView<Weapon>
}
auto part_decals = part_prop->at<ArrayProperty>("Decals_13_8B81112B453D7230C0CDE982185E14F1");
for(UnsignedInt k = 0; k < part_decals->items.size(); k++) {
auto decal_prop = part_decals->at<GenericStructProperty>(k);
auto& decal = part.decals[k];
writeDecals(part.decals, part_decals);
decal_prop->at<IntProperty>("ID_3_694C0B35404D8A3168AEC89026BC8CF9")->value = decal.id;
auto colour_prop = decal_prop->at<ColourStructProperty>("Color_8_1B0B9D2B43DA6AAB9FA549B374D3E606");
colour_prop->r = decal.colour.r();
colour_prop->g = decal.colour.g();
colour_prop->b = decal.colour.b();
colour_prop->a = decal.colour.a();
auto pos_prop = decal_prop->at<VectorStructProperty>("Position_41_022C8FE84E1AAFE587261E88F2C72250");
pos_prop->x = decal.position.x();
pos_prop->y = decal.position.y();
pos_prop->z = decal.position.z();
auto u_prop = decal_prop->at<VectorStructProperty>("UAxis_37_EBEB715F45491AECACCC07A1AE4646D1");
u_prop->x = decal.uAxis.x();
u_prop->y = decal.uAxis.y();
u_prop->z = decal.uAxis.z();
auto v_prop = decal_prop->at<VectorStructProperty>("VAxis_39_C31EB2664EE202CAECFBBB84100B5E35");
v_prop->x = decal.vAxis.x();
v_prop->y = decal.vAxis.y();
v_prop->z = decal.vAxis.z();
auto offset_prop = decal_prop->at<Vector2DStructProperty>("Offset_29_B02BBBB74FC60F5EDBEBAB8020738020");
offset_prop->x = decal.offset.x();
offset_prop->y = decal.offset.y();
decal_prop->at<FloatProperty>("Scale_32_959D1C2747AFD8D62808468235CBBA40")->value = decal.scale;
decal_prop->at<FloatProperty>("Rotation_27_12D7C314493D203D5C2326A03C5F910F")->value = decal.rotation;
decal_prop->at<BoolProperty>("Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1")->value = decal.flip;
decal_prop->at<BoolProperty>("Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62")->value = decal.wrap;
auto part_accs = part_prop->at<ArrayProperty>("Accessories_21_3878DE8B4ED0EA0DB725E98BCDC20E0C");
if(!part_accs) {
continue;
}
if(!_demo) {
auto part_accs = part_prop->at<ArrayProperty>("Accessories_21_3878DE8B4ED0EA0DB725E98BCDC20E0C");
if(!part_accs) {
_demo = true;
continue;
}
if(part_accs->items.size() != part.accessories.size()) {
_state = State::Invalid;
return false;
}
getAccessories(part.accessories, part_accs);
if(part_accs->items.size() != part.accessories.size()) {
_state = State::Invalid;
_lastError = "Accessories array size mismatch.";
return false;
}
writeAccessories(part.accessories, part_accs);
}
auto custom_styles = weapon_prop->at<ArrayProperty>("Styles_10_8C3C82444B986AD7A99595AD4985912D");
if(!custom_styles) {
_state = State::Invalid;
_lastError = "No custom styles found for weapon.";
return false;
}
if(custom_styles->items.size() != weapon.customStyles.size()) {
_state = State::Invalid;
_lastError = "Custom styles array size mismatch.";
return false;
}
@ -1231,9 +1287,23 @@ auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView<Weapon>
}
weapon_prop->at<BoolProperty>("Attach_15_D00AABBD4AD6A04778D56D81E51927B3")->value = weapon.attached;
weapon_prop->at<ByteProperty>("DamageType_18_E1FFA53540591A9087EC698117A65C83")->enumValue = weapon.damageType;
switch(weapon.damageType) {
#define c(enumerator, strenum) case DamageType::enumerator: weapon_prop->at<ByteProperty>("DamageType_18_E1FFA53540591A9087EC698117A65C83")->enumValue = strenum; break;
#include "../Maps/DamageTypes.hpp"
#undef c
default:
Utility::Warning{} << "Unknown damage type enum value in writeWeaponType().";
}
weapon_prop->at<BoolProperty>("DualWield_20_B2EB2CEA4A6A233DC7575996B6DD1222")->value = weapon.dualWield;
weapon_prop->at<ByteProperty>("ColorEfxMode_24_D254BCF943E852BF9ADB8AAA8FD80014")->enumValue = weapon.effectColourMode;
switch(weapon.effectColourMode) {
#define c(enumerator, enumstr) case EffectColourMode::enumerator: \
weapon_prop->at<ByteProperty>("ColorEfxMode_24_D254BCF943E852BF9ADB8AAA8FD80014")->enumValue = enumstr; \
break;
#include "../Maps/EffectColourModes.hpp"
#undef c
default:
Utility::Warning{} << "Unknown effect colour mode in writeWeaponType().";
}
auto effect_colour = weapon_prop->at<ColourStructProperty>("ColorEfx_26_D921B62946C493E487455A831F4520AC");
effect_colour->r = weapon.effectColour.r();
effect_colour->g = weapon.effectColour.g();
@ -1241,7 +1311,12 @@ auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView<Weapon>
effect_colour->a = weapon.effectColour.a();
}
return _mass->saveToFile();
if(!_mass->saveToFile()) {
_lastError = _mass->lastError();
return false;
}
return true;
}
void Mass::getTuningCategory(const char* big_node_prop_name, Int& big_node_id,

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -27,6 +27,14 @@
#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;
@ -34,84 +42,6 @@ using namespace Magnum;
struct ArrayProperty;
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;
};
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;
};
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;
};
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};
};
struct ArmourPart {
std::string slot;
Int id = 0;
Containers::StaticArray<4, Int> styles{ValueInit};
UnsignedInt demoDecals = 8;
Containers::StaticArray<8, Decal> decals{ValueInit};
Containers::StaticArray<8, Accessory> accessories{ValueInit};
};
struct WeaponPart {
Int id = 0;
Containers::StaticArray<4, Int> styles{ValueInit};
UnsignedInt demoDecals = 8;
Containers::StaticArray<8, Decal> decals{ValueInit};
Containers::StaticArray<8, Accessory> accessories{ValueInit};
};
struct Weapon {
std::string name;
std::string type;
Containers::Array<WeaponPart> parts;
Containers::StaticArray<16, CustomStyle> customStyles{ValueInit};
bool attached = false;
std::string damageType;
bool dualWield = false;
std::string effectColourMode;
Color4 effectColour{0.0f};
};
class Mass {
public:
enum class State : UnsignedByte {
@ -126,7 +56,7 @@ class Mass {
Mass(Mass&&) = default;
Mass& operator=(Mass&&) = default;
static auto lastError() -> std::string const&;
auto lastError() -> std::string const&;
static auto getNameFromFile(const std::string& path) -> Containers::Optional<std::string>;
@ -139,8 +69,6 @@ class Mass {
auto state() -> State;
auto demo() const -> bool;
auto dirty() const -> bool;
void setDirty(bool dirty = true);
@ -162,7 +90,7 @@ class Mass {
auto armourParts() -> Containers::ArrayView<ArmourPart>;
void getArmourParts();
auto writeArmourPart(UnsignedLong index) -> bool;
auto writeArmourPart(ArmourSlot slot) -> bool;
auto armourCustomStyles() -> Containers::ArrayView<CustomStyle>;
void getArmourCustomStyles();
@ -207,14 +135,18 @@ class Mass {
auto architecture() -> Int&;
auto techs() -> Containers::ArrayView<Int>;
auto updateSteamId(const std::string& steam_id) -> bool;
auto account() -> std::string const&;
auto updateAccount(const std::string& new_account) -> bool;
private:
void getCustomStyles(Containers::ArrayView<CustomStyle> styles, ArrayProperty* style_array);
auto setCustomStyle(const CustomStyle& style, UnsignedLong index, ArrayProperty* style_array) -> bool;
void getDecals(Containers::ArrayView<Decal> decals, ArrayProperty* decal_array);
void writeDecals(Containers::ArrayView<Decal> decals, ArrayProperty* decal_array);
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;
@ -224,12 +156,11 @@ class Mass {
Containers::Optional<UESaveFile> _mass;
static std::string _lastError;
std::string _lastError;
std::string _folder;
std::string _filename;
State _state = State::Empty;
bool _demo = false;
bool _dirty = false;
@ -260,7 +191,7 @@ class Mass {
Containers::StaticArray<4, Weapon> energyLaunchers;
} _weapons;
Containers::StaticArray<16, CustomStyle> _globalStyles;
Containers::Array<CustomStyle> _globalStyles;
struct {
Int engineId;
@ -273,5 +204,5 @@ class Mass {
Containers::StaticArray<7, Int> techIds;
} _tuning;
std::string _steamId;
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
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -49,11 +49,11 @@ auto MassManager::lastError() -> std::string const& {
return _lastError;
}
auto MassManager::hangar(int hangar) -> Mass& {
auto MassManager::hangar(Int hangar) -> Mass& {
return _hangars[hangar];
}
void MassManager::refreshHangar(int hangar) {
void MassManager::refreshHangar(Int hangar) {
if(hangar < 0 || hangar >= 32) {
return;
}
@ -63,7 +63,7 @@ void MassManager::refreshHangar(int hangar) {
_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) {
_lastError = "Hangar out of range in MassManager::importMass()";
return false;
@ -79,18 +79,22 @@ auto MassManager::importMass(const std::string& staged_fn, int hangar) -> bool {
std::string source = Utility::Directory::join(_stagingAreaDirectory, staged_fn);
Utility::Directory::copy(source, source + ".tmp");
if(!Mass{source + ".tmp"}.updateSteamId(_steamId))
{
_lastError = "The M.A.S.S. file at " + source + " seems to be corrupt.";
Utility::Directory::rm(source + ".tmp");
return false;
Mass mass{source + ".tmp"};
if(!mass.updateAccount(_steamId)) {
_lastError = mass.lastError();
Utility::Directory::rm(source + ".tmp");
return false;
}
}
if(Utility::Directory::exists(Utility::Directory::join(_saveDirectory, _hangars[hangar].filename()))) {
Utility::Directory::rm(Utility::Directory::join(_saveDirectory, _hangars[hangar].filename()));
std::string dest = Utility::Directory::join(_saveDirectory, _hangars[hangar].filename());
if(Utility::Directory::exists(dest)) {
Utility::Directory::rm(dest);
}
if(!Utility::Directory::move(source + ".tmp", Utility::Directory::join(_saveDirectory, _hangars[hangar].filename()))) {
if(!Utility::Directory::move(source + ".tmp", dest)) {
_lastError = Utility::formatString("Couldn't move {} to hangar {:.2d}", staged_fn, hangar + 1);
return false;
}
@ -98,7 +102,7 @@ auto MassManager::importMass(const std::string& staged_fn, int hangar) -> bool {
return true;
}
auto MassManager::exportMass(int hangar) -> bool {
auto MassManager::exportMass(Int hangar) -> bool {
if(hangar < 0 || hangar >= 32) {
_lastError = "Hangar out of range in MassManager::exportMass()";
return false;
@ -121,7 +125,7 @@ auto MassManager::exportMass(int hangar) -> bool {
return true;
}
auto MassManager::moveMass(int source, int destination) -> bool {
auto MassManager::moveMass(Int source, Int destination) -> bool {
if(source < 0 || source >= 32) {
_lastError = "Source hangar out of range.";
return false;
@ -156,14 +160,14 @@ auto MassManager::moveMass(int source, int destination) -> bool {
return true;
}
auto MassManager::deleteMass(int hangar) -> bool {
auto MassManager::deleteMass(Int hangar) -> bool {
if(hangar < 0 || hangar >= 32) {
_lastError = "Hangar out of bounds";
_lastError = "Hangar out of range.";
return false;
}
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;
}
@ -202,7 +206,7 @@ auto MassManager::deleteStagedMass(const std::string& filename) -> bool {
}
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;
}

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -27,8 +27,6 @@
#include "../UESaveFile/Types/IntProperty.h"
#include "../UESaveFile/Types/StringProperty.h"
#include "ResourceIDs.h"
#include "Profile.h"
using namespace Corrade;
@ -36,7 +34,6 @@ using namespace Corrade;
Profile::Profile(const std::string& path):
_profile(path)
{
_profileDirectory = Utility::Directory::path(path);
_filename = Utility::Directory::filename(path);
if(Utility::String::beginsWith(_filename, "Demo")) {
@ -46,11 +43,22 @@ Profile::Profile(const std::string& path):
_type = ProfileType::FullGame;
}
_steamId = Utility::String::ltrim(Utility::String::rtrim(_filename, ".sav"), (_type == ProfileType::Demo ? "Demo" : "") + std::string{"Profile"});
auto account_prop = _profile.at<StringProperty>("Account");
if(!account_prop) {
_lastError = "Couldn't find an account ID in " + _filename;
_valid = false;
return;
}
_account = account_prop->value;
if(Utility::String::beginsWith(_account, "PMCSlot")) {
_version = ProfileVersion::Normal;
}
else {
_version = ProfileVersion::Legacy;
}
refreshValues();
_valid = _profile.valid();
}
auto Profile::valid() const -> bool {
@ -69,8 +77,12 @@ auto Profile::type() const -> ProfileType {
return _type;
}
auto Profile::steamId() const -> std::string const& {
return _steamId;
auto Profile::version() const -> ProfileVersion {
return _version;
}
auto Profile::account() const -> std::string const& {
return _account;
}
void Profile::refreshValues() {
@ -80,7 +92,13 @@ void Profile::refreshValues() {
return;
}
_name = _profile.at<StringProperty>("CompanyName")->value;
auto name_prop = _profile.at<StringProperty>("CompanyName");
if(!name_prop) {
_lastError = "No company name in " + _filename;
_valid = false;
return;
}
_name = name_prop->value;
auto prop = _profile.at<IntProperty>("ActiveFrameSlot");
_activeFrameSlot = prop ? prop->value : 0;
@ -117,6 +135,8 @@ void Profile::refreshValues() {
_muscularConstruction = getResource("ResourceQuarkData", MuscularConstruction);
_mineralExoskeletology = getResource("ResourceQuarkData", MineralExoskeletology);
_carbonisedSkin = getResource("ResourceQuarkData", CarbonisedSkin);
_valid = true;
}
auto Profile::companyName() const -> std::string const& {
@ -125,11 +145,16 @@ auto Profile::companyName() const -> std::string const& {
auto Profile::renameCompany(const std::string& new_name) -> bool {
auto name_prop = _profile.at<StringProperty>("CompanyName");
if(!name_prop) {
_lastError = "No company name in " + _filename;
_valid = false;
return false;
}
name_prop->value = new_name;
if(!_profile.saveToFile()) {
_lastError = "Couldn't save the profile.";
_lastError = _profile.lastError();
return false;
}
@ -149,13 +174,14 @@ auto Profile::setCredits(Int amount) -> bool {
if(!credits_prop) {
credits_prop = new IntProperty;
credits_prop->name.emplace("Credit");
_profile.appendProperty(IntProperty::ptr{credits_prop});
}
credits_prop->value = amount;
if(!_profile.saveToFile()) {
_lastError = "Couldn't save the profile.";
_lastError = _profile.lastError();
return false;
}
@ -171,13 +197,14 @@ auto Profile::setStoryProgress(Int progress) -> bool {
if(!story_progress_prop) {
story_progress_prop = new IntProperty;
story_progress_prop->name.emplace("StoryProgress");
_profile.appendProperty(IntProperty::ptr{story_progress_prop});
}
story_progress_prop->value = progress;
if(!_profile.saveToFile()) {
_lastError = "Couldn't save the profile.";
_lastError = _profile.lastError();
return false;
}
@ -348,7 +375,7 @@ auto Profile::setCarbonisedSkin(Int amount) -> bool {
return setResource("ResourceQuarkData", CarbonisedSkin, amount);
}
auto Profile::getResource(const char* container, Int id) -> Int {
auto Profile::getResource(const char* container, MaterialID id) -> Int {
auto mats_prop = _profile.at<ArrayProperty>(container);
if(!mats_prop) {
@ -364,14 +391,16 @@ auto Profile::getResource(const char* container, Int id) -> Int {
return it != mats_prop->items.end() ? static_cast<ResourceItemValue*>(it->get())->quantity : 0;
}
auto Profile::setResource(const char* container, Int id, Int amount) -> bool {
auto Profile::setResource(const char* container, MaterialID id, Int amount) -> bool {
auto mats_prop = _profile.at<ArrayProperty>(container);
if(!mats_prop) {
_lastError = "Couldn't find " + std::string{container} + " in " + _filename;
_valid = false;
return false;
}
static auto predicate = [&id](UnrealPropertyBase::ptr& prop){
auto predicate = [&id](UnrealPropertyBase::ptr& prop){
auto res_prop = static_cast<ResourceItemValue*>(prop.get());
return res_prop->id == id;
};
@ -396,5 +425,5 @@ auto Profile::setResource(const char* container, Int id, Int amount) -> bool {
return false;
}
return _profile.saveToFile();
return true;
}

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -22,6 +22,8 @@
#include "../UESaveFile/UESaveFile.h"
#include "ResourceIDs.h"
using namespace Magnum;
enum class ProfileType : UnsignedByte {
@ -29,6 +31,11 @@ enum class ProfileType : UnsignedByte {
FullGame
};
enum class ProfileVersion : UnsignedByte {
Legacy, // pre-0.8
Normal // 0.8 and later
};
class Profile {
public:
explicit Profile(const std::string& path);
@ -41,7 +48,9 @@ class Profile {
auto type() const -> ProfileType;
auto steamId() const -> std::string const&;
auto version() const -> ProfileVersion;
auto account() const -> std::string const&;
void refreshValues();
@ -119,13 +128,13 @@ class Profile {
auto setCarbonisedSkin(Int amount) -> bool;
private:
auto getResource(const char* container, Int id) -> Int;
auto setResource(const char* container, Int id, Int amount) -> bool;
auto getResource(const char* container, MaterialID id) -> Int;
auto setResource(const char* container, MaterialID id, Int amount) -> bool;
std::string _profileDirectory;
std::string _filename;
ProfileType _type;
ProfileVersion _version;
UESaveFile _profile;
@ -159,7 +168,7 @@ class Profile {
Int _mineralExoskeletology = 0;
Int _carbonisedSkin = 0;
std::string _steamId;
std::string _account;
bool _valid = false;
std::string _lastError;

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -76,7 +76,7 @@ auto ProfileManager::refreshProfiles() -> bool {
}
if(_profiles.empty()) {
_lastError = "No profiles were found.";
_lastError = "No valid profiles were found.";
return false;
}
@ -100,7 +100,7 @@ auto ProfileManager::deleteProfile(std::size_t index, bool delete_builds) -> boo
for(UnsignedByte i = 0; i < 32; ++i) {
std::string filename = Utility::formatString("{}Unit{:.2d}{}.sav",
_profiles[index].type() == ProfileType::Demo ? "Demo": "",
i, _profiles[index].steamId());
i, _profiles[index].account());
Utility::Directory::rm(Utility::Directory::join(_saveDirectory, filename));
}
}
@ -158,7 +158,7 @@ auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> boo
for(UnsignedByte i = 0; i < 32; ++i) {
std::string build_filename = Utility::formatString("{}Unit{:.2d}{}.sav",
_profiles[index].type() == ProfileType::Demo ? "Demo": "",
i, _profiles[index].steamId());
i, _profiles[index].account());
if(!Utility::Directory::exists(Utility::Directory::join(_saveDirectory, build_filename))) {
continue;

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -62,7 +62,7 @@ SaveTool::SaveTool(const Arguments& arguments):
#endif
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.";
}
else {
@ -95,6 +95,10 @@ SaveTool::SaveTool(const Arguments& arguments):
_backupsDir = Utility::Directory::join(Utility::Directory::path(Utility::Directory::executableLocation()), "backups");
_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)) {
Utility::Directory::mkpath(_backupsDir);
@ -104,6 +108,22 @@ SaveTool::SaveTool(const Arguments& arguments):
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()) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error initialising the app", _lastError.c_str(), window());
exit(EXIT_FAILURE);
@ -207,7 +227,7 @@ void SaveTool::handleFileAction(efsw::WatchID watch_id,
switch(action) {
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" : ""))) {
int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) +
(filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30);
@ -221,7 +241,7 @@ void SaveTool::handleFileAction(efsw::WatchID watch_id,
}
break;
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" : ""))) {
int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) +
(filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30);
@ -235,7 +255,7 @@ void SaveTool::handleFileAction(efsw::WatchID watch_id,
if(filename == _currentProfile->filename()) {
_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" : ""))) {
int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) +
(filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30);
@ -253,7 +273,7 @@ void SaveTool::handleFileAction(efsw::WatchID watch_id,
}
break;
case efsw::Actions::Moved:
if(Utility::String::endsWith(filename, _currentProfile->steamId() + ".sav")) {
if(Utility::String::endsWith(filename, _currentProfile->account() + ".sav")) {
if(Utility::String::endsWith(old_filename, ".tmp")) {
is_moved_after_save = true;
return;
@ -399,6 +419,9 @@ void SaveTool::updateCheckEvent(SDL_Event& event) {
return ( major * 10000 + minor * 100 + 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};
@ -414,11 +437,11 @@ void SaveTool::updateCheckEvent(SDL_Event& event) {
_queue.addToast(Toast::Type::Warning, "Your version is out of date.\nCheck the settings for more information.",
std::chrono::milliseconds{5000});
_updateAvailable = true;
_latestVersion = Utility::formatString("{}.{}.{}", latest_ver.major, latest_ver.minor, latest_ver.patch);
_releaseLink = response[0]["html_url"];
_downloadLink = response[0]["assets"][0]["browser_download_url"];
_latestVersion = latest_ver;
_releaseLink = release["html_url"];
_downloadLink = release["assets"][0]["browser_download_url"];
}
else if(latest_ver == current_ver) {
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) {
@ -427,7 +450,6 @@ void SaveTool::updateCheckEvent(SDL_Event& event) {
break;
}
}
void SaveTool::initialiseConfiguration() {
@ -560,7 +582,7 @@ auto SaveTool::findGameDataDirectory() -> bool {
void SaveTool::initialiseMassManager() {
_massManager.emplace(_saveDir,
_currentProfile->steamId(),
_currentProfile->account(),
_currentProfile->type() == ProfileType::Demo,
_stagingDir);
@ -746,7 +768,7 @@ void SaveTool::drawTooltip(const char* text, Float wrap_pos) {
}
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() {

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -105,6 +105,9 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
auto drawRenamePopup(Containers::ArrayView<char> name_view) -> bool;
void drawGeneralInfo();
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();
auto drawDeleteMassPopup(int mass_index) -> ImGuiID;
auto drawDeleteStagedMassPopup(const std::string& filename) -> ImGuiID;
@ -118,6 +121,7 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
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();
@ -168,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 checkGameState();
@ -212,6 +222,10 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
std::string _backupsDir;
std::string _stagingDir;
std::string _armouryDir;
std::string _armoursDir;
std::string _weaponsDir;
std::string _stylesDir;
enum class GameState : UnsignedByte {
Unknown, NotRunning, Running
@ -225,6 +239,8 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
Containers::Pointer<MassManager> _massManager;
Mass* _currentMass{nullptr};
Weapon* _currentWeapon = nullptr;
Containers::Pointer<efsw::FileWatcher> _fileWatcher;
enum watchID {
SaveDir = 0,
@ -248,5 +264,20 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener
std::string _releaseLink;
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};
};

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -42,10 +42,9 @@ void SaveTool::drawManager() {
return;
}
ImGui::AlignTextToFramePadding();
ImGui::Text("Current profile: %s (%s)",
_currentProfile->companyName().c_str(),
_currentProfile->type() == ProfileType::Demo ? "demo" : "full game");
drawAlignedText("Current profile: %s (%s)",
_currentProfile->companyName().c_str(),
_currentProfile->type() == ProfileType::Demo ? "demo" : "full game");
ImGui::SameLine();
if(ImGui::Button(ICON_FA_ARROW_LEFT " Back to profile manager")) {
_currentProfile = nullptr;
@ -188,7 +187,7 @@ void SaveTool::drawGeneralInfo() {
{
ImGui::TextUnformatted("Story progress:");
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);
}
else {
@ -226,8 +225,7 @@ void SaveTool::drawGeneralInfo() {
}
if(drawRenamePopup(name_buf)) {
if(!_currentProfile->renameCompany(name_buf.data())) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_currentProfile->lastError().c_str(), window());
_queue.addToast(Toast::Type::Error, _currentProfile->lastError());
}
}
@ -244,8 +242,7 @@ void SaveTool::drawGeneralInfo() {
}
if(drawIntEditPopup(&credits, 20000000)) {
if(!_currentProfile->setCredits(credits)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_currentProfile->lastError().c_str(), window());
_queue.addToast(Toast::Type::Error, _currentProfile->lastError());
}
}
@ -261,11 +258,10 @@ void SaveTool::drawGeneralInfo() {
}
for(const auto& sp : story_progress) {
if(ImGui::BeginMenu(sp.chapter)) {
if(std::strcmp(sp.after, "") == 0) {
if(!sp.after) {
if(ImGui::MenuItem(sp.point)) {
if(!_currentProfile->setStoryProgress(sp.id)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_currentProfile->lastError().c_str(), window());
_queue.addToast(Toast::Type::Error, _currentProfile->lastError());
}
}
}
@ -273,8 +269,7 @@ void SaveTool::drawGeneralInfo() {
if(ImGui::BeginMenu(sp.after)) {
if(ImGui::MenuItem(sp.point)) {
if(!_currentProfile->setStoryProgress(sp.id)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_currentProfile->lastError().c_str(), window());
_queue.addToast(Toast::Type::Error, _currentProfile->lastError());
}
}
ImGui::EndMenu();
@ -292,45 +287,6 @@ void SaveTool::drawResearchInventory() {
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,
ImGuiTableFlags_BordersOuter|ImGuiTableFlags_ScrollY|ImGuiTableFlags_BordersInnerH))
{
@ -343,55 +299,136 @@ void SaveTool::drawResearchInventory() {
ImGui::TableSetColumnIndex(1);
ImGui::Text("Engine materials");
matRow("Verse steel", 1, verse_steel, verseSteel, VerseSteel)
matRow("Undinium", 2, undinium, undinium, Undinium)
matRow("Necrium alloy", 3, necrium_alloy, necriumAlloy, NecriumAlloy)
matRow("Lunarite", 4, lunarite, lunarite, Lunarite)
matRow("Asterite", 5, asterite, asterite, Asterite)
unavRow("Hallite fragma", 6)
unavRow("Unnoctinium", 7)
drawMaterialRow("Verse steel", 1,
[this]{ return _currentProfile->verseSteel(); },
[this](Int amount){ return _currentProfile->setVerseSteel(amount); });
drawMaterialRow("Undinium", 2,
[this]{ return _currentProfile->undinium(); },
[this](Int amount){ return _currentProfile->setUndinium(amount); });
drawMaterialRow("Necrium alloy", 3,
[this]{ return _currentProfile->necriumAlloy(); },
[this](Int amount){ return _currentProfile->setNecriumAlloy(amount); });
drawMaterialRow("Lunarite", 4,
[this]{ return _currentProfile->lunarite(); },
[this](Int amount){ return _currentProfile->setLunarite(amount); });
drawMaterialRow("Asterite", 5,
[this]{ return _currentProfile->asterite(); },
[this](Int amount){ return _currentProfile->setAsterite(amount); });
drawUnavailableMaterialRow("Hallite fragma", 6);
drawUnavailableMaterialRow("Unnoctinium", 7);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableSetColumnIndex(1);
ImGui::Text("OS materials");
matRow("Ednil", 1, ednil, ednil, Ednil)
matRow("Nuflalt", 2, nuflalt, nuflalt, Nuflalt)
matRow("Aurelene", 3, aurelene, aurelene, Aurelene)
matRow("Soldus", 4, soldus, soldus, Soldus)
matRow("Synthesized N", 5, synthesised_n, synthesisedN, SynthesisedN)
unavRow("Nanoc", 6)
unavRow("Abyssillite", 7)
drawMaterialRow("Ednil", 1,
[this]{ return _currentProfile->ednil(); },
[this](Int amount){ return _currentProfile->setEdnil(amount); });
drawMaterialRow("Nuflalt", 2,
[this]{ return _currentProfile->nuflalt(); },
[this](Int amount){ return _currentProfile->setNuflalt(amount); });
drawMaterialRow("Aurelene", 3,
[this]{ return _currentProfile->aurelene(); },
[this](Int amount){ return _currentProfile->setAurelene(amount); });
drawMaterialRow("Soldus", 4,
[this]{ return _currentProfile->soldus(); },
[this](Int amount){ return _currentProfile->setSoldus(amount); });
drawMaterialRow("Synthesized N", 5,
[this]{ return _currentProfile->synthesisedN(); },
[this](Int amount){ return _currentProfile->setSynthesisedN(amount); });
drawUnavailableMaterialRow("Nanoc", 6);
drawUnavailableMaterialRow("Abyssillite", 7);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableSetColumnIndex(1);
ImGui::Text("Architect materials");
matRow("Alcarbonite", 1, alcarbonite, alcarbonite, Alcarbonite)
matRow("Keriphene", 2, keriphene, keriphene, Keriphene)
matRow("Nitinol-CM", 3, nitinol_cm, nitinolCM, NitinolCM)
matRow("Quarkium", 4, quarkium, quarkium, Quarkium)
matRow("Alterene", 5, alterene, alterene, Alterene)
unavRow("Cosmium", 6)
unavRow("Purified quarkium", 7)
drawMaterialRow("Alcarbonite", 1,
[this]{ return _currentProfile->alcarbonite(); },
[this](Int amount){ return _currentProfile->setAlcarbonite(amount); });
drawMaterialRow("Keripehene", 2,
[this]{ return _currentProfile->keriphene(); },
[this](Int amount){ return _currentProfile->setKeriphene(amount); });
drawMaterialRow("Nitinol-CM", 3,
[this]{ return _currentProfile->nitinolCM(); },
[this](Int amount){ return _currentProfile->setNitinolCM(amount); });
drawMaterialRow("Quarkium", 4,
[this]{ return _currentProfile->quarkium(); },
[this](Int amount){ return _currentProfile->setQuarkium(amount); });
drawMaterialRow("Alterene", 5,
[this]{ return _currentProfile->alterene(); },
[this](Int amount){ return _currentProfile->setAlterene(amount); });
drawUnavailableMaterialRow("Cosmium", 6);
drawUnavailableMaterialRow("Purified quarkium", 7);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableSetColumnIndex(1);
ImGui::Text("Quark data");
matRow("Mixed composition", 1, mixed_composition, mixedComposition, MixedComposition)
matRow("Void residue", 2, void_residue, voidResidue, VoidResidue)
matRow("Muscular construction", 3, muscular_construction, muscularConstruction, MuscularConstruction)
matRow("Mineral exoskeletology", 4, mineral_exoskeletology, mineralExoskeletology, MineralExoskeletology)
matRow("Carbonized skin", 5, carbonised_skin, carbonisedSkin, CarbonisedSkin)
unavRow("Isolated void particle", 6)
unavRow("Weaponised physiology", 7)
drawMaterialRow("Mixed composition", 1,
[this]{ return _currentProfile->mixedComposition(); },
[this](Int amount){ return _currentProfile->setMixedComposition(amount); });
drawMaterialRow("Void residue", 2,
[this]{ return _currentProfile->voidResidue(); },
[this](Int amount){ return _currentProfile->setVoidResidue(amount); });
drawMaterialRow("Muscular construction", 3,
[this]{ return _currentProfile->muscularConstruction(); },
[this](Int amount){ return _currentProfile->setMuscularConstruction(amount); });
drawMaterialRow("Mineral exoskeletology", 4,
[this]{ return _currentProfile->mineralExoskeletology(); },
[this](Int amount){ return _currentProfile->setMineralExoskeletology(amount); });
drawMaterialRow("Carbonized skin", 5,
[this]{ return _currentProfile->carbonisedSkin(); },
[this](Int amount){ return _currentProfile->setCarbonisedSkin(amount); });
drawUnavailableMaterialRow("Isolated void particle", 6);
drawUnavailableMaterialRow("Weaponised physiology", 7);
ImGui::EndTable();
}
}
#undef unavRow
#undef matRow
template<typename Getter, typename Setter>
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() {
@ -409,7 +446,7 @@ void SaveTool::drawMassManager() {
ImGui::TableSetupColumn("##Hangar", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("##MASSName", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("##Active", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("##DeleteButton", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("##Buttons", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupScrollFreeze(0, 1);
@ -451,11 +488,10 @@ void SaveTool::drawMassManager() {
std::string file = *(static_cast<std::string*>(payload->Data));
if(!_massManager->importMass(file, i)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error importing M.A.S.S.",
_massManager->lastError().c_str(), window());
_queue.addToast(Toast::Type::Error, _massManager->lastError());
}
}
else if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("Mass")) {
else if((payload = ImGui::AcceptDragDropPayload("Mass"))) {
if(payload->DataSize != sizeof(int)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error",
"payload->DataSize != sizeof(int) in SaveTool::drawMassManager()",
@ -466,9 +502,7 @@ void SaveTool::drawMassManager() {
int index = *(static_cast<int*>(payload->Data));
if(!_massManager->moveMass(index, i)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_massManager->lastError().c_str(),
window());
_queue.addToast(Toast::Type::Error, _massManager->lastError());
}
}
@ -504,6 +538,11 @@ void SaveTool::drawMassManager() {
}
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)) {
mass_to_delete = i;
ImGui::OpenPopup(mass_deletion_popup_ID);
@ -576,9 +615,7 @@ void SaveTool::drawMassManager() {
int index = *(static_cast<int*>(payload->Data));
if(!_massManager->exportMass(index)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_massManager->lastError().c_str(),
window());
_queue.addToast(Toast::Type::Error, _massManager->lastError());
}
}
@ -627,8 +664,7 @@ auto SaveTool::drawDeleteMassPopup(int mass_index) -> ImGuiID {
ImGui::TableSetColumnIndex(1);
if(ImGui::Button("Yes")) {
if(!_massManager->deleteMass(mass_index)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when deleting M.A.S.S.",
_massManager->lastError().c_str(), window());
_queue.addToast(Toast::Type::Error, _massManager->lastError());
}
ImGui::CloseCurrentPopup();
}
@ -666,8 +702,7 @@ auto SaveTool::drawDeleteStagedMassPopup(const std::string& filename) -> ImGuiID
ImGui::TableSetColumnIndex(1);
if(ImGui::Button("Yes")) {
if(!_massManager->deleteStagedMass(filename)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when deleting M.A.S.S.",
_massManager->lastError().c_str(), window());
_queue.addToast(Toast::Type::Error, _massManager->lastError());
}
ImGui::CloseCurrentPopup();
}

File diff suppressed because it is too large Load Diff

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
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -90,7 +90,7 @@ void SaveTool::drawProfileManager() {
}
ImGui::TableSetColumnIndex(1);
ImGui::TextUnformatted(_profileManager->profiles()[i].type() == ProfileType::Demo ? "Demo" : "Full");
ImGui::TextUnformatted(_profileManager->profiles()[i].type() == ProfileType::Demo ? "Demo (legacy)" : "Full (legacy)");
ImGui::TableSetColumnIndex(2);
if(ImGui::SmallButton(ICON_FA_FILE_ARCHIVE)) {
@ -150,8 +150,7 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
ImGui::TableSetColumnIndex(1);
if(ImGui::Button("Yes")) {
if(!_profileManager->restoreBackup(backup_index)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when restoring backup",
_profileManager->lastError().c_str(), window());
_queue.addToast(Toast::Type::Error, _profileManager->lastError());
}
_profileManager->refreshProfiles();
ImGui::CloseCurrentPopup();
@ -190,8 +189,7 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
ImGui::TableSetColumnIndex(1);
if(ImGui::Button("Yes")) {
if(!_profileManager->deleteBackup(backup_index)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when deleting backup",
_profileManager->lastError().c_str(), window());
_queue.addToast(Toast::Type::Error, _profileManager->lastError());
}
ImGui::CloseCurrentPopup();
}
@ -378,8 +376,7 @@ auto SaveTool::drawDeleteProfilePopup(std::size_t profile_index) -> ImGuiID {
ImGui::TableSetColumnIndex(1);
if(ImGui::Button("Yes")) {
if(!_profileManager->deleteProfile(profile_index, delete_builds)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error when deleting profile",
_profileManager->lastError().c_str(), window());
_queue.addToast(Toast::Type::Error, _profileManager->lastError());
}
ImGui::CloseCurrentPopup();
}

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -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), "
"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";
ImGui::Text(ICON_FA_GIT_ALT " %s", repo);
drawAlignedText(ICON_FA_GIT_ALT " %s", repo);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(repo);
@ -90,9 +89,8 @@ void SaveTool::drawAbout() {
if(ImGui::TreeNodeEx("Corrade", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::Text("Version used: %s", CORRADE_VERSION_STRING);
ImGui::AlignTextToFramePadding();
const char* corrade_website = "https://magnum.graphics/corrade";
ImGui::Text(ICON_FA_GLOBE " %s", corrade_website);
drawAlignedText(ICON_FA_GLOBE " %s", corrade_website);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(corrade_website);
@ -119,9 +117,8 @@ void SaveTool::drawAbout() {
ImGui::TextUnformatted("Versions used:");
ImGui::BulletText("Magnum: %s", MAGNUM_VERSION_STRING);
ImGui::BulletText("Integration: %s", MAGNUMINTEGRATION_VERSION_STRING);
ImGui::AlignTextToFramePadding();
const char* magnum_website = "https://magnum.graphics";
ImGui::Text(ICON_FA_GLOBE " %s", magnum_website);
drawAlignedText(ICON_FA_GLOBE " %s", magnum_website);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(magnum_website);
@ -146,9 +143,8 @@ void SaveTool::drawAbout() {
if(ImGui::TreeNodeEx("Dear ImGui", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::Text("Version used: %s", IMGUI_VERSION);
ImGui::AlignTextToFramePadding();
const char* imgui_repo = "https://github.com/ocornut/imgui";
ImGui::Text(ICON_FA_GITHUB " %s", imgui_repo);
drawAlignedText(ICON_FA_GITHUB " %s", imgui_repo);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(imgui_repo);
@ -173,9 +169,8 @@ void SaveTool::drawAbout() {
if(ImGui::TreeNodeEx("Simple DirectMedia Layer (SDL) 2", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::Text("Version used: %i.%i.%i", SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
ImGui::AlignTextToFramePadding();
const char* sdl_website = "https://www.libsdl.org/";
ImGui::Text(ICON_FA_GLOBE " %s", sdl_website);
drawAlignedText(ICON_FA_GLOBE " %s", sdl_website);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(sdl_website);
@ -200,9 +195,8 @@ void SaveTool::drawAbout() {
if(ImGui::TreeNodeEx("libzip", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::Text("Version used: %s", LIBZIP_VERSION);
ImGui::AlignTextToFramePadding();
const char* libzip_website = "https://libzip.org/";
ImGui::Text(ICON_FA_GLOBE " %s", libzip_website);
drawAlignedText(ICON_FA_GLOBE " %s", libzip_website);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(libzip_website);
@ -226,9 +220,8 @@ void SaveTool::drawAbout() {
}
if(ImGui::TreeNodeEx("Entropia File System Watcher (efsw)", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::AlignTextToFramePadding();
const char* efsw_repo = "https://github.com/SpartanJ/efsw";
ImGui::Text(ICON_FA_GITHUB " %s", efsw_repo);
drawAlignedText(ICON_FA_GITHUB " %s", efsw_repo);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(efsw_repo);
@ -252,9 +245,8 @@ void SaveTool::drawAbout() {
}
if(ImGui::TreeNodeEx("C++ Requests (cpr)", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::AlignTextToFramePadding();
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();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(cpr_website);
@ -278,9 +270,8 @@ void SaveTool::drawAbout() {
}
if(ImGui::TreeNodeEx("JSON for Modern C++ (aka json.hpp)", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::AlignTextToFramePadding();
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();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(json_website);
@ -305,9 +296,8 @@ void SaveTool::drawAbout() {
if(ImGui::TreeNodeEx("Font Awesome", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::TextUnformatted("Version used: 5.15.3");
ImGui::AlignTextToFramePadding();
const char* fa_website = "https://fontawesome.com/";
ImGui::Text(ICON_FA_GLOBE " %s", fa_website);
drawAlignedText(ICON_FA_GLOBE " %s", fa_website);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(fa_website);
@ -323,9 +313,8 @@ void SaveTool::drawAbout() {
}
if(ImGui::TreeNodeEx("IconFontCppHeaders", ImGuiTreeNodeFlags_SpanAvailWidth)) {
ImGui::AlignTextToFramePadding();
const char* icon_repo = "https://github.com/juliettef/IconFontCppHeaders";
ImGui::Text(ICON_FA_GITHUB " %s", icon_repo);
drawAlignedText(ICON_FA_GITHUB " %s", icon_repo);
ImGui::SameLine();
if(ImGui::Button("Copy to clipboard")) {
ImGui::SetClipboardText(icon_repo);

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -55,8 +55,7 @@ void SaveTool::drawMainMenu() {
ImGui::Separator();
if(ImGui::BeginMenu(ICON_FA_COG " Settings")) {
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted("Frame limiter:");
drawAlignedText("Frame limiter:");
ImGui::SameLine();
static UnsignedByte selection = static_cast<UnsignedByte>(_framelimit);
@ -118,8 +117,7 @@ void SaveTool::drawMainMenu() {
}
if(_updateAvailable) {
ImGui::AlignTextToFramePadding();
ImGui::Text("Version %s is available.", _latestVersion.c_str());
drawAlignedText("Version %s is available.", _latestVersion.c_str());
if(ImGui::Button(ICON_FA_FILE_SIGNATURE " Release notes")) {
openUri(_releaseLink);
}

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -45,6 +45,10 @@ 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;

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -36,6 +36,8 @@ class BinaryReader {
auto eof() -> bool;
auto position() -> Long;
auto seek(Long position) -> bool;
void closeFile();
auto readChar(char& value) -> bool;
@ -51,6 +53,11 @@ class BinaryReader {
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;

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -46,6 +46,10 @@ auto BinaryWriter::position() -> Long {
return _ftelli64(_file);
}
auto BinaryWriter::array() const -> Containers::ArrayView<const char> {
return _data;
}
auto BinaryWriter::arrayPosition() const -> UnsignedLong {
return _index;
}
@ -102,7 +106,7 @@ auto BinaryWriter::writeDouble(Double value) -> bool {
return std::fwrite(&value, sizeof(Double), 1, _file) == 1;
}
auto BinaryWriter::writeArray(Containers::ArrayView<char> array) -> bool {
auto BinaryWriter::writeArray(Containers::ArrayView<const char> array) -> bool {
if(array.size() == 0) {
return false;
}

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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
@ -33,12 +33,19 @@ class BinaryWriter {
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;
@ -53,7 +60,11 @@ class BinaryWriter {
auto writeUnsignedLong(UnsignedLong value) -> bool;
auto writeFloat(Float value) -> bool;
auto writeDouble(Double value) -> bool;
auto writeArray(Containers::ArrayView<char> array) -> 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 {
@ -64,7 +75,7 @@ class BinaryWriter {
template<typename T, typename U = std::conditional_t<std::is_trivially_copyable<T>::value, T, T&>>
auto writeValueToArray(U value) -> UnsignedLong {
Containers::ArrayView<U> view{&value, 1};
Containers::ArrayView<T> view{&value, 1};
return writeDataToArray(view);
}

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,5 +1,5 @@
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

View File

@ -1,7 +1,7 @@
#pragma once
// MassBuilderSaveTool
// Copyright (C) 2021 Guillaume Jacquemin
// 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

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