From 4cdd1b35ec424a85794da7e96e5d0ceae82b9d0d Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 19 Aug 2021 20:33:59 +0200 Subject: [PATCH 001/128] Mass(Manager): rework to prepare for the viewer. --- src/CMakeLists.txt | 1 + src/Mass/Locators.h | 20 ++++++++ src/Mass/Mass.cpp | 72 ++++++++++----------------- src/Mass/Mass.h | 10 ++-- src/MassManager/MassManager.cpp | 16 +----- src/MassManager/MassManager.h | 3 +- src/SaveTool/SaveTool_MainManager.cpp | 16 +++--- 7 files changed, 63 insertions(+), 75 deletions(-) create mode 100644 src/Mass/Locators.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ad2ac6a..c202ffd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -42,6 +42,7 @@ add_executable(MassBuilderSaveTool WIN32 Profile/Profile.cpp MassManager/MassManager.h MassManager/MassManager.cpp + Mass/Locators.h Mass/Mass.h Mass/Mass.cpp Maps/LastMissionId.h diff --git a/src/Mass/Locators.h b/src/Mass/Locators.h new file mode 100644 index 0000000..51011c2 --- /dev/null +++ b/src/Mass/Locators.h @@ -0,0 +1,20 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +constexpr char mass_name_locator[] = "Name_45_A037C5D54E53456407BDF091344529BB\0\f\0\0\0StrProperty"; +constexpr char steamid_locator[] = "Account\0\f\0\0\0StrProperty"; diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 7f6f67f..7c689af 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -21,24 +21,24 @@ #include #include +#include "Locators.h" + #include "Mass.h" using namespace Corrade; -constexpr char mass_name_locator[] = "Name_45_A037C5D54E53456407BDF091344529BB\0\x0c\0\0\0StrProperty"; -constexpr char steamid_locator[] = "Account\0\x0c\0\0\0StrProperty"; - std::string Mass::_lastError; -Mass::Mass(const std::string& filename) { - _filename = filename; +Mass::Mass(const std::string& path) { + _folder = Utility::Directory::path(path); + _filename = Utility::Directory::filename(path); - if(!Utility::Directory::exists(_filename)) { - _lastError = "The file " + _filename + " couldn't be found."; + if(!Utility::Directory::exists(path)) { + _lastError = path + " couldn't be found."; return; } - auto mmap = Utility::Directory::mapRead(_filename); + auto mmap = Utility::Directory::mapRead(path); auto iter = std::search(mmap.begin(), mmap.end(), &mass_name_locator[0], &mass_name_locator[56]); @@ -47,7 +47,7 @@ Mass::Mass(const std::string& filename) { _state = MassState::Valid; } else { - _lastError = "The name couldn't be found in " + filename; + _lastError = "The name couldn't be found in " + _filename; _state = MassState::Invalid; } } @@ -56,15 +56,15 @@ auto Mass::lastError() -> std::string const& { return _lastError; } -auto Mass::getNameFromFile(const std::string& filename) -> std::string { - if(!Utility::Directory::exists(filename)) { - _lastError = "The file " + filename + " couldn't be found."; +auto Mass::getNameFromFile(const std::string& path) -> std::string { + if(!Utility::Directory::exists(path)) { + _lastError = path + " couldn't be found."; return ""; } - std::string name = ""; + std::string name; - auto mmap = Utility::Directory::mapRead(filename); + auto mmap = Utility::Directory::mapRead(path); auto iter = std::search(mmap.begin(), mmap.end(), &mass_name_locator[0], &mass_name_locator[56]); @@ -72,7 +72,7 @@ auto Mass::getNameFromFile(const std::string& filename) -> std::string { name = std::string{iter + 70}; } else { - _lastError = "The name couldn't be found in " + filename; + _lastError = "The name couldn't be found in " + path; } return name; @@ -86,49 +86,29 @@ auto Mass::name() -> std::string const&{ return _name; } -auto Mass::getName() -> std::string const& { - if(!Utility::Directory::exists(_filename)) { - _lastError = "The file " + _filename + " couldn't be found."; - _state = MassState::Empty; - return _name = ""; - } - - auto mmap = Utility::Directory::mapRead(_filename); - - auto iter = std::search(mmap.begin(), mmap.end(), &mass_name_locator[0], &mass_name_locator[56]); - - if(iter != mmap.end()) { - _state = MassState::Valid; - return _name = std::string{iter + 70}; - } - else { - _lastError = "The name couldn't be found in " + _filename; - _state = MassState::Invalid; - return _name = ""; - } -} - auto Mass::state() -> MassState { return _state; } auto Mass::updateSteamId(const std::string& steam_id) -> bool { - if(!Utility::Directory::exists(_filename)) { - _lastError = "The file " + _filename + " couldn't be found."; + std::string path = Utility::Directory::join(_folder, _filename); + + if(!Utility::Directory::exists(path)) { + _lastError = path + " couldn't be found."; _state = MassState::Empty; return false; } - Utility::Directory::copy(_filename, _filename + ".tmp"); + Utility::Directory::copy(path, path + ".tmp"); { - auto mmap = Utility::Directory::map(_filename + ".tmp"); + auto mmap = Utility::Directory::map(path + ".tmp"); auto iter = std::search(mmap.begin(), mmap.end(), &steamid_locator[0], &steamid_locator[23]); if(iter == mmap.end()) { - _lastError = "The M.A.S.S. file at " + _filename + " seems to be corrupt."; - Utility::Directory::rm(_filename + ".tmp"); + _lastError = "The M.A.S.S. file at " + path + " seems to be corrupt."; + Utility::Directory::rm(path + ".tmp"); return false; } @@ -141,11 +121,11 @@ auto Mass::updateSteamId(const std::string& steam_id) -> bool { } } - if(Utility::Directory::exists(_filename)) { - Utility::Directory::rm(_filename); + if(Utility::Directory::exists(path)) { + Utility::Directory::rm(path); } - Utility::Directory::move(_filename + ".tmp", _filename); + Utility::Directory::move(path + ".tmp", path); return true; } diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 1c2d0ad..124c319 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -28,7 +28,7 @@ enum class MassState : UnsignedByte { class Mass { public: - explicit Mass(const std::string& filename); + explicit Mass(const std::string& path); Mass(const Mass&) = delete; Mass& operator=(const Mass&) = delete; @@ -38,12 +38,11 @@ class Mass { static auto lastError() -> std::string const&; - static auto getNameFromFile(const std::string& filename) -> std::string; + static auto getNameFromFile(const std::string& path) -> std::string; auto filename() -> std::string const&; auto name() -> std::string const&; - auto getName() -> std::string const&; auto state() -> MassState; @@ -52,7 +51,8 @@ class Mass { private: static std::string _lastError; - std::string _filename = ""; - std::string _name = ""; + std::string _folder; + std::string _filename; + std::string _name; MassState _state = MassState::Empty; }; diff --git a/src/MassManager/MassManager.cpp b/src/MassManager/MassManager.cpp index e0c4910..be83937 100644 --- a/src/MassManager/MassManager.cpp +++ b/src/MassManager/MassManager.cpp @@ -49,20 +49,8 @@ auto MassManager::lastError() -> std::string const& { return _lastError; } -auto MassManager::massName(int hangar) -> std::string const& { - if(hangar < 0 || hangar >= 32) { - return empty_string; - } - - return _hangars[hangar].name(); -} - -auto MassManager::massState(int hangar) -> MassState { - if(hangar < 0 || hangar >= 32) { - return MassState::Empty; - } - - return _hangars[hangar].state(); +auto MassManager::hangar(int hangar) -> Mass& { + return _hangars[hangar]; } void MassManager::refreshHangar(int hangar) { diff --git a/src/MassManager/MassManager.h b/src/MassManager/MassManager.h index 2b0d0f7..a30c7e8 100644 --- a/src/MassManager/MassManager.h +++ b/src/MassManager/MassManager.h @@ -31,8 +31,7 @@ class MassManager { auto lastError() -> std::string const&; - auto massName(int hangar) -> std::string const&; - auto massState(int hangar) -> MassState; + auto hangar(int hangar) -> Mass&; void refreshHangar(int hangar); diff --git a/src/SaveTool/SaveTool_MainManager.cpp b/src/SaveTool/SaveTool_MainManager.cpp index 747aea1..3708694 100644 --- a/src/SaveTool/SaveTool_MainManager.cpp +++ b/src/SaveTool/SaveTool_MainManager.cpp @@ -429,13 +429,13 @@ void SaveTool::drawMassManager() { ImGui::TableSetColumnIndex(0); ImGui::Selectable(Utility::formatString("{:.2d}", i + 1).c_str(), false, ImGuiSelectableFlags_SpanAllColumns|ImGuiSelectableFlags_AllowItemOverlap); - if(_massManager->massState(i) == MassState::Valid && + if(_massManager->hangar(i).state() == MassState::Valid && ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) { drag_drop_index = i; ImGui::SetDragDropPayload("Mass", &drag_drop_index, sizeof(int)); - ImGui::Text("%s - Hangar %.2d", _massManager->massName(i).c_str(), i + 1); + ImGui::Text("%s - Hangar %.2d", _massManager->hangar(i).name().c_str(), i + 1); ImGui::EndDragDropSource(); } @@ -476,7 +476,7 @@ void SaveTool::drawMassManager() { } ImGui::TableSetColumnIndex(1); - switch(_massManager->massState(i)) { + switch(_massManager->hangar(i).state()) { case MassState::Empty: ImGui::TextDisabled(""); break; @@ -484,7 +484,7 @@ void SaveTool::drawMassManager() { ImGui::TextDisabled(""); break; case MassState::Valid: - ImGui::TextUnformatted(_massManager->massName(i).c_str()); + ImGui::TextUnformatted(_massManager->hangar(i).name().c_str()); break; } @@ -494,7 +494,7 @@ void SaveTool::drawMassManager() { drawTooltip("This is the currently active frame slot."); } - if(_massManager->massState(i) != MassState::Empty) { + if(_massManager->hangar(i).state() != MassState::Empty) { ImGui::TableSetColumnIndex(3); ImGui::PushID(i); if(drawUnsafeWidget(ImGui::SmallButton, ICON_FA_TRASH_ALT)) { @@ -588,7 +588,7 @@ auto SaveTool::drawDeleteMassPopup(int mass_index) -> ImGuiID { return ImGui::GetID("Confirmation##DeleteMassConfirmation"); } - if(_massManager->massState(mass_index) == MassState::Empty) { + if(_massManager->hangar(mass_index).state() == MassState::Empty) { ImGui::CloseCurrentPopup(); ImGui::EndPopup(); return 0; @@ -601,13 +601,13 @@ auto SaveTool::drawDeleteMassPopup(int mass_index) -> ImGuiID { } ImGui::PushTextWrapPos(windowSize().x() * 0.40f); - if(_massManager->massState(mass_index) == MassState::Invalid) { + if(_massManager->hangar(mass_index).state() == MassState::Invalid) { ImGui::Text("Are you sure you want to delete the invalid M.A.S.S. data in hangar %.2i ? This operation is irreversible.", mass_index + 1); } else { ImGui::Text("Are you sure you want to delete the M.A.S.S. named %s in hangar %.2i ? This operation is irreversible.", - _massManager->massName(mass_index).c_str(), mass_index + 1); + _massManager->hangar(mass_index).name().c_str(), mass_index + 1); } ImGui::PopTextWrapPos(); -- 2.39.2 From 597e9dfe983455fe1dc5e22e47c0c02707b4a425 Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 19 Aug 2021 20:35:00 +0200 Subject: [PATCH 002/128] SaveTool: initial work for the viewer UI. --- src/CMakeLists.txt | 1 + src/SaveTool/SaveTool.cpp | 3 ++ src/SaveTool/SaveTool.h | 5 ++- src/SaveTool/SaveTool_MainManager.cpp | 7 +++++ src/SaveTool/SaveTool_MassViewer.cpp | 44 +++++++++++++++++++++++++++ 5 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/SaveTool/SaveTool_MassViewer.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c202ffd..25cd758 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,6 +35,7 @@ add_executable(MassBuilderSaveTool WIN32 SaveTool/SaveTool_drawAbout.cpp SaveTool/SaveTool_drawMainMenu.cpp SaveTool/SaveTool_MainManager.cpp + SaveTool/SaveTool_MassViewer.cpp SaveTool/SaveTool_ProfileManager.cpp ProfileManager/ProfileManager.h ProfileManager/ProfileManager.cpp diff --git a/src/SaveTool/SaveTool.cpp b/src/SaveTool/SaveTool.cpp index f00fa1f..808622d 100644 --- a/src/SaveTool/SaveTool.cpp +++ b/src/SaveTool/SaveTool.cpp @@ -576,6 +576,9 @@ void SaveTool::drawGui() { case UiState::MainManager: drawManager(); break; + case UiState::MassViewer: + drawMassViewer(); + break; } if(_aboutPopup) { diff --git a/src/SaveTool/SaveTool.h b/src/SaveTool/SaveTool.h index f50f6cf..f975d06 100644 --- a/src/SaveTool/SaveTool.h +++ b/src/SaveTool/SaveTool.h @@ -100,6 +100,7 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener void drawMassManager(); auto drawDeleteMassPopup(int mass_index) -> ImGuiID; auto drawDeleteStagedMassPopup(const std::string& filename) -> ImGuiID; + void drawMassViewer(); void drawAbout(); void drawGameState(); @@ -152,7 +153,8 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener Disclaimer, Initialising, ProfileManager, - MainManager + MainManager, + MassViewer } _uiState{UiState::Disclaimer}; bool _aboutPopup{false}; @@ -190,6 +192,7 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener Profile* _currentProfile{nullptr}; Containers::Pointer _massManager; + Mass* _currentMass{nullptr}; Containers::Pointer _fileWatcher; enum watchID { diff --git a/src/SaveTool/SaveTool_MainManager.cpp b/src/SaveTool/SaveTool_MainManager.cpp index 3708694..23d1a1f 100644 --- a/src/SaveTool/SaveTool_MainManager.cpp +++ b/src/SaveTool/SaveTool_MainManager.cpp @@ -497,6 +497,13 @@ void SaveTool::drawMassManager() { if(_massManager->hangar(i).state() != MassState::Empty) { ImGui::TableSetColumnIndex(3); ImGui::PushID(i); + if(_massManager->hangar(i).state() == MassState::Valid) { + if(ImGui::SmallButton(ICON_FA_SEARCH)) { + _currentMass = &_massManager->hangar(i); + _uiState = UiState::MassViewer; + } + ImGui::SameLine(0.0f, 2.0f); + } if(drawUnsafeWidget(ImGui::SmallButton, ICON_FA_TRASH_ALT)) { mass_to_delete = i; ImGui::OpenPopup(mass_deletion_popup_ID); diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp new file mode 100644 index 0000000..7ad9212 --- /dev/null +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -0,0 +1,44 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../FontAwesome/IconsFontAwesome5.h" + +#include "SaveTool.h" + +void SaveTool::drawMassViewer() { + ImGui::SetNextWindowPos({0.0f, ImGui::GetItemRectSize().y}, ImGuiCond_Always); + ImGui::SetNextWindowSize({Float(windowSize().x()), Float(windowSize().y()) - ImGui::GetItemRectSize().y}, + ImGuiCond_Always); + if(!ImGui::Begin("##MainWindow", nullptr, + ImGuiWindowFlags_NoDecoration|ImGuiWindowFlags_NoMove| + ImGuiWindowFlags_NoBackground|ImGuiWindowFlags_NoBringToFrontOnFocus)) + { + ImGui::End(); + return; + } + + ImGui::AlignTextToFramePadding(); + ImGui::Text("Current M.A.S.S.: %s (%s)", + _currentMass->name().c_str(), + _currentMass->filename().c_str()); + ImGui::SameLine(); + if(ImGui::Button(ICON_FA_ARROW_LEFT " Back to main manager")) { + _currentMass = nullptr; + _uiState = UiState::MainManager; + } + + ImGui::End(); +} -- 2.39.2 From bd6e55826d8f8df53cc7f8186b6852d0b13d38af Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 28 Aug 2021 20:16:19 +0200 Subject: [PATCH 003/128] Add StyleNames.h. --- src/CMakeLists.txt | 1 + src/Maps/StyleNames.h | 154 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 src/Maps/StyleNames.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 25cd758..a0aa25a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,6 +48,7 @@ add_executable(MassBuilderSaveTool WIN32 Mass/Mass.cpp Maps/LastMissionId.h Maps/StoryProgress.h + Maps/StyleNames.h ToastQueue/ToastQueue.h ToastQueue/ToastQueue.cpp FontAwesome/IconsFontAwesome5.h diff --git a/src/Maps/StyleNames.h b/src/Maps/StyleNames.h new file mode 100644 index 0000000..e330e26 --- /dev/null +++ b/src/Maps/StyleNames.h @@ -0,0 +1,154 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include + +using namespace Magnum; + +static const std::map style_names { + {100, "Iron"}, + {101, "Silver"}, + {102, "Gold"}, + {103, "Bronze"}, + {104, "Copper"}, + {105, "Nickel"}, + {106, "Cobalt"}, + {107, "Aluminium"}, + {108, "Titanium"}, + {109, "Platinum"}, + {110, "Gun Metal"}, + {111, "White"}, + {112, "White Metal"}, + {113, "White Gloss"}, + {114, "Grey"}, + {115, "Grey Metal"}, + {116, "Grey Gloss"}, + {117, "Dark Grey"}, + {118, "Dark Grey Metal"}, + {119, "Dark Grey Gloss"}, + {120, "Black"}, + {121, "Black Metal"}, + {122, "Black Gloss"}, + {123, "Red"}, + {124, "Red Metal"}, + {125, "Red Gloss"}, + {126, "Dark Red"}, + {127, "Dark Red Metal"}, + {128, "Dark Red Gloss"}, + {129, "Orange"}, + {130, "Orange Metal"}, + {131, "Orange Gloss"}, + {132, "Dark Orange"}, + {133, "Dark Orange Metal"}, + {134, "Dark Orange Gloss"}, + {135, "Yellow"}, + {136, "Yellow Metal"}, + {137, "Yellow Gloss"}, + {138, "Brown"}, + {139, "Brown Metal"}, + {140, "Brown Gloss"}, + {141, "Dark Brown"}, + {142, "Dark Brown Metal"}, + {143, "Dark Brown Gloss"}, + {144, "Leafgreen"}, + {145, "Leafgreen Metal"}, + {146, "Leafgreen Gloss"}, + {147, "Military Green"}, + {148, "Military Green Metal"}, + {149, "Military Green Gloss"}, + {150, "Green"}, + {151, "Green Metal"}, + {152, "Green Gloss"}, + {153, "Dark Green"}, + {154, "Dark Green Metal"}, + {155, "Dark Green Gloss"}, + {156, "Teal"}, + {157, "Teal Metal"}, + {158, "Teal Gloss"}, + {159, "Cyan"}, + {160, "Cyan Metal"}, + {161, "Cyan Gloss"}, + {162, "Blue"}, + {163, "Blue Metal"}, + {164, "Blue Gloss"}, + {165, "Blue Sky"}, + {166, "Blue Sky Metal"}, + {167, "Blue Sky Gloss"}, + {168, "Dark Blue"}, + {169, "Dark Blue Metal"}, + {170, "Dark Blue Gloss"}, + {171, "Purple"}, + {172, "Purple Metal"}, + {173, "Purple Gloss"}, + {174, "Dark Purple"}, + {175, "Dark Purple Metal"}, + {176, "Dark Purple Gloss"}, + {177, "Pink"}, + {178, "Pink Metal"}, + {179, "Pink Gloss"}, + {180, "Rosy Brown"}, + {181, "Rosy Brown Metal"}, + {182, "Rosy Brown Gloss"}, + {183, "Ivory"}, + {184, "Ivory Metal"}, + {185, "Ivory Gloss"}, + {186, "Slate Brown"}, + {187, "Slate Brown Metal"}, + {188, "Slate Brown Gloss"}, + {189, "Slate Green"}, + {190, "Slate Green Metal"}, + {191, "Slate Green Gloss"}, + {192, "Slate Blue"}, + {193, "Slate Blue Metal"}, + {194, "Slate Blue Gloss"}, + {195, "Slate Purple"}, + {196, "Slate Purple Metal"}, + {197, "Slate Purple Gloss"}, + {198, "White Glow"}, + {199, "White Radiance"}, + {200, "Red Glow"}, + {201, "Red Radiance"}, + {202, "Orange Glow"}, + {203, "Orange Radiance"}, + {204, "Yellow Glow"}, + {205, "Yellow Radiance"}, + {206, "Leafgreen Glow"}, + {207, "Leafgreen Radiance"}, + {208, "Green Glow"}, + {209, "Green Radiance"}, + {210, "Teal Glow"}, + {211, "Teal Radiance"}, + {212, "Cyan Glow"}, + {213, "Cyan Radiance"}, + {214, "Blue Glow"}, + {215, "Blue Radiance"}, + {216, "Purple Glow"}, + {217, "Purple Radiance"}, + {218, "Pink Glow"}, + {219, "Pink Radiance"}, + {220, "Grey Camo"}, + {221, "Dark Grey Camo"}, + {222, "Green Camo"}, + {223, "Dark Green Camo"}, + {224, "Brown Camo"}, + {225, "Dark Brown Camo"}, + {226, "Blue Camo"}, + {227, "Dark Blue Camo"}, +}; -- 2.39.2 From 69021eacdf981f04ab2d67d8b3abfc74f2bcebad Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 28 Aug 2021 20:20:09 +0200 Subject: [PATCH 004/128] Mass: change how the name is obtained, and move the state enum. --- src/Mass/Mass.cpp | 72 ++++++++++++++++++++++++--------- src/Mass/Mass.h | 15 ++++--- src/MassManager/MassManager.cpp | 12 +++--- 3 files changed, 69 insertions(+), 30 deletions(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 7c689af..87726ff 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -33,23 +33,7 @@ Mass::Mass(const std::string& path) { _folder = Utility::Directory::path(path); _filename = Utility::Directory::filename(path); - if(!Utility::Directory::exists(path)) { - _lastError = path + " couldn't be found."; - return; - } - - auto mmap = Utility::Directory::mapRead(path); - - auto iter = std::search(mmap.begin(), mmap.end(), &mass_name_locator[0], &mass_name_locator[56]); - - if(iter != mmap.end()) { - _name = std::string{iter + 70}; - _state = MassState::Valid; - } - else { - _lastError = "The name couldn't be found in " + _filename; - _state = MassState::Invalid; - } + refreshValues(); } auto Mass::lastError() -> std::string const& { @@ -78,6 +62,10 @@ auto Mass::getNameFromFile(const std::string& path) -> std::string { return name; } +void Mass::refreshValues() { + getName(); +} + auto Mass::filename() -> std::string const&{ return _filename; } @@ -86,7 +74,7 @@ auto Mass::name() -> std::string const&{ return _name; } -auto Mass::state() -> MassState { +auto Mass::state() -> State { return _state; } @@ -95,7 +83,7 @@ auto Mass::updateSteamId(const std::string& steam_id) -> bool { if(!Utility::Directory::exists(path)) { _lastError = path + " couldn't be found."; - _state = MassState::Empty; + _state = State::Empty; return false; } @@ -129,3 +117,49 @@ auto Mass::updateSteamId(const std::string& steam_id) -> bool { return true; } + +void Mass::getName() { + std::string path = Utility::Directory::join(_folder, _filename); + + if(!Utility::Directory::exists(path)) { + _lastError = path + " couldn't be found."; + _state = State::Empty; + return; + } + + auto mmap = Utility::Directory::mapRead(path); + + auto iter = std::search(mmap.begin(), mmap.end(), &mass_name_locator[0], &mass_name_locator[56]); + + if(iter != mmap.end()) { + _name = std::string{iter + 70}; + _state = State::Valid; + } + else { + _lastError = "The name couldn't be found in " + _filename; + _state = State::Invalid; + } +} + +void Mass::getFrameStyles() { + std::string path = Utility::Directory::join(_folder, _filename); + + if(!Utility::Directory::exists(path)) { + _lastError = path + " couldn't be found."; + _state = State::Empty; + return; + } + + auto mmap = Utility::Directory::mapRead(path); + + auto iter = std::search(mmap.begin(), mmap.end(), &frame_styles_locator[0], &frame_styles_locator[90]); + + if(iter != mmap.end()) { + iter += 0x5A; + std::copy(reinterpret_cast(iter), reinterpret_cast(iter) + 4, _frameStyles.data()); + } + else { + _lastError = "Frame styles couldn't be found in " + path; + _state = State::Invalid; + } +} diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 124c319..c008fa1 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -22,12 +22,13 @@ using namespace Magnum; -enum class MassState : UnsignedByte { - Empty, Invalid, Valid -}; class Mass { public: + enum class State : UnsignedByte { + Empty, Invalid, Valid + }; + explicit Mass(const std::string& path); Mass(const Mass&) = delete; @@ -40,19 +41,23 @@ class Mass { static auto getNameFromFile(const std::string& path) -> std::string; + void refreshValues(); + auto filename() -> std::string const&; auto name() -> std::string const&; - auto state() -> MassState; + auto state() -> State; auto updateSteamId(const std::string& steam_id) -> bool; private: + void getName(); + static std::string _lastError; std::string _folder; std::string _filename; std::string _name; - MassState _state = MassState::Empty; + State _state = State::Empty; }; diff --git a/src/MassManager/MassManager.cpp b/src/MassManager/MassManager.cpp index be83937..033d279 100644 --- a/src/MassManager/MassManager.cpp +++ b/src/MassManager/MassManager.cpp @@ -104,7 +104,7 @@ auto MassManager::exportMass(int hangar) -> bool { return false; } - if(_hangars[hangar].state() != MassState::Valid) { + if(_hangars[hangar].state() != Mass::State::Valid) { _lastError = Utility::formatString("There is no valid data to export in hangar {:.2d}", hangar + 1); return false; } @@ -134,22 +134,22 @@ auto MassManager::moveMass(int source, int destination) -> bool { std::string source_file = _hangars[source].filename(); std::string dest_file = _hangars[destination].filename(); - MassState dest_state = _hangars[destination].state(); + Mass::State dest_state = _hangars[destination].state(); switch(dest_state) { - case MassState::Empty: + case Mass::State::Empty: break; - case MassState::Invalid: + case Mass::State::Invalid: Utility::Directory::rm(dest_file); break; - case MassState::Valid: + case Mass::State::Valid: Utility::Directory::move(dest_file, dest_file + ".tmp"); break; } Utility::Directory::move(source_file, dest_file); - if(dest_state == MassState::Valid) { + if(dest_state == Mass::State::Valid) { Utility::Directory::move(dest_file + ".tmp", source_file); } -- 2.39.2 From 7fcf8b518eebc44362ee9f8f8e51cbd5f19ae438 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 28 Aug 2021 20:21:13 +0200 Subject: [PATCH 005/128] Mass: add functions to read/write frame styles. --- src/Mass/Locators.h | 2 ++ src/Mass/Mass.cpp | 43 +++++++++++++++++++++++++++++++++++++++++-- src/Mass/Mass.h | 9 +++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/Mass/Locators.h b/src/Mass/Locators.h index 51011c2..2cd1e1a 100644 --- a/src/Mass/Locators.h +++ b/src/Mass/Locators.h @@ -18,3 +18,5 @@ constexpr char mass_name_locator[] = "Name_45_A037C5D54E53456407BDF091344529BB\0\f\0\0\0StrProperty"; constexpr char steamid_locator[] = "Account\0\f\0\0\0StrProperty"; + +constexpr char frame_styles_locator[] = "Styles_32_00A3B3284B37F1E7819458844A20EB48\0\x0e\0\0\0ArrayProperty\0\x14\0\0\0\0\0\0\0\f\0\0\0IntProperty\0\0\x04\0\0\0"; diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 87726ff..7545506 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -25,8 +25,6 @@ #include "Mass.h" -using namespace Corrade; - std::string Mass::_lastError; Mass::Mass(const std::string& path) { @@ -64,6 +62,12 @@ auto Mass::getNameFromFile(const std::string& path) -> std::string { void Mass::refreshValues() { getName(); + + if(_state != State::Valid) { + return; + } + + getFrameStyles(); } auto Mass::filename() -> std::string const&{ @@ -78,6 +82,41 @@ auto Mass::state() -> State { return _state; } +auto Mass::frameStyles() -> Containers::StaticArrayView<4, Int> { + return _frameStyles; +} + +auto Mass::setFrameStyle(Int index, Int style_id) -> bool { + if(index < 0 || index > 3) { + _lastError = "Index is out of range in Mass::setFrameStyle()."; + return false; + } + + std::string path = Utility::Directory::join(_folder, _filename); + + if(!Utility::Directory::exists(path)) { + _lastError = path + " couldn't be found."; + _state = State::Empty; + return false; + } + + auto mmap = Utility::Directory::map(path); + + auto iter = std::search(mmap.begin(), mmap.end(), &frame_styles_locator[0], &frame_styles_locator[90]); + + if(iter != mmap.end()) { + iter += 0x5A; + *(reinterpret_cast(iter) + index) = style_id; + } + else { + _lastError = "Frame styles couldn't be found in " + path; + _state = State::Invalid; + return false; + } + + return true; +} + auto Mass::updateSteamId(const std::string& steam_id) -> bool { std::string path = Utility::Directory::join(_folder, _filename); diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index c008fa1..cf9160f 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -18,8 +18,11 @@ #include +#include + #include +using namespace Corrade; using namespace Magnum; @@ -49,10 +52,14 @@ class Mass { auto state() -> State; + auto frameStyles() -> Containers::StaticArrayView<4, Int>; + auto setFrameStyle(Int index, Int style_id) -> bool; + auto updateSteamId(const std::string& steam_id) -> bool; private: void getName(); + void getFrameStyles(); static std::string _lastError; @@ -60,4 +67,6 @@ class Mass { std::string _filename; std::string _name; State _state = State::Empty; + + Containers::StaticArray<4, Int> _frameStyles; }; -- 2.39.2 From f3318e0ed12ebb29fac0a019874f3de00fe02574 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 28 Aug 2021 20:22:04 +0200 Subject: [PATCH 006/128] SaveTool: add safety measures in drawMassViewer(). --- src/SaveTool/SaveTool_MassViewer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index 7ad9212..e284bba 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -19,6 +19,12 @@ #include "SaveTool.h" void SaveTool::drawMassViewer() { + if(!_currentMass || _currentMass->state() != Mass::State::Valid) { + _currentMass = nullptr; + _uiState = UiState::MainManager; + return; + } + ImGui::SetNextWindowPos({0.0f, ImGui::GetItemRectSize().y}, ImGuiCond_Always); ImGui::SetNextWindowSize({Float(windowSize().x()), Float(windowSize().y()) - ImGui::GetItemRectSize().y}, ImGuiCond_Always); -- 2.39.2 From 5f4576a2bc85386ccb5a60b5b6a862289ffe1133 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 28 Aug 2021 20:57:06 +0200 Subject: [PATCH 007/128] SaveTool: improve readability of the header. --- src/SaveTool/SaveTool.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/SaveTool/SaveTool.h b/src/SaveTool/SaveTool.h index f975d06..09e19d1 100644 --- a/src/SaveTool/SaveTool.h +++ b/src/SaveTool/SaveTool.h @@ -88,10 +88,12 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener void drawMainMenu(); void drawDisclaimer(); void drawInitialisation(); + void drawProfileManager(); auto drawBackupListPopup() -> ImGuiID; auto drawBackupProfilePopup(std::size_t profile_index) -> ImGuiID; auto drawDeleteProfilePopup(std::size_t profile_index) -> ImGuiID; + void drawManager(); auto drawIntEditPopup(int* value_to_edit, int max) -> bool; auto drawRenamePopup(Containers::ArrayView name_view) -> bool; @@ -100,7 +102,9 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener void drawMassManager(); auto drawDeleteMassPopup(int mass_index) -> ImGuiID; auto drawDeleteStagedMassPopup(const std::string& filename) -> ImGuiID; + void drawMassViewer(); + void drawAbout(); void drawGameState(); -- 2.39.2 From a9a5bfb2af9692f6ef0db7317a115036e50aa856 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 28 Aug 2021 21:03:06 +0200 Subject: [PATCH 008/128] SaveTool: add basic skeleton for the M.A.S.S. viewer. --- src/SaveTool/SaveTool_MassViewer.cpp | 37 ++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index e284bba..f3de61e 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -46,5 +46,42 @@ void SaveTool::drawMassViewer() { _uiState = UiState::MainManager; } + ImGui::TextColored(ImColor(255, 255, 0), ICON_FA_EXCLAMATION_TRIANGLE); + ImGui::SameLine(); + ImGui::TextWrapped("WARNING: Colours in this app may look different from in-game colours, due to unavoidable differences in the rendering pipeline."); + + if(ImGui::BeginChild("##MassInfo", + {ImGui::GetContentRegionAvailWidth() * 0.60f, 0.0f}, + true, ImGuiWindowFlags_MenuBar)) + { + if(ImGui::BeginMenuBar()) { + ImGui::TextUnformatted("M.A.S.S. Information"); + ImGui::EndMenuBar(); + } + + if(ImGui::BeginTabBar("##MassTabBar")) { + if(ImGui::BeginTabItem("Frame")) { + ImGui::EndTabItem(); + } + + ImGui::EndTabBar(); + } + } + ImGui::EndChild(); + + ImGui::SameLine(); + + if(ImGui::BeginChild("##GlobalStyles", {0.0f, 0.0f}, + true, ImGuiWindowFlags_MenuBar)) + { + if(ImGui::BeginMenuBar()) { + ImGui::TextUnformatted("Global styles"); + ImGui::EndMenuBar(); + } + + ImGui::TextUnformatted("Placeholder"); + } + ImGui::EndChild(); + ImGui::End(); } -- 2.39.2 From c7c379c419be7b7287585ccfa6ce45395f4e64c1 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sun, 29 Aug 2021 19:39:29 +0200 Subject: [PATCH 009/128] SaveTool: adapt main manager to Mass changes. --- src/SaveTool/SaveTool_MainManager.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/SaveTool/SaveTool_MainManager.cpp b/src/SaveTool/SaveTool_MainManager.cpp index 23d1a1f..d5918e9 100644 --- a/src/SaveTool/SaveTool_MainManager.cpp +++ b/src/SaveTool/SaveTool_MainManager.cpp @@ -429,7 +429,7 @@ void SaveTool::drawMassManager() { ImGui::TableSetColumnIndex(0); ImGui::Selectable(Utility::formatString("{:.2d}", i + 1).c_str(), false, ImGuiSelectableFlags_SpanAllColumns|ImGuiSelectableFlags_AllowItemOverlap); - if(_massManager->hangar(i).state() == MassState::Valid && + if(_massManager->hangar(i).state() == Mass::State::Valid && ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) { drag_drop_index = i; @@ -477,13 +477,13 @@ void SaveTool::drawMassManager() { ImGui::TableSetColumnIndex(1); switch(_massManager->hangar(i).state()) { - case MassState::Empty: + case Mass::State::Empty: ImGui::TextDisabled(""); break; - case MassState::Invalid: + case Mass::State::Invalid: ImGui::TextDisabled(""); break; - case MassState::Valid: + case Mass::State::Valid: ImGui::TextUnformatted(_massManager->hangar(i).name().c_str()); break; } @@ -494,10 +494,10 @@ void SaveTool::drawMassManager() { drawTooltip("This is the currently active frame slot."); } - if(_massManager->hangar(i).state() != MassState::Empty) { + if(_massManager->hangar(i).state() != Mass::State::Empty) { ImGui::TableSetColumnIndex(3); ImGui::PushID(i); - if(_massManager->hangar(i).state() == MassState::Valid) { + if(_massManager->hangar(i).state() == Mass::State::Valid) { if(ImGui::SmallButton(ICON_FA_SEARCH)) { _currentMass = &_massManager->hangar(i); _uiState = UiState::MassViewer; @@ -595,7 +595,7 @@ auto SaveTool::drawDeleteMassPopup(int mass_index) -> ImGuiID { return ImGui::GetID("Confirmation##DeleteMassConfirmation"); } - if(_massManager->hangar(mass_index).state() == MassState::Empty) { + if(_massManager->hangar(mass_index).state() == Mass::State::Empty) { ImGui::CloseCurrentPopup(); ImGui::EndPopup(); return 0; @@ -608,7 +608,7 @@ auto SaveTool::drawDeleteMassPopup(int mass_index) -> ImGuiID { } ImGui::PushTextWrapPos(windowSize().x() * 0.40f); - if(_massManager->hangar(mass_index).state() == MassState::Invalid) { + if(_massManager->hangar(mass_index).state() == Mass::State::Invalid) { ImGui::Text("Are you sure you want to delete the invalid M.A.S.S. data in hangar %.2i ? This operation is irreversible.", mass_index + 1); } -- 2.39.2 From 40840e31289b5b0db9fedf7b00d11af5caa13996 Mon Sep 17 00:00:00 2001 From: William JCM Date: Fri, 10 Sep 2021 16:13:29 +0200 Subject: [PATCH 010/128] Mass: add support for reading joint sliders. --- src/Mass/Locators.h | 9 +++++ src/Mass/Mass.cpp | 89 +++++++++++++++++++++++++++++++++++++++++++++ src/Mass/Mass.h | 15 ++++++++ 3 files changed, 113 insertions(+) diff --git a/src/Mass/Locators.h b/src/Mass/Locators.h index 2cd1e1a..9eadd48 100644 --- a/src/Mass/Locators.h +++ b/src/Mass/Locators.h @@ -19,4 +19,13 @@ constexpr char mass_name_locator[] = "Name_45_A037C5D54E53456407BDF091344529BB\0\f\0\0\0StrProperty"; constexpr char steamid_locator[] = "Account\0\f\0\0\0StrProperty"; +constexpr char neck_slider_locator[] = "NeckLength_6_ED6AF79849C27CD1A9D523A09E2BFE58\0\x0e\0\0\0FloatProperty"; +constexpr char body_slider_locator[] = "BodyLength_7_C16287754CBA96C93BAE36A5C154996A\0\x0e\0\0\0FloatProperty"; +constexpr char shoulders_slider_locator[] = "ShoulderLength_8_220EDF304F1C1226F0D8D39117FB3883\0\x0e\0\0\0FloatProperty"; +constexpr char hips_slider_locator[] = "HipLength_14_02AEEEAC4376087B9C51F0AA7CC92818\0\x0e\0\0\0FloatProperty"; +constexpr char uarms_slider_locator[] = "ArmUpperLength_10_249FDA3E4F3B399E7B9E5C9B7C765EAE\0\x0e\0\0\0FloatProperty"; +constexpr char larms_slider_locator[] = "ArmLowerLength_12_ACD0F02745C28882619376926292FB36\0\x0e\0\0\0FloatProperty"; +constexpr char ulegs_slider_locator[] = "LegUpperLength_16_A7C4C71249A3776F7A543D96819C0C61\0\x0e\0\0\0FloatProperty"; +constexpr char llegs_slider_locator[] = "LegLowerLength_18_D2DF39964EA0F2A2129D0491B08A032F\0\x0e\0\0\0FloatProperty"; + constexpr char frame_styles_locator[] = "Styles_32_00A3B3284B37F1E7819458844A20EB48\0\x0e\0\0\0ArrayProperty\0\x14\0\0\0\0\0\0\0\f\0\0\0IntProperty\0\0\x04\0\0\0"; diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 7545506..263dd5d 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -68,6 +68,7 @@ void Mass::refreshValues() { } getFrameStyles(); + getJointSliders(); } auto Mass::filename() -> std::string const&{ @@ -82,6 +83,10 @@ auto Mass::state() -> State { return _state; } +auto Mass::jointSliders() -> Joints const& { + return _sliders; +} + auto Mass::frameStyles() -> Containers::StaticArrayView<4, Int> { return _frameStyles; } @@ -180,6 +185,90 @@ void Mass::getName() { } } +void Mass::getJointSliders() { + std::string path = Utility::Directory::join(_folder, _filename); + + if(!Utility::Directory::exists(path)) { + _lastError = path + " couldn't be found."; + _state = State::Empty; + return; + } + + auto mmap = Utility::Directory::mapRead(path); + + auto iter = std::search(mmap.begin(), mmap.end(), &neck_slider_locator[0], &neck_slider_locator[63]); + + if(iter != mmap.end()) { + _sliders.neck = *reinterpret_cast(iter + 0x49); + } + else { + _sliders.neck = 0.0f; + } + + iter = std::search(mmap.begin(), mmap.end(), &body_slider_locator[0], &body_slider_locator[63]); + + if(iter != mmap.end()) { + _sliders.body = *reinterpret_cast(iter + 0x49); + } + else { + _sliders.body = 0.0f; + } + + iter = std::search(mmap.begin(), mmap.end(), &shoulders_slider_locator[0], &shoulders_slider_locator[67]); + + if(iter != mmap.end()) { + _sliders.shoulders = *reinterpret_cast(iter + 0x4D); + } + else { + _sliders.shoulders = 0.0f; + } + + iter = std::search(mmap.begin(), mmap.end(), &hips_slider_locator[0], &hips_slider_locator[63]); + + if(iter != mmap.end()) { + _sliders.hips = *reinterpret_cast(iter + 0x49); + } + else { + _sliders.hips = 0.0f; + } + + iter = std::search(mmap.begin(), mmap.end(), &uarms_slider_locator[0], &uarms_slider_locator[68]); + + if(iter != mmap.end()) { + _sliders.upperArms = *reinterpret_cast(iter + 0x4E); + } + else { + _sliders.upperArms = 0.0f; + } + + iter = std::search(mmap.begin(), mmap.end(), &larms_slider_locator[0], &larms_slider_locator[68]); + + if(iter != mmap.end()) { + _sliders.lowerArms = *reinterpret_cast(iter + 0x4E); + } + else { + _sliders.lowerArms = 0.0f; + } + + iter = std::search(mmap.begin(), mmap.end(), &ulegs_slider_locator[0], &ulegs_slider_locator[68]); + + if(iter != mmap.end()) { + _sliders.upperLegs = *reinterpret_cast(iter + 0x4E); + } + else { + _sliders.upperLegs = 0.0f; + } + + iter = std::search(mmap.begin(), mmap.end(), &llegs_slider_locator[0], &llegs_slider_locator[68]); + + if(iter != mmap.end()) { + _sliders.lowerLegs = *reinterpret_cast(iter + 0x4E); + } + else { + _sliders.lowerLegs = 0.0f; + } +} + void Mass::getFrameStyles() { std::string path = Utility::Directory::join(_folder, _filename); diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index cf9160f..747941a 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -25,6 +25,16 @@ using namespace Corrade; 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; +}; class Mass { public: @@ -52,6 +62,8 @@ class Mass { auto state() -> State; + auto jointSliders() -> Joints const&; + auto frameStyles() -> Containers::StaticArrayView<4, Int>; auto setFrameStyle(Int index, Int style_id) -> bool; @@ -59,6 +71,7 @@ class Mass { private: void getName(); + void getJointSliders(); void getFrameStyles(); static std::string _lastError; @@ -68,5 +81,7 @@ class Mass { std::string _name; State _state = State::Empty; + Joints _sliders; + Containers::StaticArray<4, Int> _frameStyles; }; -- 2.39.2 From c64684b34cc51f00ded7cbf6fee1a0e6758943cd Mon Sep 17 00:00:00 2001 From: William JCM Date: Fri, 10 Sep 2021 16:14:31 +0200 Subject: [PATCH 011/128] StyleNames: add placeholders for custom/global style names. --- src/Maps/StyleNames.h | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/Maps/StyleNames.h b/src/Maps/StyleNames.h index e330e26..16c3988 100644 --- a/src/Maps/StyleNames.h +++ b/src/Maps/StyleNames.h @@ -23,6 +23,40 @@ using namespace Magnum; static const std::map style_names { + {0, "Custom Style 1"}, + {1, "Custom Style 2"}, + {2, "Custom Style 3"}, + {3, "Custom Style 4"}, + {4, "Custom Style 5"}, + {5, "Custom Style 6"}, + {6, "Custom Style 7"}, + {7, "Custom Style 8"}, + {8, "Custom Style 9"}, + {9, "Custom Style 10"}, + {10, "Custom Style 11"}, + {11, "Custom Style 12"}, + {12, "Custom Style 13"}, + {13, "Custom Style 14"}, + {14, "Custom Style 15"}, + {15, "Custom Style 16"}, + + {50, "Global Style 1"}, + {51, "Global Style 2"}, + {52, "Global Style 3"}, + {53, "Global Style 4"}, + {54, "Global Style 5"}, + {55, "Global Style 6"}, + {56, "Global Style 7"}, + {57, "Global Style 8"}, + {58, "Global Style 9"}, + {59, "Global Style 10"}, + {60, "Global Style 11"}, + {61, "Global Style 12"}, + {62, "Global Style 13"}, + {63, "Global Style 14"}, + {64, "Global Style 15"}, + {65, "Global Style 16"}, + {100, "Iron"}, {101, "Silver"}, {102, "Gold"}, -- 2.39.2 From 32bc17912007da50c2025856a7c060702cf63b54 Mon Sep 17 00:00:00 2001 From: William JCM Date: Fri, 10 Sep 2021 16:15:27 +0200 Subject: [PATCH 012/128] SaveTool: change an include. The old one works on my setup, but might not work on others. --- src/SaveTool/SaveTool.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SaveTool/SaveTool.h b/src/SaveTool/SaveTool.h index 09e19d1..9ce8348 100644 --- a/src/SaveTool/SaveTool.h +++ b/src/SaveTool/SaveTool.h @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include -- 2.39.2 From 918b26ab5e75456a8a5fabefa9228ffcf5dd1590 Mon Sep 17 00:00:00 2001 From: William JCM Date: Fri, 10 Sep 2021 16:16:21 +0200 Subject: [PATCH 013/128] SaveTool: add some frame info display. --- src/SaveTool/SaveTool.h | 1 + src/SaveTool/SaveTool_MassViewer.cpp | 264 +++++++++++++++++++++++++++ 2 files changed, 265 insertions(+) diff --git a/src/SaveTool/SaveTool.h b/src/SaveTool/SaveTool.h index 9ce8348..6b1e08a 100644 --- a/src/SaveTool/SaveTool.h +++ b/src/SaveTool/SaveTool.h @@ -104,6 +104,7 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener auto drawDeleteStagedMassPopup(const std::string& filename) -> ImGuiID; void drawMassViewer(); + void drawFrameInfo(); void drawAbout(); void drawGameState(); diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index f3de61e..60b078b 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include "../Maps/StyleNames.h" + #include "../FontAwesome/IconsFontAwesome5.h" #include "SaveTool.h" @@ -61,6 +63,7 @@ void SaveTool::drawMassViewer() { if(ImGui::BeginTabBar("##MassTabBar")) { if(ImGui::BeginTabItem("Frame")) { + drawFrameInfo(); ImGui::EndTabItem(); } @@ -85,3 +88,264 @@ void SaveTool::drawMassViewer() { ImGui::End(); } + +void SaveTool::drawFrameInfo() { + if(!_currentMass || _currentMass->state() != Mass::State::Valid) { + return; + } + + ImGui::TextUnformatted("Frame type: Skeleton"); // Placeholder for now, replace with actual code once other frames are implemented. + + if(ImGui::CollapsingHeader("Joint sliders")) { + static Joints sliders = _currentMass->jointSliders(); + static bool edit = false; + static bool dirty = false; + + 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); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Neck"); + ImGui::TableSetColumnIndex(1); + if(edit) { + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##NeckSlider", &sliders.neck, 0.0f, 1.0f)) { + dirty = true; + } + } + else { + ImGui::AlignTextToFramePadding(); + ImGui::Text("%.3f", Double(_currentMass->jointSliders().neck)); + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Body"); + ImGui::TableSetColumnIndex(1); + if(edit) { + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##BodySlider", &sliders.body, 0.0f, 1.0f)) { + dirty = true; + } + } + else { + ImGui::AlignTextToFramePadding(); + ImGui::Text("%.3f", Double(_currentMass->jointSliders().body)); + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Shoulders"); + ImGui::TableSetColumnIndex(1); + if(edit) { + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##ShouldersSlider", &sliders.shoulders, 0.0f, 1.0f)) { + dirty = true; + } + } + else { + ImGui::AlignTextToFramePadding(); + ImGui::Text("%.3f", Double(_currentMass->jointSliders().shoulders)); + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Hips"); + ImGui::TableSetColumnIndex(1); + if(edit) { + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##HipsSlider", &sliders.hips, 0.0f, 1.0f)) { + dirty = true; + } + } + else { + ImGui::AlignTextToFramePadding(); + ImGui::Text("%.3f", Double(_currentMass->jointSliders().hips)); + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(1); + if(ImGui::BeginTable("##UpperLowerLayoutTable", 2, ImGuiTableFlags_BordersInnerV)) { + ImGui::TableSetupColumn("##Upper", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("##Lower", ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Upper"); + ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Lower"); + + ImGui::EndTable(); + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Arms"); + ImGui::TableSetColumnIndex(1); + if(ImGui::BeginTable("##UpperLowerArmsLayoutTable", 2, ImGuiTableFlags_BordersInnerV)) { + ImGui::TableSetupColumn("##UpperArms", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("##LowerArms", ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextColumn(); + if(edit) { + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##UpperArmsSlider", &sliders.upperArms, 0.0f, 1.0f)) { + dirty = true; + } + } + else { + ImGui::AlignTextToFramePadding(); + ImGui::Text("%.3f", Double(_currentMass->jointSliders().upperArms)); + } + ImGui::TableNextColumn(); + if(edit) { + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##LowerArmsSlider", &sliders.lowerArms, 0.0f, 1.0f)) { + dirty = true; + } + } + else { + ImGui::AlignTextToFramePadding(); + ImGui::Text("%.3f", Double(_currentMass->jointSliders().lowerArms)); + } + + ImGui::EndTable(); + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Legs"); + ImGui::TableSetColumnIndex(1); + if(ImGui::BeginTable("##UpperLowerLegsLayoutTable", 2, ImGuiTableFlags_BordersInnerV)) { + ImGui::TableSetupColumn("##UpperLegs", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("##LowerLegs", ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextColumn(); + if(edit) { + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##UpperLegsSlider", &sliders.upperLegs, 0.0f, 1.0f)) { + dirty = true; + } + } + else { + ImGui::AlignTextToFramePadding(); + ImGui::Text("%.3f", Double(_currentMass->jointSliders().upperLegs)); + } + ImGui::TableNextColumn(); + if(edit) { + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##LowerLegsSlider", &sliders.lowerLegs, 0.0f, 1.0f)) { + dirty = true; + } + } + else { + ImGui::AlignTextToFramePadding(); + ImGui::Text("%.3f", Double(_currentMass->jointSliders().lowerLegs)); + } + + ImGui::EndTable(); + } + + ImGui::EndTable(); + } + + if(edit) { + if(!dirty) { + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f); + ImGui::Button(ICON_FA_SAVE " Save changes"); + ImGui::SameLine(); + ImGui::Button(ICON_FA_UNDO " Reset sliders"); + ImGui::PopStyleVar(); + ImGui::PopItemFlag(); + } + else { + if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save changes"); })) { + dirty = false; + } + ImGui::SameLine(); + if(ImGui::Button(ICON_FA_UNDO " Reset sliders")) { + sliders = _currentMass->jointSliders(); + dirty = false; + } + } + ImGui::SameLine(); + if(ImGui::Button(ICON_FA_TIMES " Cancel editing")) { + sliders = _currentMass->jointSliders(); + dirty = false; + edit = false; + } + + ImGui::TextUnformatted("To input out-of-range values, hold Ctrl and click on a slider."); + } + else { + if(ImGui::Button(ICON_FA_EDIT " Edit sliders")) { + edit = true; + } + } + } + + if(ImGui::CollapsingHeader("Paint")) { + ImGui::TextUnformatted("Frame styles:"); + for(Int i = 0; i < 4; i++) { + ImGui::AlignTextToFramePadding(); + ImGui::Text("Slot %d:", i + 1); + ImGui::SameLine(); + ImGui::PushID(i); + GameState game_state = _gameState; + if(!_unsafeMode && game_state != GameState::NotRunning) { + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f); + } + if(ImGui::BeginCombo("##Style", style_names.at(_currentMass->frameStyles()[i]))) { + for(const auto& style : style_names) { + if(ImGui::Selectable(style.second, _currentMass->frameStyles()[i] == style.first)) { + if(!_currentMass->setFrameStyle(i, style.first)) { + _queue.addToast(Toast::Type::Error, Mass::lastError()); + } + else { + _currentMass->refreshValues(); + } + } + } + + ImGui::EndCombo(); + } + if(!_unsafeMode && game_state != GameState::NotRunning) { + ImGui::PopItemFlag(); + ImGui::PopStyleVar(); + } + ImGui::PopID(); + } + + ImGui::Separator(); + + static const Int hex_literals[] = {0x3DF68F08, 0x3E791C4C, 0x00000000}; + static Float colour_components[3]; + static bool run_once = true; + if(run_once) { + std::memcpy(colour_components, hex_literals, sizeof(Float) * 3); + run_once = false; + } + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Eye flare colour:"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(-1.0f); + ImGui::ColorEdit3("##EyeFlarePicker", &colour_components[0]); + + ImGui::Separator(); + + ImGui::TextUnformatted("The frame's custom styles will go here."); + } +} -- 2.39.2 From 083b60aac45a18c74f21b823bff7a2d707f8bd8a Mon Sep 17 00:00:00 2001 From: William JCM Date: Sun, 19 Sep 2021 13:09:04 +0200 Subject: [PATCH 014/128] Update dependencies. --- third-party/SDL | 2 +- third-party/corrade | 2 +- third-party/cpr | 2 +- third-party/efsw | 2 +- third-party/imgui | 2 +- third-party/json | 2 +- third-party/libzip | 2 +- third-party/magnum | 2 +- third-party/magnum-integration | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/third-party/SDL b/third-party/SDL index d956636..857cc7c 160000 --- a/third-party/SDL +++ b/third-party/SDL @@ -1 +1 @@ -Subproject commit d956636c85aafc055d37153d52370e9b1c2c5929 +Subproject commit 857cc7c0c93f003dbfc5fed7a87de907fffb2a1b diff --git a/third-party/corrade b/third-party/corrade index 76d5492..34a53fa 160000 --- a/third-party/corrade +++ b/third-party/corrade @@ -1 +1 @@ -Subproject commit 76d54922ba22e6a1abfffb5c9dc1012dfbae149e +Subproject commit 34a53fab11928765f5fb2704b5653769a20b63a5 diff --git a/third-party/cpr b/third-party/cpr index 814bd09..eccea39 160000 --- a/third-party/cpr +++ b/third-party/cpr @@ -1 +1 @@ -Subproject commit 814bd0926379cfc786dd1d9df9ce266a4bb6cf59 +Subproject commit eccea39e4b112a088ae665eda5854cc366f86128 diff --git a/third-party/efsw b/third-party/efsw index 6fb0c9c..f5f42f4 160000 --- a/third-party/efsw +++ b/third-party/efsw @@ -1 +1 @@ -Subproject commit 6fb0c9ccd284445330723914249b7be46798ee76 +Subproject commit f5f42f4b9a1c34512b779b2c5544ae42fdf97afa diff --git a/third-party/imgui b/third-party/imgui index 94b680e..ddddabd 160000 --- a/third-party/imgui +++ b/third-party/imgui @@ -1 +1 @@ -Subproject commit 94b680e83052b9ff2e877360309020b72db057f2 +Subproject commit ddddabdccfdafffd8664fb4e29230dc4f848137e diff --git a/third-party/json b/third-party/json index 350ff4f..626e7d6 160000 --- a/third-party/json +++ b/third-party/json @@ -1 +1 @@ -Subproject commit 350ff4f7ced7c4117eae2fb93df02823c8021fcb +Subproject commit 626e7d61e44dee32887126c8f437dd077dec09cf diff --git a/third-party/libzip b/third-party/libzip index 821cbfe..e7c81b6 160000 --- a/third-party/libzip +++ b/third-party/libzip @@ -1 +1 @@ -Subproject commit 821cbfe1b260193563b04d3db1bb7eb4a4ed0d8a +Subproject commit e7c81b67ab91d5dc54c2238d9f7d9abab1a0a8c3 diff --git a/third-party/magnum b/third-party/magnum index dd3ce93..b79bc18 160000 --- a/third-party/magnum +++ b/third-party/magnum @@ -1 +1 @@ -Subproject commit dd3ce93888ada70074d63d72c5998d950fa2eba6 +Subproject commit b79bc1821902e9fd91700f0edc517b090809e19c diff --git a/third-party/magnum-integration b/third-party/magnum-integration index 73018a6..36c1b15 160000 --- a/third-party/magnum-integration +++ b/third-party/magnum-integration @@ -1 +1 @@ -Subproject commit 73018a6d5daf04ef69449e82fa757c3acf797b1b +Subproject commit 36c1b1515873eff5047b27751c12a1580c141dd8 -- 2.39.2 From 66d96bd893d920edc5f263c29f1bfabfefe8aecf Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 22 Sep 2021 10:47:23 +0200 Subject: [PATCH 015/128] SaveTool: update formatting. --- src/SaveTool/SaveTool_MainManager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SaveTool/SaveTool_MainManager.cpp b/src/SaveTool/SaveTool_MainManager.cpp index d5918e9..dbb14e8 100644 --- a/src/SaveTool/SaveTool_MainManager.cpp +++ b/src/SaveTool/SaveTool_MainManager.cpp @@ -349,7 +349,7 @@ void SaveTool::drawResearchInventory() { matRow("Lunarite", 4, lunarite, lunarite, Lunarite) matRow("Asterite", 5, asterite, asterite, Asterite) unavRow("Hallite fragma", 6) - unavRow("Unnoctinium", 7) + unavRow("Unnoctinium", 7) ImGui::TableNextRow(ImGuiTableRowFlags_Headers); ImGui::TableSetColumnIndex(1); @@ -360,7 +360,7 @@ void SaveTool::drawResearchInventory() { matRow("Aurelene", 3, aurelene, aurelene, Aurelene) matRow("Soldus", 4, soldus, soldus, Soldus) matRow("Synthesized N", 5, synthesized_n, synthesizedN, SynthesizedN) - unavRow("Nanoc", 6) + unavRow("Nanoc", 6) unavRow("Abyssillite", 7) ImGui::TableNextRow(ImGuiTableRowFlags_Headers); @@ -372,7 +372,7 @@ void SaveTool::drawResearchInventory() { matRow("Nitinol-CM", 3, nitinol_cm, nitinolCM, NitinolCM) matRow("Quarkium", 4, quarkium, quarkium, Quarkium) matRow("Alterene", 5, alterene, alterene, Alterene) - unavRow("Cosmium", 6) + unavRow("Cosmium", 6) unavRow("Purified quarkium", 7) ImGui::TableNextRow(ImGuiTableRowFlags_Headers); @@ -385,7 +385,7 @@ void SaveTool::drawResearchInventory() { matRow("Mineral exoskeletology", 4, mineral_exoskeletology, mineralExoskeletology, MineralExoskeletology) matRow("Carbonized skin", 5, carbonized_skin, carbonizedSkin, CarbonizedSkin) unavRow("Isolated void particle", 6) - unavRow("Weaponised physiology", 7) + unavRow("Weaponised physiology", 7) ImGui::EndTable(); } -- 2.39.2 From de07b760d04996384c704cd002eb94901ef810cf Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 22 Sep 2021 17:37:50 +0200 Subject: [PATCH 016/128] Add UESaveFile. --- src/CMakeLists.txt | 78 ++++++ src/UESaveFile/BinaryReader.cpp | 122 +++++++++ src/UESaveFile/BinaryReader.h | 65 +++++ src/UESaveFile/BinaryWriter.cpp | 133 +++++++++ src/UESaveFile/BinaryWriter.h | 99 +++++++ src/UESaveFile/Debug.cpp | 76 ++++++ src/UESaveFile/Debug.h | 33 +++ src/UESaveFile/PropertySerialiser.cpp | 252 ++++++++++++++++++ src/UESaveFile/PropertySerialiser.h | 51 ++++ ...stractUnrealCollectionPropertySerialiser.h | 47 ++++ .../AbstractUnrealPropertySerialiser.h | 46 ++++ .../AbstractUnrealStructSerialiser.h | 46 ++++ .../Serialisers/ArrayPropertySerialiser.cpp | 66 +++++ .../Serialisers/ArrayPropertySerialiser.h | 30 +++ .../Serialisers/BoolPropertySerialiser.cpp | 61 +++++ .../Serialisers/BoolPropertySerialiser.h | 32 +++ .../Serialisers/BytePropertySerialiser.cpp | 75 ++++++ .../Serialisers/BytePropertySerialiser.h | 32 +++ .../Serialisers/ColourPropertySerialiser.cpp | 49 ++++ .../Serialisers/ColourPropertySerialiser.h | 30 +++ .../DateTimePropertySerialiser.cpp | 46 ++++ .../Serialisers/DateTimePropertySerialiser.h | 30 +++ .../Serialisers/EnumPropertySerialiser.cpp | 62 +++++ .../Serialisers/EnumPropertySerialiser.h | 32 +++ .../Serialisers/FloatPropertySerialiser.cpp | 57 ++++ .../Serialisers/FloatPropertySerialiser.h | 32 +++ .../Serialisers/GuidPropertySerialiser.cpp | 47 ++++ .../Serialisers/GuidPropertySerialiser.h | 31 +++ .../Serialisers/IntPropertySerialiser.cpp | 66 +++++ .../Serialisers/IntPropertySerialiser.h | 30 +++ .../Serialisers/MapPropertySerialiser.cpp | 124 +++++++++ .../Serialisers/MapPropertySerialiser.h | 30 +++ .../Serialisers/RotatorPropertySerialiser.cpp | 47 ++++ .../Serialisers/RotatorPropertySerialiser.h | 30 +++ .../Serialisers/SetPropertySerialiser.cpp | 73 +++++ .../Serialisers/SetPropertySerialiser.h | 30 +++ .../Serialisers/StringPropertySerialiser.cpp | 64 +++++ .../Serialisers/StringPropertySerialiser.h | 32 +++ .../Serialisers/StructSerialiser.cpp | 210 +++++++++++++++ src/UESaveFile/Serialisers/StructSerialiser.h | 45 ++++ .../Serialisers/TextPropertySerialiser.cpp | 84 ++++++ .../Serialisers/TextPropertySerialiser.h | 30 +++ .../Serialisers/UnrealPropertySerialiser.h | 58 ++++ .../Vector2DPropertySerialiser.cpp | 46 ++++ .../Serialisers/Vector2DPropertySerialiser.h | 30 +++ .../Serialisers/VectorPropertySerialiser.cpp | 47 ++++ .../Serialisers/VectorPropertySerialiser.h | 30 +++ src/UESaveFile/Types/ArrayProperty.h | 40 +++ src/UESaveFile/Types/BoolProperty.h | 27 ++ src/UESaveFile/Types/ByteProperty.h | 33 +++ src/UESaveFile/Types/ColourStructProperty.h | 27 ++ src/UESaveFile/Types/DateTimeStructProperty.h | 29 ++ src/UESaveFile/Types/EnumProperty.h | 29 ++ src/UESaveFile/Types/FloatProperty.h | 27 ++ src/UESaveFile/Types/GenericStructProperty.h | 43 +++ src/UESaveFile/Types/GuidStructProperty.h | 31 +++ src/UESaveFile/Types/IntProperty.h | 27 ++ src/UESaveFile/Types/MapProperty.h | 37 +++ src/UESaveFile/Types/NoneProperty.h | 28 ++ src/UESaveFile/Types/RotatorStructProperty.h | 29 ++ src/UESaveFile/Types/SetProperty.h | 40 +++ src/UESaveFile/Types/StringProperty.h | 27 ++ src/UESaveFile/Types/StructProperty.h | 32 +++ src/UESaveFile/Types/TextProperty.h | 31 +++ src/UESaveFile/Types/UnrealProperty.h | 26 ++ src/UESaveFile/Types/UnrealPropertyBase.h | 37 +++ src/UESaveFile/Types/Vector2DStructProperty.h | 29 ++ src/UESaveFile/Types/VectorStructProperty.h | 29 ++ src/UESaveFile/UESaveFile.cpp | 189 +++++++++++++ src/UESaveFile/UESaveFile.h | 81 ++++++ 70 files changed, 3794 insertions(+) create mode 100644 src/UESaveFile/BinaryReader.cpp create mode 100644 src/UESaveFile/BinaryReader.h create mode 100644 src/UESaveFile/BinaryWriter.cpp create mode 100644 src/UESaveFile/BinaryWriter.h create mode 100644 src/UESaveFile/Debug.cpp create mode 100644 src/UESaveFile/Debug.h create mode 100644 src/UESaveFile/PropertySerialiser.cpp create mode 100644 src/UESaveFile/PropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/AbstractUnrealCollectionPropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/AbstractUnrealPropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/AbstractUnrealStructSerialiser.h create mode 100644 src/UESaveFile/Serialisers/ArrayPropertySerialiser.cpp create mode 100644 src/UESaveFile/Serialisers/ArrayPropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/BoolPropertySerialiser.cpp create mode 100644 src/UESaveFile/Serialisers/BoolPropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/BytePropertySerialiser.cpp create mode 100644 src/UESaveFile/Serialisers/BytePropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/ColourPropertySerialiser.cpp create mode 100644 src/UESaveFile/Serialisers/ColourPropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/DateTimePropertySerialiser.cpp create mode 100644 src/UESaveFile/Serialisers/DateTimePropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/EnumPropertySerialiser.cpp create mode 100644 src/UESaveFile/Serialisers/EnumPropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/FloatPropertySerialiser.cpp create mode 100644 src/UESaveFile/Serialisers/FloatPropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/GuidPropertySerialiser.cpp create mode 100644 src/UESaveFile/Serialisers/GuidPropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/IntPropertySerialiser.cpp create mode 100644 src/UESaveFile/Serialisers/IntPropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/MapPropertySerialiser.cpp create mode 100644 src/UESaveFile/Serialisers/MapPropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/RotatorPropertySerialiser.cpp create mode 100644 src/UESaveFile/Serialisers/RotatorPropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/SetPropertySerialiser.cpp create mode 100644 src/UESaveFile/Serialisers/SetPropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/StringPropertySerialiser.cpp create mode 100644 src/UESaveFile/Serialisers/StringPropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/StructSerialiser.cpp create mode 100644 src/UESaveFile/Serialisers/StructSerialiser.h create mode 100644 src/UESaveFile/Serialisers/TextPropertySerialiser.cpp create mode 100644 src/UESaveFile/Serialisers/TextPropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/UnrealPropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/Vector2DPropertySerialiser.cpp create mode 100644 src/UESaveFile/Serialisers/Vector2DPropertySerialiser.h create mode 100644 src/UESaveFile/Serialisers/VectorPropertySerialiser.cpp create mode 100644 src/UESaveFile/Serialisers/VectorPropertySerialiser.h create mode 100644 src/UESaveFile/Types/ArrayProperty.h create mode 100644 src/UESaveFile/Types/BoolProperty.h create mode 100644 src/UESaveFile/Types/ByteProperty.h create mode 100644 src/UESaveFile/Types/ColourStructProperty.h create mode 100644 src/UESaveFile/Types/DateTimeStructProperty.h create mode 100644 src/UESaveFile/Types/EnumProperty.h create mode 100644 src/UESaveFile/Types/FloatProperty.h create mode 100644 src/UESaveFile/Types/GenericStructProperty.h create mode 100644 src/UESaveFile/Types/GuidStructProperty.h create mode 100644 src/UESaveFile/Types/IntProperty.h create mode 100644 src/UESaveFile/Types/MapProperty.h create mode 100644 src/UESaveFile/Types/NoneProperty.h create mode 100644 src/UESaveFile/Types/RotatorStructProperty.h create mode 100644 src/UESaveFile/Types/SetProperty.h create mode 100644 src/UESaveFile/Types/StringProperty.h create mode 100644 src/UESaveFile/Types/StructProperty.h create mode 100644 src/UESaveFile/Types/TextProperty.h create mode 100644 src/UESaveFile/Types/UnrealProperty.h create mode 100644 src/UESaveFile/Types/UnrealPropertyBase.h create mode 100644 src/UESaveFile/Types/Vector2DStructProperty.h create mode 100644 src/UESaveFile/Types/VectorStructProperty.h create mode 100644 src/UESaveFile/UESaveFile.cpp create mode 100644 src/UESaveFile/UESaveFile.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a0aa25a..a1c8e24 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,6 +28,83 @@ set_directory_properties(PROPERTIES CORRADE_USE_PEDANTIC_FLAGS ON) corrade_add_resource(Assets assets.conf) +add_library(UESaveFile STATIC EXCLUDE_FROM_ALL + UESaveFile/Serialisers/AbstractUnrealCollectionPropertySerialiser.h + UESaveFile/Serialisers/AbstractUnrealPropertySerialiser.h + UESaveFile/Serialisers/AbstractUnrealStructSerialiser.h + UESaveFile/Serialisers/ArrayPropertySerialiser.h + UESaveFile/Serialisers/ArrayPropertySerialiser.cpp + UESaveFile/Serialisers/BoolPropertySerialiser.h + UESaveFile/Serialisers/BoolPropertySerialiser.cpp + UESaveFile/Serialisers/BytePropertySerialiser.h + UESaveFile/Serialisers/BytePropertySerialiser.cpp + UESaveFile/Serialisers/ColourPropertySerialiser.h + UESaveFile/Serialisers/ColourPropertySerialiser.cpp + UESaveFile/Serialisers/DateTimePropertySerialiser.h + UESaveFile/Serialisers/DateTimePropertySerialiser.cpp + UESaveFile/Serialisers/EnumPropertySerialiser.h + UESaveFile/Serialisers/EnumPropertySerialiser.cpp + UESaveFile/Serialisers/FloatPropertySerialiser.h + UESaveFile/Serialisers/FloatPropertySerialiser.cpp + UESaveFile/Serialisers/GuidPropertySerialiser.h + UESaveFile/Serialisers/GuidPropertySerialiser.cpp + UESaveFile/Serialisers/IntPropertySerialiser.h + UESaveFile/Serialisers/IntPropertySerialiser.cpp + UESaveFile/Serialisers/MapPropertySerialiser.h + UESaveFile/Serialisers/MapPropertySerialiser.cpp + UESaveFile/Serialisers/RotatorPropertySerialiser.h + UESaveFile/Serialisers/RotatorPropertySerialiser.cpp + UESaveFile/Serialisers/StringPropertySerialiser.h + UESaveFile/Serialisers/StringPropertySerialiser.cpp + UESaveFile/Serialisers/SetPropertySerialiser.h + UESaveFile/Serialisers/SetPropertySerialiser.cpp + UESaveFile/Serialisers/StructSerialiser.h + UESaveFile/Serialisers/StructSerialiser.cpp + UESaveFile/Serialisers/TextPropertySerialiser.h + UESaveFile/Serialisers/TextPropertySerialiser.cpp + UESaveFile/Serialisers/UnrealPropertySerialiser.h + UESaveFile/Serialisers/VectorPropertySerialiser.h + UESaveFile/Serialisers/VectorPropertySerialiser.cpp + UESaveFile/Serialisers/Vector2DPropertySerialiser.h + UESaveFile/Serialisers/Vector2DPropertySerialiser.cpp + + UESaveFile/Types/ArrayProperty.h + UESaveFile/Types/BoolProperty.h + UESaveFile/Types/ByteProperty.h + UESaveFile/Types/ColourStructProperty.h + UESaveFile/Types/DateTimeStructProperty.h + UESaveFile/Types/EnumProperty.h + UESaveFile/Types/FloatProperty.h + UESaveFile/Types/GenericStructProperty.h + UESaveFile/Types/GuidStructProperty.h + UESaveFile/Types/IntProperty.h + UESaveFile/Types/MapProperty.h + UESaveFile/Types/NoneProperty.h + UESaveFile/Types/RotatorStructProperty.h + UESaveFile/Types/SetProperty.h + UESaveFile/Types/StringProperty.h + UESaveFile/Types/StructProperty.h + UESaveFile/Types/TextProperty.h + UESaveFile/Types/UnrealProperty.h + UESaveFile/Types/UnrealPropertyBase.h + UESaveFile/Types/VectorStructProperty.h + + UESaveFile/Debug.h + UESaveFile/Debug.cpp + UESaveFile/UESaveFile.h + UESaveFile/UESaveFile.cpp + UESaveFile/BinaryReader.h + UESaveFile/BinaryReader.cpp + UESaveFile/BinaryWriter.h + UESaveFile/BinaryWriter.cpp + UESaveFile/PropertySerialiser.h + UESaveFile/PropertySerialiser.cpp) + +target_link_libraries(UESaveFile PRIVATE + Corrade::Containers + Corrade::Utility + Magnum::Magnum) + add_executable(MassBuilderSaveTool WIN32 main.cpp SaveTool/SaveTool.h @@ -78,6 +155,7 @@ target_link_libraries(MassBuilderSaveTool PRIVATE Magnum::GL Magnum::Sdl2Application MagnumIntegration::ImGui + UESaveFile efsw zip cpr::cpr diff --git a/src/UESaveFile/BinaryReader.cpp b/src/UESaveFile/BinaryReader.cpp new file mode 100644 index 0000000..6a9b942 --- /dev/null +++ b/src/UESaveFile/BinaryReader.cpp @@ -0,0 +1,122 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include + +#include "BinaryReader.h" + +BinaryReader::BinaryReader(const std::string& filename) { + _file = std::fopen(filename.c_str(), "rb"); + + if(!_file) { + Utility::Error{} << "Couldn't open" << filename.c_str() << "for reading:\n" + << std::strerror(errno); + } +} + +BinaryReader::~BinaryReader() { + closeFile(); +} + +auto BinaryReader::open() -> bool { + return _file; +} + +auto BinaryReader::eof() -> bool { + return std::feof(_file) != 0; +} + +auto BinaryReader::position() -> Long { + return _ftelli64(_file); +} + +void BinaryReader::closeFile() { + std::fclose(_file); + _file = nullptr; +} + +auto BinaryReader::readChar(char& value) -> bool { + return std::fread(&value, sizeof(char), 1, _file) == 1; +} + +auto BinaryReader::readByte(Byte& value) -> bool { + return std::fread(&value, sizeof(Byte), 1, _file) == 1; +} + +auto BinaryReader::readUnsignedByte(UnsignedByte& value) -> bool { + return std::fread(&value, sizeof(UnsignedByte), 1, _file) == 1; +} + +auto BinaryReader::readShort(Short& value) -> bool { + return std::fread(&value, sizeof(Short), 1, _file) == 1; +} + +auto BinaryReader::readUnsignedShort(UnsignedShort& value) -> bool { + return std::fread(&value, sizeof(UnsignedShort), 1, _file) == 1; +} + +auto BinaryReader::readInt(Int& value) -> bool { + return std::fread(&value, sizeof(Int), 1, _file) == 1; +} + +auto BinaryReader::readUnsignedInt(UnsignedInt& value) -> bool { + return std::fread(&value, sizeof(UnsignedInt), 1, _file) == 1; +} + +auto BinaryReader::readLong(Long& value) -> bool { + return std::fread(&value, sizeof(Long), 1, _file) == 1; +} + +auto BinaryReader::readUnsignedLong(UnsignedLong& value) -> bool { + return std::fread(&value, sizeof(UnsignedLong), 1, _file) == 1; +} + +auto BinaryReader::readFloat(Float& value) -> bool { + return std::fread(&value, sizeof(Float), 1, _file) == 1; +} + +auto BinaryReader::readDouble(Double& value) -> bool { + return std::fread(&value, sizeof(Double), 1, _file) == 1; +} + +auto BinaryReader::readArray(Containers::Array& array, std::size_t count) -> bool { + if(array.size() < count) { + array = Containers::Array{ValueInit, count}; + } + + return std::fread(array.data(), sizeof(char), count, _file) == count; +} + +auto BinaryReader::readUEString(std::string& str) -> bool { + UnsignedInt length = 0; + if(!readUnsignedInt(length) || length == 0) { + return false; + } + + str = std::string{}; + str.resize(length - 1); + + return std::fread(&str[0], sizeof(char), length, _file) == length; +} + +auto BinaryReader::peekChar() -> Int { + Int c; + c = std::fgetc(_file); + std::ungetc(c, _file); + return c; +} diff --git a/src/UESaveFile/BinaryReader.h b/src/UESaveFile/BinaryReader.h new file mode 100644 index 0000000..c2a52b5 --- /dev/null +++ b/src/UESaveFile/BinaryReader.h @@ -0,0 +1,65 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include +#include +#include + +#include + +using namespace Corrade; +using namespace Magnum; + +class BinaryReader { + public: + explicit BinaryReader(const std::string& filename); + ~BinaryReader(); + + auto open() -> bool; + auto eof() -> bool; + auto position() -> Long; + + void closeFile(); + + auto readChar(char& value) -> bool; + auto readByte(Byte& value) -> bool; + auto readUnsignedByte(UnsignedByte& value) -> bool; + auto readShort(Short& value) -> bool; + auto readUnsignedShort(UnsignedShort& value) -> bool; + auto readInt(Int& value) -> bool; + auto readUnsignedInt(UnsignedInt& value) -> bool; + auto readLong(Long& value) -> bool; + auto readUnsignedLong(UnsignedLong& value) -> bool; + auto readFloat(Float& value) -> bool; + auto readDouble(Double& value) -> bool; + auto readArray(Containers::Array& array, std::size_t count) -> bool; + + template + auto readStaticArray(Containers::StaticArray& array) -> bool { + return std::fread(array.data(), sizeof(char), S, _file) == S; + } + + auto readUEString(std::string& str) -> bool; + + auto peekChar() -> Int; + + private: + std::FILE* _file = nullptr; +}; diff --git a/src/UESaveFile/BinaryWriter.cpp b/src/UESaveFile/BinaryWriter.cpp new file mode 100644 index 0000000..76c2b03 --- /dev/null +++ b/src/UESaveFile/BinaryWriter.cpp @@ -0,0 +1,133 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include + +#include "BinaryWriter.h" + +BinaryWriter::BinaryWriter(const std::string& filename) { + _file = std::fopen(filename.c_str(), "wb"); + if(!_file) { + Utility::Error{} << "Couldn't open" << filename.c_str() << "for reading:\n" + << std::strerror(errno); + } +} + +BinaryWriter::~BinaryWriter() { + closeFile(); +} + +auto BinaryWriter::open() -> bool { + return _file; +} + +void BinaryWriter::closeFile() { + std::fflush(_file); + std::fclose(_file); + _file = nullptr; +} + +auto BinaryWriter::position() -> Long { + return _ftelli64(_file); +} + +auto BinaryWriter::arrayPosition() const -> UnsignedLong { + return _index; +} + +auto BinaryWriter::flushToFile() -> bool { + bool ret = writeArray(_data); + std::fflush(_file); + _data = Containers::Array{}; + _index = 0; + return ret; +} + +auto BinaryWriter::writeChar(char value) -> bool { + return std::fwrite(&value, sizeof(char), 1, _file) == 1; +} + +auto BinaryWriter::writeByte(Byte value) -> bool { + return std::fwrite(&value, sizeof(Byte), 1, _file) == 1; +} + +auto BinaryWriter::writeUnsignedByte(UnsignedByte value) -> bool { + return std::fwrite(&value, sizeof(UnsignedByte), 1, _file) == 1; +} + +auto BinaryWriter::writeShort(Short value) -> bool { + return std::fwrite(&value, sizeof(Short), 1, _file) == 1; +} + +auto BinaryWriter::writeUnsignedShort(UnsignedShort value) -> bool { + return std::fwrite(&value, sizeof(UnsignedShort), 1, _file) == 1; +} + +auto BinaryWriter::writeInt(Int value) -> bool { + return std::fwrite(&value, sizeof(Int), 1, _file) == 1; +} + +auto BinaryWriter::writeUnsignedInt(UnsignedInt value) -> bool { + return std::fwrite(&value, sizeof(UnsignedInt), 1, _file) == 1; +} + +auto BinaryWriter::writeLong(Long value) -> bool { + return std::fwrite(&value, sizeof(Long), 1, _file) == 1; +} + +auto BinaryWriter::writeUnsignedLong(UnsignedLong value) -> bool { + return std::fwrite(&value, sizeof(UnsignedLong), 1, _file) == 1; +} + +auto BinaryWriter::writeFloat(Float value) -> bool { + return std::fwrite(&value, sizeof(Float), 1, _file) == 1; +} + +auto BinaryWriter::writeDouble(Double value) -> bool { + return std::fwrite(&value, sizeof(Double), 1, _file) == 1; +} + +auto BinaryWriter::writeArray(Containers::ArrayView array) -> bool { + if(array.size() == 0) { + return false; + } + + return std::fwrite(array.data(), sizeof(char), array.size(), _file) == array.size(); +} + +auto BinaryWriter::writeUEString(const std::string& str) -> bool { + if(str.length() > UINT32_MAX) { + Utility::Error{} << "BinaryWriter::writeUEString(): string is too big."; + return false; + } + + writeUnsignedInt(static_cast(str.length()) + 1); + + if(str.length() > 0) { + std::size_t count = std::fwrite(&str[0], sizeof(char), str.length(), _file); + if(count != str.length()) { + return false; + } + } + return writeChar('\0'); +} + +auto BinaryWriter::writeUEStringToArray(const std::string& value) -> UnsignedLong { + Containers::ArrayView view{value.c_str(), value.length()}; + return writeValueToArray(UnsignedInt(value.length()) + 1u) + writeDataToArray(view) + writeValueToArray('\0'); +} diff --git a/src/UESaveFile/BinaryWriter.h b/src/UESaveFile/BinaryWriter.h new file mode 100644 index 0000000..50ea2b7 --- /dev/null +++ b/src/UESaveFile/BinaryWriter.h @@ -0,0 +1,99 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include +#include +#include +#include + +#include + +using namespace Corrade; +using namespace Magnum; + +class BinaryWriter { + public: + explicit BinaryWriter(const std::string& filename); + ~BinaryWriter(); + + auto open() -> bool; + + void closeFile(); + + auto position() -> Long; + + auto arrayPosition() const -> UnsignedLong; + auto flushToFile() -> bool; + + auto writeByte(Byte value) -> bool; + auto writeChar(char value) -> bool; + auto writeUnsignedByte(UnsignedByte value) -> bool; + auto writeShort(Short value) -> bool; + auto writeUnsignedShort(UnsignedShort value) -> bool; + auto writeInt(Int value) -> bool; + auto writeUnsignedInt(UnsignedInt value) -> bool; + auto writeLong(Long value) -> bool; + auto writeUnsignedLong(UnsignedLong value) -> bool; + auto writeFloat(Float value) -> bool; + auto writeDouble(Double value) -> bool; + auto writeArray(Containers::ArrayView array) -> bool; + + template + auto writeStaticArray(Containers::StaticArrayView array) -> bool { + return std::fwrite(array.data(), sizeof(char), S, _file) == S; + } + + auto writeUEString(const std::string& str) -> bool; + + template::value, T, T&>> + auto writeValueToArray(U value) -> UnsignedLong { + Containers::ArrayView view{&value, 1}; + return writeDataToArray(view); + } + + auto writeUEStringToArray(const std::string& value) -> UnsignedLong; + + template + void writeValueToArrayAt(T& value, UnsignedLong position) { + Containers::ArrayView view{&value, 1}; + writeDataToArrayAt(view, position); + } + + template + auto writeDataToArray(Containers::ArrayView view) -> UnsignedLong { + arrayAppend(_data, Containers::arrayCast(view)); + _index += sizeof(T) * view.size(); + return sizeof(T) * view.size(); + } + + template + void writeDataToArrayAt(Containers::ArrayView view, UnsignedLong position) { + auto casted_view = Containers::arrayCast(view); + for(UnsignedLong i = 0; i < casted_view.size(); i++) { + _data[position + i] = casted_view[i]; + } + } + + private: + FILE* _file = nullptr; + + Containers::Array _data; + UnsignedLong _index = 0; +}; diff --git a/src/UESaveFile/Debug.cpp b/src/UESaveFile/Debug.cpp new file mode 100644 index 0000000..6d60d35 --- /dev/null +++ b/src/UESaveFile/Debug.cpp @@ -0,0 +1,76 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "Types/UnrealPropertyBase.h" +#include "Types/ArrayProperty.h" +#include "Types/SetProperty.h" +#include "Types/StructProperty.h" +#include "Types/GenericStructProperty.h" + +#include "Debug.h" + +Utility::Debug& operator<<(Utility::Debug& debug, const ArrayProperty* prop) { + return debug << (*prop->name).c_str() << Utility::Debug::nospace << ":" << + prop->propertyType.c_str() << "of" << prop->items.size() << prop->itemType.c_str(); +} + +Utility::Debug& operator<<(Utility::Debug& debug, const SetProperty* prop) { + return debug << (*prop->name).c_str() << Utility::Debug::nospace << ":" << + prop->propertyType.c_str() << "of" << prop->items.size() << prop->itemType.c_str(); +} + +Utility::Debug& operator<<(Utility::Debug& debug, const GenericStructProperty* prop) { + debug << (*prop->name).c_str() << Utility::Debug::nospace << ":" << + prop->structType.c_str() << "(" << Utility::Debug::nospace << prop->propertyType.c_str() << Utility::Debug::nospace << + ") Contents:"; + for(const auto& item : prop->properties) { + debug << "\n " << Utility::Debug::nospace << item.get(); + } + return debug; +} + +Utility::Debug& operator<<(Utility::Debug& debug, const StructProperty* prop) { + auto cast = dynamic_cast(prop); + if(cast) { + return debug << cast; + } + + return debug << (*prop->name).c_str() << Utility::Debug::nospace << ":" << + prop->structType.c_str() << "(" << Utility::Debug::nospace << prop->propertyType.c_str() << Utility::Debug::nospace << ")"; +} + +Utility::Debug& operator<<(Utility::Debug& debug, const UnrealPropertyBase* prop) { + if(prop->propertyType == "ArrayProperty") { + auto array_prop = dynamic_cast(prop); + if(array_prop) { + return debug << array_prop; + } + } + else if(prop->propertyType == "SetProperty") { + auto set_prop = dynamic_cast(prop); + if(set_prop) { + return debug << set_prop; + } + } + else if(prop->propertyType == "StructProperty") { + auto struct_prop = dynamic_cast(prop); + if(struct_prop) { + return debug << struct_prop; + } + } + + return debug << (*prop->name).c_str() << Utility::Debug::nospace << ":" << prop->propertyType.c_str(); +} diff --git a/src/UESaveFile/Debug.h b/src/UESaveFile/Debug.h new file mode 100644 index 0000000..016d7e0 --- /dev/null +++ b/src/UESaveFile/Debug.h @@ -0,0 +1,33 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +struct ArrayProperty; +struct SetProperty; +struct GenericStructProperty; +struct StructProperty; +struct UnrealPropertyBase; + +using namespace Corrade; + +Utility::Debug& operator<<(Utility::Debug& debug, const ArrayProperty* prop); +Utility::Debug& operator<<(Utility::Debug& debug, const SetProperty* prop); +Utility::Debug& operator<<(Utility::Debug& debug, const GenericStructProperty* prop); +Utility::Debug& operator<<(Utility::Debug& debug, const StructProperty* prop); +Utility::Debug& operator<<(Utility::Debug& debug, const UnrealPropertyBase* prop); diff --git a/src/UESaveFile/PropertySerialiser.cpp b/src/UESaveFile/PropertySerialiser.cpp new file mode 100644 index 0000000..4c64756 --- /dev/null +++ b/src/UESaveFile/PropertySerialiser.cpp @@ -0,0 +1,252 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "Serialisers/ArrayPropertySerialiser.h" +#include "Serialisers/BoolPropertySerialiser.h" +#include "Serialisers/BytePropertySerialiser.h" +#include "Serialisers/ColourPropertySerialiser.h" +#include "Serialisers/DateTimePropertySerialiser.h" +#include "Serialisers/EnumPropertySerialiser.h" +#include "Serialisers/FloatPropertySerialiser.h" +#include "Serialisers/GuidPropertySerialiser.h" +#include "Serialisers/IntPropertySerialiser.h" +#include "Serialisers/MapPropertySerialiser.h" +#include "Serialisers/RotatorPropertySerialiser.h" +#include "Serialisers/StringPropertySerialiser.h" +#include "Serialisers/SetPropertySerialiser.h" +#include "Serialisers/StructSerialiser.h" +#include "Serialisers/TextPropertySerialiser.h" +#include "Serialisers/VectorPropertySerialiser.h" +#include "Serialisers/Vector2DPropertySerialiser.h" + +#include "Types/NoneProperty.h" + +#include "BinaryReader.h" +#include "BinaryWriter.h" + +#include "PropertySerialiser.h" + + +PropertySerialiser::PropertySerialiser() { + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); + + arrayAppend(_collectionSerialisers, Containers::pointer()); +} + +auto PropertySerialiser::read(BinaryReader& reader) -> UnrealPropertyBase::ptr { + if(reader.peekChar() < 0 || reader.eof()) { + return nullptr; + } + + std::string name; + if(!reader.readUEString(name)) { + return nullptr; + } + + if(name == "None") { + return Containers::pointer(); + } + + std::string type; + if(!reader.readUEString(type)) { + return nullptr; + } + + UnsignedLong value_length; + if(!reader.readUnsignedLong(value_length)) { + return nullptr; + } + + return deserialise(std::move(name), std::move(type), value_length, reader); +} + +auto PropertySerialiser::readItem(BinaryReader& reader, std::string type, UnsignedLong value_length, std::string name) -> UnrealPropertyBase::ptr { + if(reader.peekChar() < 0 || reader.eof()) { + return nullptr; + } + + return deserialise(std::move(name), std::move(type), value_length, reader); +} + +auto PropertySerialiser::readSet(BinaryReader& reader, const std::string& item_type, UnsignedInt count) -> Containers::Array { + if(reader.peekChar() < 0 || reader.eof()) { + return nullptr; + } + + auto serialiser = getCollectionSerialiser(item_type); + + Containers::Array array; + + if(serialiser) { + std::string name; + if(!reader.readUEString(name)) { + return nullptr; + } + + std::string type; + if(!reader.readUEString(type)) { + return nullptr; + } + + UnsignedLong value_length; + if(!reader.readUnsignedLong(value_length)) { + return nullptr; + } + + array = serialiser->deserialise(name, type, value_length, count, reader, *this); + + for(auto& item : array) { + if(item->name == Containers::NullOpt) { + item->name.emplace(name); + } + } + } + else { + for(UnsignedInt i = 0; i < count; i++) { + auto item = readItem(reader, item_type, -1, ""); + arrayAppend(array, std::move(item)); + } + } + + return array; +} + +auto PropertySerialiser::deserialise(std::string name, std::string type, UnsignedLong value_length, + BinaryReader& reader) -> UnrealPropertyBase::ptr +{ + UnrealPropertyBase::ptr prop; + auto serialiser = getSerialiser(type); + + if(serialiser == nullptr) { + return nullptr; + } + + prop = serialiser->deserialise(name, type, value_length, reader, *this); + + if(!prop) { + !Utility::Error{} << "No prop in" << __func__; + return nullptr; + } + + prop->name = std::move(name); + prop->propertyType = std::move(type); + + return prop; +} + +auto PropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, const std::string& item_type, UnsignedLong& bytes_written, + BinaryWriter& writer) -> bool +{ + auto serialiser = getSerialiser(item_type); + if(!serialiser) { + return false; + } + return serialiser->serialise(prop, bytes_written, writer, *this); +} + +auto PropertySerialiser::write(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer) -> bool { + if(prop->name == "None" && prop->propertyType == "NoneProperty" && dynamic_cast(prop.get())) { + bytes_written += writer.writeUEStringToArray(*prop->name); + return true; + } + + bytes_written += writer.writeUEStringToArray(*prop->name); + bytes_written += writer.writeUEStringToArray(prop->propertyType); + + UnsignedLong value_length = 0; + UnsignedLong vl_position = writer.arrayPosition(); + + bytes_written += writer.writeValueToArray(value_length); + + bool ret = serialise(prop, prop->propertyType, value_length, writer); + + writer.writeValueToArrayAt(value_length, vl_position); + + bytes_written += value_length; + + return ret; +} + +auto PropertySerialiser::writeItem(UnrealPropertyBase::ptr& prop, const std::string& item_type, + UnsignedLong& bytes_written, BinaryWriter& writer) -> bool +{ + if(prop->name == "None" && prop->propertyType == "NoneProperty" && dynamic_cast(prop.get())) { + bytes_written += writer.writeUEStringToArray(*prop->name); + return true; + } + + return serialise(prop, item_type, bytes_written, writer); +} + +auto PropertySerialiser::writeSet(Containers::ArrayView props, const std::string& item_type, + UnsignedLong& bytes_written, BinaryWriter& writer) -> bool +{ + auto serialiser = getCollectionSerialiser(item_type); + if(serialiser) { + return serialiser->serialise(props, item_type, bytes_written, writer, *this); + } + else { + for(auto& prop : props) { + if(!writeItem(prop, item_type, bytes_written, writer)) { + return false; + } + } + + return true; + } +} + +auto PropertySerialiser::getSerialiser(const std::string& item_type) -> AbstractUnrealPropertySerialiser* { + for(auto& item : _serialisers) { + for(const std::string& serialiser_type : item->types()) { + if(item_type == serialiser_type) { + return item.get(); + } + } + } + + return nullptr; +} + +auto PropertySerialiser::getCollectionSerialiser(const std::string& item_type) -> AbstractUnrealCollectionPropertySerialiser* { + for(auto& item : _collectionSerialisers) { + for(const std::string& serialiser_type : item->types()) { + if(item_type == serialiser_type) { + return item.get(); + } + } + } + + return nullptr; +} diff --git a/src/UESaveFile/PropertySerialiser.h b/src/UESaveFile/PropertySerialiser.h new file mode 100644 index 0000000..8757aea --- /dev/null +++ b/src/UESaveFile/PropertySerialiser.h @@ -0,0 +1,51 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "Serialisers/AbstractUnrealPropertySerialiser.h" +#include "Serialisers/AbstractUnrealCollectionPropertySerialiser.h" + +#include "Types/UnrealPropertyBase.h" + +using namespace Corrade; + +class BinaryReader; +class BinaryWriter; + +class PropertySerialiser { + public: + PropertySerialiser(); + + auto read(BinaryReader& reader) -> UnrealPropertyBase::ptr; + auto readItem(BinaryReader& reader, std::string type, UnsignedLong value_length, std::string name) -> UnrealPropertyBase::ptr; + auto readSet(BinaryReader& reader, const std::string& item_type, UnsignedInt count) -> Containers::Array; + auto deserialise(std::string name, std::string type, UnsignedLong value_length, BinaryReader& reader) -> UnrealPropertyBase::ptr; + + auto serialise(UnrealPropertyBase::ptr& prop, const std::string& item_type, UnsignedLong& bytes_written, BinaryWriter& writer) -> bool; + auto write(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer) -> bool; + auto writeItem(UnrealPropertyBase::ptr& prop, const std::string& item_type, UnsignedLong& bytes_written, BinaryWriter& writer) -> bool; + auto writeSet(Containers::ArrayView props, const std::string& item_type, UnsignedLong& bytes_written, BinaryWriter& writer) -> bool; + + private: + auto getSerialiser(const std::string& item_type) -> AbstractUnrealPropertySerialiser*; + auto getCollectionSerialiser(const std::string& item_type) -> AbstractUnrealCollectionPropertySerialiser*; + + Containers::Array _serialisers; + Containers::Array _collectionSerialisers; +}; diff --git a/src/UESaveFile/Serialisers/AbstractUnrealCollectionPropertySerialiser.h b/src/UESaveFile/Serialisers/AbstractUnrealCollectionPropertySerialiser.h new file mode 100644 index 0000000..df004d4 --- /dev/null +++ b/src/UESaveFile/Serialisers/AbstractUnrealCollectionPropertySerialiser.h @@ -0,0 +1,47 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include +#include +#include + +#include + +#include "../Types/UnrealPropertyBase.h" + +using namespace Corrade; +using namespace Magnum; + +class BinaryReader; +class BinaryWriter; +class PropertySerialiser; + +class AbstractUnrealCollectionPropertySerialiser { + public: + using ptr = Containers::Pointer; + + virtual ~AbstractUnrealCollectionPropertySerialiser() = default; + + virtual auto types() -> Containers::ArrayView = 0; + + virtual auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, UnsignedInt count, BinaryReader& reader, PropertySerialiser& serialiser) -> Containers::Array = 0; + + virtual auto serialise(Containers::ArrayView props, const std::string& item_type, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool = 0; +}; diff --git a/src/UESaveFile/Serialisers/AbstractUnrealPropertySerialiser.h b/src/UESaveFile/Serialisers/AbstractUnrealPropertySerialiser.h new file mode 100644 index 0000000..6605550 --- /dev/null +++ b/src/UESaveFile/Serialisers/AbstractUnrealPropertySerialiser.h @@ -0,0 +1,46 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include +#include + +#include + +#include "../Types/UnrealPropertyBase.h" + +using namespace Corrade; +using namespace Magnum; + +class BinaryReader; +class BinaryWriter; +class PropertySerialiser; + +class AbstractUnrealPropertySerialiser { + public: + using ptr = Containers::Pointer; + + virtual ~AbstractUnrealPropertySerialiser() = default; + + virtual auto types() -> Containers::ArrayView = 0; + + virtual auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr = 0; + + virtual auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool = 0; +}; diff --git a/src/UESaveFile/Serialisers/AbstractUnrealStructSerialiser.h b/src/UESaveFile/Serialisers/AbstractUnrealStructSerialiser.h new file mode 100644 index 0000000..e52175b --- /dev/null +++ b/src/UESaveFile/Serialisers/AbstractUnrealStructSerialiser.h @@ -0,0 +1,46 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include +#include +#include + +#include + +#include "../Types/UnrealPropertyBase.h" + +using namespace Corrade; +using namespace Magnum; + +class BinaryReader; +class BinaryWriter; + +class AbstractUnrealStructSerialiser { + public: + using ptr = Containers::Pointer; + + virtual ~AbstractUnrealStructSerialiser() = default; + + virtual auto supportsType(const std::string& type) -> bool = 0; + + virtual auto deserialise(BinaryReader& reader) -> UnrealPropertyBase::ptr = 0; + + virtual auto serialise(UnrealPropertyBase::ptr& structProp, BinaryWriter& writer, UnsignedLong& bytes_written) -> bool = 0; +}; diff --git a/src/UESaveFile/Serialisers/ArrayPropertySerialiser.cpp b/src/UESaveFile/Serialisers/ArrayPropertySerialiser.cpp new file mode 100644 index 0000000..70cff56 --- /dev/null +++ b/src/UESaveFile/Serialisers/ArrayPropertySerialiser.cpp @@ -0,0 +1,66 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../BinaryReader.h" +#include "../BinaryWriter.h" +#include "../PropertySerialiser.h" + +#include "ArrayPropertySerialiser.h" + +auto ArrayPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +{ + std::string item_type; + if(!reader.readUEString(item_type)) { + return nullptr; + } + + char terminator; + if(!reader.readChar(terminator) || terminator != '\0') { + return nullptr; + } + + UnsignedInt item_count; + if(!reader.readUnsignedInt(item_count)) { + return nullptr; + } + + auto prop = Containers::pointer(); + prop->itemType = std::move(item_type); + prop->items = serialiser.readSet(reader, prop->itemType, item_count); + + return prop; +} + +auto ArrayPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto array_prop = dynamic_cast(prop.get()); + if(!array_prop) { + return false; + } + + writer.writeUEStringToArray(array_prop->itemType); + writer.writeValueToArray('\0'); + bytes_written += writer.writeValueToArray(UnsignedInt(array_prop->items.size())); + + UnsignedLong start_pos = writer.arrayPosition(); + UnsignedLong dummy_bytes_written = 0; + bool ret = serialiser.writeSet(array_prop->items, array_prop->itemType, dummy_bytes_written, writer); + bytes_written += writer.arrayPosition() - start_pos; + + return ret; +} diff --git a/src/UESaveFile/Serialisers/ArrayPropertySerialiser.h b/src/UESaveFile/Serialisers/ArrayPropertySerialiser.h new file mode 100644 index 0000000..f05db71 --- /dev/null +++ b/src/UESaveFile/Serialisers/ArrayPropertySerialiser.h @@ -0,0 +1,30 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealPropertySerialiser.h" + +#include "../Types/ArrayProperty.h" + +class ArrayPropertySerialiser : public UnrealPropertySerialiser { + public: + using ptr = Containers::Pointer; + + private: + auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; +}; diff --git a/src/UESaveFile/Serialisers/BoolPropertySerialiser.cpp b/src/UESaveFile/Serialisers/BoolPropertySerialiser.cpp new file mode 100644 index 0000000..10e761f --- /dev/null +++ b/src/UESaveFile/Serialisers/BoolPropertySerialiser.cpp @@ -0,0 +1,61 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../BinaryReader.h" +#include "../BinaryWriter.h" + +#include "BoolPropertySerialiser.h" + +auto BoolPropertySerialiser::types() -> Containers::ArrayView { + static const Containers::Array types{InPlaceInit, {"BoolProperty"}}; + return types; +} + +auto BoolPropertySerialiser::deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +{ + if(value_length != 0) { + return nullptr; + } + + Short value; + if(!reader.readShort(value)) { + return nullptr; + } + + if(value > 1 || value < 0) { + return nullptr; + } + + auto prop = Containers::pointer(); + prop->value = value; + + return prop; +} + +auto BoolPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto bool_prop = dynamic_cast(prop.get()); + + if(!bool_prop) { + return false; + } + + writer.writeValueToArray(Short(bool_prop->value)); + + return true; +} diff --git a/src/UESaveFile/Serialisers/BoolPropertySerialiser.h b/src/UESaveFile/Serialisers/BoolPropertySerialiser.h new file mode 100644 index 0000000..1b0bb3d --- /dev/null +++ b/src/UESaveFile/Serialisers/BoolPropertySerialiser.h @@ -0,0 +1,32 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "AbstractUnrealPropertySerialiser.h" + +#include "../Types/BoolProperty.h" + +class BoolPropertySerialiser : public AbstractUnrealPropertySerialiser { + public: + using ptr = Containers::Pointer; + + auto types() -> Containers::ArrayView override; + + auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + + auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; +}; diff --git a/src/UESaveFile/Serialisers/BytePropertySerialiser.cpp b/src/UESaveFile/Serialisers/BytePropertySerialiser.cpp new file mode 100644 index 0000000..4cc597f --- /dev/null +++ b/src/UESaveFile/Serialisers/BytePropertySerialiser.cpp @@ -0,0 +1,75 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../BinaryReader.h" +#include "../BinaryWriter.h" + +#include "BytePropertySerialiser.h" + +auto BytePropertySerialiser::types() -> Containers::ArrayView { + static const Containers::Array types{InPlaceInit, {"ByteProperty"}}; + return types; +} + +auto BytePropertySerialiser::deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +{ + auto prop = Containers::pointer(); + + if(!reader.readUEString(prop->enumType)) { + return nullptr; + } + + char terminator; + if(!reader.readChar(terminator) || terminator != '\0') { + return nullptr; + } + + if(!reader.readUEString(prop->enumValue)) { + return nullptr; + } + + //UnsignedInt count = 0; + //if(!reader.readUnsignedInt(count)) { + // return nullptr; + //} + + //if(!reader.readArray(prop->value, count)) { + // return nullptr; + //} + + return prop; +} + +auto BytePropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto byte_prop = dynamic_cast(prop.get()); + + if(!byte_prop) { + return false; + } + + //writer.writeValueToArray('\0'); + //bytes_written += writer.writeValueToArray(byte_prop->value.size()); + //bytes_written += writer.writeDataToArray(byte_prop->value); + + writer.writeUEStringToArray(byte_prop->enumType); + writer.writeValueToArray('\0'); + bytes_written += writer.writeUEStringToArray(byte_prop->enumValue); + + return true; +} diff --git a/src/UESaveFile/Serialisers/BytePropertySerialiser.h b/src/UESaveFile/Serialisers/BytePropertySerialiser.h new file mode 100644 index 0000000..3fd882d --- /dev/null +++ b/src/UESaveFile/Serialisers/BytePropertySerialiser.h @@ -0,0 +1,32 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "AbstractUnrealPropertySerialiser.h" + +#include "../Types/ByteProperty.h" + +class BytePropertySerialiser : public AbstractUnrealPropertySerialiser { + public: + using ptr = Containers::Pointer; + + auto types() -> Containers::ArrayView override; + + auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + + auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; +}; diff --git a/src/UESaveFile/Serialisers/ColourPropertySerialiser.cpp b/src/UESaveFile/Serialisers/ColourPropertySerialiser.cpp new file mode 100644 index 0000000..befd069 --- /dev/null +++ b/src/UESaveFile/Serialisers/ColourPropertySerialiser.cpp @@ -0,0 +1,49 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../BinaryReader.h" +#include "../BinaryWriter.h" + +#include "ColourPropertySerialiser.h" + +auto ColourPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +{ + auto prop = Containers::pointer(); + + if(!reader.readFloat(prop->r) || !reader.readFloat(prop->g) || + !reader.readFloat(prop->b) || !reader.readFloat(prop->a)) + { + return nullptr; + } + + return prop; +} + +auto ColourPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto colour_prop = dynamic_cast(prop.get()); + + if(!colour_prop) { + return false; + } + + bytes_written += writer.writeValueToArray(colour_prop->r) + writer.writeValueToArray(colour_prop->g) + + writer.writeValueToArray(colour_prop->b) + writer.writeValueToArray(colour_prop->a); + + return true; +} diff --git a/src/UESaveFile/Serialisers/ColourPropertySerialiser.h b/src/UESaveFile/Serialisers/ColourPropertySerialiser.h new file mode 100644 index 0000000..f858739 --- /dev/null +++ b/src/UESaveFile/Serialisers/ColourPropertySerialiser.h @@ -0,0 +1,30 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealPropertySerialiser.h" + +#include "../Types/ColourStructProperty.h" + +class ColourPropertySerialiser : public UnrealPropertySerialiser { + public: + using ptr = Containers::Pointer; + + private: + auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; +}; diff --git a/src/UESaveFile/Serialisers/DateTimePropertySerialiser.cpp b/src/UESaveFile/Serialisers/DateTimePropertySerialiser.cpp new file mode 100644 index 0000000..615449e --- /dev/null +++ b/src/UESaveFile/Serialisers/DateTimePropertySerialiser.cpp @@ -0,0 +1,46 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../BinaryReader.h" +#include "../BinaryWriter.h" + +#include "DateTimePropertySerialiser.h" + +auto DateTimePropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +{ + auto prop = Containers::pointer(); + + if(!reader.readUnsignedLong(prop->timestamp)) { + return nullptr; + } + + return prop; +} + +auto DateTimePropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto dt_prop = dynamic_cast(prop.get()); + + if(!dt_prop) { + return false; + } + + bytes_written += writer.writeValueToArray(dt_prop->timestamp); + + return true; +} diff --git a/src/UESaveFile/Serialisers/DateTimePropertySerialiser.h b/src/UESaveFile/Serialisers/DateTimePropertySerialiser.h new file mode 100644 index 0000000..b0e43ac --- /dev/null +++ b/src/UESaveFile/Serialisers/DateTimePropertySerialiser.h @@ -0,0 +1,30 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealPropertySerialiser.h" + +#include "../Types/DateTimeStructProperty.h" + +class DateTimePropertySerialiser : public UnrealPropertySerialiser { + public: + using ptr = Containers::Pointer; + + private: + auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; +}; diff --git a/src/UESaveFile/Serialisers/EnumPropertySerialiser.cpp b/src/UESaveFile/Serialisers/EnumPropertySerialiser.cpp new file mode 100644 index 0000000..cd7364c --- /dev/null +++ b/src/UESaveFile/Serialisers/EnumPropertySerialiser.cpp @@ -0,0 +1,62 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../BinaryReader.h" +#include "../BinaryWriter.h" + +#include "EnumPropertySerialiser.h" + +auto EnumPropertySerialiser::types() -> Containers::ArrayView { + static const Containers::Array types{InPlaceInit, {"EnumProperty"}}; + return types; +} + +auto EnumPropertySerialiser::deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +{ + auto prop = Containers::pointer(); + + if(!reader.readUEString(prop->enumType)) { + return nullptr; + } + + char terminator; + if(!reader.readChar(terminator) || terminator != '\0') { + return nullptr; + } + + if(!reader.readUEString(prop->value)) { + return nullptr; + } + + return prop; +} + +auto EnumPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto enum_prop = dynamic_cast(prop.get()); + + if(!enum_prop) { + return false; + } + + writer.writeUEStringToArray(enum_prop->enumType); + writer.writeValueToArray('\0'); + bytes_written += writer.writeUEStringToArray(enum_prop->value); + + return true; +} diff --git a/src/UESaveFile/Serialisers/EnumPropertySerialiser.h b/src/UESaveFile/Serialisers/EnumPropertySerialiser.h new file mode 100644 index 0000000..ab4335f --- /dev/null +++ b/src/UESaveFile/Serialisers/EnumPropertySerialiser.h @@ -0,0 +1,32 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "AbstractUnrealPropertySerialiser.h" + +#include "../Types/EnumProperty.h" + +class EnumPropertySerialiser : public AbstractUnrealPropertySerialiser { + public: + using ptr = Containers::Pointer; + + auto types() -> Containers::ArrayView override; + + auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + + auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; +}; diff --git a/src/UESaveFile/Serialisers/FloatPropertySerialiser.cpp b/src/UESaveFile/Serialisers/FloatPropertySerialiser.cpp new file mode 100644 index 0000000..c13a72e --- /dev/null +++ b/src/UESaveFile/Serialisers/FloatPropertySerialiser.cpp @@ -0,0 +1,57 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../BinaryReader.h" +#include "../BinaryWriter.h" + +#include "FloatPropertySerialiser.h" + +auto FloatPropertySerialiser::types() -> Containers::ArrayView { + static const Containers::Array types{InPlaceInit, {"FloatProperty"}}; + return types; +} + +auto FloatPropertySerialiser::deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +{ + auto prop = Containers::pointer(); + + char terminator; + if(!reader.readChar(terminator) || terminator != '\0') { + return nullptr; + } + + if(!reader.readFloat(prop->value)) { + return nullptr; + } + + return prop; +} + +auto FloatPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto float_prop = dynamic_cast(prop.get()); + + if(!float_prop) { + return false; + } + + writer.writeValueToArray('\0'); + bytes_written += writer.writeValueToArray(float_prop->value); + + return true; +} diff --git a/src/UESaveFile/Serialisers/FloatPropertySerialiser.h b/src/UESaveFile/Serialisers/FloatPropertySerialiser.h new file mode 100644 index 0000000..c4e45ab --- /dev/null +++ b/src/UESaveFile/Serialisers/FloatPropertySerialiser.h @@ -0,0 +1,32 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "AbstractUnrealPropertySerialiser.h" + +#include "../Types/FloatProperty.h" + +class FloatPropertySerialiser : public AbstractUnrealPropertySerialiser { + public: + using ptr = Containers::Pointer; + + auto types() -> Containers::ArrayView override; + + auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + + auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; +}; diff --git a/src/UESaveFile/Serialisers/GuidPropertySerialiser.cpp b/src/UESaveFile/Serialisers/GuidPropertySerialiser.cpp new file mode 100644 index 0000000..9fe235b --- /dev/null +++ b/src/UESaveFile/Serialisers/GuidPropertySerialiser.cpp @@ -0,0 +1,47 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../BinaryReader.h" +#include "../BinaryWriter.h" + +#include "GuidPropertySerialiser.h" + +auto GuidPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +{ + auto prop = Containers::pointer(); + + if(!reader.readStaticArray(prop->guid)) { + Utility::Error{} << "Couldn't read guid in" << __func__; + return nullptr; + } + + return prop; +} + +auto GuidPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto guid_prop = dynamic_cast(prop.get()); + + if(!guid_prop) { + return false; + } + + bytes_written += writer.writeDataToArray({guid_prop->guid.data(), guid_prop->guid.size()}); + + return true; +} diff --git a/src/UESaveFile/Serialisers/GuidPropertySerialiser.h b/src/UESaveFile/Serialisers/GuidPropertySerialiser.h new file mode 100644 index 0000000..7dd1d67 --- /dev/null +++ b/src/UESaveFile/Serialisers/GuidPropertySerialiser.h @@ -0,0 +1,31 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealPropertySerialiser.h" + +#include "../Types/GuidStructProperty.h" + +class GuidPropertySerialiser : public UnrealPropertySerialiser { + public: + using ptr = Containers::Pointer; + + private: + auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; +}; diff --git a/src/UESaveFile/Serialisers/IntPropertySerialiser.cpp b/src/UESaveFile/Serialisers/IntPropertySerialiser.cpp new file mode 100644 index 0000000..5c70d45 --- /dev/null +++ b/src/UESaveFile/Serialisers/IntPropertySerialiser.cpp @@ -0,0 +1,66 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../BinaryReader.h" +#include "../BinaryWriter.h" + +#include "IntPropertySerialiser.h" + +auto IntPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +{ + auto prop = Containers::pointer(); + + if(value_length == UnsignedLong(-1)) { + if(!reader.readInt(prop->value)) { + return nullptr; + } + + prop->valueLength = UnsignedLong(-1); + return prop; + } + + char terminator; + if(!reader.readChar(terminator) || terminator != '\0') { + return nullptr; + } + + if(!reader.readInt(prop->value)) { + return nullptr; + } + + prop->name.emplace(name); + + return prop; +} + +auto IntPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto int_prop = dynamic_cast(prop.get()); + + if(!int_prop) { + return false; + } + + if(prop->valueLength != UnsignedLong(-1)) { + writer.writeValueToArray('\0'); + } + + bytes_written += writer.writeValueToArray(int_prop->value); + + return true; +} diff --git a/src/UESaveFile/Serialisers/IntPropertySerialiser.h b/src/UESaveFile/Serialisers/IntPropertySerialiser.h new file mode 100644 index 0000000..8274299 --- /dev/null +++ b/src/UESaveFile/Serialisers/IntPropertySerialiser.h @@ -0,0 +1,30 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealPropertySerialiser.h" + +#include "../Types/IntProperty.h" + +class IntPropertySerialiser : public UnrealPropertySerialiser { + public: + using ptr = Containers::Pointer; + + private: + auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; +}; diff --git a/src/UESaveFile/Serialisers/MapPropertySerialiser.cpp b/src/UESaveFile/Serialisers/MapPropertySerialiser.cpp new file mode 100644 index 0000000..27b721d --- /dev/null +++ b/src/UESaveFile/Serialisers/MapPropertySerialiser.cpp @@ -0,0 +1,124 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../BinaryReader.h" +#include "../BinaryWriter.h" +#include "../PropertySerialiser.h" + +#include "../Types/NoneProperty.h" + +#include "MapPropertySerialiser.h" + +auto MapPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +{ + auto prop = Containers::pointer(); + + if(!reader.readUEString(prop->keyType)) { + return nullptr; + } + + if(!reader.readUEString(prop->valueType)) { + return nullptr; + } + + char terminator; + if(!reader.readChar(terminator) || terminator != '\0') { + return nullptr; + } + + UnsignedInt null; + if(!reader.readUnsignedInt(null) || null != 0u) { + return nullptr; + } + + UnsignedInt count; + if(!reader.readUnsignedInt(count)) { + return nullptr; + } + + // Begin dirty code because the MapProperty format doesn't seem to match any of the GVAS reading stuff I've found, + // so I'm just gonna write stuff that matches the only MapProperty I can find in MB's save files. + + arrayReserve(prop->map, count); + + for(UnsignedInt i = 0; i < count; i++) { + MapProperty::KeyValuePair pair; + + if(prop->keyType == "IntProperty") { + pair.key = serialiser.readItem(reader, prop->keyType, -1, name); + if(pair.key == nullptr) { + return nullptr; + } + } + else { // Add other branches depending on key type, should more maps appear in the future. + return nullptr; + } + + UnrealPropertyBase::ptr value_item; + while((value_item = serialiser.read(reader)) != nullptr) { + arrayAppend(pair.values, std::move(value_item)); + + if(pair.values.back()->name == "None" && + pair.values.back()->propertyType == "NoneProperty" && + dynamic_cast(pair.values.back().get()) != nullptr) + { + break; + } + } + + arrayAppend(prop->map, std::move(pair)); + } + + // End dirty code + + return prop; +} + +auto MapPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto map_prop = dynamic_cast(prop.get()); + if(!map_prop) { + return false; + } + + writer.writeUEStringToArray(map_prop->keyType); + writer.writeUEStringToArray(map_prop->valueType); + writer.writeValueToArray('\0'); + + UnsignedLong value_start = writer.arrayPosition(); + writer.writeValueToArray(0u); + + writer.writeValueToArray(UnsignedInt(map_prop->map.size())); + + UnsignedLong dummy_bytes_written = 0; + for(auto& pair : map_prop->map) { + if(!serialiser.writeItem(pair.key, map_prop->keyType, dummy_bytes_written, writer)) { + return false; + } + + for(auto& value : pair.values) { + if(!serialiser.write(value, dummy_bytes_written, writer)) { + return false; + } + } + } + + bytes_written += (writer.arrayPosition() - value_start); + + return true; +} diff --git a/src/UESaveFile/Serialisers/MapPropertySerialiser.h b/src/UESaveFile/Serialisers/MapPropertySerialiser.h new file mode 100644 index 0000000..aba537b --- /dev/null +++ b/src/UESaveFile/Serialisers/MapPropertySerialiser.h @@ -0,0 +1,30 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealPropertySerialiser.h" + +#include "../Types/MapProperty.h" + +class MapPropertySerialiser : public UnrealPropertySerialiser { + public: + using ptr = Containers::Pointer; + + private: + auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; +}; diff --git a/src/UESaveFile/Serialisers/RotatorPropertySerialiser.cpp b/src/UESaveFile/Serialisers/RotatorPropertySerialiser.cpp new file mode 100644 index 0000000..4be77bf --- /dev/null +++ b/src/UESaveFile/Serialisers/RotatorPropertySerialiser.cpp @@ -0,0 +1,47 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../BinaryReader.h" +#include "../BinaryWriter.h" + +#include "RotatorPropertySerialiser.h" + +auto RotatorPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +{ + auto prop = Containers::pointer(); + + if(!reader.readFloat(prop->x) || !reader.readFloat(prop->y) || !reader.readFloat(prop->z)) { + return nullptr; + } + + return prop; +} + +auto RotatorPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto rotator = dynamic_cast(prop.get()); + + if(!rotator) { + return false; + } + + bytes_written += writer.writeValueToArray(rotator->x) + writer.writeValueToArray(rotator->y) + + writer.writeValueToArray(rotator->z); + + return true; +} diff --git a/src/UESaveFile/Serialisers/RotatorPropertySerialiser.h b/src/UESaveFile/Serialisers/RotatorPropertySerialiser.h new file mode 100644 index 0000000..9a7192e --- /dev/null +++ b/src/UESaveFile/Serialisers/RotatorPropertySerialiser.h @@ -0,0 +1,30 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealPropertySerialiser.h" + +#include "../Types/RotatorStructProperty.h" + +class RotatorPropertySerialiser : public UnrealPropertySerialiser { + public: + using ptr = Containers::Pointer; + + private: + auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; +}; diff --git a/src/UESaveFile/Serialisers/SetPropertySerialiser.cpp b/src/UESaveFile/Serialisers/SetPropertySerialiser.cpp new file mode 100644 index 0000000..7b81683 --- /dev/null +++ b/src/UESaveFile/Serialisers/SetPropertySerialiser.cpp @@ -0,0 +1,73 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../BinaryReader.h" +#include "../BinaryWriter.h" +#include "../PropertySerialiser.h" + +#include "SetPropertySerialiser.h" + +auto SetPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +{ + std::string item_type; + if(!reader.readUEString(item_type)) { + return nullptr; + } + + char terminator; + if(!reader.readChar(terminator) || terminator != '\0') { + return nullptr; + } + + UnsignedInt four_bytes; + if(!reader.readUnsignedInt(four_bytes) || four_bytes != 0u) { + return nullptr; + } + + UnsignedInt item_count; + if(!reader.readUnsignedInt(item_count)) { + return nullptr; + } + + auto prop = Containers::pointer(); + prop->itemType = std::move(item_type); + prop->items = serialiser.readSet(reader, prop->itemType, item_count); + + return prop; +} + +auto SetPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto set_prop = dynamic_cast(prop.get()); + if(!set_prop) { + return false; + } + + writer.writeUEStringToArray(set_prop->itemType); + writer.writeValueToArray('\0'); + + bytes_written += writer.writeValueToArray(0u); + bytes_written += writer.writeValueToArray(UnsignedInt(set_prop->items.size())); + + UnsignedLong start_pos = writer.arrayPosition(); + UnsignedLong dummy_bytes_written = 0; + serialiser.writeSet(set_prop->items, set_prop->itemType, dummy_bytes_written, writer); + bytes_written += writer.arrayPosition() - start_pos; + + return true; +} diff --git a/src/UESaveFile/Serialisers/SetPropertySerialiser.h b/src/UESaveFile/Serialisers/SetPropertySerialiser.h new file mode 100644 index 0000000..a19f560 --- /dev/null +++ b/src/UESaveFile/Serialisers/SetPropertySerialiser.h @@ -0,0 +1,30 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealPropertySerialiser.h" + +#include "../Types/SetProperty.h" + +class SetPropertySerialiser : public UnrealPropertySerialiser { + public: + using ptr = Containers::Pointer; + + private: + auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; +}; diff --git a/src/UESaveFile/Serialisers/StringPropertySerialiser.cpp b/src/UESaveFile/Serialisers/StringPropertySerialiser.cpp new file mode 100644 index 0000000..c455fd5 --- /dev/null +++ b/src/UESaveFile/Serialisers/StringPropertySerialiser.cpp @@ -0,0 +1,64 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../BinaryReader.h" +#include "../BinaryWriter.h" + +#include "StringPropertySerialiser.h" + +auto StringPropertySerialiser::types() -> Containers::ArrayView { + static const Containers::Array types{InPlaceInit, {"NameProperty", "StrProperty", "SoftObjectProperty", "ObjectProperty"}}; + return types; +} + +auto StringPropertySerialiser::deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +{ + auto prop = Containers::pointer(type); + + if(value_length != UnsignedLong(-1)) { + char terminator; + if(!reader.readChar(terminator) || terminator != '\0') { + return nullptr; + } + } + + if(!reader.readUEString(prop->value)) { + return nullptr; + } + + prop->valueLength = value_length; + + return prop; +} + +auto StringPropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto str_prop = dynamic_cast(prop.get()); + + if(!str_prop) { + return false; + } + + if(str_prop->valueLength != UnsignedLong(-1)) { + writer.writeValueToArray('\0'); + } + + bytes_written += writer.writeUEStringToArray(str_prop->value); + + return true; +} diff --git a/src/UESaveFile/Serialisers/StringPropertySerialiser.h b/src/UESaveFile/Serialisers/StringPropertySerialiser.h new file mode 100644 index 0000000..0c9e69b --- /dev/null +++ b/src/UESaveFile/Serialisers/StringPropertySerialiser.h @@ -0,0 +1,32 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "AbstractUnrealPropertySerialiser.h" + +#include "../Types/StringProperty.h" + +class StringPropertySerialiser : public AbstractUnrealPropertySerialiser { + public: + using ptr = Containers::Pointer; + + auto types() -> Containers::ArrayView override; + + auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + + auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; +}; diff --git a/src/UESaveFile/Serialisers/StructSerialiser.cpp b/src/UESaveFile/Serialisers/StructSerialiser.cpp new file mode 100644 index 0000000..58d08d4 --- /dev/null +++ b/src/UESaveFile/Serialisers/StructSerialiser.cpp @@ -0,0 +1,210 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../BinaryReader.h" +#include "../BinaryWriter.h" +#include "../PropertySerialiser.h" + +#include "../Types/GenericStructProperty.h" +#include "../Types/NoneProperty.h" + +#include "StructSerialiser.h" + +auto StructSerialiser::types() -> Containers::ArrayView { + static const Containers::Array types{InPlaceInit, {"StructProperty"}}; + return types; +} + +auto StructSerialiser::deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, + UnsignedInt count, BinaryReader& reader, PropertySerialiser& serialiser) -> Containers::Array +{ + std::string item_type; + if(!reader.readUEString(item_type)) { + return nullptr; + } + + Containers::StaticArray<16, char> guid{ValueInit}; + if(!reader.readStaticArray(guid)) { + return nullptr; + } + + char terminator; + if(!reader.readChar(terminator) || terminator != '\0') { + return nullptr; + } + + Containers::Array array; + + if(count == 0) { + auto prop = Containers::pointer(); + prop->structType = std::move(item_type); + prop->structGuid = std::move(guid); + } + else { + for(UnsignedInt i = 0; i < count; i++) { + auto prop = readStructValue(name, item_type, value_length, reader, serialiser); + + if(!prop) { + return nullptr; + } + + prop->structGuid = guid; + + arrayAppend(array, std::move(prop)); + } + } + + return array; +} + +auto StructSerialiser::deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +{ + std::string item_type; + if(!reader.readUEString(item_type)) { + return nullptr; + } + + if(item_type == "None") { + return Containers::pointer(); + } + + Containers::StaticArray<16, char> guid{ValueInit}; + if(!reader.readStaticArray(guid)) { + return nullptr; + } + + char terminator; + if(!reader.readChar(terminator) || terminator != '\0') { + return nullptr; + } + + UnrealPropertyBase::ptr prop = serialiser.readItem(reader, item_type, value_length, name); + + if(!prop) { + prop = readStructValue(name, item_type, value_length, reader, serialiser); + if(prop) { + dynamic_cast(prop.get())->structGuid = std::move(guid); + } + } + + return prop; +} + +auto StructSerialiser::serialise(Containers::ArrayView props, const std::string& item_type, + UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + bytes_written += writer.writeUEStringToArray(*(props.front()->name)); + bytes_written += writer.writeUEStringToArray(item_type); + UnsignedLong vl_pos = writer.arrayPosition(); + bytes_written += writer.writeValueToArray(0ull); + + auto struct_prop = dynamic_cast(props.front().get()); + if(!struct_prop) { + return false; + } + + bytes_written += writer.writeUEStringToArray(struct_prop->structType); + bytes_written += writer.writeDataToArray(arrayView(struct_prop->structGuid)); + bytes_written += writer.writeValueToArray('\0'); + + UnsignedLong vl_start = writer.arrayPosition(); + + UnsignedLong bytes_written_here = 0; + for(auto& prop : props) { + struct_prop = dynamic_cast(prop.get()); + + if(!struct_prop) { + return false; + } + + if(!writeStructValue(struct_prop, bytes_written_here, writer, serialiser)) { + return false; + } + } + + UnsignedLong vl_stop = writer.arrayPosition() - vl_start; + writer.writeValueToArrayAt(vl_stop, vl_pos); + bytes_written += vl_stop; + + return true; +} + +auto StructSerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto struct_prop = dynamic_cast(prop.get()); + + if(!struct_prop) { + return false; + } + + writer.writeUEStringToArray(struct_prop->structType); + writer.writeDataToArray(arrayView(struct_prop->structGuid)); + writer.writeValueToArray('\0'); + + if(!serialiser.writeItem(prop, struct_prop->structType, bytes_written, writer)) { + UnsignedLong dummy_bytes_written = 0; + UnsignedLong vl_start = writer.arrayPosition(); + if(!writeStructValue(struct_prop, dummy_bytes_written, writer, serialiser)) { + return false; + } + bytes_written += writer.arrayPosition() - vl_start; + } + + return true; +} + +auto StructSerialiser::readStructValue(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> StructProperty::ptr +{ + auto st_prop = Containers::pointer(); + st_prop->structType = type; + + UnrealPropertyBase::ptr prop; + while((prop = serialiser.read(reader)) != nullptr) { + arrayAppend(st_prop->properties, std::move(prop)); + + if(st_prop->properties.back()->name == "None" && + st_prop->properties.back()->propertyType == "NoneProperty" && + dynamic_cast(st_prop->properties.back().get()) != nullptr) + { + break; + } + } + + st_prop->name.emplace(name); + + return st_prop; +} + +auto StructSerialiser::writeStructValue(StructProperty* prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto struct_prop = dynamic_cast(prop); + + if(!struct_prop) { + return false; + } + + for(auto& item : struct_prop->properties) { + if(!serialiser.write(item, bytes_written, writer)) { + return false; + } + } + + return true; +} diff --git a/src/UESaveFile/Serialisers/StructSerialiser.h b/src/UESaveFile/Serialisers/StructSerialiser.h new file mode 100644 index 0000000..e219360 --- /dev/null +++ b/src/UESaveFile/Serialisers/StructSerialiser.h @@ -0,0 +1,45 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "AbstractUnrealCollectionPropertySerialiser.h" +#include "AbstractUnrealPropertySerialiser.h" +#include "AbstractUnrealStructSerialiser.h" + +#include "../Types/StructProperty.h" + +class StructSerialiser : public AbstractUnrealPropertySerialiser, public AbstractUnrealCollectionPropertySerialiser { + public: + using ptr = Containers::Pointer; + + auto types() -> Containers::ArrayView override; + + auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, UnsignedInt count, + BinaryReader& reader, PropertySerialiser& serialiser) -> Containers::Array override; + auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + + auto serialise(Containers::ArrayView props, const std::string& item_type, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; + auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; + + private: + auto readStructValue(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> StructProperty::ptr; + auto writeStructValue(StructProperty* prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool; +}; diff --git a/src/UESaveFile/Serialisers/TextPropertySerialiser.cpp b/src/UESaveFile/Serialisers/TextPropertySerialiser.cpp new file mode 100644 index 0000000..72a0f54 --- /dev/null +++ b/src/UESaveFile/Serialisers/TextPropertySerialiser.cpp @@ -0,0 +1,84 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../BinaryReader.h" +#include "../BinaryWriter.h" + +#include "TextPropertySerialiser.h" + +auto TextPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +{ + auto prop = Containers::pointer(); + + Long start_position = reader.position(); + + char terminator; + if(!reader.readChar(terminator) || terminator != '\0') { + return nullptr; + } + + if(reader.peekChar() > 0) { + if(!reader.readArray(prop->flags, 8)) { + return nullptr; + } + } + else { + if(!reader.readArray(prop->flags, 4)) { + return nullptr; + } + } + + if(!reader.readChar(prop->id)) { + return nullptr; + } + + auto interval = reader.position() - start_position; + + do { + std::string str; + + if(!reader.readUEString(str)) { + return nullptr; + } + + arrayAppend(prop->data, std::move(str)); + + interval = reader.position() - start_position; + } while(UnsignedLong(interval) < value_length); + + prop->value = prop->data.back(); + + return prop; +} + +auto TextPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto text_prop = dynamic_cast(prop.get()); + + if(!text_prop) { + return false; + } + + writer.writeValueToArray('\0'); + bytes_written += writer.writeDataToArray(text_prop->flags); + for(const auto& str : text_prop->data) { + bytes_written += writer.writeUEStringToArray(str); + } + + return true; +} diff --git a/src/UESaveFile/Serialisers/TextPropertySerialiser.h b/src/UESaveFile/Serialisers/TextPropertySerialiser.h new file mode 100644 index 0000000..3b99676 --- /dev/null +++ b/src/UESaveFile/Serialisers/TextPropertySerialiser.h @@ -0,0 +1,30 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealPropertySerialiser.h" + +#include "../Types/TextProperty.h" + +class TextPropertySerialiser : public UnrealPropertySerialiser { + public: + using ptr = Containers::Pointer; + + private: + auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; +}; diff --git a/src/UESaveFile/Serialisers/UnrealPropertySerialiser.h b/src/UESaveFile/Serialisers/UnrealPropertySerialiser.h new file mode 100644 index 0000000..0364908 --- /dev/null +++ b/src/UESaveFile/Serialisers/UnrealPropertySerialiser.h @@ -0,0 +1,58 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "AbstractUnrealPropertySerialiser.h" +#include "../Types/StructProperty.h" + +template +class UnrealPropertySerialiser : public AbstractUnrealPropertySerialiser { + static_assert(std::is_base_of::value, "T must be derived from UnrealPropertyBase."); + + public: + using ptr = Containers::Pointer>; + + auto types() -> Containers::ArrayView override { + static const Containers::Array types = []{ + Containers::Array array; + Containers::Pointer p(new T); + if(std::is_base_of::value) { + array = Containers::Array{InPlaceInit, {dynamic_cast(p.get())->structType}}; + } + else { + array = Containers::Array{InPlaceInit, {p->propertyType}}; + } + return array; + }(); + return types; + } + + auto deserialise(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override { + return deserialiseProperty(name, type, value_length, reader, serialiser); + } + + auto serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override { + return serialiseProperty(prop, bytes_written, writer, serialiser); + } + + private: + virtual auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> typename UnrealPropertyBase::ptr = 0; + + virtual auto serialiseProperty(typename UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool = 0; +}; diff --git a/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.cpp b/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.cpp new file mode 100644 index 0000000..f272d6f --- /dev/null +++ b/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.cpp @@ -0,0 +1,46 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../BinaryReader.h" +#include "../BinaryWriter.h" + +#include "Vector2DPropertySerialiser.h" + +auto Vector2DPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +{ + auto prop = Containers::pointer(); + + if(!reader.readFloat(prop->x) || !reader.readFloat(prop->y)) { + return nullptr; + } + + return prop; +} + +auto Vector2DPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto vector = dynamic_cast(prop.get()); + + if(!vector) { + return false; + } + + bytes_written += writer.writeValueToArray(vector->x) + writer.writeValueToArray(vector->y); + + return true; +} diff --git a/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.h b/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.h new file mode 100644 index 0000000..c754bda --- /dev/null +++ b/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.h @@ -0,0 +1,30 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealPropertySerialiser.h" + +#include "../Types/Vector2DStructProperty.h" + +class Vector2DPropertySerialiser : public UnrealPropertySerialiser { + public: + using ptr = Containers::Pointer; + + private: + auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; +}; diff --git a/src/UESaveFile/Serialisers/VectorPropertySerialiser.cpp b/src/UESaveFile/Serialisers/VectorPropertySerialiser.cpp new file mode 100644 index 0000000..feaa803 --- /dev/null +++ b/src/UESaveFile/Serialisers/VectorPropertySerialiser.cpp @@ -0,0 +1,47 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../BinaryReader.h" +#include "../BinaryWriter.h" + +#include "VectorPropertySerialiser.h" + +auto VectorPropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +{ + auto prop = Containers::pointer(); + + if(!reader.readFloat(prop->x) || !reader.readFloat(prop->y) || !reader.readFloat(prop->z)) { + return nullptr; + } + + return prop; +} + +auto VectorPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto vector = dynamic_cast(prop.get()); + + if(!vector) { + return false; + } + + bytes_written += writer.writeValueToArray(vector->x) + writer.writeValueToArray(vector->y) + + writer.writeValueToArray(vector->z); + + return true; +} diff --git a/src/UESaveFile/Serialisers/VectorPropertySerialiser.h b/src/UESaveFile/Serialisers/VectorPropertySerialiser.h new file mode 100644 index 0000000..3afb0dc --- /dev/null +++ b/src/UESaveFile/Serialisers/VectorPropertySerialiser.h @@ -0,0 +1,30 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealPropertySerialiser.h" + +#include "../Types/VectorStructProperty.h" + +class VectorPropertySerialiser : public UnrealPropertySerialiser { + public: + using ptr = Containers::Pointer; + + private: + auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; +}; diff --git a/src/UESaveFile/Types/ArrayProperty.h b/src/UESaveFile/Types/ArrayProperty.h new file mode 100644 index 0000000..b01a9ba --- /dev/null +++ b/src/UESaveFile/Types/ArrayProperty.h @@ -0,0 +1,40 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "UnrealPropertyBase.h" + +struct ArrayProperty : public UnrealPropertyBase { + using ptr = Containers::Pointer; + + ArrayProperty() { + propertyType = "ArrayProperty"; + } + + auto at(std::size_t index) -> UnrealPropertyBase* { + if(index >= items.size()) { + return nullptr; + } + + return items[index].get(); + } + + std::string itemType; + Containers::Array items; +}; diff --git a/src/UESaveFile/Types/BoolProperty.h b/src/UESaveFile/Types/BoolProperty.h new file mode 100644 index 0000000..f80a3b9 --- /dev/null +++ b/src/UESaveFile/Types/BoolProperty.h @@ -0,0 +1,27 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealProperty.h" + +struct BoolProperty : public UnrealProperty { + using ptr = Containers::Pointer; + + BoolProperty() { + propertyType = "BoolProperty"; + } +}; diff --git a/src/UESaveFile/Types/ByteProperty.h b/src/UESaveFile/Types/ByteProperty.h new file mode 100644 index 0000000..8587d2e --- /dev/null +++ b/src/UESaveFile/Types/ByteProperty.h @@ -0,0 +1,33 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "UnrealProperty.h" + +struct ByteProperty : public UnrealProperty> { + using ptr = Containers::Pointer; + + ByteProperty() { + propertyType = "ByteProperty"; + } + + // For some reason, M.A.S.S. Builder stores EnumProperties as ByteProperties. Ugh... + std::string enumType; + std::string enumValue; +}; diff --git a/src/UESaveFile/Types/ColourStructProperty.h b/src/UESaveFile/Types/ColourStructProperty.h new file mode 100644 index 0000000..6df355d --- /dev/null +++ b/src/UESaveFile/Types/ColourStructProperty.h @@ -0,0 +1,27 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "StructProperty.h" + +struct ColourStructProperty : public StructProperty { + using ptr = Containers::Pointer; + ColourStructProperty() { + structType = "LinearColor"; + } + Float r = 0.0f, g = 0.0f, b = 0.0f, a = 0.0f; +}; diff --git a/src/UESaveFile/Types/DateTimeStructProperty.h b/src/UESaveFile/Types/DateTimeStructProperty.h new file mode 100644 index 0000000..86da227 --- /dev/null +++ b/src/UESaveFile/Types/DateTimeStructProperty.h @@ -0,0 +1,29 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "StructProperty.h" + +struct DateTimeStructProperty : public StructProperty { + using ptr = Containers::Pointer; + + DateTimeStructProperty() { + structType = "DateTime"; + } + + UnsignedLong timestamp = 0; +}; diff --git a/src/UESaveFile/Types/EnumProperty.h b/src/UESaveFile/Types/EnumProperty.h new file mode 100644 index 0000000..e9418af --- /dev/null +++ b/src/UESaveFile/Types/EnumProperty.h @@ -0,0 +1,29 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealProperty.h" + +struct EnumProperty : public UnrealProperty { + using ptr = Containers::Pointer; + + EnumProperty() { + propertyType = "EnumProperty"; + } + + std::string enumType; +}; diff --git a/src/UESaveFile/Types/FloatProperty.h b/src/UESaveFile/Types/FloatProperty.h new file mode 100644 index 0000000..1594959 --- /dev/null +++ b/src/UESaveFile/Types/FloatProperty.h @@ -0,0 +1,27 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealProperty.h" + +struct FloatProperty : public UnrealProperty { + using ptr = Containers::Pointer; + + FloatProperty() { + propertyType = "FloatProperty"; + } +}; diff --git a/src/UESaveFile/Types/GenericStructProperty.h b/src/UESaveFile/Types/GenericStructProperty.h new file mode 100644 index 0000000..93b6b88 --- /dev/null +++ b/src/UESaveFile/Types/GenericStructProperty.h @@ -0,0 +1,43 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include + +#include "StructProperty.h" + +struct GenericStructProperty : public StructProperty { + using ptr = Containers::Pointer; + + template + auto at(const std::string& name) -> T* { + for(auto& item : properties) { + if(item->name == name) { + return static_cast(item.get()); + } + } + return nullptr; + } + + auto props() -> Containers::ArrayView { + return properties; + } + + Containers::Array properties; +}; diff --git a/src/UESaveFile/Types/GuidStructProperty.h b/src/UESaveFile/Types/GuidStructProperty.h new file mode 100644 index 0000000..e5a88dc --- /dev/null +++ b/src/UESaveFile/Types/GuidStructProperty.h @@ -0,0 +1,31 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "StructProperty.h" + +struct GuidStructProperty : public StructProperty { + using ptr = Containers::Pointer; + + GuidStructProperty() { + structType = "Guid"; + } + + Containers::StaticArray<16, char> guid{ValueInit}; +}; diff --git a/src/UESaveFile/Types/IntProperty.h b/src/UESaveFile/Types/IntProperty.h new file mode 100644 index 0000000..d03f77b --- /dev/null +++ b/src/UESaveFile/Types/IntProperty.h @@ -0,0 +1,27 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealProperty.h" + +struct IntProperty : public UnrealProperty { + using ptr = Containers::Pointer; + + IntProperty() { + propertyType = "IntProperty"; + } +}; diff --git a/src/UESaveFile/Types/MapProperty.h b/src/UESaveFile/Types/MapProperty.h new file mode 100644 index 0000000..3f8768b --- /dev/null +++ b/src/UESaveFile/Types/MapProperty.h @@ -0,0 +1,37 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealPropertyBase.h" + +struct MapProperty : public UnrealPropertyBase { + using ptr = Containers::Pointer; + + MapProperty() { + propertyType = "MapProperty"; + } + + std::string keyType; + std::string valueType; + + struct KeyValuePair { + UnrealPropertyBase::ptr key; + Containers::Array values; + }; + + Containers::Array map; +}; diff --git a/src/UESaveFile/Types/NoneProperty.h b/src/UESaveFile/Types/NoneProperty.h new file mode 100644 index 0000000..e52ec61 --- /dev/null +++ b/src/UESaveFile/Types/NoneProperty.h @@ -0,0 +1,28 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealPropertyBase.h" + +struct NoneProperty : UnrealPropertyBase { + using ptr = Containers::Pointer; + + NoneProperty() { + name.emplace("None"); + propertyType = "NoneProperty"; + } +}; diff --git a/src/UESaveFile/Types/RotatorStructProperty.h b/src/UESaveFile/Types/RotatorStructProperty.h new file mode 100644 index 0000000..89add8b --- /dev/null +++ b/src/UESaveFile/Types/RotatorStructProperty.h @@ -0,0 +1,29 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "StructProperty.h" + +struct RotatorStructProperty : public StructProperty { + using ptr = Containers::Pointer; + + RotatorStructProperty() { + structType = "Rotator"; + } + + Float x = 0.0f, y = 0.0f, z = 0.0f; +}; diff --git a/src/UESaveFile/Types/SetProperty.h b/src/UESaveFile/Types/SetProperty.h new file mode 100644 index 0000000..b426dc6 --- /dev/null +++ b/src/UESaveFile/Types/SetProperty.h @@ -0,0 +1,40 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "UnrealPropertyBase.h" + +struct SetProperty : public UnrealPropertyBase { + using ptr = Containers::Pointer; + + SetProperty() { + propertyType = "SetProperty"; + } + + auto at(std::size_t index) -> UnrealPropertyBase* { + if(index >= items.size()) { + return nullptr; + } + + return items[index].get(); + } + + std::string itemType; + Containers::Array items; +}; diff --git a/src/UESaveFile/Types/StringProperty.h b/src/UESaveFile/Types/StringProperty.h new file mode 100644 index 0000000..07463ac --- /dev/null +++ b/src/UESaveFile/Types/StringProperty.h @@ -0,0 +1,27 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealProperty.h" + +struct StringProperty : public UnrealProperty { + using ptr = Containers::Pointer; + + explicit StringProperty(const std::string& type = "StrProperty") { + propertyType = type; + } +}; diff --git a/src/UESaveFile/Types/StructProperty.h b/src/UESaveFile/Types/StructProperty.h new file mode 100644 index 0000000..80b9efc --- /dev/null +++ b/src/UESaveFile/Types/StructProperty.h @@ -0,0 +1,32 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include "UnrealPropertyBase.h" + +struct StructProperty : public UnrealPropertyBase { + using ptr = Containers::Pointer; + + StructProperty() { + std::string propertyType = "StructProperty"; + } + + Containers::StaticArray<16, char> structGuid{ValueInit}; + std::string structType; +}; diff --git a/src/UESaveFile/Types/TextProperty.h b/src/UESaveFile/Types/TextProperty.h new file mode 100644 index 0000000..46bd99e --- /dev/null +++ b/src/UESaveFile/Types/TextProperty.h @@ -0,0 +1,31 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealProperty.h" + +struct TextProperty : public UnrealProperty { + using ptr = Containers::Pointer; + + TextProperty() { + propertyType = "TextProperty"; + } + + Containers::Array flags; + char id = 0; + Containers::Array data; +}; diff --git a/src/UESaveFile/Types/UnrealProperty.h b/src/UESaveFile/Types/UnrealProperty.h new file mode 100644 index 0000000..08290fe --- /dev/null +++ b/src/UESaveFile/Types/UnrealProperty.h @@ -0,0 +1,26 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealPropertyBase.h" + +template +struct UnrealProperty : public UnrealPropertyBase { + using ptr = Containers::Pointer>; + + T value; +}; diff --git a/src/UESaveFile/Types/UnrealPropertyBase.h b/src/UESaveFile/Types/UnrealPropertyBase.h new file mode 100644 index 0000000..9165e5f --- /dev/null +++ b/src/UESaveFile/Types/UnrealPropertyBase.h @@ -0,0 +1,37 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include +#include + +#include + +using namespace Corrade; +using namespace Magnum; + +struct UnrealPropertyBase { + using ptr = Containers::Pointer; + + virtual ~UnrealPropertyBase() = default; + + Containers::Optional name = Containers::NullOpt; + std::string propertyType; + UnsignedLong valueLength; +}; diff --git a/src/UESaveFile/Types/Vector2DStructProperty.h b/src/UESaveFile/Types/Vector2DStructProperty.h new file mode 100644 index 0000000..1795b05 --- /dev/null +++ b/src/UESaveFile/Types/Vector2DStructProperty.h @@ -0,0 +1,29 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "StructProperty.h" + +struct Vector2DStructProperty : public StructProperty { + using ptr = Containers::Pointer; + + Vector2DStructProperty() { + structType = "Vector2D"; + } + + Float x = 0.0f, y = 0.0f; +}; diff --git a/src/UESaveFile/Types/VectorStructProperty.h b/src/UESaveFile/Types/VectorStructProperty.h new file mode 100644 index 0000000..529522e --- /dev/null +++ b/src/UESaveFile/Types/VectorStructProperty.h @@ -0,0 +1,29 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "StructProperty.h" + +struct VectorStructProperty : public StructProperty { + using ptr = Containers::Pointer; + + VectorStructProperty() { + structType = "Vector"; + } + + Float x = 0.0f, y = 0.0f, z = 0.0f; +}; diff --git a/src/UESaveFile/UESaveFile.cpp b/src/UESaveFile/UESaveFile.cpp new file mode 100644 index 0000000..e1c4790 --- /dev/null +++ b/src/UESaveFile/UESaveFile.cpp @@ -0,0 +1,189 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include +#include +#include + +#include "BinaryReader.h" +#include "BinaryWriter.h" + +#include "UESaveFile.h" + +UESaveFile::UESaveFile(std::string filepath) +{ + _filepath = std::move(filepath); + + loadData(); +} + +auto UESaveFile::valid() const -> bool { + return _valid; +} + +auto UESaveFile::lastError() const -> const std::string& { + return _lastError; +} + +auto UESaveFile::reloadData() -> bool { + loadData(); + return valid(); +} + +auto UESaveFile::at(const std::string& name) -> UnrealPropertyBase* { + for(auto& prop : _properties) { + if(*(prop->name) == name) { + return prop.get(); + } + } + return nullptr; +} + +auto UESaveFile::props() -> Containers::ArrayView { + return _properties; +} + +auto UESaveFile::saveToFile() -> bool { + BinaryWriter writer{_filepath + ".other"}; + + if(!writer.open()) { + return false; + } + + if(!writer.writeStaticArray(staticArrayView(_magicBytes)) || + !writer.writeUnsignedInt(_saveVersion) || + !writer.writeUnsignedInt(_packageVersion) || + !writer.writeUnsignedShort(_engineVersion.major) || + !writer.writeUnsignedShort(_engineVersion.minor) || + !writer.writeUnsignedShort(_engineVersion.patch) || + !writer.writeUnsignedInt(_engineVersion.build) || + !writer.writeUEString(_engineVersion.buildId)) + { + return false; + } + + if(!writer.writeUnsignedInt(_customFormatVersion) || + !writer.writeUnsignedInt(_customFormatData.size())) + { + return false; + } + + for(UnsignedLong i = 0; i < _customFormatData.size(); i++) { + if(!writer.writeStaticArray(Containers::StaticArrayView<16, const char>{_customFormatData[i].id}) || + !writer.writeUnsignedInt(_customFormatData[i].value)) + { + return false; + } + } + + if(!writer.writeUEString(_saveType)) { + return false; + } + + for(auto& prop : _properties) { + UnsignedLong bytes_written = 0; + if(!_propSerialiser.write(prop, bytes_written, writer)) { + return false; + } + + if(!writer.flushToFile()) { + return false; + } + } + + writer.writeUnsignedInt(0); + + return true; +} + +void UESaveFile::loadData() { + _valid = false; + + if(!Utility::Directory::exists(_filepath)) { + return; + } + + BinaryReader reader{_filepath}; + + if(!reader.open()) { + _lastError = _filepath + " couldn't be opened."; + return; + } + + Containers::Array magic; + if(!reader.readArray(magic, 4)) { + _lastError = "Couldn't read magic bytes in " + _filepath; + return; + } + + std::string invalid = _filepath + " isn't a valid UE4 save."; + + if(std::strncmp(magic.data(), _magicBytes.data(), 4) != 0) { + _lastError = std::move(invalid); + return; + } + + if(!reader.readUnsignedInt(_saveVersion) || + !reader.readUnsignedInt(_packageVersion) || + !reader.readUnsignedShort(_engineVersion.major) || + !reader.readUnsignedShort(_engineVersion.minor) || + !reader.readUnsignedShort(_engineVersion.patch) || + !reader.readUnsignedInt(_engineVersion.build) || + !reader.readUEString(_engineVersion.buildId)) + { + _lastError = std::move(invalid); + return; + } + + if(!reader.readUnsignedInt(_customFormatVersion)) { + _lastError = std::move(invalid); + return; + } + + UnsignedInt custom_format_data_size = 0; + + if(!reader.readUnsignedInt(custom_format_data_size)) { + _lastError = std::move(invalid); + return; + } + + arrayReserve(_customFormatData, custom_format_data_size); + + for(UnsignedInt i = 0; i < custom_format_data_size; i++) { + CustomFormatDataEntry entry; + + if(!reader.readStaticArray(entry.id) || + !reader.readInt(entry.value)) + { + _lastError = std::move(invalid); + return; + } + + arrayAppend(_customFormatData, entry); + } + + if(!reader.readUEString(_saveType)) { + _lastError = std::move(invalid); + return; + } + + UnrealPropertyBase::ptr prop; + while((prop = _propSerialiser.read(reader)) != nullptr) { + arrayAppend(_properties, std::move(prop)); + } + + _valid = true; +} diff --git a/src/UESaveFile/UESaveFile.h b/src/UESaveFile/UESaveFile.h new file mode 100644 index 0000000..88e87cd --- /dev/null +++ b/src/UESaveFile/UESaveFile.h @@ -0,0 +1,81 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include +#include +#include + +#include + +#include "Types/UnrealPropertyBase.h" + +#include "PropertySerialiser.h" + +using namespace Corrade; +using namespace Magnum; + +class UESaveFile { + public: + explicit UESaveFile(std::string filepath); + + auto valid() const -> bool; + auto lastError() const -> std::string const&; + + auto reloadData() -> bool; + + auto at(const std::string& name) -> UnrealPropertyBase*; + + auto props() -> Containers::ArrayView; + + auto saveToFile() -> bool; + + private: + void loadData(); + + bool _valid{false}; + std::string _lastError; + + std::string _filepath; + + const Containers::StaticArray<4, char> _magicBytes{'G', 'V', 'A', 'S'}; + + UnsignedInt _saveVersion = 0; + UnsignedInt _packageVersion = 0; + struct { + UnsignedShort major = 0; + UnsignedShort minor = 0; + UnsignedShort patch = 0; + UnsignedInt build = 0; + std::string buildId; + } _engineVersion; + + UnsignedInt _customFormatVersion = 0; + struct CustomFormatDataEntry { + Containers::StaticArray<16, char> id; + Int value = 0; + }; + Containers::Array _customFormatData; + + std::string _saveType; + + Containers::Array _properties; + + PropertySerialiser _propSerialiser; +}; -- 2.39.2 From ce29d6174ca368ea40c947277950c9fcef2711de Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 22 Sep 2021 18:22:48 +0200 Subject: [PATCH 017/128] UESaveFile: allow the class to be moved. --- src/UESaveFile/UESaveFile.cpp | 14 +++++++------- src/UESaveFile/UESaveFile.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/UESaveFile/UESaveFile.cpp b/src/UESaveFile/UESaveFile.cpp index e1c4790..36cca06 100644 --- a/src/UESaveFile/UESaveFile.cpp +++ b/src/UESaveFile/UESaveFile.cpp @@ -63,13 +63,13 @@ auto UESaveFile::saveToFile() -> bool { return false; } - if(!writer.writeStaticArray(staticArrayView(_magicBytes)) || - !writer.writeUnsignedInt(_saveVersion) || - !writer.writeUnsignedInt(_packageVersion) || - !writer.writeUnsignedShort(_engineVersion.major) || - !writer.writeUnsignedShort(_engineVersion.minor) || - !writer.writeUnsignedShort(_engineVersion.patch) || - !writer.writeUnsignedInt(_engineVersion.build) || + if(!writer.writeArray(arrayView(_magicBytes)) || + !writer.writeUnsignedInt(_saveVersion) || + !writer.writeUnsignedInt(_packageVersion) || + !writer.writeUnsignedShort(_engineVersion.major) || + !writer.writeUnsignedShort(_engineVersion.minor) || + !writer.writeUnsignedShort(_engineVersion.patch) || + !writer.writeUnsignedInt(_engineVersion.build) || !writer.writeUEString(_engineVersion.buildId)) { return false; diff --git a/src/UESaveFile/UESaveFile.h b/src/UESaveFile/UESaveFile.h index 88e87cd..61e49d7 100644 --- a/src/UESaveFile/UESaveFile.h +++ b/src/UESaveFile/UESaveFile.h @@ -54,7 +54,7 @@ class UESaveFile { std::string _filepath; - const Containers::StaticArray<4, char> _magicBytes{'G', 'V', 'A', 'S'}; + Containers::StaticArray<4, char> _magicBytes{'G', 'V', 'A', 'S'}; UnsignedInt _saveVersion = 0; UnsignedInt _packageVersion = 0; -- 2.39.2 From f286ec0633df240ed1096c043e19078ccd8db678 Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 22 Sep 2021 18:23:16 +0200 Subject: [PATCH 018/128] UESaveFile,GenericStructProperty: update at(). --- src/UESaveFile/Types/GenericStructProperty.h | 3 ++- src/UESaveFile/UESaveFile.cpp | 9 --------- src/UESaveFile/UESaveFile.h | 11 ++++++++++- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/UESaveFile/Types/GenericStructProperty.h b/src/UESaveFile/Types/GenericStructProperty.h index 93b6b88..367f6f0 100644 --- a/src/UESaveFile/Types/GenericStructProperty.h +++ b/src/UESaveFile/Types/GenericStructProperty.h @@ -26,7 +26,8 @@ struct GenericStructProperty : public StructProperty { using ptr = Containers::Pointer; template - auto at(const std::string& name) -> T* { + std::enable_if_t::value, T*> + at(const std::string& name) { for(auto& item : properties) { if(item->name == name) { return static_cast(item.get()); diff --git a/src/UESaveFile/UESaveFile.cpp b/src/UESaveFile/UESaveFile.cpp index 36cca06..5b995f2 100644 --- a/src/UESaveFile/UESaveFile.cpp +++ b/src/UESaveFile/UESaveFile.cpp @@ -43,15 +43,6 @@ auto UESaveFile::reloadData() -> bool { return valid(); } -auto UESaveFile::at(const std::string& name) -> UnrealPropertyBase* { - for(auto& prop : _properties) { - if(*(prop->name) == name) { - return prop.get(); - } - } - return nullptr; -} - auto UESaveFile::props() -> Containers::ArrayView { return _properties; } diff --git a/src/UESaveFile/UESaveFile.h b/src/UESaveFile/UESaveFile.h index 61e49d7..fba6e4d 100644 --- a/src/UESaveFile/UESaveFile.h +++ b/src/UESaveFile/UESaveFile.h @@ -40,7 +40,16 @@ class UESaveFile { auto reloadData() -> bool; - auto at(const std::string& name) -> UnrealPropertyBase*; + template + std::enable_if_t::value, T*> + at(const std::string& name) { + for(auto& prop : _properties) { + if(prop->name == name) { + return static_cast(prop.get()); + } + } + return nullptr; + } auto props() -> Containers::ArrayView; -- 2.39.2 From 10becfdc318da4ce8e42909fa5a9eb29a63a5c94 Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 22 Sep 2021 18:25:15 +0200 Subject: [PATCH 019/128] PropertySerialiser: add an explicit cast. --- src/UESaveFile/PropertySerialiser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UESaveFile/PropertySerialiser.cpp b/src/UESaveFile/PropertySerialiser.cpp index 4c64756..cc69f68 100644 --- a/src/UESaveFile/PropertySerialiser.cpp +++ b/src/UESaveFile/PropertySerialiser.cpp @@ -134,7 +134,7 @@ auto PropertySerialiser::readSet(BinaryReader& reader, const std::string& item_t } else { for(UnsignedInt i = 0; i < count; i++) { - auto item = readItem(reader, item_type, -1, ""); + auto item = readItem(reader, item_type, UnsignedLong(-1), ""); arrayAppend(array, std::move(item)); } } -- 2.39.2 From 2e1949ed5dd46d9ed04aeb9bcb6089515ccea183 Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 22 Sep 2021 19:16:33 +0200 Subject: [PATCH 020/128] BytePropertySerialiser: add support for demo props. --- .../Serialisers/BytePropertySerialiser.cpp | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/UESaveFile/Serialisers/BytePropertySerialiser.cpp b/src/UESaveFile/Serialisers/BytePropertySerialiser.cpp index 4cc597f..7bfec24 100644 --- a/src/UESaveFile/Serialisers/BytePropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/BytePropertySerialiser.cpp @@ -29,19 +29,23 @@ auto BytePropertySerialiser::deserialise(const std::string& name, const std::str { auto prop = Containers::pointer(); - if(!reader.readUEString(prop->enumType)) { - return nullptr; - } + if(value_length != UnsignedLong(-1)) { + if(!reader.readUEString(prop->enumType)) { + return nullptr; + } - char terminator; - if(!reader.readChar(terminator) || terminator != '\0') { - return nullptr; + char terminator; + if(!reader.readChar(terminator) || terminator != '\0') { + return nullptr; + } } if(!reader.readUEString(prop->enumValue)) { return nullptr; } + prop->valueLength = value_length; + //UnsignedInt count = 0; //if(!reader.readUnsignedInt(count)) { // return nullptr; @@ -67,8 +71,11 @@ auto BytePropertySerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLo //bytes_written += writer.writeValueToArray(byte_prop->value.size()); //bytes_written += writer.writeDataToArray(byte_prop->value); - writer.writeUEStringToArray(byte_prop->enumType); - writer.writeValueToArray('\0'); + if(byte_prop->valueLength != UnsignedLong(-1)) { + writer.writeUEStringToArray(byte_prop->enumType); + writer.writeValueToArray('\0'); + } + bytes_written += writer.writeUEStringToArray(byte_prop->enumValue); return true; -- 2.39.2 From f500e982e6930afec7011b68683286d54d0114b1 Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 22 Sep 2021 19:35:16 +0200 Subject: [PATCH 021/128] MapPropertySerialiser: add support for demo props. --- .../Serialisers/MapPropertySerialiser.cpp | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/UESaveFile/Serialisers/MapPropertySerialiser.cpp b/src/UESaveFile/Serialisers/MapPropertySerialiser.cpp index 27b721d..9a687fc 100644 --- a/src/UESaveFile/Serialisers/MapPropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/MapPropertySerialiser.cpp @@ -58,7 +58,7 @@ auto MapPropertySerialiser::deserialiseProperty(const std::string& name, const s for(UnsignedInt i = 0; i < count; i++) { MapProperty::KeyValuePair pair; - if(prop->keyType == "IntProperty") { + if(prop->keyType == "IntProperty" || prop->keyType == "StrProperty") { pair.key = serialiser.readItem(reader, prop->keyType, -1, name); if(pair.key == nullptr) { return nullptr; @@ -69,16 +69,25 @@ auto MapPropertySerialiser::deserialiseProperty(const std::string& name, const s } UnrealPropertyBase::ptr value_item; - while((value_item = serialiser.read(reader)) != nullptr) { - arrayAppend(pair.values, std::move(value_item)); + if(prop->valueType == "StructProperty") { + while((value_item = serialiser.read(reader)) != nullptr) { + arrayAppend(pair.values, std::move(value_item)); - if(pair.values.back()->name == "None" && - pair.values.back()->propertyType == "NoneProperty" && - dynamic_cast(pair.values.back().get()) != nullptr) - { - break; + if(pair.values.back()->name == "None" && + pair.values.back()->propertyType == "NoneProperty" && + dynamic_cast(pair.values.back().get()) != nullptr) + { + break; + } } } + else if(prop->valueType == "ByteProperty") { + if((value_item = serialiser.readItem(reader, prop->valueType, -1, name)) == nullptr) { + return nullptr; + } + + arrayAppend(pair.values, std::move(value_item)); + } arrayAppend(prop->map, std::move(pair)); } -- 2.39.2 From 48210c7186c6c070274c8bf279615cff07d9a092 Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 22 Sep 2021 19:46:41 +0200 Subject: [PATCH 022/128] UESaveFile: prevent reloading data on save. --- src/UESaveFile/UESaveFile.cpp | 21 ++++++++++++++++++++- src/UESaveFile/UESaveFile.h | 2 ++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/UESaveFile/UESaveFile.cpp b/src/UESaveFile/UESaveFile.cpp index 5b995f2..a8cf307 100644 --- a/src/UESaveFile/UESaveFile.cpp +++ b/src/UESaveFile/UESaveFile.cpp @@ -39,6 +39,10 @@ auto UESaveFile::lastError() const -> const std::string& { } auto UESaveFile::reloadData() -> bool { + if(_noReloadAfterSave) { + return valid(); + } + loadData(); return valid(); } @@ -48,7 +52,7 @@ auto UESaveFile::props() -> Containers::ArrayView { } auto UESaveFile::saveToFile() -> bool { - BinaryWriter writer{_filepath + ".other"}; + BinaryWriter writer{_filepath + ".tmp"}; if(!writer.open()) { return false; @@ -97,6 +101,21 @@ auto UESaveFile::saveToFile() -> bool { writer.writeUnsignedInt(0); + if(!Utility::Directory::copy(_filepath, _filepath + ".bak")) { + return false; + } + + if(!Utility::Directory::rm(_filepath)) { + return false; + } + + if(!Utility::Directory::move(_filepath + ".tmp", _filepath)) { + Utility::Directory::move(_filepath + ".bak", _filepath); + return false; + } + + _noReloadAfterSave = true; + return true; } diff --git a/src/UESaveFile/UESaveFile.h b/src/UESaveFile/UESaveFile.h index fba6e4d..7dedaa7 100644 --- a/src/UESaveFile/UESaveFile.h +++ b/src/UESaveFile/UESaveFile.h @@ -63,6 +63,8 @@ class UESaveFile { std::string _filepath; + bool _noReloadAfterSave = false; + Containers::StaticArray<4, char> _magicBytes{'G', 'V', 'A', 'S'}; UnsignedInt _saveVersion = 0; -- 2.39.2 From b3220ca8e160b8352ccf9968bf9466d44250709d Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 22 Sep 2021 21:50:08 +0200 Subject: [PATCH 023/128] MapPropertySerialiser: fix serialisation of demo saves. --- src/UESaveFile/Serialisers/MapPropertySerialiser.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/UESaveFile/Serialisers/MapPropertySerialiser.cpp b/src/UESaveFile/Serialisers/MapPropertySerialiser.cpp index 9a687fc..daa6ce9 100644 --- a/src/UESaveFile/Serialisers/MapPropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/MapPropertySerialiser.cpp @@ -121,8 +121,15 @@ auto MapPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, Uns } for(auto& value : pair.values) { - if(!serialiser.write(value, dummy_bytes_written, writer)) { - return false; + if(map_prop->valueType == "StructProperty") { + if(!serialiser.write(value, dummy_bytes_written, writer)) { + return false; + } + } + else { + if(!serialiser.writeItem(value, map_prop->valueType, dummy_bytes_written, writer)) { + return false; + } } } } -- 2.39.2 From 0826d4aedeadd2dfb5c9307de5a2b6599cb6bb15 Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 22 Sep 2021 21:50:39 +0200 Subject: [PATCH 024/128] UESaveFile: close the file after we're done writing to it. --- src/UESaveFile/UESaveFile.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/UESaveFile/UESaveFile.cpp b/src/UESaveFile/UESaveFile.cpp index a8cf307..563dc96 100644 --- a/src/UESaveFile/UESaveFile.cpp +++ b/src/UESaveFile/UESaveFile.cpp @@ -101,6 +101,8 @@ auto UESaveFile::saveToFile() -> bool { writer.writeUnsignedInt(0); + writer.closeFile(); + if(!Utility::Directory::copy(_filepath, _filepath + ".bak")) { return false; } -- 2.39.2 From 0006c90a2109a65d4b94e589f08af32beed9c3b9 Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 23 Sep 2021 15:07:29 +0200 Subject: [PATCH 025/128] UESaveFile: add sanity check when reading files. --- src/UESaveFile/UESaveFile.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/UESaveFile/UESaveFile.cpp b/src/UESaveFile/UESaveFile.cpp index 563dc96..8abd5b1 100644 --- a/src/UESaveFile/UESaveFile.cpp +++ b/src/UESaveFile/UESaveFile.cpp @@ -197,5 +197,9 @@ void UESaveFile::loadData() { arrayAppend(_properties, std::move(prop)); } + if(_properties.back()->name != "None" && _properties.back()->propertyType != "NoneProperty") { + return; + } + _valid = true; } -- 2.39.2 From 9c1aeb753e0b94f0349be4e70c965c98dcccefcc Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 23 Sep 2021 15:08:20 +0200 Subject: [PATCH 026/128] UESaveFile: add API to append a property. --- src/UESaveFile/UESaveFile.cpp | 6 ++++++ src/UESaveFile/UESaveFile.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/UESaveFile/UESaveFile.cpp b/src/UESaveFile/UESaveFile.cpp index 8abd5b1..dd87b35 100644 --- a/src/UESaveFile/UESaveFile.cpp +++ b/src/UESaveFile/UESaveFile.cpp @@ -47,6 +47,12 @@ auto UESaveFile::reloadData() -> bool { return valid(); } +void UESaveFile::appendProperty(UnrealPropertyBase::ptr prop) { + auto none_prop = std::move(_properties.back()); + _properties.back() = std::move(prop); + arrayAppend(_properties, std::move(none_prop)); +} + auto UESaveFile::props() -> Containers::ArrayView { return _properties; } diff --git a/src/UESaveFile/UESaveFile.h b/src/UESaveFile/UESaveFile.h index 7dedaa7..d3b095d 100644 --- a/src/UESaveFile/UESaveFile.h +++ b/src/UESaveFile/UESaveFile.h @@ -51,6 +51,8 @@ class UESaveFile { return nullptr; } + void appendProperty(UnrealPropertyBase::ptr prop); + auto props() -> Containers::ArrayView; auto saveToFile() -> bool; -- 2.39.2 From b8b156a724a7492e54586af61cffd00f6ed23e55 Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 23 Sep 2021 15:09:02 +0200 Subject: [PATCH 027/128] Add a serialiser for struct sttResourceItemValue. --- src/CMakeLists.txt | 3 + src/UESaveFile/PropertySerialiser.cpp | 2 + .../ResourcePropertySerialiser.cpp | 103 ++++++++++++++++++ .../Serialisers/ResourcePropertySerialiser.h | 30 +++++ src/UESaveFile/Types/ResourceItemValue.h | 30 +++++ 5 files changed, 168 insertions(+) create mode 100644 src/UESaveFile/Serialisers/ResourcePropertySerialiser.cpp create mode 100644 src/UESaveFile/Serialisers/ResourcePropertySerialiser.h create mode 100644 src/UESaveFile/Types/ResourceItemValue.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a1c8e24..671640e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -52,6 +52,8 @@ add_library(UESaveFile STATIC EXCLUDE_FROM_ALL UESaveFile/Serialisers/IntPropertySerialiser.cpp UESaveFile/Serialisers/MapPropertySerialiser.h UESaveFile/Serialisers/MapPropertySerialiser.cpp + UESaveFile/Serialisers/ResourcePropertySerialiser.h + UESaveFile/Serialisers/ResourcePropertySerialiser.cpp UESaveFile/Serialisers/RotatorPropertySerialiser.h UESaveFile/Serialisers/RotatorPropertySerialiser.cpp UESaveFile/Serialisers/StringPropertySerialiser.h @@ -84,6 +86,7 @@ add_library(UESaveFile STATIC EXCLUDE_FROM_ALL UESaveFile/Types/SetProperty.h UESaveFile/Types/StringProperty.h UESaveFile/Types/StructProperty.h + UESaveFile/Types/ResourceItemValue.h UESaveFile/Types/TextProperty.h UESaveFile/Types/UnrealProperty.h UESaveFile/Types/UnrealPropertyBase.h diff --git a/src/UESaveFile/PropertySerialiser.cpp b/src/UESaveFile/PropertySerialiser.cpp index cc69f68..cb65cea 100644 --- a/src/UESaveFile/PropertySerialiser.cpp +++ b/src/UESaveFile/PropertySerialiser.cpp @@ -26,6 +26,7 @@ #include "Serialisers/GuidPropertySerialiser.h" #include "Serialisers/IntPropertySerialiser.h" #include "Serialisers/MapPropertySerialiser.h" +#include "Serialisers/ResourcePropertySerialiser.h" #include "Serialisers/RotatorPropertySerialiser.h" #include "Serialisers/StringPropertySerialiser.h" #include "Serialisers/SetPropertySerialiser.h" @@ -53,6 +54,7 @@ PropertySerialiser::PropertySerialiser() { arrayAppend(_serialisers, Containers::pointer()); arrayAppend(_serialisers, Containers::pointer()); arrayAppend(_serialisers, Containers::pointer()); + arrayAppend(_serialisers, Containers::pointer()); arrayAppend(_serialisers, Containers::pointer()); arrayAppend(_serialisers, Containers::pointer()); arrayAppend(_serialisers, Containers::pointer()); diff --git a/src/UESaveFile/Serialisers/ResourcePropertySerialiser.cpp b/src/UESaveFile/Serialisers/ResourcePropertySerialiser.cpp new file mode 100644 index 0000000..a92406f --- /dev/null +++ b/src/UESaveFile/Serialisers/ResourcePropertySerialiser.cpp @@ -0,0 +1,103 @@ +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "../BinaryReader.h" +#include "../BinaryWriter.h" +#include "../PropertySerialiser.h" + +#include "../Types/IntProperty.h" +#include "../Types/NoneProperty.h" + +#include "ResourcePropertySerialiser.h" + +auto ResourcePropertySerialiser::deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, + BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr +{ + auto prop = Containers::pointer(); + + std::string str; + if(!reader.readUEString(str) || str != "ID_4_AAE08F17428E229EC7A2209F51081A21") { + return nullptr; + } + + if(!reader.readUEString(str) || str != "IntProperty") { + return nullptr; + } + + if(!reader.readUnsignedLong(value_length) || value_length != 4ull) { + return nullptr; + } + + char terminator; + if(!reader.readChar(terminator) || terminator != '\0') { + return nullptr; + } + + if(!reader.readInt(prop->id)) { + return nullptr; + } + + if(!reader.readUEString(str) || str != "Quantity_3_560F09B5485C365D3041888910019CE3") { + return nullptr; + } + + if(!reader.readUEString(str) || str != "IntProperty") { + return nullptr; + } + + if(!reader.readUnsignedLong(value_length) || value_length != 4ull) { + return nullptr; + } + + if(!reader.readChar(terminator) || terminator != '\0') { + return nullptr; + } + + if(!reader.readInt(prop->quantity)) { + return nullptr; + } + + if(!reader.readUEString(str) || str != "None") { + return nullptr; + } + + return prop; +} + +auto ResourcePropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, + BinaryWriter& writer, PropertySerialiser& serialiser) -> bool +{ + auto res_prop = dynamic_cast(prop.get()); + if(!res_prop) { + return false; + } + + bytes_written += writer.writeUEStringToArray("ID_4_AAE08F17428E229EC7A2209F51081A21") + + writer.writeUEStringToArray("IntProperty") + + writer.writeValueToArray(4ull) + + writer.writeValueToArray('\0') + + writer.writeValueToArray(res_prop->id); + + bytes_written += writer.writeUEStringToArray("Quantity_3_560F09B5485C365D3041888910019CE3") + + writer.writeUEStringToArray("IntProperty") + + writer.writeValueToArray(4ull) + + writer.writeValueToArray('\0') + + writer.writeValueToArray(res_prop->quantity); + + bytes_written += writer.writeUEStringToArray("None"); + + return true; +} diff --git a/src/UESaveFile/Serialisers/ResourcePropertySerialiser.h b/src/UESaveFile/Serialisers/ResourcePropertySerialiser.h new file mode 100644 index 0000000..b085667 --- /dev/null +++ b/src/UESaveFile/Serialisers/ResourcePropertySerialiser.h @@ -0,0 +1,30 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "UnrealPropertySerialiser.h" + +#include "../Types/ResourceItemValue.h" + +class ResourcePropertySerialiser : public UnrealPropertySerialiser { + public: + using ptr = Containers::Pointer; + + private: + auto deserialiseProperty(const std::string& name, const std::string& type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr override; + auto serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool override; +}; diff --git a/src/UESaveFile/Types/ResourceItemValue.h b/src/UESaveFile/Types/ResourceItemValue.h new file mode 100644 index 0000000..d5f5221 --- /dev/null +++ b/src/UESaveFile/Types/ResourceItemValue.h @@ -0,0 +1,30 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "StructProperty.h" + +struct ResourceItemValue : public StructProperty { + using ptr = Containers::Pointer; + + ResourceItemValue() { + structType = "sttResourceItemValue"; + structGuid = Containers::StaticArray<16, char>{'\xB7', '\xA7', '\x77', '\xAB', '\xD3', '\x1B', '\xA6', '\x43', '\xAF', '\x42', '\xE5', '\x9E', '\xBF', '\xFD', '\x37', '\x55'}; + } + + Int id = 0, quantity = 0; +}; -- 2.39.2 From d3d065c945cbc1abff508b4d7440ca9836b16815 Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 23 Sep 2021 18:24:55 +0200 Subject: [PATCH 028/128] StructSerialiser: fix serialisation of array'd structs. Not all of them are generic structs, after all. --- src/UESaveFile/Serialisers/StructSerialiser.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/UESaveFile/Serialisers/StructSerialiser.cpp b/src/UESaveFile/Serialisers/StructSerialiser.cpp index 58d08d4..8ff7d06 100644 --- a/src/UESaveFile/Serialisers/StructSerialiser.cpp +++ b/src/UESaveFile/Serialisers/StructSerialiser.cpp @@ -55,13 +55,19 @@ auto StructSerialiser::deserialise(const std::string& name, const std::string& t } else { for(UnsignedInt i = 0; i < count; i++) { - auto prop = readStructValue(name, item_type, value_length, reader, serialiser); + auto prop = Containers::pointer(); + + prop = serialiser.readItem(reader, item_type, UnsignedLong(-1), name); + + if(!prop) { + prop = readStructValue(name, item_type, value_length, reader, serialiser); + } if(!prop) { return nullptr; } - prop->structGuid = guid; + static_cast(prop.get())->structGuid = guid; arrayAppend(array, std::move(prop)); } @@ -131,8 +137,10 @@ auto StructSerialiser::serialise(Containers::ArrayView return false; } - if(!writeStructValue(struct_prop, bytes_written_here, writer, serialiser)) { - return false; + if(!serialiser.writeItem(prop, struct_prop->structType, bytes_written_here, writer)) { + if(!writeStructValue(struct_prop, bytes_written_here, writer, serialiser)) { + return false; + } } } -- 2.39.2 From 1caa472833e8a643a287dc8a2eb53a666af937d4 Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 23 Sep 2021 18:25:28 +0200 Subject: [PATCH 029/128] UESaveFile: add more error messages. --- src/UESaveFile/UESaveFile.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/UESaveFile/UESaveFile.cpp b/src/UESaveFile/UESaveFile.cpp index dd87b35..16e6310 100644 --- a/src/UESaveFile/UESaveFile.cpp +++ b/src/UESaveFile/UESaveFile.cpp @@ -61,6 +61,7 @@ auto UESaveFile::saveToFile() -> bool { BinaryWriter writer{_filepath + ".tmp"}; if(!writer.open()) { + _lastError = "Couldn't open the file for saving."; return false; } @@ -73,12 +74,14 @@ auto UESaveFile::saveToFile() -> bool { !writer.writeUnsignedInt(_engineVersion.build) || !writer.writeUEString(_engineVersion.buildId)) { + _lastError = "Couldn't write the header."; return false; } if(!writer.writeUnsignedInt(_customFormatVersion) || !writer.writeUnsignedInt(_customFormatData.size())) { + _lastError = "Couldn't write the header."; return false; } @@ -86,21 +89,25 @@ auto UESaveFile::saveToFile() -> bool { if(!writer.writeStaticArray(Containers::StaticArrayView<16, const char>{_customFormatData[i].id}) || !writer.writeUnsignedInt(_customFormatData[i].value)) { + _lastError = "Couldn't write the header."; return false; } } if(!writer.writeUEString(_saveType)) { + _lastError = "Couldn't write the header."; return false; } for(auto& prop : _properties) { UnsignedLong bytes_written = 0; if(!_propSerialiser.write(prop, bytes_written, writer)) { + _lastError = "Couldn't write the property " + *prop->name + " to the array."; return false; } if(!writer.flushToFile()) { + _lastError = "Couldn't write the property " + *prop->name + " to the file."; return false; } } -- 2.39.2 From 50a7b1d7f0b79a826fb69b30f4efd440443cd1f7 Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 23 Sep 2021 19:01:42 +0200 Subject: [PATCH 030/128] Profile: adapt to UESaveFile. Also change ProfileManager to use growable arrays instead of vectors. --- src/Profile/Profile.cpp | 951 ++++------------------- src/Profile/Profile.h | 114 +-- src/ProfileManager/ProfileManager.cpp | 64 +- src/ProfileManager/ProfileManager.h | 15 +- src/SaveTool/SaveTool_ProfileManager.cpp | 54 +- 5 files changed, 254 insertions(+), 944 deletions(-) diff --git a/src/Profile/Profile.cpp b/src/Profile/Profile.cpp index 46a1819..d5286c2 100644 --- a/src/Profile/Profile.cpp +++ b/src/Profile/Profile.cpp @@ -17,24 +17,25 @@ #include #include +#include #include #include #include +#include "../UESaveFile/Types/ArrayProperty.h" +#include "../UESaveFile/Types/ResourceItemValue.h" +#include "../UESaveFile/Types/IntProperty.h" +#include "../UESaveFile/Types/StringProperty.h" + #include "Profile.h" #include "Locators.h" using namespace Corrade; -Profile::Profile(const std::string& path) { - auto map = Utility::Directory::mapRead(path); - - if(!map) { - _lastError = "Couldn't memory-map " + Utility::Directory::filename(path); - return; - } - +Profile::Profile(const std::string& path): + _profile(path) +{ _profileDirectory = Utility::Directory::path(path); _filename = Utility::Directory::filename(path); @@ -47,16 +48,7 @@ Profile::Profile(const std::string& path) { _steamId = Utility::String::ltrim(Utility::String::rtrim(_filename, ".sav"), (_type == ProfileType::Demo ? "Demo" : "") + std::string{"Profile"}); - auto it = std::search(map.begin(), map.end(), &company_name_locator[0], &company_name_locator[27]); - - if(it == map.end()) { - _lastError = "Couldn't find a company name in " + _filename; - return; - } - - _companyName = std::string{it + 41}; - - _valid = true; + _valid = _profile.valid(); } auto Profile::valid() const -> bool { @@ -80,924 +72,281 @@ auto Profile::steamId() const -> std::string const& { } void Profile::refreshValues() { - getCompanyName(); - getActiveFrameSlot(); - getCredits(); - getStoryProgress(); - getLastMissionId(); - - getVerseSteel(); - getUndinium(); - getNecriumAlloy(); - getLunarite(); - getAsterite(); - - getEdnil(); - getNuflalt(); - getAurelene(); - getSoldus(); - getSynthesizedN(); - - getAlcarbonite(); - getKeriphene(); - getNitinolCM(); - getQuarkium(); - getAlterene(); - - getMixedComposition(); - getVoidResidue(); - getMuscularConstruction(); - getMineralExoskeletology(); - getCarbonizedSkin(); + _profile.reloadData(); } -auto Profile::companyName() const -> std::string const& { - return _companyName; -} - -auto Profile::getCompanyName() -> std::string const& { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto it = std::search(mmap.begin(), mmap.end(), &company_name_locator[0], &company_name_locator[27]); - - if(it == mmap.end()) { - _lastError = "Couldn't find a company name in " + _filename; - _companyName = ""; - } - else { - _companyName = std::string{it + 41}; - } - - return _companyName; +auto Profile::companyName() -> std::string const& { + return _profile.at("CompanyName")->value; } auto Profile::renameCompany(const std::string& new_name) -> bool { - char length_difference = static_cast(_companyName.length() - new_name.length()); + auto name_prop = _profile.at("CompanyName"); - std::string profile_data = Utility::Directory::readString(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(profile_data.begin(), profile_data.end(), &company_name_locator[0], &company_name_locator[27]); - - if(iter != profile_data.end()) { - - *(iter + 0x1C) = *(iter + 0x1C) - length_difference; - *(iter + 0x25) = *(iter + 0x25) - length_difference; - - while(*(iter + 0x29) != '\0') { - profile_data.erase(iter + 0x29); - } - - profile_data.insert(iter + 0x29, new_name.cbegin(), new_name.cend()); - - if(!Utility::Directory::writeString(Utility::Directory::join(_profileDirectory, _filename), profile_data)) { - _lastError = "The file" + _filename + " couldn't be written to."; - return false; - } - - _companyName = new_name; - - return true; - } - else { - _lastError = "Couldn't find the company name in " + _filename; + name_prop->value = new_name; + if(!_profile.saveToFile()) { + _lastError = "Couldn't save the profile."; return false; } + + return true; } -auto Profile::activeFrameSlot() const -> Int { - return _activeFrameSlot; +auto Profile::activeFrameSlot() -> Int { + auto active_frame_slot_prop = _profile.at("ActiveFrameSlot"); + return active_frame_slot_prop ? active_frame_slot_prop->value : 0; } -auto Profile::getActiveFrameSlot() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &active_slot_locator[0], &active_slot_locator[31]); - - if(iter == mmap.end()) { - if(std::search(mmap.begin(), mmap.end(), &credits_locator[0], &credits_locator[22]) != mmap.end()) { - _activeFrameSlot = 0; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _activeFrameSlot = -1; - } - } - else { - _activeFrameSlot = *(iter + 41); - } - - return _activeFrameSlot; -} - -auto Profile::credits() const -> Int { - return _credits; -} - -auto Profile::getCredits() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &credits_locator[0], &credits_locator[22]); - - if(iter != mmap.end()) { - _credits = *reinterpret_cast(iter + 0x20); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _credits = -1; - } - - return _credits; +auto Profile::credits() -> Int { + auto credits_prop = _profile.at("Credit"); + return credits_prop ? credits_prop->value : 0; } auto Profile::setCredits(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + auto credits_prop = _profile.at("Credit"); - auto iter = std::search(mmap.begin(), mmap.end(), &credits_locator[0], &credits_locator[22]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x20) = amount; - _credits = amount; - return true; + if(!credits_prop) { + credits_prop = new IntProperty; + _profile.appendProperty(IntProperty::ptr{credits_prop}); } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + + credits_prop->value = amount; + + if(!_profile.saveToFile()) { + _lastError = "Couldn't save the profile."; return false; } + + return true; } -auto Profile::storyProgress() const -> Int { - return _storyProgress; -} - -auto Profile::getStoryProgress() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &story_progress_locator[0], &story_progress_locator[29]); - - if(iter != mmap.end()) { - _storyProgress = *reinterpret_cast(iter + 0x27); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _storyProgress = -1; - } - - return _storyProgress; +auto Profile::storyProgress() -> Int { + auto story_progress_prop = _profile.at("StoryProgress"); + return story_progress_prop ? story_progress_prop->value : 0; } auto Profile::setStoryProgress(Int progress) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + auto story_progress_prop = _profile.at("StoryProgress"); - auto iter = std::search(mmap.begin(), mmap.end(), &story_progress_locator[0], &story_progress_locator[29]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x27) = progress; - _storyProgress = progress; - return true; + if(!story_progress_prop) { + story_progress_prop = new IntProperty; + _profile.appendProperty(IntProperty::ptr{story_progress_prop}); } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + + story_progress_prop->value = progress; + + if(!_profile.saveToFile()) { + _lastError = "Couldn't save the profile."; return false; } + + return true; } -auto Profile::lastMissionId() const -> Int { - return _lastMissionId; +auto Profile::lastMissionId() -> Int { + auto last_mission_id_prop = _profile.at("LastMissionID"); + return last_mission_id_prop ? last_mission_id_prop->value : 0; } -auto Profile::getLastMissionId() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &last_mission_id_locator[0], &last_mission_id_locator[29]); - - if(iter != mmap.end()) { - _lastMissionId = *reinterpret_cast(iter + 0x27); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _lastMissionId = -1; - } - - return _lastMissionId; -} - -auto Profile::verseSteel() const -> Int { - return _verseSteel; -} - -auto Profile::getVerseSteel() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &verse_steel_locator[0], &verse_steel_locator[129]); - - if(iter != mmap.end()) { - _verseSteel = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _verseSteel = -1; - } - - return _verseSteel; +auto Profile::verseSteel() -> Int { + return resource("ResourceMaterial", 0xC3500); } auto Profile::setVerseSteel(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &verse_steel_locator[0], &verse_steel_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _verseSteel = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceMaterial", 0xC3500, amount); } -auto Profile::undinium() const -> Int { - return _undinium; -} - -auto Profile::getUndinium() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &undinium_locator[0], &undinium_locator[129]); - - if(iter != mmap.end()) { - _undinium = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _undinium = -1; - } - - return _undinium; +auto Profile::undinium() -> Int { + return resource("ResourceMaterial", 0xC3501); } auto Profile::setUndinium(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &undinium_locator[0], &undinium_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _undinium = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceMaterial", 0xC3501, amount); } -auto Profile::necriumAlloy() const -> Int { - return _necriumAlloy; -} - -auto Profile::getNecriumAlloy() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &necrium_alloy_locator[0], &necrium_alloy_locator[129]); - - if(iter != mmap.end()) { - _necriumAlloy = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _necriumAlloy = -1; - } - - return _necriumAlloy; +auto Profile::necriumAlloy() -> Int { + return resource("ResourceMaterial", 0xC3502); } auto Profile::setNecriumAlloy(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &necrium_alloy_locator[0], &necrium_alloy_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _necriumAlloy = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceMaterial", 0xC3502, amount); } -auto Profile::lunarite() const -> Int { - return _lunarite; -} - -auto Profile::getLunarite() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &lunarite_locator[0], &lunarite_locator[129]); - - if(iter != mmap.end()) { - _lunarite = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _lunarite = -1; - } - - return _lunarite; +auto Profile::lunarite() -> Int { + return resource("ResourceMaterial", 0xC3503); } auto Profile::setLunarite(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &lunarite_locator[0], &lunarite_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _lunarite = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceMaterial", 0xC3503, amount); } -auto Profile::asterite() const -> Int { - return _asterite; -} - -auto Profile::getAsterite() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &asterite_locator[0], &asterite_locator[129]); - - if(iter != mmap.end()) { - _asterite = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _asterite = -1; - } - - return _asterite; +auto Profile::asterite() -> Int { + return resource("ResourceMaterial", 0xC3504); } auto Profile::setAsterite(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &asterite_locator[0], &asterite_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _asterite = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceMaterial", 0xC3504, amount); } -auto Profile::ednil() const -> Int { - return _ednil; -} - -auto Profile::getEdnil() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &ednil_locator[0], &ednil_locator[129]); - - if(iter != mmap.end()) { - _ednil = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _ednil = -1; - } - - return _ednil; +auto Profile::ednil() -> Int { + return resource("ResourceMaterial", 0xC350A); } auto Profile::setEdnil(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &ednil_locator[0], &ednil_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _ednil = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceMaterial", 0xC350A, amount); } -auto Profile::nuflalt() const -> Int { - return _nuflalt; -} - -auto Profile::getNuflalt() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &nuflalt_locator[0], &nuflalt_locator[129]); - - if(iter != mmap.end()) { - _nuflalt = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _nuflalt = -1; - } - - return _nuflalt; +auto Profile::nuflalt() -> Int { + return resource("ResourceMaterial", 0xC350B); } auto Profile::setNuflalt(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &nuflalt_locator[0], &nuflalt_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _nuflalt = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceMaterial", 0xC350B, amount); } -auto Profile::aurelene() const -> Int { - return _aurelene; -} - -auto Profile::getAurelene() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &aurelene_locator[0], &aurelene_locator[129]); - - if(iter != mmap.end()) { - _aurelene = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _aurelene = -1; - } - - return _aurelene; +auto Profile::aurelene() -> Int { + return resource("ResourceMaterial", 0xC350C); } auto Profile::setAurelene(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &aurelene_locator[0], &aurelene_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _aurelene = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceMaterial", 0xC350C, amount); } -auto Profile::soldus() const -> Int { - return _soldus; -} - -auto Profile::getSoldus() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &soldus_locator[0], &soldus_locator[129]); - - if(iter != mmap.end()) { - _soldus = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _soldus = -1; - } - - return _soldus; +auto Profile::soldus() -> Int { + return resource("ResourceMaterial", 0xC350D); } auto Profile::setSoldus(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &soldus_locator[0], &soldus_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _soldus = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceMaterial", 0xC350D, amount); } -auto Profile::synthesizedN() const -> Int { - return _synthesizedN; -} - -auto Profile::getSynthesizedN() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &synthesized_n_locator[0], &synthesized_n_locator[129]); - - if(iter != mmap.end()) { - _synthesizedN = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _synthesizedN = -1; - } - - return _synthesizedN; +auto Profile::synthesizedN() -> Int { + return resource("ResourceMaterial", 0xC350E); } auto Profile::setSynthesizedN(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &synthesized_n_locator[0], &synthesized_n_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _synthesizedN = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceMaterial", 0xC350E, amount); } -auto Profile::alcarbonite() const -> Int { - return _alcarbonite; -} - -auto Profile::getAlcarbonite() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &alcarbonite_locator[0], &alcarbonite_locator[129]); - - if(iter != mmap.end()) { - _alcarbonite = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _alcarbonite = -1; - } - - return _alcarbonite; +auto Profile::alcarbonite() -> Int { + return resource("ResourceMaterial", 0xC3514); } auto Profile::setAlcarbonite(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &alcarbonite_locator[0], &alcarbonite_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _alcarbonite = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceMaterial", 0xC3514, amount); } -auto Profile::keriphene() const -> Int { - return _keriphene; -} - -auto Profile::getKeriphene() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &keriphene_locator[0], &keriphene_locator[129]); - - if(iter != mmap.end()) { - _keriphene = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _keriphene= -1; - } - - return _keriphene; +auto Profile::keriphene() -> Int { + return resource("ResourceMaterial", 0xC3515); } auto Profile::setKeriphene(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &keriphene_locator[0], &keriphene_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _keriphene = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceMaterial", 0xC3515, amount); } -auto Profile::nitinolCM() const -> Int { - return _nitinolCM; -} - -auto Profile::getNitinolCM() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &nitinol_cm_locator[0], &nitinol_cm_locator[129]); - - if(iter != mmap.end()) { - _nitinolCM = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _nitinolCM = -1; - } - - return _nitinolCM; +auto Profile::nitinolCM() -> Int { + return resource("ResourceMaterial", 0xC3516); } auto Profile::setNitinolCM(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &nitinol_cm_locator[0], &nitinol_cm_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _nitinolCM = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceMaterial", 0xC3516, amount); } -auto Profile::quarkium() const -> Int { - return _quarkium; -} - -auto Profile::getQuarkium() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &quarkium_locator[0], &quarkium_locator[129]); - - if(iter != mmap.end()) { - _quarkium = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _quarkium = -1; - } - - return _quarkium; +auto Profile::quarkium() -> Int { + return resource("ResourceMaterial", 0xC3517); } auto Profile::setQuarkium(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &quarkium_locator[0], &quarkium_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _quarkium = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceMaterial", 0xC3517, amount); } -auto Profile::alterene() const -> Int { - return _alterene; -} - -auto Profile::getAlterene() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &alterene_locator[0], &alterene_locator[129]); - - if(iter != mmap.end()) { - _alterene = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _alterene = -1; - } - - return _alterene; +auto Profile::alterene() -> Int { + return resource("ResourceMaterial", 0xC3518); } auto Profile::setAlterene(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &alterene_locator[0], &alterene_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _alterene = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceMaterial", 0xC3518, amount); } -auto Profile::mixedComposition() const -> Int { - return _mixedComposition; -} - -auto Profile::getMixedComposition() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &mixed_composition_locator[0], &mixed_composition_locator[129]); - - if(iter != mmap.end()) { - _mixedComposition = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _mixedComposition = -1; - } - - return _mixedComposition; +auto Profile::mixedComposition() -> Int { + return resource("ResourceQuarkData", 0xDBBA0); } auto Profile::setMixedComposition(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &mixed_composition_locator[0], &mixed_composition_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _mixedComposition = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceQuarkData", 0xDBBA0, amount); } -auto Profile::voidResidue() const -> Int { - return _voidResidue; -} - -auto Profile::getVoidResidue() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &void_residue_locator[0], &void_residue_locator[129]); - - if(iter != mmap.end()) { - _voidResidue = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _voidResidue = -1; - } - - return _voidResidue; +auto Profile::voidResidue() -> Int { + return resource("ResourceQuarkData", 0xDBBA1); } auto Profile::setVoidResidue(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &void_residue_locator[0], &void_residue_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _voidResidue = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceQuarkData", 0xDBBA1, amount); } -auto Profile::muscularConstruction() const -> Int { - return _muscularConstruction; -} - -auto Profile::getMuscularConstruction() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &muscular_construction_locator[0], &muscular_construction_locator[129]); - - if(iter != mmap.end()) { - _muscularConstruction = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _muscularConstruction = -1; - } - - return _muscularConstruction; +auto Profile::muscularConstruction() -> Int { + return resource("ResourceQuarkData", 0xDBBA2); } auto Profile::setMuscularConstruction(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &muscular_construction_locator[0], &muscular_construction_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _muscularConstruction = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceQuarkData", 0xDBBA2, amount); } -auto Profile::mineralExoskeletology() const -> Int { - return _mineralExoskeletology; -} - -auto Profile::getMineralExoskeletology() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &mineral_exoskeletology_locator[0], &mineral_exoskeletology_locator[129]); - - if(iter != mmap.end()) { - _mineralExoskeletology = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _mineralExoskeletology = -1; - } - - return _mineralExoskeletology; +auto Profile::mineralExoskeletology() -> Int { + return resource("ResourceQuarkData", 0xDBBA3); } auto Profile::setMineralExoskeletology(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &mineral_exoskeletology_locator[0], &mineral_exoskeletology_locator[129]); - - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _mineralExoskeletology = amount; - return true; - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - return false; - } + return setResource("ResourceQuarkData", 0xDBBA3, amount); } -auto Profile::carbonizedSkin() const -> Int { - return _carbonizedSkin; -} - -auto Profile::getCarbonizedSkin() -> Int { - auto mmap = Utility::Directory::mapRead(Utility::Directory::join(_profileDirectory, _filename)); - - auto iter = std::search(mmap.begin(), mmap.end(), &carbonized_skin_locator[0], &carbonized_skin_locator[129]); - - if(iter != mmap.end()) { - _carbonizedSkin = *reinterpret_cast(iter + 0x8C); - } - else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; - _carbonizedSkin = -1; - } - - return _carbonizedSkin; +auto Profile::carbonizedSkin() -> Int { + return resource("ResourceQuarkData", 0xDBBA4); } auto Profile::setCarbonizedSkin(Int amount) -> bool { - auto mmap = Utility::Directory::map(Utility::Directory::join(_profileDirectory, _filename)); + return setResource("ResourceQuarkData", 0xDBBA4, amount); +} - auto iter = std::search(mmap.begin(), mmap.end(), &carbonized_skin_locator[0], &carbonized_skin_locator[129]); +auto Profile::resource(const char* container, Int id) -> Int { + auto mats_prop = _profile.at(container); - if(iter != mmap.end()) { - *reinterpret_cast(iter + 0x8C) = amount; - _carbonizedSkin = amount; - return true; + static auto predicate = [&id](UnrealPropertyBase::ptr& prop){ + auto res_prop = static_cast(prop.get()); + return res_prop->id == id; + }; + + auto it = std::find_if(mats_prop->items.begin(), mats_prop->items.end(), predicate); + return it != mats_prop->items.end() ? static_cast(it->get())->quantity : 0; +} + +auto Profile::setResource(const char* container, Int id, Int amount) -> bool { + auto mats_prop = _profile.at(container); + + static auto predicate = [&id](UnrealPropertyBase::ptr& prop){ + auto res_prop = static_cast(prop.get()); + return res_prop->id == id; + }; + + auto it = std::find_if(mats_prop->items.begin(), mats_prop->items.end(), predicate); + + ResourceItemValue* res_prop; + if(it == mats_prop->items.end()) { + res_prop = new ResourceItemValue; + res_prop->id = id; + ResourceItemValue::ptr prop{res_prop}; + arrayAppend(mats_prop->items, std::move(prop)); } else { - _lastError = "The profile save seems to be corrupted or the game didn't release the handle on the file."; + res_prop = static_cast(it->get()); + } + + res_prop->quantity = amount; + + if(!_profile.saveToFile()) { + _lastError = _profile.lastError(); return false; } + + return _profile.saveToFile(); } diff --git a/src/Profile/Profile.h b/src/Profile/Profile.h index 63cf5bc..513e817 100644 --- a/src/Profile/Profile.h +++ b/src/Profile/Profile.h @@ -20,6 +20,8 @@ #include +#include "../UESaveFile/UESaveFile.h" + using namespace Magnum; enum class ProfileType : UnsignedByte { @@ -43,144 +45,92 @@ class Profile { void refreshValues(); - auto companyName() const -> std::string const&; - auto getCompanyName() -> std::string const&; + auto companyName() -> std::string const&; auto renameCompany(const std::string& new_name) -> bool; - auto activeFrameSlot() const -> Int; - auto getActiveFrameSlot() -> Int; + auto activeFrameSlot() -> Int; - auto credits() const -> Int; - auto getCredits() -> Int; + auto credits() -> Int; auto setCredits(Int credits) -> bool; - auto storyProgress() const -> Int; - auto getStoryProgress() -> Int; + auto storyProgress() -> Int; auto setStoryProgress(Int progress) -> bool; - auto lastMissionId() const -> Int; - auto getLastMissionId() -> Int; + auto lastMissionId() -> Int; - auto verseSteel() const -> Int; - auto getVerseSteel() -> Int; + auto verseSteel() -> Int; auto setVerseSteel(Int amount) -> bool; - auto undinium() const -> Int; - auto getUndinium() -> Int; + auto undinium() -> Int; auto setUndinium(Int amount) -> bool; - auto necriumAlloy() const -> Int; - auto getNecriumAlloy() -> Int; + auto necriumAlloy() -> Int; auto setNecriumAlloy(Int amount) -> bool; - auto lunarite() const -> Int; - auto getLunarite() -> Int; + auto lunarite() -> Int; auto setLunarite(Int amount) -> bool; - auto asterite() const -> Int; - auto getAsterite() -> Int; + auto asterite() -> Int; auto setAsterite(Int amount) -> bool; - auto ednil() const -> Int; - auto getEdnil() -> Int; + auto ednil() -> Int; auto setEdnil(Int amount) -> bool; - auto nuflalt() const -> Int; - auto getNuflalt() -> Int; + auto nuflalt() -> Int; auto setNuflalt(Int amount) -> bool; - auto aurelene() const -> Int; - auto getAurelene() -> Int; + auto aurelene() -> Int; auto setAurelene(Int amount) -> bool; - auto soldus() const -> Int; - auto getSoldus() -> Int; + auto soldus() -> Int; auto setSoldus(Int amount) -> bool; - auto synthesizedN() const -> Int; - auto getSynthesizedN() -> Int; + auto synthesizedN() -> Int; auto setSynthesizedN(Int amount) -> bool; - auto alcarbonite() const -> Int; - auto getAlcarbonite() -> Int; + auto alcarbonite() -> Int; auto setAlcarbonite(Int amount) -> bool; - auto keriphene() const -> Int; - auto getKeriphene() -> Int; + auto keriphene() -> Int; auto setKeriphene(Int amount) -> bool; - auto nitinolCM() const -> Int; - auto getNitinolCM() -> Int; + auto nitinolCM() -> Int; auto setNitinolCM(Int amount) -> bool; - auto quarkium() const -> Int; - auto getQuarkium() -> Int; + auto quarkium() -> Int; auto setQuarkium(Int amount) -> bool; - auto alterene() const -> Int; - auto getAlterene() -> Int; + auto alterene() -> Int; auto setAlterene(Int amount) -> bool; - auto mixedComposition() const -> Int; - auto getMixedComposition() -> Int; + auto mixedComposition() -> Int; auto setMixedComposition(Int amount) -> bool; - auto voidResidue() const -> Int; - auto getVoidResidue() -> Int; + auto voidResidue() -> Int; auto setVoidResidue(Int amount) -> bool; - auto muscularConstruction() const -> Int; - auto getMuscularConstruction() -> Int; + auto muscularConstruction() -> Int; auto setMuscularConstruction(Int amount) -> bool; - auto mineralExoskeletology() const -> Int; - auto getMineralExoskeletology() -> Int; + auto mineralExoskeletology() -> Int; auto setMineralExoskeletology(Int amount) -> bool; - auto carbonizedSkin() const -> Int; - auto getCarbonizedSkin() -> Int; + auto carbonizedSkin() -> Int; auto setCarbonizedSkin(Int amount) -> bool; private: + auto resource(const char* container, Int id) -> Int; + auto setResource(const char* container, Int id, Int amount) -> bool; + std::string _profileDirectory; std::string _filename; ProfileType _type; + UESaveFile _profile; + std::string _steamId; bool _valid = false; std::string _lastError; - - std::string _companyName; - - Int _activeFrameSlot = 0; - - Int _credits; - - Int _storyProgress; - - Int _lastMissionId; - - Int _verseSteel; - Int _undinium; - Int _necriumAlloy; - Int _lunarite; - Int _asterite; - Int _ednil; - Int _nuflalt; - Int _aurelene; - Int _soldus; - Int _synthesizedN; - Int _alcarbonite; - Int _keriphene; - Int _nitinolCM; - Int _quarkium; - Int _alterene; - - Int _mixedComposition; - Int _voidResidue; - Int _muscularConstruction; - Int _mineralExoskeletology; - Int _carbonizedSkin; }; diff --git a/src/ProfileManager/ProfileManager.cpp b/src/ProfileManager/ProfileManager.cpp index b7124e7..57da93a 100644 --- a/src/ProfileManager/ProfileManager.cpp +++ b/src/ProfileManager/ProfileManager.cpp @@ -31,8 +31,6 @@ #include "ProfileManager.h" -using namespace Corrade; - ProfileManager::ProfileManager(const std::string& save_dir, const std::string& backup_dir): _saveDirectory{save_dir}, _backupsDirectory{backup_dir} @@ -48,12 +46,12 @@ auto ProfileManager::lastError() -> std::string const& { return _lastError; } -auto ProfileManager::profiles() -> std::vector const& { +auto ProfileManager::profiles() -> Containers::ArrayView { return _profiles; } auto ProfileManager::refreshProfiles() -> bool { - _profiles.clear(); + _profiles = Containers::Array{}; using Utility::Directory::Flag; std::vector files = Utility::Directory::list(_saveDirectory, Flag::SkipSpecial|Flag::SkipDirectories|Flag::SkipDotAndDotDot); @@ -74,7 +72,7 @@ auto ProfileManager::refreshProfiles() -> bool { continue; } - _profiles.push_back(std::move(profile)); + arrayAppend(_profiles, std::move(profile)); } if(_profiles.empty()) { @@ -86,14 +84,14 @@ auto ProfileManager::refreshProfiles() -> bool { } auto ProfileManager::getProfile(std::size_t index) -> Profile* { - return &(_profiles.at(index)); + return index <= _profiles.size() ? &(_profiles[index]) : nullptr; } auto ProfileManager::deleteProfile(std::size_t index, bool delete_builds) -> bool { - if(!Utility::Directory::rm(Utility::Directory::join(_saveDirectory, _profiles.at(index).filename()))) { + if(!Utility::Directory::rm(Utility::Directory::join(_saveDirectory, _profiles[index].filename()))) { _lastError = Utility::formatString("Couldn't delete {} (filename: {}).", - _profiles.at(index).companyName(), - _profiles.at(index).filename()); + _profiles[index].companyName(), + _profiles[index].filename()); refreshProfiles(); return false; } @@ -101,13 +99,18 @@ auto ProfileManager::deleteProfile(std::size_t index, bool delete_builds) -> boo if(delete_builds) { for(UnsignedByte i = 0; i < 32; ++i) { std::string filename = Utility::formatString("{}Unit{:.2d}{}.sav", - _profiles.at(index).type() == ProfileType::Demo ? "Demo": "", - i, _profiles.at(index).steamId()); + _profiles[index].type() == ProfileType::Demo ? "Demo": "", + i, _profiles[index].steamId()); Utility::Directory::rm(Utility::Directory::join(_saveDirectory, filename)); } } - _profiles.erase(_profiles.cbegin() + index); + std::string file = _profiles[index].filename(); + auto it = std::remove_if(_profiles.begin(), _profiles.end(), [&file](Profile& profile){return profile.filename() == file;}); + + if(it != _profiles.end()) { + arrayRemoveSuffix(_profiles, 1); + } return true; } @@ -117,7 +120,7 @@ auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> boo std::tm* time = std::localtime(×tamp); std::string filename = Utility::formatString("{}_{}{:.2d}{:.2d}_{:.2d}{:.2d}{:.2d}.mbprofbackup", - Utility::String::replaceAll(_profiles.at(index).companyName(), " ", "_"), + Utility::String::replaceAll(_profiles[index].companyName(), " ", "_"), time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec); @@ -130,21 +133,21 @@ auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> boo return false; } - zip_source_t* profile_source = zip_source_file(zip, Utility::Directory::toNativeSeparators(Utility::Directory::join(_saveDirectory, _profiles.at(index).filename())).c_str(), 0, 0); + zip_source_t* profile_source = zip_source_file(zip, Utility::Directory::toNativeSeparators(Utility::Directory::join(_saveDirectory, _profiles[index].filename())).c_str(), 0, 0); if(profile_source == nullptr) { _lastError = zip_strerror(zip); zip_source_free(profile_source); return false; } - if(zip_file_add(zip, _profiles.at(index).filename().c_str(), profile_source, ZIP_FL_ENC_UTF_8) == -1) { + if(zip_file_add(zip, _profiles[index].filename().c_str(), profile_source, ZIP_FL_ENC_UTF_8) == -1) { _lastError = zip_strerror(zip); zip_source_free(profile_source); return false; } - std::string comment = Utility::String::join({_profiles.at(index).companyName(), - _profiles.at(index).type() == ProfileType::Demo ? "demo" : "full", + std::string comment = Utility::String::join({_profiles[index].companyName(), + _profiles[index].type() == ProfileType::Demo ? "demo" : "full", Utility::formatString("{}-{:.2d}-{:.2d}-{:.2d}-{:.2d}-{:.2d}", time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec) @@ -154,8 +157,8 @@ auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> boo if(backup_builds) { for(UnsignedByte i = 0; i < 32; ++i) { std::string build_filename = Utility::formatString("{}Unit{:.2d}{}.sav", - _profiles.at(index).type() == ProfileType::Demo ? "Demo": "", - i, _profiles.at(index).steamId()); + _profiles[index].type() == ProfileType::Demo ? "Demo": "", + i, _profiles[index].steamId()); if(!Utility::Directory::exists(Utility::Directory::join(_saveDirectory, build_filename))) { continue; @@ -184,12 +187,12 @@ auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> boo return true; } -auto ProfileManager::backups() -> std::vector const& { +auto ProfileManager::backups() -> Containers::ArrayView { return _backups; } void ProfileManager::refreshBackups() { - _backups.clear(); + _backups = Containers::Array{}; using Utility::Directory::Flag; std::vector files = Utility::Directory::list(_backupsDirectory, Flag::SkipSpecial|Flag::SkipDirectories|Flag::SkipDotAndDotDot); @@ -255,29 +258,34 @@ void ProfileManager::refreshBackups() { continue; } - backup.includedFiles.reserve(num_entries); + arrayReserve(backup.includedFiles, num_entries); for(Long i = 0; i < num_entries; i++) { - backup.includedFiles.emplace_back(zip_get_name(zip, i, ZIP_FL_UNCHANGED)); + arrayAppend(backup.includedFiles, InPlaceInit, zip_get_name(zip, i, ZIP_FL_UNCHANGED)); } - _backups.push_back(std::move(backup)); + arrayAppend(_backups, std::move(backup)); } } auto ProfileManager::deleteBackup(std::size_t index) -> bool { - if(!Utility::Directory::rm(Utility::Directory::join(_backupsDirectory, _backups.at(index).filename))) { - _lastError = "Couldn't delete " + _backups.at(index).filename; + if(!Utility::Directory::rm(Utility::Directory::join(_backupsDirectory, _backups[index].filename))) { + _lastError = "Couldn't delete " + _backups[index].filename; return false; } - _backups.erase(_backups.begin() + index); + std::string file = _backups[index].filename; + auto it = std::remove_if(_backups.begin(), _backups.end(), [&file](Backup& backup){return backup.filename == file;}); + + if(it != _backups.end()) { + arrayRemoveSuffix(_backups, 1); + } return true; } auto ProfileManager::restoreBackup(std::size_t index) -> bool { - const Backup& backup = _backups.at(index); + const Backup& backup = _backups[index]; static const char* error_format = "Extraction of file {} failed: {}"; diff --git a/src/ProfileManager/ProfileManager.h b/src/ProfileManager/ProfileManager.h index d1d47b6..aeb91ae 100644 --- a/src/ProfileManager/ProfileManager.h +++ b/src/ProfileManager/ProfileManager.h @@ -17,10 +17,13 @@ // along with this program. If not, see . #include -#include + +#include #include "../Profile/Profile.h" +using namespace Corrade; + struct Backup { std::string filename; std::string company; @@ -33,7 +36,7 @@ struct Backup { int minute; int second; } timestamp; - std::vector includedFiles; + Containers::Array includedFiles; }; class ProfileManager { @@ -43,14 +46,14 @@ class ProfileManager { auto ready() const -> bool; auto lastError() -> std::string const&; - auto profiles() -> std::vector const&; + auto profiles() -> Containers::ArrayView; auto refreshProfiles() -> bool; auto getProfile(std::size_t index) -> Profile*; auto deleteProfile(std::size_t index, bool delete_builds) -> bool; auto backupProfile(std::size_t index, bool backup_builds) -> bool; - auto backups() -> std::vector const&; + auto backups() -> Containers::ArrayView; void refreshBackups(); auto deleteBackup(std::size_t index) -> bool; @@ -63,6 +66,6 @@ class ProfileManager { const std::string& _saveDirectory; const std::string& _backupsDirectory; - std::vector _profiles; - std::vector _backups; + Containers::Array _profiles; + Containers::Array _backups; }; diff --git a/src/SaveTool/SaveTool_ProfileManager.cpp b/src/SaveTool/SaveTool_ProfileManager.cpp index 490d9ee..9bdf20c 100644 --- a/src/SaveTool/SaveTool_ProfileManager.cpp +++ b/src/SaveTool/SaveTool_ProfileManager.cpp @@ -81,7 +81,7 @@ void SaveTool::drawProfileManager() { ImGui::TableSetColumnIndex(0); ImGui::PushID(i); - if(ImGui::Selectable(_profileManager->profiles().at(i).companyName().c_str(), false, + if(ImGui::Selectable(_profileManager->profiles()[i].companyName().c_str(), false, ImGuiSelectableFlags_SpanAllColumns|ImGuiSelectableFlags_AllowItemOverlap)) { _currentProfile = _profileManager->getProfile(i); @@ -90,7 +90,7 @@ void SaveTool::drawProfileManager() { } ImGui::TableSetColumnIndex(1); - ImGui::TextUnformatted(_profileManager->profiles().at(i).type() == ProfileType::Demo ? "Demo" : "Full"); + ImGui::TextUnformatted(_profileManager->profiles()[i].type() == ProfileType::Demo ? "Demo" : "Full"); ImGui::TableSetColumnIndex(2); if(ImGui::SmallButton(ICON_FA_FILE_ARCHIVE)) { @@ -132,13 +132,13 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID { { ImGui::PushTextWrapPos(windowSize().x() * 0.40f); ImGui::Text("Are you sure you want to restore the %s backup from %.4i-%.2i-%.2i %.2i:%.2i:%.2i ? Any existing data will be overwritten.", - _profileManager->backups().at(backup_index).company.c_str(), - _profileManager->backups().at(backup_index).timestamp.year, - _profileManager->backups().at(backup_index).timestamp.month, - _profileManager->backups().at(backup_index).timestamp.day, - _profileManager->backups().at(backup_index).timestamp.hour, - _profileManager->backups().at(backup_index).timestamp.minute, - _profileManager->backups().at(backup_index).timestamp.second); + _profileManager->backups()[backup_index].company.c_str(), + _profileManager->backups()[backup_index].timestamp.year, + _profileManager->backups()[backup_index].timestamp.month, + _profileManager->backups()[backup_index].timestamp.day, + _profileManager->backups()[backup_index].timestamp.hour, + _profileManager->backups()[backup_index].timestamp.minute, + _profileManager->backups()[backup_index].timestamp.second); ImGui::PopTextWrapPos(); if(ImGui::BeginTable("##RestoreBackupLayout", 2)) { @@ -172,13 +172,13 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID { { ImGui::PushTextWrapPos(windowSize().x() * 0.40f); ImGui::Text("Are you sure you want to delete the %s backup from %.4i-%.2i-%.2i %.2i:%.2i:%.2i ? This operation is irreversible.", - _profileManager->backups().at(backup_index).company.c_str(), - _profileManager->backups().at(backup_index).timestamp.year, - _profileManager->backups().at(backup_index).timestamp.month, - _profileManager->backups().at(backup_index).timestamp.day, - _profileManager->backups().at(backup_index).timestamp.hour, - _profileManager->backups().at(backup_index).timestamp.minute, - _profileManager->backups().at(backup_index).timestamp.second); + _profileManager->backups()[backup_index].company.c_str(), + _profileManager->backups()[backup_index].timestamp.year, + _profileManager->backups()[backup_index].timestamp.month, + _profileManager->backups()[backup_index].timestamp.day, + _profileManager->backups()[backup_index].timestamp.hour, + _profileManager->backups()[backup_index].timestamp.minute, + _profileManager->backups()[backup_index].timestamp.second); ImGui::PopTextWrapPos(); if(ImGui::BeginTable("##DeleteBackupLayout", 2)) { @@ -251,10 +251,10 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - ImGui::TextUnformatted(_profileManager->backups().at(i).company.c_str()); + ImGui::TextUnformatted(_profileManager->backups()[i].company.c_str()); if(ImGui::IsItemHovered()) { ImGui::BeginTooltip(); - for(const auto& file : _profileManager->backups().at(i).includedFiles) { + for(const auto& file : _profileManager->backups()[i].includedFiles) { ImGui::TextUnformatted(file.c_str()); } ImGui::EndTooltip(); @@ -262,15 +262,15 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID { ImGui::TableSetColumnIndex(1); ImGui::Text("%.4i-%.2i-%.2i %.2i:%.2i:%.2i", - _profileManager->backups().at(i).timestamp.year, - _profileManager->backups().at(i).timestamp.month, - _profileManager->backups().at(i).timestamp.day, - _profileManager->backups().at(i).timestamp.hour, - _profileManager->backups().at(i).timestamp.minute, - _profileManager->backups().at(i).timestamp.second); + _profileManager->backups()[i].timestamp.year, + _profileManager->backups()[i].timestamp.month, + _profileManager->backups()[i].timestamp.day, + _profileManager->backups()[i].timestamp.hour, + _profileManager->backups()[i].timestamp.minute, + _profileManager->backups()[i].timestamp.second); ImGui::TableSetColumnIndex(2); - ImGui::TextUnformatted(_profileManager->backups().at(i).type == ProfileType::Demo ? "Demo" : "Full"); + ImGui::TextUnformatted(_profileManager->backups()[i].type == ProfileType::Demo ? "Demo" : "Full"); ImGui::TableSetColumnIndex(3); ImGui::PushID(i); @@ -363,8 +363,8 @@ auto SaveTool::drawDeleteProfilePopup(std::size_t profile_index) -> ImGuiID { ImGui::PushTextWrapPos(windowSize().x() * 0.40f); ImGui::Text("Are you sure you want to delete the %s %s profile ? This operation is irreversible.", - _profileManager->profiles().at(profile_index).companyName().c_str(), - _profileManager->profiles().at(profile_index).type() == ProfileType::Demo ? "demo" : "full game"); + _profileManager->profiles()[profile_index].companyName().c_str(), + _profileManager->profiles()[profile_index].type() == ProfileType::Demo ? "demo" : "full game"); ImGui::PopTextWrapPos(); if(ImGui::BeginTable("##DeleteProfileLayout", 2)) { -- 2.39.2 From 76e36791d734ed9d822017138a3207ff56d37e7b Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 23 Sep 2021 19:11:08 +0200 Subject: [PATCH 031/128] Profile: remove Locators.h. It's not needed anymore. --- src/Profile/Locators.h | 94 ----------------------------------------- src/Profile/Profile.cpp | 2 - 2 files changed, 96 deletions(-) delete mode 100644 src/Profile/Locators.h diff --git a/src/Profile/Locators.h b/src/Profile/Locators.h deleted file mode 100644 index 428040e..0000000 --- a/src/Profile/Locators.h +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once - -// MassBuilderSaveTool -// Copyright (C) 2021 Guillaume Jacquemin -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -constexpr char company_name_locator[] = "CompanyName\0\f\0\0\0StrProperty"; -constexpr char active_slot_locator[] = "ActiveFrameSlot\0\f\0\0\0IntProperty"; -constexpr char credits_locator[] = "Credit\0\f\0\0\0IntProperty"; -constexpr char story_progress_locator[] = "StoryProgress\0\f\0\0\0IntProperty"; -constexpr char last_mission_id_locator[] = "LastMissionID\0\f\0\0\0IntProperty"; - -constexpr char verse_steel_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x00\x35\f\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; -constexpr char undinium_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x01\x35\f\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; -constexpr char necrium_alloy_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x02\x35\f\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; -constexpr char lunarite_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x03\x35\f\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; -constexpr char asterite_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x04\x35\f\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; - -constexpr char ednil_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x0a\x35\f\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; -constexpr char nuflalt_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x0b\x35\f\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; -constexpr char aurelene_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\f\x35\f\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; -constexpr char soldus_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x0d\x35\f\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; -constexpr char synthesized_n_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x0e\x35\f\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; - -constexpr char alcarbonite_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x14\x35\f\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; -constexpr char keriphene_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x15\x35\f\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; -constexpr char nitinol_cm_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x16\x35\f\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; -constexpr char quarkium_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x17\x35\f\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; -constexpr char alterene_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\x18\x35\f\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; - -constexpr char mixed_composition_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\xa0\xbb\x0d\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; -constexpr char void_residue_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\xa1\xbb\x0d\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; -constexpr char muscular_construction_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\xa2\xbb\x0d\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; -constexpr char mineral_exoskeletology_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\xa3\xbb\x0d\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; -constexpr char carbonized_skin_locator[] = - "ID_4_AAE08F17428E229EC7A2209F51081A21\0\f\0\0\0IntProperty\0\x04\0\0\0\0\0\0\0\0\xa4\xbb\x0d\0,\0\0\0" - "Quantity_3_560F09B5485C365D3041888910019CE3\0\f\0\0\0IntProperty"; - -constexpr char engine_inventory_locator[] = "InventoryEngine\0\x0e\0\0\0ArrayProperty"; -constexpr char gear_inventory_locator[] = "InventoryGear\0\x0e\0\0\0ArrayProperty"; -constexpr char os_inventory_locator[] = "InventoryOS\0\x0e\0\0\0ArrayProperty"; -constexpr char module_inventory_locator[] = "InventoryModule\0\x0e\0\0\0ArrayProperty"; -constexpr char arch_inventory_locator[] = "InventoryArchitect\0\x0e\0\0\0ArrayProperty"; -constexpr char tech_inventory_locator[] = "InventoryTech\0\x0e\0\0\0ArrayProperty"; diff --git a/src/Profile/Profile.cpp b/src/Profile/Profile.cpp index d5286c2..69ddccc 100644 --- a/src/Profile/Profile.cpp +++ b/src/Profile/Profile.cpp @@ -29,8 +29,6 @@ #include "Profile.h" -#include "Locators.h" - using namespace Corrade; Profile::Profile(const std::string& path): -- 2.39.2 From 79e3193309689d61a2897f9a4747754854ce6633 Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 23 Sep 2021 21:54:59 +0200 Subject: [PATCH 032/128] ArrayProperty: update at() to cast as well. --- src/UESaveFile/Types/ArrayProperty.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/UESaveFile/Types/ArrayProperty.h b/src/UESaveFile/Types/ArrayProperty.h index b01a9ba..f453720 100644 --- a/src/UESaveFile/Types/ArrayProperty.h +++ b/src/UESaveFile/Types/ArrayProperty.h @@ -27,12 +27,14 @@ struct ArrayProperty : public UnrealPropertyBase { propertyType = "ArrayProperty"; } - auto at(std::size_t index) -> UnrealPropertyBase* { + template + std::enable_if_t::value, T*> + at(std::size_t index) { if(index >= items.size()) { return nullptr; } - return items[index].get(); + return static_cast(items[index].get()); } std::string itemType; -- 2.39.2 From 9bc4aaf66b34634019242fe710fcf521296c54a5 Mon Sep 17 00:00:00 2001 From: William JCM Date: Fri, 24 Sep 2021 21:51:06 +0200 Subject: [PATCH 033/128] Profile: add data caching. Querying the properties each frame isn't performant because of all the casts and pointer indirections. --- src/CMakeLists.txt | 1 + src/Profile/Profile.cpp | 203 +++++++++++++++++++++++--------------- src/Profile/Profile.h | 86 ++++++++++------ src/Profile/ResourceIDs.h | 47 +++++++++ 4 files changed, 231 insertions(+), 106 deletions(-) create mode 100644 src/Profile/ResourceIDs.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 671640e..9add043 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -121,6 +121,7 @@ add_executable(MassBuilderSaveTool WIN32 ProfileManager/ProfileManager.cpp Profile/Profile.h Profile/Profile.cpp + Profile/ResourceIDs.h MassManager/MassManager.h MassManager/MassManager.cpp Mass/Locators.h diff --git a/src/Profile/Profile.cpp b/src/Profile/Profile.cpp index 69ddccc..7c850de 100644 --- a/src/Profile/Profile.cpp +++ b/src/Profile/Profile.cpp @@ -27,6 +27,8 @@ #include "../UESaveFile/Types/IntProperty.h" #include "../UESaveFile/Types/StringProperty.h" +#include "ResourceIDs.h" + #include "Profile.h" using namespace Corrade; @@ -70,11 +72,52 @@ auto Profile::steamId() const -> std::string const& { } void Profile::refreshValues() { - _profile.reloadData(); + if(!_profile.reloadData()) { + _lastError = _profile.lastError(); + return; + } + + _name = _profile.at("CompanyName")->value; + + auto prop = _profile.at("ActiveFrameSlot"); + _activeFrameSlot = prop ? prop->value : 0; + + prop = _profile.at("Credit"); + _credits = prop ? prop->value : 0; + + prop = _profile.at("StoryProgress"); + _storyProgress = prop ? prop->value : 0; + + prop = _profile.at("LastMissionID"); + _lastMissionId = prop ? prop->value : 0; + + _verseSteel = getResource("ResourceMaterial", VerseSteel); + _undinium = getResource("ResourceMaterial", Undinium); + _necriumAlloy = getResource("ResourceMaterial", NecriumAlloy); + _lunarite = getResource("ResourceMaterial", Lunarite); + _asterite = getResource("ResourceMaterial", Asterite); + + _ednil = getResource("ResourceMaterial", Ednil); + _nuflalt = getResource("ResourceMaterial", Nuflalt); + _aurelene = getResource("ResourceMaterial", Aurelene); + _soldus = getResource("ResourceMaterial", Soldus); + _synthesisedN = getResource("ResourceMaterial", SynthesisedN); + + _alcarbonite = getResource("ResourceMaterial", Alcarbonite); + _keriphene = getResource("ResourceMaterial", Keriphene); + _nitinolCM = getResource("ResourceMaterial", NitinolCM); + _quarkium = getResource("ResourceMaterial", Quarkium); + _alterene = getResource("ResourceMaterial", Alterene); + + _mixedComposition = getResource("ResourceQuarkData", MixedComposition); + _voidResidue = getResource("ResourceQuarkData", VoidResidue); + _muscularConstruction = getResource("ResourceQuarkData", MuscularConstruction); + _mineralExoskeletology = getResource("ResourceQuarkData", MineralExoskeletology); + _carbonisedSkin = getResource("ResourceQuarkData", CarbonisedSkin); } -auto Profile::companyName() -> std::string const& { - return _profile.at("CompanyName")->value; +auto Profile::companyName() const -> std::string const& { + return _name; } auto Profile::renameCompany(const std::string& new_name) -> bool { @@ -90,14 +133,12 @@ auto Profile::renameCompany(const std::string& new_name) -> bool { return true; } -auto Profile::activeFrameSlot() -> Int { - auto active_frame_slot_prop = _profile.at("ActiveFrameSlot"); - return active_frame_slot_prop ? active_frame_slot_prop->value : 0; +auto Profile::activeFrameSlot() const -> Int { + return _activeFrameSlot; } -auto Profile::credits() -> Int { - auto credits_prop = _profile.at("Credit"); - return credits_prop ? credits_prop->value : 0; +auto Profile::credits() const -> Int { + return _credits; } auto Profile::setCredits(Int amount) -> bool { @@ -118,9 +159,8 @@ auto Profile::setCredits(Int amount) -> bool { return true; } -auto Profile::storyProgress() -> Int { - auto story_progress_prop = _profile.at("StoryProgress"); - return story_progress_prop ? story_progress_prop->value : 0; +auto Profile::storyProgress() const -> Int { + return _storyProgress; } auto Profile::setStoryProgress(Int progress) -> bool { @@ -141,174 +181,177 @@ auto Profile::setStoryProgress(Int progress) -> bool { return true; } -auto Profile::lastMissionId() -> Int { - auto last_mission_id_prop = _profile.at("LastMissionID"); - return last_mission_id_prop ? last_mission_id_prop->value : 0; +auto Profile::lastMissionId() const -> Int { + return _lastMissionId; } -auto Profile::verseSteel() -> Int { - return resource("ResourceMaterial", 0xC3500); +auto Profile::verseSteel() const -> Int { + return _verseSteel; } auto Profile::setVerseSteel(Int amount) -> bool { - return setResource("ResourceMaterial", 0xC3500, amount); + return setResource("ResourceMaterial", VerseSteel, amount); } -auto Profile::undinium() -> Int { - return resource("ResourceMaterial", 0xC3501); +auto Profile::undinium() const -> Int { + return _undinium; } auto Profile::setUndinium(Int amount) -> bool { - return setResource("ResourceMaterial", 0xC3501, amount); + return setResource("ResourceMaterial", Undinium, amount); } -auto Profile::necriumAlloy() -> Int { - return resource("ResourceMaterial", 0xC3502); +auto Profile::necriumAlloy() const -> Int { + return _necriumAlloy; } auto Profile::setNecriumAlloy(Int amount) -> bool { - return setResource("ResourceMaterial", 0xC3502, amount); + return setResource("ResourceMaterial", NecriumAlloy, amount); } -auto Profile::lunarite() -> Int { - return resource("ResourceMaterial", 0xC3503); +auto Profile::lunarite() const -> Int { + return _lunarite; } auto Profile::setLunarite(Int amount) -> bool { - return setResource("ResourceMaterial", 0xC3503, amount); + return setResource("ResourceMaterial", Lunarite, amount); } -auto Profile::asterite() -> Int { - return resource("ResourceMaterial", 0xC3504); +auto Profile::asterite() const -> Int { + return _asterite; } auto Profile::setAsterite(Int amount) -> bool { - return setResource("ResourceMaterial", 0xC3504, amount); + return setResource("ResourceMaterial", Asterite, amount); } -auto Profile::ednil() -> Int { - return resource("ResourceMaterial", 0xC350A); +auto Profile::ednil() const -> Int { + return _ednil; } auto Profile::setEdnil(Int amount) -> bool { - return setResource("ResourceMaterial", 0xC350A, amount); + return setResource("ResourceMaterial", Ednil, amount); } -auto Profile::nuflalt() -> Int { - return resource("ResourceMaterial", 0xC350B); +auto Profile::nuflalt() const -> Int { + return _nuflalt; } auto Profile::setNuflalt(Int amount) -> bool { - return setResource("ResourceMaterial", 0xC350B, amount); + return setResource("ResourceMaterial", Nuflalt, amount); } -auto Profile::aurelene() -> Int { - return resource("ResourceMaterial", 0xC350C); +auto Profile::aurelene() const -> Int { + return _aurelene; } auto Profile::setAurelene(Int amount) -> bool { - return setResource("ResourceMaterial", 0xC350C, amount); + return setResource("ResourceMaterial", Aurelene, amount); } -auto Profile::soldus() -> Int { - return resource("ResourceMaterial", 0xC350D); +auto Profile::soldus() const -> Int { + return _soldus; } auto Profile::setSoldus(Int amount) -> bool { - return setResource("ResourceMaterial", 0xC350D, amount); + return setResource("ResourceMaterial", Soldus, amount); } -auto Profile::synthesizedN() -> Int { - return resource("ResourceMaterial", 0xC350E); +auto Profile::synthesisedN() const -> Int { + return _synthesisedN; } -auto Profile::setSynthesizedN(Int amount) -> bool { - return setResource("ResourceMaterial", 0xC350E, amount); +auto Profile::setSynthesisedN(Int amount) -> bool { + return setResource("ResourceMaterial", SynthesisedN, amount); } -auto Profile::alcarbonite() -> Int { - return resource("ResourceMaterial", 0xC3514); +auto Profile::alcarbonite() const -> Int { + return _alcarbonite; } auto Profile::setAlcarbonite(Int amount) -> bool { - return setResource("ResourceMaterial", 0xC3514, amount); + return setResource("ResourceMaterial", Alcarbonite, amount); } -auto Profile::keriphene() -> Int { - return resource("ResourceMaterial", 0xC3515); +auto Profile::keriphene() const -> Int { + return _keriphene; } auto Profile::setKeriphene(Int amount) -> bool { - return setResource("ResourceMaterial", 0xC3515, amount); + return setResource("ResourceMaterial", Keriphene, amount); } -auto Profile::nitinolCM() -> Int { - return resource("ResourceMaterial", 0xC3516); +auto Profile::nitinolCM() const -> Int { + return _nitinolCM; } auto Profile::setNitinolCM(Int amount) -> bool { - return setResource("ResourceMaterial", 0xC3516, amount); + return setResource("ResourceMaterial", NitinolCM, amount); } -auto Profile::quarkium() -> Int { - return resource("ResourceMaterial", 0xC3517); +auto Profile::quarkium() const -> Int { + return _quarkium; } auto Profile::setQuarkium(Int amount) -> bool { - return setResource("ResourceMaterial", 0xC3517, amount); + return setResource("ResourceMaterial", Quarkium, amount); } -auto Profile::alterene() -> Int { - return resource("ResourceMaterial", 0xC3518); +auto Profile::alterene() const -> Int { + return _alterene; } auto Profile::setAlterene(Int amount) -> bool { - return setResource("ResourceMaterial", 0xC3518, amount); + return setResource("ResourceMaterial", Alterene, amount); } -auto Profile::mixedComposition() -> Int { - return resource("ResourceQuarkData", 0xDBBA0); +auto Profile::mixedComposition() const -> Int { + return _mixedComposition; } auto Profile::setMixedComposition(Int amount) -> bool { - return setResource("ResourceQuarkData", 0xDBBA0, amount); + return setResource("ResourceQuarkData", MixedComposition, amount); } -auto Profile::voidResidue() -> Int { - return resource("ResourceQuarkData", 0xDBBA1); +auto Profile::voidResidue() const -> Int { + return _voidResidue; } auto Profile::setVoidResidue(Int amount) -> bool { - return setResource("ResourceQuarkData", 0xDBBA1, amount); + return setResource("ResourceQuarkData", VoidResidue, amount); } -auto Profile::muscularConstruction() -> Int { - return resource("ResourceQuarkData", 0xDBBA2); +auto Profile::muscularConstruction() const -> Int { + return _muscularConstruction; } auto Profile::setMuscularConstruction(Int amount) -> bool { - return setResource("ResourceQuarkData", 0xDBBA2, amount); + return setResource("ResourceQuarkData", MuscularConstruction, amount); } -auto Profile::mineralExoskeletology() -> Int { - return resource("ResourceQuarkData", 0xDBBA3); +auto Profile::mineralExoskeletology() const -> Int { + return _mineralExoskeletology; } auto Profile::setMineralExoskeletology(Int amount) -> bool { - return setResource("ResourceQuarkData", 0xDBBA3, amount); + return setResource("ResourceQuarkData", MineralExoskeletology, amount); } -auto Profile::carbonizedSkin() -> Int { - return resource("ResourceQuarkData", 0xDBBA4); +auto Profile::carbonisedSkin() const -> Int { + return _carbonisedSkin; } -auto Profile::setCarbonizedSkin(Int amount) -> bool { - return setResource("ResourceQuarkData", 0xDBBA4, amount); +auto Profile::setCarbonisedSkin(Int amount) -> bool { + return setResource("ResourceQuarkData", CarbonisedSkin, amount); } -auto Profile::resource(const char* container, Int id) -> Int { +auto Profile::getResource(const char* container, Int id) -> Int { auto mats_prop = _profile.at(container); + if(!mats_prop) { + return 0; + } + static auto predicate = [&id](UnrealPropertyBase::ptr& prop){ auto res_prop = static_cast(prop.get()); return res_prop->id == id; @@ -321,6 +364,10 @@ auto Profile::resource(const char* container, Int id) -> Int { auto Profile::setResource(const char* container, Int id, Int amount) -> bool { auto mats_prop = _profile.at(container); + if(!mats_prop) { + return false; + } + static auto predicate = [&id](UnrealPropertyBase::ptr& prop){ auto res_prop = static_cast(prop.get()); return res_prop->id == id; diff --git a/src/Profile/Profile.h b/src/Profile/Profile.h index 513e817..3d7061b 100644 --- a/src/Profile/Profile.h +++ b/src/Profile/Profile.h @@ -45,81 +45,81 @@ class Profile { void refreshValues(); - auto companyName() -> std::string const&; + auto companyName() const -> std::string const&; auto renameCompany(const std::string& new_name) -> bool; - auto activeFrameSlot() -> Int; + auto activeFrameSlot() const -> Int; - auto credits() -> Int; + auto credits() const -> Int; auto setCredits(Int credits) -> bool; - auto storyProgress() -> Int; + auto storyProgress() const -> Int; auto setStoryProgress(Int progress) -> bool; - auto lastMissionId() -> Int; + auto lastMissionId() const -> Int; - auto verseSteel() -> Int; + auto verseSteel() const -> Int; auto setVerseSteel(Int amount) -> bool; - auto undinium() -> Int; + auto undinium() const -> Int; auto setUndinium(Int amount) -> bool; - auto necriumAlloy() -> Int; + auto necriumAlloy() const -> Int; auto setNecriumAlloy(Int amount) -> bool; - auto lunarite() -> Int; + auto lunarite() const -> Int; auto setLunarite(Int amount) -> bool; - auto asterite() -> Int; + auto asterite() const -> Int; auto setAsterite(Int amount) -> bool; - auto ednil() -> Int; + auto ednil() const -> Int; auto setEdnil(Int amount) -> bool; - auto nuflalt() -> Int; + auto nuflalt() const -> Int; auto setNuflalt(Int amount) -> bool; - auto aurelene() -> Int; + auto aurelene() const -> Int; auto setAurelene(Int amount) -> bool; - auto soldus() -> Int; + auto soldus() const -> Int; auto setSoldus(Int amount) -> bool; - auto synthesizedN() -> Int; - auto setSynthesizedN(Int amount) -> bool; + auto synthesisedN() const -> Int; + auto setSynthesisedN(Int amount) -> bool; - auto alcarbonite() -> Int; + auto alcarbonite() const -> Int; auto setAlcarbonite(Int amount) -> bool; - auto keriphene() -> Int; + auto keriphene() const -> Int; auto setKeriphene(Int amount) -> bool; - auto nitinolCM() -> Int; + auto nitinolCM() const -> Int; auto setNitinolCM(Int amount) -> bool; - auto quarkium() -> Int; + auto quarkium() const -> Int; auto setQuarkium(Int amount) -> bool; - auto alterene() -> Int; + auto alterene() const -> Int; auto setAlterene(Int amount) -> bool; - auto mixedComposition() -> Int; + auto mixedComposition() const -> Int; auto setMixedComposition(Int amount) -> bool; - auto voidResidue() -> Int; + auto voidResidue() const -> Int; auto setVoidResidue(Int amount) -> bool; - auto muscularConstruction() -> Int; + auto muscularConstruction() const -> Int; auto setMuscularConstruction(Int amount) -> bool; - auto mineralExoskeletology() -> Int; + auto mineralExoskeletology() const -> Int; auto setMineralExoskeletology(Int amount) -> bool; - auto carbonizedSkin() -> Int; - auto setCarbonizedSkin(Int amount) -> bool; + auto carbonisedSkin() const -> Int; + auto setCarbonisedSkin(Int amount) -> bool; private: - auto resource(const char* container, Int id) -> Int; + auto getResource(const char* container, Int id) -> Int; auto setResource(const char* container, Int id, Int amount) -> bool; std::string _profileDirectory; @@ -129,6 +129,36 @@ class Profile { UESaveFile _profile; + std::string _name; + Int _activeFrameSlot; + Int _credits; + Int _storyProgress; + Int _lastMissionId; + + Int _verseSteel = 0; + Int _undinium = 0; + Int _necriumAlloy = 0; + Int _lunarite = 0; + Int _asterite = 0; + + Int _ednil = 0; + Int _nuflalt = 0; + Int _aurelene = 0; + Int _soldus = 0; + Int _synthesisedN = 0; + + Int _alcarbonite = 0; + Int _keriphene = 0; + Int _nitinolCM = 0; + Int _quarkium = 0; + Int _alterene = 0; + + Int _mixedComposition = 0; + Int _voidResidue = 0; + Int _muscularConstruction = 0; + Int _mineralExoskeletology = 0; + Int _carbonisedSkin = 0; + std::string _steamId; bool _valid = false; diff --git a/src/Profile/ResourceIDs.h b/src/Profile/ResourceIDs.h new file mode 100644 index 0000000..085bf40 --- /dev/null +++ b/src/Profile/ResourceIDs.h @@ -0,0 +1,47 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +using namespace Magnum; + +enum MaterialID : Int { + VerseSteel = 0xC3500, + Undinium = 0xC3501, + NecriumAlloy = 0xC3502, + Lunarite = 0xC3503, + Asterite = 0xC3504, + + Ednil = 0xC350A, + Nuflalt = 0xC350B, + Aurelene = 0xC350C, + Soldus = 0xC350D, + SynthesisedN = 0xC350E, + + Alcarbonite = 0xC3514, + Keriphene = 0xC3515, + NitinolCM = 0xC3516, + Quarkium = 0xC3517, + Alterene = 0xC3518, + + MixedComposition = 0xDBBA0, + VoidResidue = 0xDBBA1, + MuscularConstruction = 0xDBBA2, + MineralExoskeletology = 0xDBBA3, + CarbonisedSkin = 0xDBBA4 +}; -- 2.39.2 From 0c257bcfa66894d9014f5c30c1d4ebba1aaf3963 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 25 Sep 2021 15:46:01 +0200 Subject: [PATCH 034/128] GenericStructProperty: remove a redundant function. --- src/UESaveFile/Types/GenericStructProperty.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/UESaveFile/Types/GenericStructProperty.h b/src/UESaveFile/Types/GenericStructProperty.h index 367f6f0..2e5b8bb 100644 --- a/src/UESaveFile/Types/GenericStructProperty.h +++ b/src/UESaveFile/Types/GenericStructProperty.h @@ -36,9 +36,5 @@ struct GenericStructProperty : public StructProperty { return nullptr; } - auto props() -> Containers::ArrayView { - return properties; - } - Containers::Array properties; }; -- 2.39.2 From 911e18fc0a7cba96988abe8ea96e188fc39ab170 Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 27 Sep 2021 16:16:47 +0200 Subject: [PATCH 035/128] UESaveFile: add a few sanity things. --- src/UESaveFile/UESaveFile.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/UESaveFile/UESaveFile.cpp b/src/UESaveFile/UESaveFile.cpp index 16e6310..c8bdd93 100644 --- a/src/UESaveFile/UESaveFile.cpp +++ b/src/UESaveFile/UESaveFile.cpp @@ -40,6 +40,7 @@ auto UESaveFile::lastError() const -> const std::string& { auto UESaveFile::reloadData() -> bool { if(_noReloadAfterSave) { + _noReloadAfterSave = false; return valid(); } @@ -211,8 +212,11 @@ void UESaveFile::loadData() { } if(_properties.back()->name != "None" && _properties.back()->propertyType != "NoneProperty") { + _lastError = "Couldn't find a final NoneProperty."; return; } + reader.closeFile(); + _valid = true; } -- 2.39.2 From bd255ef8d5757332b2a9aa4fc25801dfa3178445 Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 27 Sep 2021 16:17:32 +0200 Subject: [PATCH 036/128] Profile: ensure (in)validity. --- src/Profile/Profile.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Profile/Profile.cpp b/src/Profile/Profile.cpp index 7c850de..024be15 100644 --- a/src/Profile/Profile.cpp +++ b/src/Profile/Profile.cpp @@ -48,6 +48,8 @@ Profile::Profile(const std::string& path): _steamId = Utility::String::ltrim(Utility::String::rtrim(_filename, ".sav"), (_type == ProfileType::Demo ? "Demo" : "") + std::string{"Profile"}); + refreshValues(); + _valid = _profile.valid(); } @@ -74,6 +76,7 @@ auto Profile::steamId() const -> std::string const& { void Profile::refreshValues() { if(!_profile.reloadData()) { _lastError = _profile.lastError(); + _valid = false; return; } -- 2.39.2 From 10368e09dbaad5a3f9e6acf4e756114a3436661c Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 27 Sep 2021 16:18:03 +0200 Subject: [PATCH 037/128] Profile: add default values for some members. --- src/Profile/Profile.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Profile/Profile.h b/src/Profile/Profile.h index 3d7061b..1759e36 100644 --- a/src/Profile/Profile.h +++ b/src/Profile/Profile.h @@ -130,10 +130,10 @@ class Profile { UESaveFile _profile; std::string _name; - Int _activeFrameSlot; - Int _credits; - Int _storyProgress; - Int _lastMissionId; + Int _activeFrameSlot = 0; + Int _credits = 0; + Int _storyProgress = 0; + Int _lastMissionId = 0; Int _verseSteel = 0; Int _undinium = 0; -- 2.39.2 From 2b2320ae0ada3cf61340a8aa1a53632cad4442c4 Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 27 Sep 2021 16:21:39 +0200 Subject: [PATCH 038/128] UESaveFile: clear properties when reloading data. --- src/UESaveFile/UESaveFile.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/UESaveFile/UESaveFile.cpp b/src/UESaveFile/UESaveFile.cpp index c8bdd93..e696a95 100644 --- a/src/UESaveFile/UESaveFile.cpp +++ b/src/UESaveFile/UESaveFile.cpp @@ -44,6 +44,7 @@ auto UESaveFile::reloadData() -> bool { return valid(); } + _properties = Containers::Array{}; loadData(); return valid(); } -- 2.39.2 From c2d0fbd9413aba69798eaa4939af9a691012f31c Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 27 Sep 2021 17:52:47 +0200 Subject: [PATCH 039/128] Mass: adapt to UESaveFile. --- src/CMakeLists.txt | 1 - src/Mass/Locators.h | 31 --- src/Mass/Mass.cpp | 525 +++++++++++++++++++++++++++----------------- src/Mass/Mass.h | 134 ++++++++++- 4 files changed, 443 insertions(+), 248 deletions(-) delete mode 100644 src/Mass/Locators.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9add043..ddee28b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -124,7 +124,6 @@ add_executable(MassBuilderSaveTool WIN32 Profile/ResourceIDs.h MassManager/MassManager.h MassManager/MassManager.cpp - Mass/Locators.h Mass/Mass.h Mass/Mass.cpp Maps/LastMissionId.h diff --git a/src/Mass/Locators.h b/src/Mass/Locators.h deleted file mode 100644 index 9eadd48..0000000 --- a/src/Mass/Locators.h +++ /dev/null @@ -1,31 +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 . - -constexpr char mass_name_locator[] = "Name_45_A037C5D54E53456407BDF091344529BB\0\f\0\0\0StrProperty"; -constexpr char steamid_locator[] = "Account\0\f\0\0\0StrProperty"; - -constexpr char neck_slider_locator[] = "NeckLength_6_ED6AF79849C27CD1A9D523A09E2BFE58\0\x0e\0\0\0FloatProperty"; -constexpr char body_slider_locator[] = "BodyLength_7_C16287754CBA96C93BAE36A5C154996A\0\x0e\0\0\0FloatProperty"; -constexpr char shoulders_slider_locator[] = "ShoulderLength_8_220EDF304F1C1226F0D8D39117FB3883\0\x0e\0\0\0FloatProperty"; -constexpr char hips_slider_locator[] = "HipLength_14_02AEEEAC4376087B9C51F0AA7CC92818\0\x0e\0\0\0FloatProperty"; -constexpr char uarms_slider_locator[] = "ArmUpperLength_10_249FDA3E4F3B399E7B9E5C9B7C765EAE\0\x0e\0\0\0FloatProperty"; -constexpr char larms_slider_locator[] = "ArmLowerLength_12_ACD0F02745C28882619376926292FB36\0\x0e\0\0\0FloatProperty"; -constexpr char ulegs_slider_locator[] = "LegUpperLength_16_A7C4C71249A3776F7A543D96819C0C61\0\x0e\0\0\0FloatProperty"; -constexpr char llegs_slider_locator[] = "LegLowerLength_18_D2DF39964EA0F2A2129D0491B08A032F\0\x0e\0\0\0FloatProperty"; - -constexpr char frame_styles_locator[] = "Styles_32_00A3B3284B37F1E7819458844A20EB48\0\x0e\0\0\0ArrayProperty\0\x14\0\0\0\0\0\0\0\f\0\0\0IntProperty\0\0\x04\0\0\0"; diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 263dd5d..58309e1 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -21,7 +21,12 @@ #include #include -#include "Locators.h" +#include "../UESaveFile/Types/ArrayProperty.h" +#include "../UESaveFile/Types/ColourStructProperty.h" +#include "../UESaveFile/Types/FloatProperty.h" +#include "../UESaveFile/Types/GenericStructProperty.h" +#include "../UESaveFile/Types/IntProperty.h" +#include "../UESaveFile/Types/StringProperty.h" #include "Mass.h" @@ -38,256 +43,362 @@ auto Mass::lastError() -> std::string const& { return _lastError; } -auto Mass::getNameFromFile(const std::string& path) -> std::string { +auto Mass::getNameFromFile(const std::string& path) -> Containers::Optional { if(!Utility::Directory::exists(path)) { _lastError = path + " couldn't be found."; - return ""; + return Containers::NullOpt; } - std::string name; + UESaveFile mass{path}; - auto mmap = Utility::Directory::mapRead(path); - - auto iter = std::search(mmap.begin(), mmap.end(), &mass_name_locator[0], &mass_name_locator[56]); - - if(iter != mmap.end()) { - name = std::string{iter + 70}; - } - else { - _lastError = "The name couldn't be found in " + path; + if(!mass.valid()) { + _lastError = "The unit file seems to be corrupt."; + return Containers::NullOpt; } - return name; + auto unit_data = mass.at("UnitData"); + + if(!unit_data) { + _lastError = "Couldn't find unit data in the file."; + return Containers::NullOpt; + } + + auto name_prop = unit_data->at("Name_45_A037C5D54E53456407BDF091344529BB"); + + if(!name_prop) { + _lastError = "Couldn't find the name in the file."; + return Containers::NullOpt; + } + + return name_prop->value; } void Mass::refreshValues() { - getName(); - - if(_state != State::Valid) { + if(!Utility::Directory::exists(Utility::Directory::join(_folder, _filename))) { + _state = State::Empty; return; } - getFrameStyles(); - getJointSliders(); + if(!_mass) { + _mass.emplace(Utility::Directory::join(_folder, _filename)); + if(!_mass->valid()) { + _state = State::Invalid; + return; + } + } + else { + if(!_mass->reloadData()) { + _state = State::Invalid; + return; + } + } + + auto unit_data = _mass->at("UnitData"); + + if(!unit_data) { + _state = State::Invalid; + return; + } + + auto name_prop = unit_data->at("Name_45_A037C5D54E53456407BDF091344529BB"); + + if(!name_prop) { + _name = Containers::NullOpt; + _state = State::Invalid; + return; + } + + _name = name_prop->value; + + { + auto frame_prop = unit_data->at("Frame_3_F92B0F6A44A15088AF7F41B9FF290653"); + + if(!frame_prop) { + _state = State::Invalid; + return; + } + + auto length = frame_prop->at("NeckLength_6_ED6AF79849C27CD1A9D523A09E2BFE58"); + _frame.joints.neck = (length ? length->value : 0.0f); + length = frame_prop->at("BodyLength_7_C16287754CBA96C93BAE36A5C154996A"); + _frame.joints.body = (length ? length->value : 0.0f); + length = frame_prop->at("ShoulderLength_8_220EDF304F1C1226F0D8D39117FB3883"); + _frame.joints.shoulders = (length ? length->value : 0.0f); + length = frame_prop->at("HipLength_14_02AEEEAC4376087B9C51F0AA7CC92818"); + _frame.joints.hips = (length ? length->value : 0.0f); + length = frame_prop->at("ArmUpperLength_10_249FDA3E4F3B399E7B9E5C9B7C765EAE"); + _frame.joints.upperArms = (length ? length->value : 0.0f); + length = frame_prop->at("ArmLowerLength_12_ACD0F02745C28882619376926292FB36"); + _frame.joints.lowerArms = (length ? length->value : 0.0f); + length = frame_prop->at("LegUpperLength_16_A7C4C71249A3776F7A543D96819C0C61"); + _frame.joints.upperLegs = (length ? length->value : 0.0f); + length = frame_prop->at("LegLowerLength_18_D2DF39964EA0F2A2129D0491B08A032F"); + _frame.joints.lowerLegs = (length ? length->value : 0.0f); + + auto frame_styles = frame_prop->at("Styles_32_00A3B3284B37F1E7819458844A20EB48"); + + if(!frame_styles) { + _state = State::Invalid; + return; + } + + for(UnsignedInt i = 0; i < 4; i++) { + _frame.styles[i] = frame_styles->at(i)->value; + } + + auto eye_flare_prop = frame_prop->at("EyeFlareColor_36_AF79999C40FCA0E88A2F9A84488A38CA"); + if(!eye_flare_prop) { + _state = State::Invalid; + return; + } + + _frame.eyeFlare = Color4{eye_flare_prop->r, eye_flare_prop->g, eye_flare_prop->b, eye_flare_prop->a}; + } + + auto account_prop = _mass->at("Account"); + if(!account_prop) { + _state = State::Invalid; + return; + } + + _steamId = account_prop->value; + + _state = State::Valid; } auto Mass::filename() -> std::string const&{ return _filename; } -auto Mass::name() -> std::string const&{ +auto Mass::name() -> Containers::Optional const& { return _name; } +auto Mass::setName(std::string new_name) -> bool { + _name = new_name; + + auto unit_data = _mass->at("UnitData"); + + if(!unit_data) { + _state = State::Invalid; + return false; + } + + auto name_prop = unit_data->at("Name_45_A037C5D54E53456407BDF091344529BB"); + + if(!name_prop) { + _state = State::Invalid; + return false; + } + + name_prop->value = std::move(new_name); + + return _mass->saveToFile(); +} + auto Mass::state() -> State { return _state; } -auto Mass::jointSliders() -> Joints const& { - return _sliders; +auto Mass::dirty() const -> bool { + return _dirty; +} + +void Mass::setDirty(bool dirty) { + _dirty = dirty; +} + +auto Mass::jointSliders() const -> Joints const& { + return _frame.joints; +} + +auto Mass::setSliders(Joints joints) -> bool { + _frame.joints = joints; + + auto unit_data = _mass->at("UnitData"); + + if(!unit_data) { + _state = State::Invalid; + return false; + } + + auto frame_prop = unit_data->at("Frame_3_F92B0F6A44A15088AF7F41B9FF290653"); + + if(!frame_prop) { + _state = State::Invalid; + return false; + } + + auto length = frame_prop->at("NeckLength_6_ED6AF79849C27CD1A9D523A09E2BFE58"); + if(!length && _frame.joints.neck != 0.0f) { + length = new FloatProperty; + auto length_prop = FloatProperty::ptr{length}; + length_prop->name.emplace("NeckLength_6_ED6AF79849C27CD1A9D523A09E2BFE58"); + arrayAppend(frame_prop->properties, std::move(length_prop)); + } + if(length) { + length->value = _frame.joints.neck; + } + + length = frame_prop->at("BodyLength_7_C16287754CBA96C93BAE36A5C154996A"); + if(!length && _frame.joints.body != 0.0f) { + length = new FloatProperty; + auto length_prop = FloatProperty::ptr{length}; + length_prop->name.emplace("BodyLength_7_C16287754CBA96C93BAE36A5C154996A"); + arrayAppend(frame_prop->properties, std::move(length_prop)); + } + if(length) { + length->value = _frame.joints.body; + } + + length = frame_prop->at("ShoulderLength_8_220EDF304F1C1226F0D8D39117FB3883"); + if(!length && _frame.joints.shoulders != 0.0f) { + length = new FloatProperty; + auto length_prop = FloatProperty::ptr{length}; + length_prop->name.emplace("ShoulderLength_8_220EDF304F1C1226F0D8D39117FB3883"); + arrayAppend(frame_prop->properties, std::move(length_prop)); + } + if(length) { + length->value = _frame.joints.shoulders; + } + + length = frame_prop->at("HipLength_14_02AEEEAC4376087B9C51F0AA7CC92818"); + if(!length && _frame.joints.hips != 0.0f) { + length = new FloatProperty; + auto length_prop = FloatProperty::ptr{length}; + length_prop->name.emplace("HipLength_14_02AEEEAC4376087B9C51F0AA7CC92818"); + arrayAppend(frame_prop->properties, std::move(length_prop)); + } + if(length) { + length->value = _frame.joints.hips; + } + + length = frame_prop->at("ArmUpperLength_10_249FDA3E4F3B399E7B9E5C9B7C765EAE"); + if(!length && _frame.joints.upperArms != 0.0f) { + length = new FloatProperty; + auto length_prop = FloatProperty::ptr{length}; + length_prop->name.emplace("ArmUpperLength_10_249FDA3E4F3B399E7B9E5C9B7C765EAE"); + arrayAppend(frame_prop->properties, std::move(length_prop)); + } + if(length) { + length->value = _frame.joints.upperArms; + } + + length = frame_prop->at("ArmLowerLength_12_ACD0F02745C28882619376926292FB36"); + if(!length && _frame.joints.lowerArms != 0.0f) { + length = new FloatProperty; + auto length_prop = FloatProperty::ptr{length}; + length_prop->name.emplace("ArmLowerLength_12_ACD0F02745C28882619376926292FB36"); + arrayAppend(frame_prop->properties, std::move(length_prop)); + } + if(length) { + length->value = _frame.joints.lowerArms; + } + + length = frame_prop->at("LegUpperLength_16_A7C4C71249A3776F7A543D96819C0C61"); + if(!length && _frame.joints.upperLegs != 0.0f) { + length = new FloatProperty; + auto length_prop = FloatProperty::ptr{length}; + length_prop->name.emplace("LegUpperLength_16_A7C4C71249A3776F7A543D96819C0C61"); + arrayAppend(frame_prop->properties, std::move(length_prop)); + } + if(length) { + length->value = _frame.joints.upperLegs; + } + + length = frame_prop->at("LegLowerLength_18_D2DF39964EA0F2A2129D0491B08A032F"); + if(!length && _frame.joints.lowerLegs != 0.0f) { + length = new FloatProperty; + auto length_prop = FloatProperty::ptr{length}; + length_prop->name.emplace("LegLowerLength_18_D2DF39964EA0F2A2129D0491B08A032F"); + arrayAppend(frame_prop->properties, std::move(length_prop)); + } + if(length) { + length->value = _frame.joints.lowerLegs; + } + + return _mass->saveToFile(); } auto Mass::frameStyles() -> Containers::StaticArrayView<4, Int> { - return _frameStyles; + return _frame.styles; } auto Mass::setFrameStyle(Int index, Int style_id) -> bool { - if(index < 0 || index > 3) { - _lastError = "Index is out of range in Mass::setFrameStyle()."; - return false; - } + _frame.styles[index] = style_id; - std::string path = Utility::Directory::join(_folder, _filename); - - if(!Utility::Directory::exists(path)) { - _lastError = path + " couldn't be found."; - _state = State::Empty; - return false; - } - - auto mmap = Utility::Directory::map(path); - - auto iter = std::search(mmap.begin(), mmap.end(), &frame_styles_locator[0], &frame_styles_locator[90]); - - if(iter != mmap.end()) { - iter += 0x5A; - *(reinterpret_cast(iter) + index) = style_id; - } - else { - _lastError = "Frame styles couldn't be found in " + path; + auto unit_data = _mass->at("UnitData"); + if(!unit_data) { _state = State::Invalid; return false; } - return true; + auto frame = unit_data->at("Frame_3_F92B0F6A44A15088AF7F41B9FF290653"); + if(!frame) { + _state = State::Invalid; + return false; + } + + auto frame_styles = frame->at("Styles_32_00A3B3284B37F1E7819458844A20EB48"); + if(!frame_styles) { + _state = State::Invalid; + return false; + } + + frame_styles->at(index)->value = style_id; + + return _mass->saveToFile(); +} + +auto Mass::eyeFlareColour() const -> const Color4& { + return _frame.eyeFlare; +} + +auto Mass::setEyeFlareColour(Color4 new_colour) -> bool { + _frame.eyeFlare = new_colour; + + auto unit_data = _mass->at("UnitData"); + if(!unit_data) { + _state = State::Invalid; + return false; + } + + auto frame = unit_data->at("Frame_3_F92B0F6A44A15088AF7F41B9FF290653"); + if(!frame) { + _state = State::Invalid; + return false; + } + + auto eye_flare_prop = frame->at("EyeFlareColor_36_AF79999C40FCA0E88A2F9A84488A38CA"); + if(!eye_flare_prop) { + _state = State::Invalid; + return false; + } + + eye_flare_prop->r = new_colour.r(); + eye_flare_prop->g = new_colour.g(); + eye_flare_prop->b = new_colour.b(); + eye_flare_prop->a = new_colour.a(); + + return _mass->saveToFile(); } auto Mass::updateSteamId(const std::string& steam_id) -> bool { - std::string path = Utility::Directory::join(_folder, _filename); + _steamId = steam_id; - if(!Utility::Directory::exists(path)) { - _lastError = path + " couldn't be found."; - _state = State::Empty; + auto unit_data = _mass->at("UnitData"); + if(!unit_data) { + _state = State::Invalid; return false; } - Utility::Directory::copy(path, path + ".tmp"); - - { - auto mmap = Utility::Directory::map(path + ".tmp"); - - auto iter = std::search(mmap.begin(), mmap.end(), &steamid_locator[0], &steamid_locator[23]); - - if(iter == mmap.end()) { - _lastError = "The M.A.S.S. file at " + path + " seems to be corrupt."; - Utility::Directory::rm(path + ".tmp"); - return false; - } - - iter += 37; - - if(std::strncmp(iter, steam_id.c_str(), steam_id.length()) != 0) { - for(int i = 0; i < 17; ++i) { - *(iter + i) = steam_id[i]; - } - } - } - - if(Utility::Directory::exists(path)) { - Utility::Directory::rm(path); - } - - Utility::Directory::move(path + ".tmp", path); - - return true; -} - -void Mass::getName() { - std::string path = Utility::Directory::join(_folder, _filename); - - if(!Utility::Directory::exists(path)) { - _lastError = path + " couldn't be found."; - _state = State::Empty; - return; - } - - auto mmap = Utility::Directory::mapRead(path); - - auto iter = std::search(mmap.begin(), mmap.end(), &mass_name_locator[0], &mass_name_locator[56]); - - if(iter != mmap.end()) { - _name = std::string{iter + 70}; - _state = State::Valid; - } - else { - _lastError = "The name couldn't be found in " + _filename; - _state = State::Invalid; - } -} - -void Mass::getJointSliders() { - std::string path = Utility::Directory::join(_folder, _filename); - - if(!Utility::Directory::exists(path)) { - _lastError = path + " couldn't be found."; - _state = State::Empty; - return; - } - - auto mmap = Utility::Directory::mapRead(path); - - auto iter = std::search(mmap.begin(), mmap.end(), &neck_slider_locator[0], &neck_slider_locator[63]); - - if(iter != mmap.end()) { - _sliders.neck = *reinterpret_cast(iter + 0x49); - } - else { - _sliders.neck = 0.0f; - } - - iter = std::search(mmap.begin(), mmap.end(), &body_slider_locator[0], &body_slider_locator[63]); - - if(iter != mmap.end()) { - _sliders.body = *reinterpret_cast(iter + 0x49); - } - else { - _sliders.body = 0.0f; - } - - iter = std::search(mmap.begin(), mmap.end(), &shoulders_slider_locator[0], &shoulders_slider_locator[67]); - - if(iter != mmap.end()) { - _sliders.shoulders = *reinterpret_cast(iter + 0x4D); - } - else { - _sliders.shoulders = 0.0f; - } - - iter = std::search(mmap.begin(), mmap.end(), &hips_slider_locator[0], &hips_slider_locator[63]); - - if(iter != mmap.end()) { - _sliders.hips = *reinterpret_cast(iter + 0x49); - } - else { - _sliders.hips = 0.0f; - } - - iter = std::search(mmap.begin(), mmap.end(), &uarms_slider_locator[0], &uarms_slider_locator[68]); - - if(iter != mmap.end()) { - _sliders.upperArms = *reinterpret_cast(iter + 0x4E); - } - else { - _sliders.upperArms = 0.0f; - } - - iter = std::search(mmap.begin(), mmap.end(), &larms_slider_locator[0], &larms_slider_locator[68]); - - if(iter != mmap.end()) { - _sliders.lowerArms = *reinterpret_cast(iter + 0x4E); - } - else { - _sliders.lowerArms = 0.0f; - } - - iter = std::search(mmap.begin(), mmap.end(), &ulegs_slider_locator[0], &ulegs_slider_locator[68]); - - if(iter != mmap.end()) { - _sliders.upperLegs = *reinterpret_cast(iter + 0x4E); - } - else { - _sliders.upperLegs = 0.0f; - } - - iter = std::search(mmap.begin(), mmap.end(), &llegs_slider_locator[0], &llegs_slider_locator[68]); - - if(iter != mmap.end()) { - _sliders.lowerLegs = *reinterpret_cast(iter + 0x4E); - } - else { - _sliders.lowerLegs = 0.0f; - } -} - -void Mass::getFrameStyles() { - std::string path = Utility::Directory::join(_folder, _filename); - - if(!Utility::Directory::exists(path)) { - _lastError = path + " couldn't be found."; - _state = State::Empty; - return; - } - - auto mmap = Utility::Directory::mapRead(path); - - auto iter = std::search(mmap.begin(), mmap.end(), &frame_styles_locator[0], &frame_styles_locator[90]); - - if(iter != mmap.end()) { - iter += 0x5A; - std::copy(reinterpret_cast(iter), reinterpret_cast(iter) + 4, _frameStyles.data()); - } - else { - _lastError = "Frame styles couldn't be found in " + path; + auto account_prop = unit_data->at("Account"); + if(!account_prop) { _state = State::Invalid; + return false; } + + account_prop->value = steam_id; + + return _mass->saveToFile(); } diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 747941a..5bfe2b1 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -18,9 +18,16 @@ #include +#include +#include #include #include +#include +#include +#include + +#include "../UESaveFile/UESaveFile.h" using namespace Corrade; using namespace Magnum; @@ -36,6 +43,70 @@ struct Joints { Float lowerLegs = 0.0f; }; +struct CustomStyle { + std::string name; + Color4 colour{0.0f}; + Float metallic = 0.0f; + Float gloss = 0.0f; + + Int patternId = 0; + Float opacity = 0.0f; + Float offsetX = 0.0f; + Float rotation = 0.0f; + Float scale = 0.0f; +}; + +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 Armour { + std::string slot; + Int id = 0; + Containers::StaticArray<4, Int> styles{ValueInit}; + Containers::StaticArray<8, Decal> decals{ValueInit}; + Containers::StaticArray<8, Accessory> accessories{ValueInit}; +}; + +struct WeaponPart { + Int id = 0; + Containers::StaticArray<4, Int> styles{ValueInit}; + Containers::StaticArray<8, Decal> decals{ValueInit}; + Containers::StaticArray<8, Accessory> accessories{ValueInit}; +}; + +struct Weapon { + std::string name; + std::string type; + Containers::Array 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 { @@ -52,36 +123,81 @@ class Mass { static auto lastError() -> std::string const&; - static auto getNameFromFile(const std::string& path) -> std::string; + static auto getNameFromFile(const std::string& path) -> Containers::Optional; void refreshValues(); auto filename() -> std::string const&; - auto name() -> std::string const&; + auto name() -> Containers::Optional const&; + auto setName(std::string new_name) -> bool; auto state() -> State; - auto jointSliders() -> Joints const&; + auto dirty() const -> bool; + void setDirty(bool dirty = true); + + auto jointSliders() const -> Joints const&; + auto setSliders(Joints joints) -> bool; auto frameStyles() -> Containers::StaticArrayView<4, Int>; auto setFrameStyle(Int index, Int style_id) -> bool; + auto eyeFlareColour() const -> Color4 const&; + auto setEyeFlareColour(Color4 new_colour) -> bool; + auto updateSteamId(const std::string& steam_id) -> bool; private: - void getName(); - void getJointSliders(); - void getFrameStyles(); + Containers::Optional _mass; static std::string _lastError; std::string _folder; std::string _filename; - std::string _name; State _state = State::Empty; - Joints _sliders; + bool _dirty = false; - Containers::StaticArray<4, Int> _frameStyles; + Containers::Optional _name = Containers::NullOpt; + + struct { + Joints joints{}; + + Containers::StaticArray<4, Int> styles{ValueInit}; + + Color4 eyeFlare{0.0f}; + + Containers::StaticArray<16, CustomStyle> frameCustomStyles; + } _frame; + + struct { + Containers::StaticArray<38, Armour> parts; + + Containers::StaticArray<16, CustomStyle> armourCustomStyles; + } _armour; + + struct { + Containers::StaticArray<8, Weapon> meleeWeapons; + Containers::StaticArray<1, Weapon> shields; + Containers::StaticArray<4, Weapon> bulletShooters; + Containers::StaticArray<4, Weapon> energyShooters; + Containers::StaticArray<4, Weapon> bulletLaunchers; + Containers::StaticArray<4, Weapon> energyLaunchers; + } _weapons; + + Containers::StaticArray<16, CustomStyle> _globalStyles; + + struct { + Int engineId; + Containers::StaticArray<7, Int> gearIds; + + Int osId; + Containers::StaticArray<7, Int> moduleIds; + + Int archId; + Containers::StaticArray<7, Int> techIds; + } _tuning; + + std::string _steamId; }; -- 2.39.2 From 5689ec6c1acf04f88c035fdcdcc44db66dfdff73 Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 27 Sep 2021 17:53:56 +0200 Subject: [PATCH 040/128] SaveTool: adapt to Profile changes. --- src/SaveTool/SaveTool_MainManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SaveTool/SaveTool_MainManager.cpp b/src/SaveTool/SaveTool_MainManager.cpp index dbb14e8..72b0309 100644 --- a/src/SaveTool/SaveTool_MainManager.cpp +++ b/src/SaveTool/SaveTool_MainManager.cpp @@ -359,7 +359,7 @@ void SaveTool::drawResearchInventory() { matRow("Nuflalt", 2, nuflalt, nuflalt, Nuflalt) matRow("Aurelene", 3, aurelene, aurelene, Aurelene) matRow("Soldus", 4, soldus, soldus, Soldus) - matRow("Synthesized N", 5, synthesized_n, synthesizedN, SynthesizedN) + matRow("Synthesized N", 5, synthesised_n, synthesisedN, SynthesisedN) unavRow("Nanoc", 6) unavRow("Abyssillite", 7) @@ -383,7 +383,7 @@ void SaveTool::drawResearchInventory() { 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, carbonized_skin, carbonizedSkin, CarbonizedSkin) + matRow("Carbonized skin", 5, carbonised_skin, carbonisedSkin, CarbonisedSkin) unavRow("Isolated void particle", 6) unavRow("Weaponised physiology", 7) -- 2.39.2 From e839d1c19b4903c331a360031f926ca1aa1638aa Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 27 Sep 2021 17:54:30 +0200 Subject: [PATCH 041/128] SaveTool: adapt to Mass changes. --- src/SaveTool/SaveTool.cpp | 33 +- src/SaveTool/SaveTool.h | 3 + src/SaveTool/SaveTool_MainManager.cpp | 6 +- src/SaveTool/SaveTool_MassViewer.cpp | 512 +++++++++++++++----------- 4 files changed, 325 insertions(+), 229 deletions(-) diff --git a/src/SaveTool/SaveTool.cpp b/src/SaveTool/SaveTool.cpp index 808622d..97887ec 100644 --- a/src/SaveTool/SaveTool.cpp +++ b/src/SaveTool/SaveTool.cpp @@ -212,7 +212,12 @@ void SaveTool::handleFileAction(efsw::WatchID watch_id, if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) { int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) + (filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30); - _massManager->refreshHangar(index); + if(!_currentMass || _currentMass != &(_massManager->hangar(index))) { + _massManager->refreshHangar(index); + } + else { + _currentMass->setDirty(); + } } } break; @@ -221,7 +226,12 @@ void SaveTool::handleFileAction(efsw::WatchID watch_id, if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) { int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) + (filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30); - _massManager->refreshHangar(index); + if(!_currentMass || _currentMass != &(_massManager->hangar(index))) { + _massManager->refreshHangar(index); + } + else { + _currentMass->setDirty(); + } } } break; @@ -233,16 +243,29 @@ void SaveTool::handleFileAction(efsw::WatchID watch_id, if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) { int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) + (filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30); - _massManager->refreshHangar(index); + + if(!_currentMass || _currentMass != &(_massManager->hangar(index))) { + _massManager->refreshHangar(index); + } + else { + _currentMass->setDirty(); + } } } break; case efsw::Actions::Moved: if(Utility::String::endsWith(filename, _currentProfile->steamId() + ".sav")) { - if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : ""))) { + if(Utility::String::endsWith(old_filename, ".tmp")) { + return; + } + + if(Utility::String::beginsWith(filename, Utility::formatString("{}Unit", _currentProfile->type() == ProfileType::Demo ? "Demo" : "")) && + Utility::String::endsWith(old_filename, ".sav")) + { int index = ((filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) + (filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30); _massManager->refreshHangar(index); + int old_index = ((old_filename[_currentProfile->type() == ProfileType::Demo ? 8 : 4] - 0x30) * 10) + (old_filename[_currentProfile->type() == ProfileType::Demo ? 9 : 5] - 0x30); _massManager->refreshHangar(old_index); @@ -526,8 +549,6 @@ auto SaveTool::findGameDataDirectory() -> bool { } void SaveTool::initialiseMassManager() { - _currentProfile->refreshValues(); - _massManager.emplace(_saveDir, _currentProfile->steamId(), _currentProfile->type() == ProfileType::Demo, diff --git a/src/SaveTool/SaveTool.h b/src/SaveTool/SaveTool.h index 6b1e08a..60dd66a 100644 --- a/src/SaveTool/SaveTool.h +++ b/src/SaveTool/SaveTool.h @@ -105,6 +105,9 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener void drawMassViewer(); void drawFrameInfo(); + void drawJointSliders(); + void drawFramePaint(); + void drawCustomStyles(Containers::ArrayView styles); void drawAbout(); void drawGameState(); diff --git a/src/SaveTool/SaveTool_MainManager.cpp b/src/SaveTool/SaveTool_MainManager.cpp index 72b0309..ef3c719 100644 --- a/src/SaveTool/SaveTool_MainManager.cpp +++ b/src/SaveTool/SaveTool_MainManager.cpp @@ -435,7 +435,7 @@ void SaveTool::drawMassManager() { drag_drop_index = i; ImGui::SetDragDropPayload("Mass", &drag_drop_index, sizeof(int)); - ImGui::Text("%s - Hangar %.2d", _massManager->hangar(i).name().c_str(), i + 1); + ImGui::Text("%s - Hangar %.2d", (*_massManager->hangar(i).name()).c_str(), i + 1); ImGui::EndDragDropSource(); } @@ -484,7 +484,7 @@ void SaveTool::drawMassManager() { ImGui::TextDisabled(""); break; case Mass::State::Valid: - ImGui::TextUnformatted(_massManager->hangar(i).name().c_str()); + ImGui::TextUnformatted((*_massManager->hangar(i).name()).c_str()); break; } @@ -614,7 +614,7 @@ auto SaveTool::drawDeleteMassPopup(int mass_index) -> ImGuiID { } else { ImGui::Text("Are you sure you want to delete the M.A.S.S. named %s in hangar %.2i ? This operation is irreversible.", - _massManager->hangar(mass_index).name().c_str(), mass_index + 1); + (*_massManager->hangar(mass_index).name()).c_str(), mass_index + 1); } ImGui::PopTextWrapPos(); diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index 60b078b..5b1fb86 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include + #include "../Maps/StyleNames.h" #include "../FontAwesome/IconsFontAwesome5.h" @@ -24,6 +26,7 @@ void SaveTool::drawMassViewer() { if(!_currentMass || _currentMass->state() != Mass::State::Valid) { _currentMass = nullptr; _uiState = UiState::MainManager; + _queue.addToast(Toast::Type::Error, "The selected M.A.S.S. isn't valid anymore."); return; } @@ -38,12 +41,11 @@ void SaveTool::drawMassViewer() { return; } - ImGui::AlignTextToFramePadding(); ImGui::Text("Current M.A.S.S.: %s (%s)", - _currentMass->name().c_str(), + (*_currentMass->name()).c_str(), _currentMass->filename().c_str()); ImGui::SameLine(); - if(ImGui::Button(ICON_FA_ARROW_LEFT " Back to main manager")) { + if(ImGui::SmallButton(ICON_FA_ARROW_LEFT " Back to main manager")) { _currentMass = nullptr; _uiState = UiState::MainManager; } @@ -52,6 +54,17 @@ void SaveTool::drawMassViewer() { ImGui::SameLine(); ImGui::TextWrapped("WARNING: Colours in this app may look different from in-game colours, due to unavoidable differences in the rendering pipeline."); + ImGui::TextColored(ImColor(255, 255, 0), ICON_FA_EXCLAMATION_TRIANGLE); + ImGui::SameLine(); + ImGui::TextWrapped("Real-time updates are disabled while on this screen. %s", _currentMass->dirty() ? "The save file has been changed." : ""); + if(_currentMass->dirty()) { + ImGui::SameLine(); + if(ImGui::SmallButton(ICON_FA_SYNC_ALT " Refresh")) { + _currentMass->refreshValues(); + _currentMass->setDirty(false); + } + } + if(ImGui::BeginChild("##MassInfo", {ImGui::GetContentRegionAvailWidth() * 0.60f, 0.0f}, true, ImGuiWindowFlags_MenuBar)) @@ -96,256 +109,315 @@ void SaveTool::drawFrameInfo() { ImGui::TextUnformatted("Frame type: Skeleton"); // Placeholder for now, replace with actual code once other frames are implemented. - if(ImGui::CollapsingHeader("Joint sliders")) { - static Joints sliders = _currentMass->jointSliders(); - static bool edit = false; - static bool dirty = false; + drawJointSliders(); - ImGui::TextWrapped("In-game values are multiplied by 100.\nFor example, 0.500 here is equal to 50 in-game."); + drawFramePaint(); +} - if(ImGui::BeginTable("##JointSliderTable", 2, ImGuiTableFlags_Borders)) { - ImGui::TableSetupColumn("##SliderLabel", ImGuiTableColumnFlags_WidthFixed); - ImGui::TableSetupColumn("##Sliders", ImGuiTableColumnFlags_WidthStretch); +void SaveTool::drawJointSliders() { + if(!ImGui::CollapsingHeader("Joint sliders")) { + return; + } - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); + static Joints sliders = _currentMass->jointSliders(); + static bool joints_edit = false; + static bool joints_dirty = false; + + 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); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Neck"); + ImGui::TableSetColumnIndex(1); + if(joints_edit) { + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##NeckSlider", &sliders.neck, 0.0f, 1.0f)) { + joints_dirty = true; + } + } + else { ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Neck"); - ImGui::TableSetColumnIndex(1); - if(edit) { + ImGui::Text("%.3f", Double(_currentMass->jointSliders().neck)); + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Body"); + ImGui::TableSetColumnIndex(1); + if(joints_edit) { + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##BodySlider", &sliders.body, 0.0f, 1.0f)) { + joints_dirty = true; + } + } + else { + ImGui::AlignTextToFramePadding(); + ImGui::Text("%.3f", Double(_currentMass->jointSliders().body)); + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Shoulders"); + ImGui::TableSetColumnIndex(1); + if(joints_edit) { + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##ShouldersSlider", &sliders.shoulders, 0.0f, 1.0f)) { + joints_dirty = true; + } + } + else { + ImGui::AlignTextToFramePadding(); + ImGui::Text("%.3f", Double(_currentMass->jointSliders().shoulders)); + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Hips"); + ImGui::TableSetColumnIndex(1); + if(joints_edit) { + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##HipsSlider", &sliders.hips, 0.0f, 1.0f)) { + joints_dirty = true; + } + } + else { + ImGui::AlignTextToFramePadding(); + ImGui::Text("%.3f", Double(_currentMass->jointSliders().hips)); + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(1); + if(ImGui::BeginTable("##UpperLowerLayoutTable", 2, ImGuiTableFlags_BordersInnerV)) { + ImGui::TableSetupColumn("##Upper", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("##Lower", ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Upper"); + ImGui::TableNextColumn(); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Lower"); + + ImGui::EndTable(); + } + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Arms"); + ImGui::TableSetColumnIndex(1); + if(ImGui::BeginTable("##UpperLowerArmsLayoutTable", 2, ImGuiTableFlags_BordersInnerV)) { + ImGui::TableSetupColumn("##UpperArms", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("##LowerArms", ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextColumn(); + if(joints_edit) { ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##NeckSlider", &sliders.neck, 0.0f, 1.0f)) { - dirty = true; + if(ImGui::SliderFloat("##UpperArmsSlider", &sliders.upperArms, 0.0f, 1.0f)) { + joints_dirty = true; } } else { ImGui::AlignTextToFramePadding(); - ImGui::Text("%.3f", Double(_currentMass->jointSliders().neck)); + ImGui::Text("%.3f", Double(_currentMass->jointSliders().upperArms)); } - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Body"); - ImGui::TableSetColumnIndex(1); - if(edit) { + ImGui::TableNextColumn(); + if(joints_edit) { ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##BodySlider", &sliders.body, 0.0f, 1.0f)) { - dirty = true; + if(ImGui::SliderFloat("##LowerArmsSlider", &sliders.lowerArms, 0.0f, 1.0f)) { + joints_dirty = true; } } else { ImGui::AlignTextToFramePadding(); - ImGui::Text("%.3f", Double(_currentMass->jointSliders().body)); - } - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Shoulders"); - ImGui::TableSetColumnIndex(1); - if(edit) { - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##ShouldersSlider", &sliders.shoulders, 0.0f, 1.0f)) { - dirty = true; - } - } - else { - ImGui::AlignTextToFramePadding(); - ImGui::Text("%.3f", Double(_currentMass->jointSliders().shoulders)); - } - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Hips"); - ImGui::TableSetColumnIndex(1); - if(edit) { - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##HipsSlider", &sliders.hips, 0.0f, 1.0f)) { - dirty = true; - } - } - else { - ImGui::AlignTextToFramePadding(); - ImGui::Text("%.3f", Double(_currentMass->jointSliders().hips)); - } - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(1); - if(ImGui::BeginTable("##UpperLowerLayoutTable", 2, ImGuiTableFlags_BordersInnerV)) { - ImGui::TableSetupColumn("##Upper", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("##Lower", ImGuiTableColumnFlags_WidthStretch); - - ImGui::TableNextColumn(); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Upper"); - ImGui::TableNextColumn(); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Lower"); - - ImGui::EndTable(); - } - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Arms"); - ImGui::TableSetColumnIndex(1); - if(ImGui::BeginTable("##UpperLowerArmsLayoutTable", 2, ImGuiTableFlags_BordersInnerV)) { - ImGui::TableSetupColumn("##UpperArms", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("##LowerArms", ImGuiTableColumnFlags_WidthStretch); - - ImGui::TableNextColumn(); - if(edit) { - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##UpperArmsSlider", &sliders.upperArms, 0.0f, 1.0f)) { - dirty = true; - } - } - else { - ImGui::AlignTextToFramePadding(); - ImGui::Text("%.3f", Double(_currentMass->jointSliders().upperArms)); - } - ImGui::TableNextColumn(); - if(edit) { - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##LowerArmsSlider", &sliders.lowerArms, 0.0f, 1.0f)) { - dirty = true; - } - } - else { - ImGui::AlignTextToFramePadding(); - ImGui::Text("%.3f", Double(_currentMass->jointSliders().lowerArms)); - } - - ImGui::EndTable(); - } - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Legs"); - ImGui::TableSetColumnIndex(1); - if(ImGui::BeginTable("##UpperLowerLegsLayoutTable", 2, ImGuiTableFlags_BordersInnerV)) { - ImGui::TableSetupColumn("##UpperLegs", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("##LowerLegs", ImGuiTableColumnFlags_WidthStretch); - - ImGui::TableNextColumn(); - if(edit) { - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##UpperLegsSlider", &sliders.upperLegs, 0.0f, 1.0f)) { - dirty = true; - } - } - else { - ImGui::AlignTextToFramePadding(); - ImGui::Text("%.3f", Double(_currentMass->jointSliders().upperLegs)); - } - ImGui::TableNextColumn(); - if(edit) { - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##LowerLegsSlider", &sliders.lowerLegs, 0.0f, 1.0f)) { - dirty = true; - } - } - else { - ImGui::AlignTextToFramePadding(); - ImGui::Text("%.3f", Double(_currentMass->jointSliders().lowerLegs)); - } - - ImGui::EndTable(); + ImGui::Text("%.3f", Double(_currentMass->jointSliders().lowerArms)); } ImGui::EndTable(); } - if(edit) { - if(!dirty) { - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f); - ImGui::Button(ICON_FA_SAVE " Save changes"); - ImGui::SameLine(); - ImGui::Button(ICON_FA_UNDO " Reset sliders"); - ImGui::PopStyleVar(); - ImGui::PopItemFlag(); + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Legs"); + ImGui::TableSetColumnIndex(1); + if(ImGui::BeginTable("##UpperLowerLegsLayoutTable", 2, ImGuiTableFlags_BordersInnerV)) { + ImGui::TableSetupColumn("##UpperLegs", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("##LowerLegs", ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextColumn(); + if(joints_edit) { + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##UpperLegsSlider", &sliders.upperLegs, 0.0f, 1.0f)) { + joints_dirty = true; + } } else { - if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save changes"); })) { - dirty = false; - } - ImGui::SameLine(); - if(ImGui::Button(ICON_FA_UNDO " Reset sliders")) { - sliders = _currentMass->jointSliders(); - dirty = false; + ImGui::AlignTextToFramePadding(); + ImGui::Text("%.3f", Double(_currentMass->jointSliders().upperLegs)); + } + ImGui::TableNextColumn(); + if(joints_edit) { + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##LowerLegsSlider", &sliders.lowerLegs, 0.0f, 1.0f)) { + joints_dirty = true; } } - ImGui::SameLine(); - if(ImGui::Button(ICON_FA_TIMES " Cancel editing")) { - sliders = _currentMass->jointSliders(); - dirty = false; - edit = false; + else { + ImGui::AlignTextToFramePadding(); + ImGui::Text("%.3f", Double(_currentMass->jointSliders().lowerLegs)); } - ImGui::TextUnformatted("To input out-of-range values, hold Ctrl and click on a slider."); + ImGui::EndTable(); + } + + ImGui::EndTable(); + } + + if(joints_edit) { + if(!joints_dirty) { + ImGui::BeginDisabled(); + ImGui::Button(ICON_FA_SAVE " Save changes"); + ImGui::SameLine(); + ImGui::Button(ICON_FA_UNDO " Reset sliders"); + ImGui::EndDisabled(); } else { - if(ImGui::Button(ICON_FA_EDIT " Edit sliders")) { - edit = true; - } - } - } - - if(ImGui::CollapsingHeader("Paint")) { - ImGui::TextUnformatted("Frame styles:"); - for(Int i = 0; i < 4; i++) { - ImGui::AlignTextToFramePadding(); - ImGui::Text("Slot %d:", i + 1); - ImGui::SameLine(); - ImGui::PushID(i); - GameState game_state = _gameState; - if(!_unsafeMode && game_state != GameState::NotRunning) { - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f); - } - if(ImGui::BeginCombo("##Style", style_names.at(_currentMass->frameStyles()[i]))) { - for(const auto& style : style_names) { - if(ImGui::Selectable(style.second, _currentMass->frameStyles()[i] == style.first)) { - if(!_currentMass->setFrameStyle(i, style.first)) { - _queue.addToast(Toast::Type::Error, Mass::lastError()); - } - else { - _currentMass->refreshValues(); - } - } + if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save changes"); })) { + if(!_currentMass->setSliders(sliders)) { + _queue.addToast(Toast::Type::Error, "Error writing the joint sliders."); } - - ImGui::EndCombo(); + joints_dirty = false; + joints_edit = false; } - if(!_unsafeMode && game_state != GameState::NotRunning) { - ImGui::PopItemFlag(); - ImGui::PopStyleVar(); + ImGui::SameLine(); + if(ImGui::Button(ICON_FA_UNDO " Reset sliders")) { + sliders = _currentMass->jointSliders(); + joints_dirty = false; } - ImGui::PopID(); } - - ImGui::Separator(); - - static const Int hex_literals[] = {0x3DF68F08, 0x3E791C4C, 0x00000000}; - static Float colour_components[3]; - static bool run_once = true; - if(run_once) { - std::memcpy(colour_components, hex_literals, sizeof(Float) * 3); - run_once = false; - } - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Eye flare colour:"); ImGui::SameLine(); - ImGui::SetNextItemWidth(-1.0f); - ImGui::ColorEdit3("##EyeFlarePicker", &colour_components[0]); + if(ImGui::Button(ICON_FA_TIMES " Cancel editing")) { + sliders = _currentMass->jointSliders(); + joints_dirty = false; + joints_edit = false; + } - ImGui::Separator(); - - ImGui::TextUnformatted("The frame's custom styles will go here."); + ImGui::TextUnformatted("To input out-of-range values, hold Ctrl and click on a slider."); + } + else { + if(ImGui::Button(ICON_FA_EDIT " Edit sliders")) { + sliders = _currentMass->jointSliders(); + joints_edit = true; + } } } + +void SaveTool::drawFramePaint() { + if(!ImGui::CollapsingHeader("Paint")) { + return; + } + + ImGui::TextUnformatted("Frame styles:"); + for(Int i = 0; i < 4; i++) { + ImGui::AlignTextToFramePadding(); + ImGui::Text("Slot %d:", i + 1); + ImGui::SameLine(); + ImGui::PushID(i); + GameState game_state = _gameState; + if(!_unsafeMode && game_state != GameState::NotRunning) { + ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f); + } + if(ImGui::BeginCombo("##Style", style_names.at(_currentMass->frameStyles()[i]))) { + for(const auto& style : style_names) { + if(ImGui::Selectable(style.second, _currentMass->frameStyles()[i] == style.first)) { + if(!_currentMass->setFrameStyle(i, style.first)) { + _queue.addToast(Toast::Type::Error, Mass::lastError()); + } + else { + _currentMass->refreshValues(); + } + } + } + + ImGui::EndCombo(); + } + if(!_unsafeMode && game_state != GameState::NotRunning) { + ImGui::PopItemFlag(); + ImGui::PopStyleVar(); + } + ImGui::PopID(); + } + + ImGui::Separator(); + + static bool eye_flare_edit = false; + static bool eye_flare_dirty = false; + + static Color4 eye_flare = _currentMass->eyeFlareColour(); + + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Eye flare colour:"); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + if(eye_flare_edit) { + if(ImGui::ColorEdit3("##EyeFlarePicker", &eye_flare.x())) { + eye_flare_dirty = true; + } + ImGui::SameLine(); + drawHelpMarker("Right-click for more option, click the coloured square for the full picker.", 250.0f); + + if(!eye_flare_dirty) { + 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->setEyeFlareColour(eye_flare)) { + _queue.addToast(Toast::Type::Error, "Error writing the eye flare colour."); + } + eye_flare_dirty = false; + eye_flare_edit = false; + } + ImGui::SameLine(); + if(ImGui::Button(ICON_FA_UNDO " Reset")) { + eye_flare = _currentMass->eyeFlareColour(); + eye_flare_dirty = false; + } + } + + ImGui::SameLine(); + + if(ImGui::Button(ICON_FA_TIMES " Cancel")) { + eye_flare = _currentMass->eyeFlareColour(); + eye_flare_dirty = false; + eye_flare_edit = false; + } + } + else { + ImGui::BeginDisabled(); + ImColor colour{_currentMass->eyeFlareColour()}; + ImGui::ColorEdit3("##EyeFlarePicker", &colour.Value.x); + ImGui::EndDisabled(); + + if(ImGui::Button(ICON_FA_EDIT " Edit")) { + eye_flare = _currentMass->eyeFlareColour(); + eye_flare_edit = true; + } + } + ImGui::EndGroup(); +} -- 2.39.2 From 0a438a4d728aff488a328b508659842878192a65 Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 27 Sep 2021 17:54:42 +0200 Subject: [PATCH 042/128] MassManager: adapt to Mass changes. --- src/MassManager/MassManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MassManager/MassManager.cpp b/src/MassManager/MassManager.cpp index 033d279..1327781 100644 --- a/src/MassManager/MassManager.cpp +++ b/src/MassManager/MassManager.cpp @@ -111,7 +111,7 @@ auto MassManager::exportMass(int hangar) -> bool { std::string source = Utility::Directory::join(_saveDirectory, _hangars[hangar].filename()); std::string dest = Utility::Directory::join(_stagingAreaDirectory, - Utility::formatString("{}_{}.sav", _hangars[hangar].name(), _steamId)); + Utility::formatString("{}_{}.sav", *_hangars[hangar].name(), _steamId)); if(!Utility::Directory::copy(source, dest)) { _lastError = Utility::formatString("Couldn't export data from hangar {:.2d} to {}", hangar, dest); @@ -187,7 +187,7 @@ void MassManager::refreshStagedMasses() { file_list.erase(iter, file_list.end()); for(const std::string& file : file_list) { - std::string name = Mass::getNameFromFile(Utility::Directory::join(_stagingAreaDirectory, file)); + std::string name = *Mass::getNameFromFile(Utility::Directory::join(_stagingAreaDirectory, file)); if(!name.empty()) { _stagedMasses[file] = name; -- 2.39.2 From b377e0de6ce6754283d829b037385d37eb6723d6 Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 27 Sep 2021 20:50:03 +0200 Subject: [PATCH 043/128] Mass: add a missing variable to CustomStyle. --- src/Mass/Mass.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 5bfe2b1..e0a7265 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -52,6 +52,7 @@ struct CustomStyle { Int patternId = 0; Float opacity = 0.0f; Float offsetX = 0.0f; + Float offsetY = 0.0f; Float rotation = 0.0f; Float scale = 0.0f; }; -- 2.39.2 From 1612e4372b8b76e2e0b1c43e6f5e5facdb89ad6f Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 27 Sep 2021 20:50:37 +0200 Subject: [PATCH 044/128] SaveTool: make the file watcher less aggressive. --- src/SaveTool/SaveTool.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/SaveTool/SaveTool.cpp b/src/SaveTool/SaveTool.cpp index 97887ec..900823b 100644 --- a/src/SaveTool/SaveTool.cpp +++ b/src/SaveTool/SaveTool.cpp @@ -206,6 +206,8 @@ void SaveTool::handleFileAction(efsw::WatchID watch_id, return; } + static bool is_moved_after_save = false; + switch(action) { case efsw::Actions::Add: if(Utility::String::endsWith(filename, _currentProfile->steamId() + ".sav")) { @@ -229,9 +231,6 @@ void SaveTool::handleFileAction(efsw::WatchID watch_id, if(!_currentMass || _currentMass != &(_massManager->hangar(index))) { _massManager->refreshHangar(index); } - else { - _currentMass->setDirty(); - } } } break; @@ -248,7 +247,10 @@ void SaveTool::handleFileAction(efsw::WatchID watch_id, _massManager->refreshHangar(index); } else { - _currentMass->setDirty(); + if(!is_moved_after_save) { + is_moved_after_save = false; + _currentMass->setDirty(); + } } } } @@ -256,6 +258,7 @@ void SaveTool::handleFileAction(efsw::WatchID watch_id, case efsw::Actions::Moved: if(Utility::String::endsWith(filename, _currentProfile->steamId() + ".sav")) { if(Utility::String::endsWith(old_filename, ".tmp")) { + is_moved_after_save = true; return; } -- 2.39.2 From 18aa7f659ea4022478064a10ac72a591783fb3ad Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 27 Sep 2021 20:51:48 +0200 Subject: [PATCH 045/128] SaveTool: move the tw macro so all SaveTool files can use it. --- src/SaveTool/SaveTool.cpp | 3 --- src/SaveTool/SaveTool.h | 6 ++++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/SaveTool/SaveTool.cpp b/src/SaveTool/SaveTool.cpp index 900823b..662d821 100644 --- a/src/SaveTool/SaveTool.cpp +++ b/src/SaveTool/SaveTool.cpp @@ -49,9 +49,6 @@ extern const ImVec2 center_pivot = {0.5f, 0.5f}; #ifdef SAVETOOL_DEBUG_BUILD -#include - -#define tw CORRADE_TWEAKABLE Utility::Tweakable tweak; #endif diff --git a/src/SaveTool/SaveTool.h b/src/SaveTool/SaveTool.h index 60dd66a..7ff8409 100644 --- a/src/SaveTool/SaveTool.h +++ b/src/SaveTool/SaveTool.h @@ -36,6 +36,12 @@ #include "../MassManager/MassManager.h" #include "../ToastQueue/ToastQueue.h" +#ifdef SAVETOOL_DEBUG_BUILD +#include + +#define tw CORRADE_TWEAKABLE +#endif + using namespace Corrade; using namespace Magnum; -- 2.39.2 From e77cce5b4292f8d4f896555fb19052ffb87de9d2 Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 27 Sep 2021 21:55:05 +0200 Subject: [PATCH 046/128] Mass: add missing value to CustomStyle. --- src/Mass/Mass.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index e0a7265..538861c 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -48,6 +48,7 @@ struct CustomStyle { Color4 colour{0.0f}; Float metallic = 0.0f; Float gloss = 0.0f; + bool glow = false; Int patternId = 0; Float opacity = 0.0f; -- 2.39.2 From e4cfd3834a07f74bb5c73bac19fcbb5eef90f358 Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 29 Sep 2021 11:07:10 +0200 Subject: [PATCH 047/128] Mass: update CustomStyle. --- src/Mass/Mass.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 538861c..e042d8f 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -51,11 +51,10 @@ struct CustomStyle { bool glow = false; Int patternId = 0; - Float opacity = 0.0f; - Float offsetX = 0.0f; - Float offsetY = 0.0f; + Float opacity = 0.5f; + Vector2 offset{0.0f}; Float rotation = 0.0f; - Float scale = 0.0f; + Float scale = 1.0f; }; struct Decal { -- 2.39.2 From b5b5b3b38c0c420b3bdb58df8b5b3dddd6d6e71d Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 2 Oct 2021 14:51:39 +0200 Subject: [PATCH 048/128] Mass: rename some members. --- src/Mass/Mass.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index e042d8f..916e1ea 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -169,13 +169,13 @@ class Mass { Color4 eyeFlare{0.0f}; - Containers::StaticArray<16, CustomStyle> frameCustomStyles; + Containers::StaticArray<16, CustomStyle> customStyles; } _frame; struct { Containers::StaticArray<38, Armour> parts; - Containers::StaticArray<16, CustomStyle> armourCustomStyles; + Containers::StaticArray<16, CustomStyle> customStyles; } _armour; struct { -- 2.39.2 From 79762e176e1487b9275d49834b553189c85eb32d Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 2 Oct 2021 14:52:48 +0200 Subject: [PATCH 049/128] Mass: add (partial) support for custom style edition. --- src/Mass/Mass.cpp | 204 ++++++++++++++++++++++++++++++++++++++++++++-- src/Mass/Mass.h | 11 +++ 2 files changed, 208 insertions(+), 7 deletions(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 58309e1..63f8c53 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -100,16 +100,21 @@ void Mass::refreshValues() { return; } - auto name_prop = unit_data->at("Name_45_A037C5D54E53456407BDF091344529BB"); + // region MassName + { + auto name_prop = unit_data->at("Name_45_A037C5D54E53456407BDF091344529BB"); - if(!name_prop) { - _name = Containers::NullOpt; - _state = State::Invalid; - return; + if(!name_prop) { + _name = Containers::NullOpt; + _state = State::Invalid; + return; + } + + _name = name_prop->value; } + // endregion - _name = name_prop->value; - + // region FrameData { auto frame_prop = unit_data->at("Frame_3_F92B0F6A44A15088AF7F41B9FF290653"); @@ -154,6 +159,115 @@ void Mass::refreshValues() { _frame.eyeFlare = Color4{eye_flare_prop->r, eye_flare_prop->g, eye_flare_prop->b, eye_flare_prop->a}; } + // endregion + + // region FrameStyles + { + auto frame_styles = unit_data->at("FrameStyle_44_04A44C9440363CCEC5443D98BFAF22AA"); + if(!frame_styles) { + _state = State::Invalid; + return; + } + + for(UnsignedInt i = 0; i < frame_styles->items.size(); i++) { + auto style_prop = frame_styles->at(i); + + CustomStyle style; + + style.name = style_prop->at("Name_27_1532115A46EF2B2FA283908DF561A86B")->value; + auto colour_prop = style_prop->at("Color_5_F0D383DF40474C9464AE48A0984A212E"); + style.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; + style.metallic = style_prop->at("Metallic_10_0A4CD1E4482CBF41CA61D0A856DE90B9")->value; + style.gloss = style_prop->at("Gloss_11_9769599842CC275A401C4282A236E240")->value; + style.glow = colour_prop->a == 0.0f ? false : true; + + style.patternId = style_prop->at("PatternID_14_516DB85641DAF8ECFD2920BE2BDF1311")->value; + style.opacity = style_prop->at("Opacity_30_53BD060B4DFCA1C92302D6A0F7831131")->value; + style.offset = Vector2{ + style_prop->at("OffsetX_23_70FC2E814C64BBB82452748D2AF9CD48")->value, + style_prop->at("OffsetY_24_5E1F866C4C054D9B2EE337ADC180C17F")->value + }; + style.rotation = style_prop->at("Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D")->value; + style.scale = style_prop->at("Scale_26_19DF0708409262183E1247B317137671")->value; + + _frame.customStyles[i] = std::move(style); + } + } + // endregion + + // TODO: armour + + // region ArmourStyles + { + auto armour_styles = unit_data->at("ArmorStyle_42_E2F6AC3647788CB366BD469B3B7E899E"); + if(!armour_styles) { + _state = State::Invalid; + return; + } + + for(UnsignedInt i = 0; i < armour_styles->items.size(); i++) { + auto style_prop = armour_styles->at(i); + + CustomStyle style; + + style.name = style_prop->at("Name_27_1532115A46EF2B2FA283908DF561A86B")->value; + auto colour_prop = style_prop->at("Color_5_F0D383DF40474C9464AE48A0984A212E"); + style.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; + style.metallic = style_prop->at("Metallic_10_0A4CD1E4482CBF41CA61D0A856DE90B9")->value; + style.gloss = style_prop->at("Gloss_11_9769599842CC275A401C4282A236E240")->value; + style.glow = colour_prop->a == 0.0f ? false : true; + + style.patternId = style_prop->at("PatternID_14_516DB85641DAF8ECFD2920BE2BDF1311")->value; + style.opacity = style_prop->at("Opacity_30_53BD060B4DFCA1C92302D6A0F7831131")->value; + style.offset = Vector2{ + style_prop->at("OffsetX_23_70FC2E814C64BBB82452748D2AF9CD48")->value, + style_prop->at("OffsetY_24_5E1F866C4C054D9B2EE337ADC180C17F")->value + }; + style.rotation = style_prop->at("Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D")->value; + style.scale = style_prop->at("Scale_26_19DF0708409262183E1247B317137671")->value; + + _armour.customStyles[i] = std::move(style); + } + } + // endregion + + // TODO: weapons + + // region GlobalStyles + { + auto global_styles = unit_data->at("GlobalStyles_57_6A681C114035241F7BDAAE9B43A8BF1B"); + if(!global_styles) { + _state = State::Invalid; + return; + } + + for(UnsignedInt i = 0; i < global_styles->items.size(); i++) { + auto style_prop = global_styles->at(i); + + CustomStyle style; + + style.name = style_prop->at("Name_27_1532115A46EF2B2FA283908DF561A86B")->value; + auto colour_prop = style_prop->at("Color_5_F0D383DF40474C9464AE48A0984A212E"); + style.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; + style.metallic = style_prop->at("Metallic_10_0A4CD1E4482CBF41CA61D0A856DE90B9")->value; + style.gloss = style_prop->at("Gloss_11_9769599842CC275A401C4282A236E240")->value; + style.glow = colour_prop->a == 0.0f ? false : true; + + style.patternId = style_prop->at("PatternID_14_516DB85641DAF8ECFD2920BE2BDF1311")->value; + style.opacity = style_prop->at("Opacity_30_53BD060B4DFCA1C92302D6A0F7831131")->value; + style.offset = Vector2{ + style_prop->at("OffsetX_23_70FC2E814C64BBB82452748D2AF9CD48")->value, + style_prop->at("OffsetY_24_5E1F866C4C054D9B2EE337ADC180C17F")->value + }; + style.rotation = style_prop->at("Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D")->value; + style.scale = style_prop->at("Scale_26_19DF0708409262183E1247B317137671")->value; + + _globalStyles[i] = std::move(style); + } + } + // endregion + + // TODO: tuning auto account_prop = _mass->at("Account"); if(!account_prop) { @@ -383,6 +497,48 @@ auto Mass::setEyeFlareColour(Color4 new_colour) -> bool { return _mass->saveToFile(); } +auto Mass::frameCustomStyles() -> Containers::StaticArrayView<16, CustomStyle> { + return _frame.customStyles; +} + +auto Mass::setFrameCustomStyle(CustomStyle style, UnsignedLong index) -> bool { + if(index > _frame.customStyles.size()) { + return false; + } + + _frame.customStyles[index] = std::move(style); + + return setCustomStyle(_frame.customStyles[index], index, "FrameStyle_44_04A44C9440363CCEC5443D98BFAF22AA"); +} + +auto Mass::armourCustomStyles() -> Containers::StaticArrayView<16, CustomStyle> { + return _armour.customStyles; +} + +auto Mass::setArmourCustomStyle(CustomStyle style, UnsignedLong index) -> bool { + if(index > _armour.customStyles.size()) { + return false; + } + + _armour.customStyles[index] = std::move(style); + + return setCustomStyle(_armour.customStyles[index], index, "ArmorStyle_42_E2F6AC3647788CB366BD469B3B7E899E"); +} + +auto Mass::globalStyles() -> Containers::StaticArrayView<16, CustomStyle> { + return _globalStyles; +} + +auto Mass::setGlobalStyle(CustomStyle style, UnsignedLong index) -> bool { + if(index > _globalStyles.size()) { + return false; + } + + _globalStyles[index] = std::move(style); + + return setCustomStyle(_globalStyles[index], index, "GlobalStyles_57_6A681C114035241F7BDAAE9B43A8BF1B"); +} + auto Mass::updateSteamId(const std::string& steam_id) -> bool { _steamId = steam_id; @@ -402,3 +558,37 @@ auto Mass::updateSteamId(const std::string& steam_id) -> bool { return _mass->saveToFile(); } + +auto Mass::setCustomStyle(const CustomStyle& style, UnsignedLong index, const char* prop_name) -> bool { + auto unit_data = _mass->at("UnitData"); + if(!unit_data) { + _state = State::Invalid; + return false; + } + + auto frame_styles = unit_data->at(prop_name); + if(!frame_styles) { + _state = State::Invalid; + return false; + } + + auto style_prop = frame_styles->at(index); + + style_prop->at("Name_27_1532115A46EF2B2FA283908DF561A86B")->value = style.name; + auto colour_prop = style_prop->at("Color_5_F0D383DF40474C9464AE48A0984A212E"); + colour_prop->r = style.colour.r(); + colour_prop->g = style.colour.g(); + colour_prop->b = style.colour.b(); + colour_prop->a = style.glow ? 1.0f : 0.0f; + style_prop->at("Metallic_10_0A4CD1E4482CBF41CA61D0A856DE90B9")->value = style.metallic; + style_prop->at("Gloss_11_9769599842CC275A401C4282A236E240")->value = style.gloss; + + style_prop->at("PatternID_14_516DB85641DAF8ECFD2920BE2BDF1311")->value = style.patternId; + style_prop->at("Opacity_30_53BD060B4DFCA1C92302D6A0F7831131")->value = style.opacity; + style_prop->at("OffsetX_23_70FC2E814C64BBB82452748D2AF9CD48")->value = style.offset.x(); + style_prop->at("OffsetY_24_5E1F866C4C054D9B2EE337ADC180C17F")->value = style.offset.y(); + style_prop->at("Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D")->value = style.rotation; + style_prop->at("Scale_26_19DF0708409262183E1247B317137671")->value = style.scale; + + return _mass->saveToFile(); +} diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 916e1ea..f2cd17c 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -147,9 +147,20 @@ class Mass { auto eyeFlareColour() const -> Color4 const&; auto setEyeFlareColour(Color4 new_colour) -> bool; + auto frameCustomStyles() -> Containers::StaticArrayView<16, CustomStyle>; + auto setFrameCustomStyle(CustomStyle style, UnsignedLong index) -> bool; + + auto armourCustomStyles() -> Containers::StaticArrayView<16, CustomStyle>; + auto setArmourCustomStyle(CustomStyle style, UnsignedLong index) -> bool; + + auto globalStyles() -> Containers::StaticArrayView<16, CustomStyle>; + auto setGlobalStyle(CustomStyle style, UnsignedLong index) -> bool; + auto updateSteamId(const std::string& steam_id) -> bool; private: + auto setCustomStyle(const CustomStyle& style, UnsignedLong index, const char* prop_name) -> bool; + Containers::Optional _mass; static std::string _lastError; -- 2.39.2 From 19c00a3ce3072c0567fed207b6d57049289257d3 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 2 Oct 2021 19:22:52 +0200 Subject: [PATCH 050/128] Mass: rename Armour to ArmourPart. This is more consistent with WeaponPart, which designates a part of the full weapon. --- src/Mass/Mass.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index f2cd17c..61988da 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -81,7 +81,7 @@ struct Accessory { Vector3 localScale{1.0f}; }; -struct Armour { +struct ArmourPart { std::string slot; Int id = 0; Containers::StaticArray<4, Int> styles{ValueInit}; @@ -184,7 +184,7 @@ class Mass { } _frame; struct { - Containers::StaticArray<38, Armour> parts; + Containers::StaticArray<38, ArmourPart> parts; Containers::StaticArray<16, CustomStyle> customStyles; } _armour; -- 2.39.2 From 6f3da0b4a738a6662348b4f81f45853b714a0483 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 2 Oct 2021 19:23:35 +0200 Subject: [PATCH 051/128] Mass: add some sanity checks. --- src/Mass/Mass.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 63f8c53..18b4b4e 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -147,6 +147,11 @@ void Mass::refreshValues() { return; } + if(frame_styles->items.size() != 4) { + _state = State::Invalid; + return; + } + for(UnsignedInt i = 0; i < 4; i++) { _frame.styles[i] = frame_styles->at(i)->value; } @@ -169,6 +174,11 @@ void Mass::refreshValues() { return; } + if(frame_styles->items.size() != 16) { + _state = State::Invalid; + return; + } + for(UnsignedInt i = 0; i < frame_styles->items.size(); i++) { auto style_prop = frame_styles->at(i); @@ -205,6 +215,11 @@ void Mass::refreshValues() { return; } + if(armour_styles->items.size() != 16) { + _state = State::Invalid; + return; + } + for(UnsignedInt i = 0; i < armour_styles->items.size(); i++) { auto style_prop = armour_styles->at(i); @@ -241,6 +256,11 @@ void Mass::refreshValues() { return; } + if(global_styles->items.size() != 16) { + _state = State::Invalid; + return; + } + for(UnsignedInt i = 0; i < global_styles->items.size(); i++) { auto style_prop = global_styles->at(i); -- 2.39.2 From 0904384e0d32f72a66d03dfe319e9daa024a571d Mon Sep 17 00:00:00 2001 From: William JCM Date: Sun, 3 Oct 2021 00:14:07 +0200 Subject: [PATCH 052/128] SaveTool: fixed a condition for drag and dropping builds. --- src/SaveTool/SaveTool_MainManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SaveTool/SaveTool_MainManager.cpp b/src/SaveTool/SaveTool_MainManager.cpp index ef3c719..52868c2 100644 --- a/src/SaveTool/SaveTool_MainManager.cpp +++ b/src/SaveTool/SaveTool_MainManager.cpp @@ -439,7 +439,7 @@ void SaveTool::drawMassManager() { ImGui::EndDragDropSource(); } - if((!_unsafeMode && _gameState == GameState::NotRunning) && ImGui::BeginDragDropTarget()) { + if((_unsafeMode || _gameState == GameState::NotRunning) && ImGui::BeginDragDropTarget()) { if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("StagedMass")) { if(payload->DataSize != sizeof(std::string)) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Fatal error", -- 2.39.2 From 8c81b7811be88e1ca1a4b4fe7d1592092549dc02 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sun, 3 Oct 2021 16:32:47 +0200 Subject: [PATCH 053/128] Mass: add support for reading armour parts. --- src/Mass/Mass.cpp | 121 +++++++++++++++++++++++++++++++++++++++++++++- src/Mass/Mass.h | 3 ++ 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 18b4b4e..dad48cc 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -22,11 +22,16 @@ #include #include "../UESaveFile/Types/ArrayProperty.h" +#include "../UESaveFile/Types/BoolProperty.h" +#include "../UESaveFile/Types/ByteProperty.h" #include "../UESaveFile/Types/ColourStructProperty.h" #include "../UESaveFile/Types/FloatProperty.h" #include "../UESaveFile/Types/GenericStructProperty.h" #include "../UESaveFile/Types/IntProperty.h" +#include "../UESaveFile/Types/RotatorStructProperty.h" #include "../UESaveFile/Types/StringProperty.h" +#include "../UESaveFile/Types/VectorStructProperty.h" +#include "../UESaveFile/Types/Vector2DStructProperty.h" #include "Mass.h" @@ -205,7 +210,117 @@ void Mass::refreshValues() { } // endregion - // TODO: armour + // region ArmourParts + { + auto armour_array = unit_data->at("Armor_10_12E266C44116DDAF57E99ABB575A4B3C"); + if(!armour_array) { + _state = State::Invalid; + return; + } + + if(armour_array->items.size() != 38) { + _state = State::Invalid; + return; + } + + for(UnsignedInt i = 0; i < armour_array->items.size(); i++) { + auto part_prop = armour_array->at(i); + + ArmourPart part; + + part.slot = part_prop->at("Slot_3_408BA56F4C9605C7E805CF91B642249C")->enumValue; + part.id = part_prop->at("ID_5_ACD101864D3481DE96EDACACC09BDD25")->value; + + auto part_styles = part_prop->at("Styles_47_3E31870441DFD7DB8BEE5C85C26B365B"); + if(!part_styles) { + _state = State::Invalid; + return; + } + + if(part_styles->items.size() != 4) { + _state = State::Invalid; + return; + } + + for(UnsignedInt j = 0; j < part_styles->items.size(); j++) { + part.styles[j] = part_styles->at(j)->value; + } + + auto decals_array = part_prop->at("Decals_42_F358794A4F18497970F56BA9627D3603"); + if(!decals_array) { + _state = State::Invalid; + return; + } + + if(decals_array->items.size() != 8) { + _state = State::Invalid; + return; + } + + for(UnsignedInt j = 0; j < decals_array->items.size(); j++) { + auto decal_prop = decals_array->at(j); + + Decal decal; + + decal.id = decal_prop->at("ID_3_694C0B35404D8A3168AEC89026BC8CF9")->value; + auto colour_prop = decal_prop->at("Color_8_1B0B9D2B43DA6AAB9FA549B374D3E606"); + decal.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; + auto pos_prop = decal_prop->at("Position_41_022C8FE84E1AAFE587261E88F2C72250"); + decal.position = Vector3{pos_prop->x, pos_prop->y, pos_prop->z}; + auto u_prop = decal_prop->at("UAxis_37_EBEB715F45491AECACCC07A1AE4646D1"); + decal.uAxis = Vector3{u_prop->x, u_prop->y, u_prop->z}; + auto v_prop = decal_prop->at("VAxis_39_C31EB2664EE202CAECFBBB84100B5E35"); + decal.vAxis = Vector3{v_prop->x, v_prop->y, v_prop->z}; + auto offset_prop = decal_prop->at("Offset_29_B02BBBB74FC60F5EDBEBAB8020738020"); + decal.offset = Vector2{offset_prop->x, offset_prop->y}; + decal.scale = decal_prop->at("Scale_32_959D1C2747AFD8D62808468235CBBA40")->value; + decal.rotation = decal_prop->at("Rotation_27_12D7C314493D203D5C2326A03C5F910F")->value; + decal.flip = decal_prop->at("Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1")->value; + decal.wrap = decal_prop->at("Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62")->value; + + part.decals[j] = decal; + } + + auto accs_array = part_prop->at("Accessories_52_D902DD4241FA0050C2529596255153F3"); + if(!accs_array) { + _state = State::Invalid; + return; + } + + if(accs_array->items.size() != 8) { + _state = State::Invalid; + return; + } + + for(UnsignedInt j = 0; j < accs_array->items.size(); j++) { + auto acc_prop = accs_array->at(j); + + Accessory accessory; + + accessory.attachIndex = acc_prop->at("AttachIndex_2_4AFCF6024E4BA7426C6B9F80B8179D20")->value; + accessory.id = acc_prop->at("ID_4_5757B32647BAE263266259B8A7DFFFC1")->value; + auto acc_styles = acc_prop->at("Styles_7_91DEB0F24E24D13FC9472882C11D0DFD"); + for(UnsignedInt k = 0; k < acc_styles->items.size(); k++) { + accessory.styles[k] = acc_styles->at(k)->value; + } + auto rel_pos_prop = acc_prop->at("RelativePosition_14_BE8FB2A94074F34B3EDA6683B227D3A1"); + accessory.relativePosition = Vector3{rel_pos_prop->x, rel_pos_prop->y, rel_pos_prop->z}; + auto rel_pos_offset_prop = acc_prop->at("RelativePositionOffset_15_98FD0CE74E44BBAFC2D46FB4CA4E0ED6"); + accessory.relativePositionOffset = Vector3{rel_pos_offset_prop->x, rel_pos_offset_prop->y, rel_pos_offset_prop->z}; + auto rel_rot_prop = acc_prop->at("RelativeRotation_20_C78C73274E6E78E7878F8C98ECA342C0"); + accessory.relativeRotation = Vector3{rel_rot_prop->x, rel_rot_prop->y, rel_rot_prop->z}; + auto rel_rot_offset_prop = acc_prop->at("RelativeRotationOffset_21_E07FA0EC46728B7BA763C6861249ABAA"); + accessory.relativeRotationOffset = Vector3{rel_rot_offset_prop->x, rel_rot_offset_prop->y, rel_rot_offset_prop->z}; + auto local_scale_prop = acc_prop->at("LocalScale_24_DC2D93A742A41A46E7E61D988F15ED53"); + accessory.localScale = Vector3{local_scale_prop->x, local_scale_prop->y, local_scale_prop->z}; + + part.accessories[j] = std::move(accessory); + } + + _armour.parts[i] = std::move(part); + } + } + // endregion // region ArmourStyles { @@ -531,6 +646,10 @@ auto Mass::setFrameCustomStyle(CustomStyle style, UnsignedLong index) -> bool { return setCustomStyle(_frame.customStyles[index], index, "FrameStyle_44_04A44C9440363CCEC5443D98BFAF22AA"); } +auto Mass::armourParts() -> Containers::StaticArrayView<38, ArmourPart> { + return _armour.parts; +} + auto Mass::armourCustomStyles() -> Containers::StaticArrayView<16, CustomStyle> { return _armour.customStyles; } diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 61988da..89e2ed0 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -150,6 +150,9 @@ class Mass { auto frameCustomStyles() -> Containers::StaticArrayView<16, CustomStyle>; auto setFrameCustomStyle(CustomStyle style, UnsignedLong index) -> bool; + auto armourParts() -> Containers::StaticArrayView<38, ArmourPart>; + auto setArmourPart(ArmourPart& part, UnsignedLong index) -> bool; + auto armourCustomStyles() -> Containers::StaticArrayView<16, CustomStyle>; auto setArmourCustomStyle(CustomStyle style, UnsignedLong index) -> bool; -- 2.39.2 From bfe9a2c3a8b4434f7181217781162b2b0a6ee64c Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 4 Oct 2021 18:17:51 +0200 Subject: [PATCH 054/128] Profile: fix a crash that happens when restoring a backup. --- src/Profile/Profile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Profile/Profile.cpp b/src/Profile/Profile.cpp index 024be15..d1998ae 100644 --- a/src/Profile/Profile.cpp +++ b/src/Profile/Profile.cpp @@ -355,7 +355,7 @@ auto Profile::getResource(const char* container, Int id) -> Int { return 0; } - static auto predicate = [&id](UnrealPropertyBase::ptr& prop){ + auto predicate = [&id](UnrealPropertyBase::ptr& prop){ auto res_prop = static_cast(prop.get()); return res_prop->id == id; }; -- 2.39.2 From 2d0d5817f231d9ef5f707e3e8da1e42dcc9c5ebb Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 4 Oct 2021 18:18:20 +0200 Subject: [PATCH 055/128] SaveTool: add a basic guide to the help menu. --- src/SaveTool/SaveTool_drawMainMenu.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/SaveTool/SaveTool_drawMainMenu.cpp b/src/SaveTool/SaveTool_drawMainMenu.cpp index 6fd1128..d51074e 100644 --- a/src/SaveTool/SaveTool_drawMainMenu.cpp +++ b/src/SaveTool/SaveTool_drawMainMenu.cpp @@ -181,6 +181,20 @@ void SaveTool::drawMainMenu() { #endif if(ImGui::BeginMenu("Help")) { + if(ImGui::BeginMenu(ICON_FA_BOOK " ImGui user guide")) { + ImGui::BulletText("CTRL+Click on a slider or drag box to input value as text."); + ImGui::BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields."); + ImGui::BulletText("While inputing text:\n"); + ImGui::Indent(); + ImGui::BulletText("CTRL+Left/Right to word jump."); + ImGui::BulletText("CTRL+A or double-click to select all."); + ImGui::BulletText("CTRL+X/C/V to use clipboard cut/copy/paste."); + ImGui::BulletText("CTRL+Z,CTRL+Y to undo/redo."); + ImGui::BulletText("ESCAPE to revert."); + ImGui::BulletText("You can apply arithmetic operators +,*,/ on numerical values.\nUse +- to subtract."); + ImGui::Unindent(); + ImGui::EndMenu(); + } ImGui::MenuItem(ICON_FA_INFO_CIRCLE " About", nullptr, &_aboutPopup); ImGui::EndMenu(); -- 2.39.2 From 4429e581f3125b1efbfb8dacc153ac4f1e430718 Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 4 Oct 2021 18:18:53 +0200 Subject: [PATCH 056/128] Mass: reorganise a whole chunk of code. --- src/Mass/Mass.cpp | 698 ++++++++++++++++++++++++++-------------------- src/Mass/Mass.h | 28 +- 2 files changed, 411 insertions(+), 315 deletions(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index dad48cc..49abb7e 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -99,308 +99,57 @@ void Mass::refreshValues() { } auto unit_data = _mass->at("UnitData"); - if(!unit_data) { _state = State::Invalid; return; } - // region MassName - { - auto name_prop = unit_data->at("Name_45_A037C5D54E53456407BDF091344529BB"); + auto name_prop = unit_data->at("Name_45_A037C5D54E53456407BDF091344529BB"); - if(!name_prop) { - _name = Containers::NullOpt; - _state = State::Invalid; - return; - } - - _name = name_prop->value; + if(!name_prop) { + _name = Containers::NullOpt; + _state = State::Invalid; + return; } - // endregion - // region FrameData - { - auto frame_prop = unit_data->at("Frame_3_F92B0F6A44A15088AF7F41B9FF290653"); + _name = name_prop->value; - if(!frame_prop) { - _state = State::Invalid; - return; - } - - auto length = frame_prop->at("NeckLength_6_ED6AF79849C27CD1A9D523A09E2BFE58"); - _frame.joints.neck = (length ? length->value : 0.0f); - length = frame_prop->at("BodyLength_7_C16287754CBA96C93BAE36A5C154996A"); - _frame.joints.body = (length ? length->value : 0.0f); - length = frame_prop->at("ShoulderLength_8_220EDF304F1C1226F0D8D39117FB3883"); - _frame.joints.shoulders = (length ? length->value : 0.0f); - length = frame_prop->at("HipLength_14_02AEEEAC4376087B9C51F0AA7CC92818"); - _frame.joints.hips = (length ? length->value : 0.0f); - length = frame_prop->at("ArmUpperLength_10_249FDA3E4F3B399E7B9E5C9B7C765EAE"); - _frame.joints.upperArms = (length ? length->value : 0.0f); - length = frame_prop->at("ArmLowerLength_12_ACD0F02745C28882619376926292FB36"); - _frame.joints.lowerArms = (length ? length->value : 0.0f); - length = frame_prop->at("LegUpperLength_16_A7C4C71249A3776F7A543D96819C0C61"); - _frame.joints.upperLegs = (length ? length->value : 0.0f); - length = frame_prop->at("LegLowerLength_18_D2DF39964EA0F2A2129D0491B08A032F"); - _frame.joints.lowerLegs = (length ? length->value : 0.0f); - - auto frame_styles = frame_prop->at("Styles_32_00A3B3284B37F1E7819458844A20EB48"); - - if(!frame_styles) { - _state = State::Invalid; - return; - } - - if(frame_styles->items.size() != 4) { - _state = State::Invalid; - return; - } - - for(UnsignedInt i = 0; i < 4; i++) { - _frame.styles[i] = frame_styles->at(i)->value; - } - - auto eye_flare_prop = frame_prop->at("EyeFlareColor_36_AF79999C40FCA0E88A2F9A84488A38CA"); - if(!eye_flare_prop) { - _state = State::Invalid; - return; - } - - _frame.eyeFlare = Color4{eye_flare_prop->r, eye_flare_prop->g, eye_flare_prop->b, eye_flare_prop->a}; + getJointSliders(); + if(_state == State::Invalid) { + return; } - // endregion - // region FrameStyles - { - auto frame_styles = unit_data->at("FrameStyle_44_04A44C9440363CCEC5443D98BFAF22AA"); - if(!frame_styles) { - _state = State::Invalid; - return; - } - - if(frame_styles->items.size() != 16) { - _state = State::Invalid; - return; - } - - for(UnsignedInt i = 0; i < frame_styles->items.size(); i++) { - auto style_prop = frame_styles->at(i); - - CustomStyle style; - - style.name = style_prop->at("Name_27_1532115A46EF2B2FA283908DF561A86B")->value; - auto colour_prop = style_prop->at("Color_5_F0D383DF40474C9464AE48A0984A212E"); - style.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; - style.metallic = style_prop->at("Metallic_10_0A4CD1E4482CBF41CA61D0A856DE90B9")->value; - style.gloss = style_prop->at("Gloss_11_9769599842CC275A401C4282A236E240")->value; - style.glow = colour_prop->a == 0.0f ? false : true; - - style.patternId = style_prop->at("PatternID_14_516DB85641DAF8ECFD2920BE2BDF1311")->value; - style.opacity = style_prop->at("Opacity_30_53BD060B4DFCA1C92302D6A0F7831131")->value; - style.offset = Vector2{ - style_prop->at("OffsetX_23_70FC2E814C64BBB82452748D2AF9CD48")->value, - style_prop->at("OffsetY_24_5E1F866C4C054D9B2EE337ADC180C17F")->value - }; - style.rotation = style_prop->at("Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D")->value; - style.scale = style_prop->at("Scale_26_19DF0708409262183E1247B317137671")->value; - - _frame.customStyles[i] = std::move(style); - } + getFrameStyles(); + if(_state == State::Invalid) { + return; } - // endregion - // region ArmourParts - { - auto armour_array = unit_data->at("Armor_10_12E266C44116DDAF57E99ABB575A4B3C"); - if(!armour_array) { - _state = State::Invalid; - return; - } - - if(armour_array->items.size() != 38) { - _state = State::Invalid; - return; - } - - for(UnsignedInt i = 0; i < armour_array->items.size(); i++) { - auto part_prop = armour_array->at(i); - - ArmourPart part; - - part.slot = part_prop->at("Slot_3_408BA56F4C9605C7E805CF91B642249C")->enumValue; - part.id = part_prop->at("ID_5_ACD101864D3481DE96EDACACC09BDD25")->value; - - auto part_styles = part_prop->at("Styles_47_3E31870441DFD7DB8BEE5C85C26B365B"); - if(!part_styles) { - _state = State::Invalid; - return; - } - - if(part_styles->items.size() != 4) { - _state = State::Invalid; - return; - } - - for(UnsignedInt j = 0; j < part_styles->items.size(); j++) { - part.styles[j] = part_styles->at(j)->value; - } - - auto decals_array = part_prop->at("Decals_42_F358794A4F18497970F56BA9627D3603"); - if(!decals_array) { - _state = State::Invalid; - return; - } - - if(decals_array->items.size() != 8) { - _state = State::Invalid; - return; - } - - for(UnsignedInt j = 0; j < decals_array->items.size(); j++) { - auto decal_prop = decals_array->at(j); - - Decal decal; - - decal.id = decal_prop->at("ID_3_694C0B35404D8A3168AEC89026BC8CF9")->value; - auto colour_prop = decal_prop->at("Color_8_1B0B9D2B43DA6AAB9FA549B374D3E606"); - decal.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; - auto pos_prop = decal_prop->at("Position_41_022C8FE84E1AAFE587261E88F2C72250"); - decal.position = Vector3{pos_prop->x, pos_prop->y, pos_prop->z}; - auto u_prop = decal_prop->at("UAxis_37_EBEB715F45491AECACCC07A1AE4646D1"); - decal.uAxis = Vector3{u_prop->x, u_prop->y, u_prop->z}; - auto v_prop = decal_prop->at("VAxis_39_C31EB2664EE202CAECFBBB84100B5E35"); - decal.vAxis = Vector3{v_prop->x, v_prop->y, v_prop->z}; - auto offset_prop = decal_prop->at("Offset_29_B02BBBB74FC60F5EDBEBAB8020738020"); - decal.offset = Vector2{offset_prop->x, offset_prop->y}; - decal.scale = decal_prop->at("Scale_32_959D1C2747AFD8D62808468235CBBA40")->value; - decal.rotation = decal_prop->at("Rotation_27_12D7C314493D203D5C2326A03C5F910F")->value; - decal.flip = decal_prop->at("Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1")->value; - decal.wrap = decal_prop->at("Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62")->value; - - part.decals[j] = decal; - } - - auto accs_array = part_prop->at("Accessories_52_D902DD4241FA0050C2529596255153F3"); - if(!accs_array) { - _state = State::Invalid; - return; - } - - if(accs_array->items.size() != 8) { - _state = State::Invalid; - return; - } - - for(UnsignedInt j = 0; j < accs_array->items.size(); j++) { - auto acc_prop = accs_array->at(j); - - Accessory accessory; - - accessory.attachIndex = acc_prop->at("AttachIndex_2_4AFCF6024E4BA7426C6B9F80B8179D20")->value; - accessory.id = acc_prop->at("ID_4_5757B32647BAE263266259B8A7DFFFC1")->value; - auto acc_styles = acc_prop->at("Styles_7_91DEB0F24E24D13FC9472882C11D0DFD"); - for(UnsignedInt k = 0; k < acc_styles->items.size(); k++) { - accessory.styles[k] = acc_styles->at(k)->value; - } - auto rel_pos_prop = acc_prop->at("RelativePosition_14_BE8FB2A94074F34B3EDA6683B227D3A1"); - accessory.relativePosition = Vector3{rel_pos_prop->x, rel_pos_prop->y, rel_pos_prop->z}; - auto rel_pos_offset_prop = acc_prop->at("RelativePositionOffset_15_98FD0CE74E44BBAFC2D46FB4CA4E0ED6"); - accessory.relativePositionOffset = Vector3{rel_pos_offset_prop->x, rel_pos_offset_prop->y, rel_pos_offset_prop->z}; - auto rel_rot_prop = acc_prop->at("RelativeRotation_20_C78C73274E6E78E7878F8C98ECA342C0"); - accessory.relativeRotation = Vector3{rel_rot_prop->x, rel_rot_prop->y, rel_rot_prop->z}; - auto rel_rot_offset_prop = acc_prop->at("RelativeRotationOffset_21_E07FA0EC46728B7BA763C6861249ABAA"); - accessory.relativeRotationOffset = Vector3{rel_rot_offset_prop->x, rel_rot_offset_prop->y, rel_rot_offset_prop->z}; - auto local_scale_prop = acc_prop->at("LocalScale_24_DC2D93A742A41A46E7E61D988F15ED53"); - accessory.localScale = Vector3{local_scale_prop->x, local_scale_prop->y, local_scale_prop->z}; - - part.accessories[j] = std::move(accessory); - } - - _armour.parts[i] = std::move(part); - } + getEyeFlareColour(); + if(_state == State::Invalid) { + return; } - // endregion - // region ArmourStyles - { - auto armour_styles = unit_data->at("ArmorStyle_42_E2F6AC3647788CB366BD469B3B7E899E"); - if(!armour_styles) { - _state = State::Invalid; - return; - } - - if(armour_styles->items.size() != 16) { - _state = State::Invalid; - return; - } - - for(UnsignedInt i = 0; i < armour_styles->items.size(); i++) { - auto style_prop = armour_styles->at(i); - - CustomStyle style; - - style.name = style_prop->at("Name_27_1532115A46EF2B2FA283908DF561A86B")->value; - auto colour_prop = style_prop->at("Color_5_F0D383DF40474C9464AE48A0984A212E"); - style.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; - style.metallic = style_prop->at("Metallic_10_0A4CD1E4482CBF41CA61D0A856DE90B9")->value; - style.gloss = style_prop->at("Gloss_11_9769599842CC275A401C4282A236E240")->value; - style.glow = colour_prop->a == 0.0f ? false : true; - - style.patternId = style_prop->at("PatternID_14_516DB85641DAF8ECFD2920BE2BDF1311")->value; - style.opacity = style_prop->at("Opacity_30_53BD060B4DFCA1C92302D6A0F7831131")->value; - style.offset = Vector2{ - style_prop->at("OffsetX_23_70FC2E814C64BBB82452748D2AF9CD48")->value, - style_prop->at("OffsetY_24_5E1F866C4C054D9B2EE337ADC180C17F")->value - }; - style.rotation = style_prop->at("Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D")->value; - style.scale = style_prop->at("Scale_26_19DF0708409262183E1247B317137671")->value; - - _armour.customStyles[i] = std::move(style); - } + getFrameCustomStyles(); + if(_state == State::Invalid) { + return; + } + + getArmourParts(); + if(_state == State::Invalid) { + return; + } + + getArmourCustomStyles(); + if(_state == State::Invalid) { + return; } - // endregion // TODO: weapons - // region GlobalStyles + if(!_demo) { - auto global_styles = unit_data->at("GlobalStyles_57_6A681C114035241F7BDAAE9B43A8BF1B"); - if(!global_styles) { - _state = State::Invalid; - return; - } - - if(global_styles->items.size() != 16) { - _state = State::Invalid; - return; - } - - for(UnsignedInt i = 0; i < global_styles->items.size(); i++) { - auto style_prop = global_styles->at(i); - - CustomStyle style; - - style.name = style_prop->at("Name_27_1532115A46EF2B2FA283908DF561A86B")->value; - auto colour_prop = style_prop->at("Color_5_F0D383DF40474C9464AE48A0984A212E"); - style.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; - style.metallic = style_prop->at("Metallic_10_0A4CD1E4482CBF41CA61D0A856DE90B9")->value; - style.gloss = style_prop->at("Gloss_11_9769599842CC275A401C4282A236E240")->value; - style.glow = colour_prop->a == 0.0f ? false : true; - - style.patternId = style_prop->at("PatternID_14_516DB85641DAF8ECFD2920BE2BDF1311")->value; - style.opacity = style_prop->at("Opacity_30_53BD060B4DFCA1C92302D6A0F7831131")->value; - style.offset = Vector2{ - style_prop->at("OffsetX_23_70FC2E814C64BBB82452748D2AF9CD48")->value, - style_prop->at("OffsetY_24_5E1F866C4C054D9B2EE337ADC180C17F")->value - }; - style.rotation = style_prop->at("Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D")->value; - style.scale = style_prop->at("Scale_26_19DF0708409262183E1247B317137671")->value; - - _globalStyles[i] = std::move(style); - } + getGlobalStyles(); } - // endregion // TODO: tuning @@ -449,6 +198,10 @@ auto Mass::state() -> State { return _state; } +auto Mass::demo() const -> bool { + return _demo; +} + auto Mass::dirty() const -> bool { return _dirty; } @@ -457,17 +210,47 @@ void Mass::setDirty(bool dirty) { _dirty = dirty; } -auto Mass::jointSliders() const -> Joints const& { +auto Mass::jointSliders() -> Joints& { return _frame.joints; } -auto Mass::setSliders(Joints joints) -> bool { - _frame.joints = joints; +void Mass::getJointSliders() { + auto unit_data = _mass->at("UnitData"); + if(!unit_data) { + _state = State::Invalid; + return; + } + auto frame_prop = unit_data->at("Frame_3_F92B0F6A44A15088AF7F41B9FF290653"); + if(!frame_prop) { + _state = State::Invalid; + return; + } + + auto length = frame_prop->at("NeckLength_6_ED6AF79849C27CD1A9D523A09E2BFE58"); + _frame.joints.neck = (length ? length->value : 0.0f); + length = frame_prop->at("BodyLength_7_C16287754CBA96C93BAE36A5C154996A"); + _frame.joints.body = (length ? length->value : 0.0f); + length = frame_prop->at("ShoulderLength_8_220EDF304F1C1226F0D8D39117FB3883"); + _frame.joints.shoulders = (length ? length->value : 0.0f); + length = frame_prop->at("HipLength_14_02AEEEAC4376087B9C51F0AA7CC92818"); + _frame.joints.hips = (length ? length->value : 0.0f); + length = frame_prop->at("ArmUpperLength_10_249FDA3E4F3B399E7B9E5C9B7C765EAE"); + _frame.joints.upperArms = (length ? length->value : 0.0f); + length = frame_prop->at("ArmLowerLength_12_ACD0F02745C28882619376926292FB36"); + _frame.joints.lowerArms = (length ? length->value : 0.0f); + length = frame_prop->at("LegUpperLength_16_A7C4C71249A3776F7A543D96819C0C61"); + _frame.joints.upperLegs = (length ? length->value : 0.0f); + length = frame_prop->at("LegLowerLength_18_D2DF39964EA0F2A2129D0491B08A032F"); + _frame.joints.lowerLegs = (length ? length->value : 0.0f); +} + +auto Mass::writeJointSliders() -> bool { auto unit_data = _mass->at("UnitData"); if(!unit_data) { _state = State::Invalid; + _lastError = "No unit data in " + _filename; return false; } @@ -475,6 +258,7 @@ auto Mass::setSliders(Joints joints) -> bool { if(!frame_prop) { _state = State::Invalid; + _lastError = "No frame data in " + _filename; return false; } @@ -566,46 +350,108 @@ auto Mass::setSliders(Joints joints) -> bool { length->value = _frame.joints.lowerLegs; } - return _mass->saveToFile(); + if(!_mass->saveToFile()) { + _lastError = "Couldn't write data to " + _filename; + return false; + } + + return true; } auto Mass::frameStyles() -> Containers::StaticArrayView<4, Int> { return _frame.styles; } -auto Mass::setFrameStyle(Int index, Int style_id) -> bool { - _frame.styles[index] = style_id; - +void Mass::getFrameStyles() { auto unit_data = _mass->at("UnitData"); if(!unit_data) { _state = State::Invalid; + return; + } + + auto frame_prop = unit_data->at("Frame_3_F92B0F6A44A15088AF7F41B9FF290653"); + if(!frame_prop) { + _state = State::Invalid; + return; + } + + auto frame_styles = frame_prop->at("Styles_32_00A3B3284B37F1E7819458844A20EB48"); + if(!frame_styles) { + _state = State::Invalid; + return; + } + + if(frame_styles->items.size() != 4) { + _state = State::Invalid; + return; + } + + for(UnsignedInt i = 0; i < 4; i++) { + _frame.styles[i] = frame_styles->at(i)->value; + } +} + +auto Mass::writeFrameStyles() -> bool { + auto unit_data = _mass->at("UnitData"); + if(!unit_data) { + _state = State::Invalid; + _lastError = "No unit data in " + _filename; return false; } auto frame = unit_data->at("Frame_3_F92B0F6A44A15088AF7F41B9FF290653"); if(!frame) { _state = State::Invalid; + _lastError = "No frame data in " + _filename; return false; } auto frame_styles = frame->at("Styles_32_00A3B3284B37F1E7819458844A20EB48"); if(!frame_styles) { _state = State::Invalid; + _lastError = "No frame styles in " + _filename; return false; } - frame_styles->at(index)->value = style_id; + for(UnsignedInt i = 0; i < frame_styles->items.size(); i++) { + frame_styles->at(i)->value = _frame.styles[i]; + } - return _mass->saveToFile(); + if(!_mass->saveToFile()) { + _lastError = "Couldn't write data to " + _filename; + return false; + } + + return true; } -auto Mass::eyeFlareColour() const -> const Color4& { +auto Mass::eyeFlareColour() -> Color4& { return _frame.eyeFlare; } -auto Mass::setEyeFlareColour(Color4 new_colour) -> bool { - _frame.eyeFlare = new_colour; +void Mass::getEyeFlareColour() { + auto unit_data = _mass->at("UnitData"); + if(!unit_data) { + _state = State::Invalid; + return; + } + auto frame_prop = unit_data->at("Frame_3_F92B0F6A44A15088AF7F41B9FF290653"); + if(!frame_prop) { + _state = State::Invalid; + return; + } + + auto eye_flare_prop = frame_prop->at("EyeFlareColor_36_AF79999C40FCA0E88A2F9A84488A38CA"); + if(!eye_flare_prop) { + _state = State::Invalid; + return; + } + + _frame.eyeFlare = Color4{eye_flare_prop->r, eye_flare_prop->g, eye_flare_prop->b, eye_flare_prop->a}; +} + +auto Mass::writeEyeFlareColour() -> bool { auto unit_data = _mass->at("UnitData"); if(!unit_data) { _state = State::Invalid; @@ -624,10 +470,10 @@ auto Mass::setEyeFlareColour(Color4 new_colour) -> bool { return false; } - eye_flare_prop->r = new_colour.r(); - eye_flare_prop->g = new_colour.g(); - eye_flare_prop->b = new_colour.b(); - eye_flare_prop->a = new_colour.a(); + eye_flare_prop->r = _frame.eyeFlare.r(); + eye_flare_prop->g = _frame.eyeFlare.g(); + eye_flare_prop->b = _frame.eyeFlare.b(); + eye_flare_prop->a = _frame.eyeFlare.a(); return _mass->saveToFile(); } @@ -636,13 +482,54 @@ auto Mass::frameCustomStyles() -> Containers::StaticArrayView<16, CustomStyle> { return _frame.customStyles; } -auto Mass::setFrameCustomStyle(CustomStyle style, UnsignedLong index) -> bool { +void Mass::getFrameCustomStyles() { + auto unit_data = _mass->at("UnitData"); + if(!unit_data) { + _state = State::Invalid; + return; + } + + auto frame_styles = unit_data->at("FrameStyle_44_04A44C9440363CCEC5443D98BFAF22AA"); + if(!frame_styles) { + _state = State::Invalid; + return; + } + + if(frame_styles->items.size() != 16) { + _state = State::Invalid; + return; + } + + for(UnsignedInt i = 0; i < frame_styles->items.size(); i++) { + auto style_prop = frame_styles->at(i); + + CustomStyle style; + + style.name = style_prop->at("Name_27_1532115A46EF2B2FA283908DF561A86B")->value; + auto colour_prop = style_prop->at("Color_5_F0D383DF40474C9464AE48A0984A212E"); + style.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; + style.metallic = style_prop->at("Metallic_10_0A4CD1E4482CBF41CA61D0A856DE90B9")->value; + style.gloss = style_prop->at("Gloss_11_9769599842CC275A401C4282A236E240")->value; + style.glow = colour_prop->a == 0.0f ? false : true; + + style.patternId = style_prop->at("PatternID_14_516DB85641DAF8ECFD2920BE2BDF1311")->value; + style.opacity = style_prop->at("Opacity_30_53BD060B4DFCA1C92302D6A0F7831131")->value; + style.offset = Vector2{ + style_prop->at("OffsetX_23_70FC2E814C64BBB82452748D2AF9CD48")->value, + style_prop->at("OffsetY_24_5E1F866C4C054D9B2EE337ADC180C17F")->value + }; + style.rotation = style_prop->at("Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D")->value; + style.scale = style_prop->at("Scale_26_19DF0708409262183E1247B317137671")->value; + + _frame.customStyles[i] = std::move(style); + } +} + +auto Mass::writeFrameCustomStyle(UnsignedLong index) -> bool { if(index > _frame.customStyles.size()) { return false; } - _frame.customStyles[index] = std::move(style); - return setCustomStyle(_frame.customStyles[index], index, "FrameStyle_44_04A44C9440363CCEC5443D98BFAF22AA"); } @@ -650,17 +537,175 @@ auto Mass::armourParts() -> Containers::StaticArrayView<38, ArmourPart> { return _armour.parts; } +void Mass::getArmourParts() { + auto unit_data = _mass->at("UnitData"); + if(!unit_data) { + _state = State::Invalid; + return; + } + + auto armour_array = unit_data->at("Armor_10_12E266C44116DDAF57E99ABB575A4B3C"); + if(!armour_array) { + _state = State::Invalid; + return; + } + + if(armour_array->items.size() != 38) { + _state = State::Invalid; + return; + } + + for(UnsignedInt i = 0; i < armour_array->items.size(); i++) { + auto part_prop = armour_array->at(i); + + ArmourPart part; + + part.slot = part_prop->at("Slot_3_408BA56F4C9605C7E805CF91B642249C")->enumValue; + part.id = part_prop->at("ID_5_ACD101864D3481DE96EDACACC09BDD25")->value; + + auto part_styles = part_prop->at("Styles_47_3E31870441DFD7DB8BEE5C85C26B365B"); + if(!part_styles) { + _state = State::Invalid; + return; + } + + if(part_styles->items.size() != 4) { + _state = State::Invalid; + return; + } + + for(UnsignedInt j = 0; j < part_styles->items.size(); j++) { + part.styles[j] = part_styles->at(j)->value; + } + + auto decals_array = part_prop->at("Decals_42_F358794A4F18497970F56BA9627D3603"); + if(!decals_array) { + _state = State::Invalid; + return; + } + + if(decals_array->items.size() != 8) { + _state = State::Invalid; + return; + } + + for(UnsignedInt j = 0; j < decals_array->items.size(); j++) { + auto decal_prop = decals_array->at(j); + + Decal decal; + + decal.id = decal_prop->at("ID_3_694C0B35404D8A3168AEC89026BC8CF9")->value; + auto colour_prop = decal_prop->at("Color_8_1B0B9D2B43DA6AAB9FA549B374D3E606"); + decal.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; + auto pos_prop = decal_prop->at("Position_41_022C8FE84E1AAFE587261E88F2C72250"); + decal.position = Vector3{pos_prop->x, pos_prop->y, pos_prop->z}; + auto u_prop = decal_prop->at("UAxis_37_EBEB715F45491AECACCC07A1AE4646D1"); + decal.uAxis = Vector3{u_prop->x, u_prop->y, u_prop->z}; + auto v_prop = decal_prop->at("VAxis_39_C31EB2664EE202CAECFBBB84100B5E35"); + decal.vAxis = Vector3{v_prop->x, v_prop->y, v_prop->z}; + auto offset_prop = decal_prop->at("Offset_29_B02BBBB74FC60F5EDBEBAB8020738020"); + decal.offset = Vector2{offset_prop->x, offset_prop->y}; + decal.scale = decal_prop->at("Scale_32_959D1C2747AFD8D62808468235CBBA40")->value; + decal.rotation = decal_prop->at("Rotation_27_12D7C314493D203D5C2326A03C5F910F")->value; + decal.flip = decal_prop->at("Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1")->value; + decal.wrap = decal_prop->at("Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62")->value; + + part.decals[j] = decal; + } + + if(!_demo) { + auto accs_array = part_prop->at("Accessories_52_D902DD4241FA0050C2529596255153F3"); + if(!accs_array) { + _demo = true; + } + + if(accs_array->items.size() != 8) { + _state = State::Invalid; + return; + } + + for(UnsignedInt j = 0; j < accs_array->items.size(); j++) { + auto acc_prop = accs_array->at(j); + + Accessory accessory; + + accessory.attachIndex = acc_prop->at("AttachIndex_2_4AFCF6024E4BA7426C6B9F80B8179D20")->value; + accessory.id = acc_prop->at("ID_4_5757B32647BAE263266259B8A7DFFFC1")->value; + auto acc_styles = acc_prop->at("Styles_7_91DEB0F24E24D13FC9472882C11D0DFD"); + for(UnsignedInt k = 0; k < acc_styles->items.size(); k++) { + accessory.styles[k] = acc_styles->at(k)->value; + } + auto rel_pos_prop = acc_prop->at("RelativePosition_14_BE8FB2A94074F34B3EDA6683B227D3A1"); + accessory.relativePosition = Vector3{rel_pos_prop->x, rel_pos_prop->y, rel_pos_prop->z}; + auto rel_pos_offset_prop = acc_prop->at("RelativePositionOffset_15_98FD0CE74E44BBAFC2D46FB4CA4E0ED6"); + accessory.relativePositionOffset = Vector3{rel_pos_offset_prop->x, rel_pos_offset_prop->y, rel_pos_offset_prop->z}; + auto rel_rot_prop = acc_prop->at("RelativeRotation_20_C78C73274E6E78E7878F8C98ECA342C0"); + accessory.relativeRotation = Vector3{rel_rot_prop->x, rel_rot_prop->y, rel_rot_prop->z}; + auto rel_rot_offset_prop = acc_prop->at("RelativeRotationOffset_21_E07FA0EC46728B7BA763C6861249ABAA"); + accessory.relativeRotationOffset = Vector3{rel_rot_offset_prop->x, rel_rot_offset_prop->y, rel_rot_offset_prop->z}; + auto local_scale_prop = acc_prop->at("LocalScale_24_DC2D93A742A41A46E7E61D988F15ED53"); + accessory.localScale = Vector3{local_scale_prop->x, local_scale_prop->y, local_scale_prop->z}; + + part.accessories[j] = std::move(accessory); + } + } + + _armour.parts[i] = std::move(part); + } +} + auto Mass::armourCustomStyles() -> Containers::StaticArrayView<16, CustomStyle> { return _armour.customStyles; } -auto Mass::setArmourCustomStyle(CustomStyle style, UnsignedLong index) -> bool { +void Mass::getArmourCustomStyles() { + auto unit_data = _mass->at("UnitData"); + if(!unit_data) { + _state = State::Invalid; + return; + } + + auto armour_styles = unit_data->at("ArmorStyle_42_E2F6AC3647788CB366BD469B3B7E899E"); + if(!armour_styles) { + _state = State::Invalid; + return; + } + + if(armour_styles->items.size() != 16) { + _state = State::Invalid; + return; + } + + for(UnsignedInt i = 0; i < armour_styles->items.size(); i++) { + auto style_prop = armour_styles->at(i); + + CustomStyle style; + + style.name = style_prop->at("Name_27_1532115A46EF2B2FA283908DF561A86B")->value; + auto colour_prop = style_prop->at("Color_5_F0D383DF40474C9464AE48A0984A212E"); + style.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; + style.metallic = style_prop->at("Metallic_10_0A4CD1E4482CBF41CA61D0A856DE90B9")->value; + style.gloss = style_prop->at("Gloss_11_9769599842CC275A401C4282A236E240")->value; + style.glow = colour_prop->a == 0.0f ? false : true; + + style.patternId = style_prop->at("PatternID_14_516DB85641DAF8ECFD2920BE2BDF1311")->value; + style.opacity = style_prop->at("Opacity_30_53BD060B4DFCA1C92302D6A0F7831131")->value; + style.offset = Vector2{ + style_prop->at("OffsetX_23_70FC2E814C64BBB82452748D2AF9CD48")->value, + style_prop->at("OffsetY_24_5E1F866C4C054D9B2EE337ADC180C17F")->value + }; + style.rotation = style_prop->at("Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D")->value; + style.scale = style_prop->at("Scale_26_19DF0708409262183E1247B317137671")->value; + + _armour.customStyles[i] = std::move(style); + } +} + +auto Mass::writeArmourCustomStyle(UnsignedLong index) -> bool { if(index > _armour.customStyles.size()) { return false; } - _armour.customStyles[index] = std::move(style); - return setCustomStyle(_armour.customStyles[index], index, "ArmorStyle_42_E2F6AC3647788CB366BD469B3B7E899E"); } @@ -668,13 +713,54 @@ auto Mass::globalStyles() -> Containers::StaticArrayView<16, CustomStyle> { return _globalStyles; } -auto Mass::setGlobalStyle(CustomStyle style, UnsignedLong index) -> bool { +void Mass::getGlobalStyles() { + auto unit_data = _mass->at("UnitData"); + if(!unit_data) { + _state = State::Invalid; + return; + } + + auto global_styles = unit_data->at("GlobalStyles_57_6A681C114035241F7BDAAE9B43A8BF1B"); + if(!global_styles) { + _state = State::Invalid; + return; + } + + if(global_styles->items.size() != 16) { + _state = State::Invalid; + return; + } + + for(UnsignedInt i = 0; i < global_styles->items.size(); i++) { + auto style_prop = global_styles->at(i); + + CustomStyle style; + + style.name = style_prop->at("Name_27_1532115A46EF2B2FA283908DF561A86B")->value; + auto colour_prop = style_prop->at("Color_5_F0D383DF40474C9464AE48A0984A212E"); + style.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; + style.metallic = style_prop->at("Metallic_10_0A4CD1E4482CBF41CA61D0A856DE90B9")->value; + style.gloss = style_prop->at("Gloss_11_9769599842CC275A401C4282A236E240")->value; + style.glow = colour_prop->a == 0.0f ? false : true; + + style.patternId = style_prop->at("PatternID_14_516DB85641DAF8ECFD2920BE2BDF1311")->value; + style.opacity = style_prop->at("Opacity_30_53BD060B4DFCA1C92302D6A0F7831131")->value; + style.offset = Vector2{ + style_prop->at("OffsetX_23_70FC2E814C64BBB82452748D2AF9CD48")->value, + style_prop->at("OffsetY_24_5E1F866C4C054D9B2EE337ADC180C17F")->value + }; + style.rotation = style_prop->at("Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D")->value; + style.scale = style_prop->at("Scale_26_19DF0708409262183E1247B317137671")->value; + + _globalStyles[i] = std::move(style); + } +} + +auto Mass::writeGlobalStyle(UnsignedLong index) -> bool { if(index > _globalStyles.size()) { return false; } - _globalStyles[index] = std::move(style); - return setCustomStyle(_globalStyles[index], index, "GlobalStyles_57_6A681C114035241F7BDAAE9B43A8BF1B"); } diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 89e2ed0..b49029e 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -135,29 +135,38 @@ class Mass { auto state() -> State; + auto demo() const -> bool; + auto dirty() const -> bool; void setDirty(bool dirty = true); - auto jointSliders() const -> Joints const&; - auto setSliders(Joints joints) -> bool; + auto jointSliders() -> Joints&; + void getJointSliders(); + auto writeJointSliders() -> bool; auto frameStyles() -> Containers::StaticArrayView<4, Int>; - auto setFrameStyle(Int index, Int style_id) -> bool; + void getFrameStyles(); + auto writeFrameStyles() -> bool; - auto eyeFlareColour() const -> Color4 const&; - auto setEyeFlareColour(Color4 new_colour) -> bool; + auto eyeFlareColour() -> Color4&; + void getEyeFlareColour(); + auto writeEyeFlareColour() -> bool; auto frameCustomStyles() -> Containers::StaticArrayView<16, CustomStyle>; - auto setFrameCustomStyle(CustomStyle style, UnsignedLong index) -> bool; + void getFrameCustomStyles(); + auto writeFrameCustomStyle(UnsignedLong index) -> bool; auto armourParts() -> Containers::StaticArrayView<38, ArmourPart>; - auto setArmourPart(ArmourPart& part, UnsignedLong index) -> bool; + void getArmourParts(); + auto setArmourPart(UnsignedLong index) -> bool; auto armourCustomStyles() -> Containers::StaticArrayView<16, CustomStyle>; - auto setArmourCustomStyle(CustomStyle style, UnsignedLong index) -> bool; + void getArmourCustomStyles(); + auto writeArmourCustomStyle(UnsignedLong index) -> bool; auto globalStyles() -> Containers::StaticArrayView<16, CustomStyle>; - auto setGlobalStyle(CustomStyle style, UnsignedLong index) -> bool; + void getGlobalStyles(); + auto writeGlobalStyle(UnsignedLong index) -> bool; auto updateSteamId(const std::string& steam_id) -> bool; @@ -171,6 +180,7 @@ class Mass { std::string _folder; std::string _filename; State _state = State::Empty; + bool _demo = false; bool _dirty = false; -- 2.39.2 From d03e75a8e91207b66064ef6cac4ef62da7e6f2e3 Mon Sep 17 00:00:00 2001 From: William JCM Date: Tue, 5 Oct 2021 10:03:52 +0200 Subject: [PATCH 057/128] Mass: not all parts can have 8 decals in the demo. For now, at least. Just like the code that treats a lack of global styles as a demo thing, I'll remove it in due time. --- src/Mass/Mass.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 49abb7e..4f5b1d1 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -585,8 +585,7 @@ void Mass::getArmourParts() { } if(decals_array->items.size() != 8) { - _state = State::Invalid; - return; + _demo = true; } for(UnsignedInt j = 0; j < decals_array->items.size(); j++) { -- 2.39.2 From a22aa6f7aed318a81cfc9ac6c3301b6a7056e082 Mon Sep 17 00:00:00 2001 From: William JCM Date: Tue, 5 Oct 2021 10:10:18 +0200 Subject: [PATCH 058/128] Mass: update CustomStyle default values to match game. --- src/Mass/Mass.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index b49029e..7a0f315 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -46,15 +46,15 @@ struct Joints { struct CustomStyle { std::string name; Color4 colour{0.0f}; - Float metallic = 0.0f; - Float gloss = 0.0f; + Float metallic = 0.5f; + Float gloss = 0.5f; bool glow = false; Int patternId = 0; Float opacity = 0.5f; - Vector2 offset{0.0f}; + Vector2 offset{0.5f}; Float rotation = 0.0f; - Float scale = 1.0f; + Float scale = 0.5f; }; struct Decal { -- 2.39.2 From 3714162b50acd4cfb8de6a2780e6b1906195e5db Mon Sep 17 00:00:00 2001 From: William JCM Date: Tue, 5 Oct 2021 10:31:06 +0200 Subject: [PATCH 059/128] UnrealPropertyBase: initialise valueLength. --- src/UESaveFile/Types/UnrealPropertyBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UESaveFile/Types/UnrealPropertyBase.h b/src/UESaveFile/Types/UnrealPropertyBase.h index 9165e5f..4ef94f8 100644 --- a/src/UESaveFile/Types/UnrealPropertyBase.h +++ b/src/UESaveFile/Types/UnrealPropertyBase.h @@ -33,5 +33,5 @@ struct UnrealPropertyBase { Containers::Optional name = Containers::NullOpt; std::string propertyType; - UnsignedLong valueLength; + UnsignedLong valueLength = 0; }; -- 2.39.2 From 8bae723018193cd662663413060200efeba45f86 Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 11 Oct 2021 17:17:14 +0200 Subject: [PATCH 060/128] Mass: finish implementing armour part support. --- src/Mass/Mass.cpp | 97 +++++++++++++++++++++++++++++++++++++++++++++++ src/Mass/Mass.h | 2 +- 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 4f5b1d1..35e52e3 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -653,6 +653,103 @@ void Mass::getArmourParts() { } } +auto Mass::writeArmourPart(UnsignedLong index) -> bool { + if(index > _armour.parts.size()) { + return false; + } + + auto unit_data = _mass->at("UnitData"); + + auto armour_array = unit_data->at("Armor_10_12E266C44116DDAF57E99ABB575A4B3C"); + + auto part_prop = armour_array->at(index); + + auto& part = _armour.parts[index]; + + if(part_prop->at("Slot_3_408BA56F4C9605C7E805CF91B642249C")->enumValue != part.slot) { + return false; + } + + part_prop->at("ID_5_ACD101864D3481DE96EDACACC09BDD25")->value = part.id; + + auto part_styles = part_prop->at("Styles_47_3E31870441DFD7DB8BEE5C85C26B365B"); + for(UnsignedInt i = 0; i < part.styles.size(); i++) { + part_styles->at(i)->value = part.styles[i]; + } + + auto decals_array = part_prop->at("Decals_42_F358794A4F18497970F56BA9627D3603"); + for(UnsignedInt i = 0; i < decals_array->items.size(); i++) { + auto decal_prop = decals_array->at(i); + + auto& decal = part.decals[i]; + + decal_prop->at("ID_3_694C0B35404D8A3168AEC89026BC8CF9")->value = decal.id; + auto colour_prop = decal_prop->at("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("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("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("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("Offset_29_B02BBBB74FC60F5EDBEBAB8020738020"); + offset_prop->x = decal.offset.x(); + offset_prop->y = decal.offset.y(); + decal_prop->at("Scale_32_959D1C2747AFD8D62808468235CBBA40")->value = decal.scale; + decal_prop->at("Rotation_27_12D7C314493D203D5C2326A03C5F910F")->value = decal.rotation; + decal_prop->at("Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1")->value = decal.flip; + decal_prop->at("Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62")->value = decal.wrap; + } + + if(!_demo) { + auto accs_array = part_prop->at("Accessories_52_D902DD4241FA0050C2529596255153F3"); + + for(UnsignedInt i = 0; i < accs_array->items.size(); i++) { + auto acc_prop = accs_array->at(i); + + auto& accessory = part.accessories[i]; + + acc_prop->at("AttachIndex_2_4AFCF6024E4BA7426C6B9F80B8179D20")->value = accessory.attachIndex; + acc_prop->at("ID_4_5757B32647BAE263266259B8A7DFFFC1")->value = accessory.id; + auto acc_styles = acc_prop->at("Styles_7_91DEB0F24E24D13FC9472882C11D0DFD"); + for(UnsignedInt j = 0; j < acc_styles->items.size(); j++) { + acc_styles->at(j)->value = accessory.styles[j]; + } + auto rel_pos_prop = acc_prop->at("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("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("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("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("LocalScale_24_DC2D93A742A41A46E7E61D988F15ED53"); + local_scale_prop->x = accessory.localScale.x(); + local_scale_prop->y = accessory.localScale.y(); + local_scale_prop->z = accessory.localScale.z(); + } + } + + return _mass->saveToFile(); +} + auto Mass::armourCustomStyles() -> Containers::StaticArrayView<16, CustomStyle> { return _armour.customStyles; } diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 7a0f315..f557522 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -158,7 +158,7 @@ class Mass { auto armourParts() -> Containers::StaticArrayView<38, ArmourPart>; void getArmourParts(); - auto setArmourPart(UnsignedLong index) -> bool; + auto writeArmourPart(UnsignedLong index) -> bool; auto armourCustomStyles() -> Containers::StaticArrayView<16, CustomStyle>; void getArmourCustomStyles(); -- 2.39.2 From 3ac5288f1279c69fd839059f67be366e668a7a40 Mon Sep 17 00:00:00 2001 From: William JCM Date: Tue, 12 Oct 2021 16:35:49 +0200 Subject: [PATCH 061/128] Mass: prepare decals for edition. --- src/Mass/Mass.cpp | 1 + src/Mass/Mass.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 35e52e3..031846d 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -585,6 +585,7 @@ void Mass::getArmourParts() { } if(decals_array->items.size() != 8) { + part.demoDecals = decals_array->items.size(); _demo = true; } diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index f557522..6ab0218 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -85,6 +85,7 @@ 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}; }; -- 2.39.2 From 8fedbdd4e5025867618c2a471528c5a96dbf18d1 Mon Sep 17 00:00:00 2001 From: William JCM Date: Tue, 12 Oct 2021 16:52:40 +0200 Subject: [PATCH 062/128] Add armour sets and slots maps. --- src/CMakeLists.txt | 2 ++ src/Maps/ArmourSets.h | 51 +++++++++++++++++++++++++++++++++++ src/Maps/ArmourSlots.h | 61 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 src/Maps/ArmourSets.h create mode 100644 src/Maps/ArmourSlots.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ddee28b..3cd84a0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -126,6 +126,8 @@ add_executable(MassBuilderSaveTool WIN32 MassManager/MassManager.cpp Mass/Mass.h Mass/Mass.cpp + Maps/ArmourSets.h + Maps/ArmourSlots.h Maps/LastMissionId.h Maps/StoryProgress.h Maps/StyleNames.h diff --git a/src/Maps/ArmourSets.h b/src/Maps/ArmourSets.h new file mode 100644 index 0000000..e1fbda0 --- /dev/null +++ b/src/Maps/ArmourSets.h @@ -0,0 +1,51 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include + +using namespace Magnum; + +struct ArmourSet { + const char* name; + bool neck_compatible; +}; + +static const std::map armour_sets { + {-1, {"", true}}, + {0, {"Vanguard", true}}, + {1, {"Assault Mk.I", true}}, + {2, {"Assault Mk.II", false}}, + {3, {"Assault Mk.III", false}}, + {7, {"Titan 001", true}}, + {8, {"Titan 002", false}}, + {9, {"Titan 003", false}}, + {13, {"Blitz X", true}}, + {14, {"Blitz EX", false}}, + {15, {"Blitz EXS", false}}, + {16, {"Kaiser S-R0", true}}, + {17, {"Kaiser S-R1", false}}, + {18, {"Kaiser S-R2", false}}, + {19, {"Hammerfall MG-A", true}}, + {20, {"Hammerfall MG-S", false}}, + {21, {"Hammerfall MG-X", false}}, + {22, {"Panzer S-UC", true}}, + {23, {"Panzer L-UC", false}}, + {24, {"Panzer H-UC", false}}, +}; diff --git a/src/Maps/ArmourSlots.h b/src/Maps/ArmourSlots.h new file mode 100644 index 0000000..cfee3a7 --- /dev/null +++ b/src/Maps/ArmourSlots.h @@ -0,0 +1,61 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include +#include + +static const std::unordered_map 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"}, +}; -- 2.39.2 From 05a2b1cfb04d6e848d3de07613555c475180cc1c Mon Sep 17 00:00:00 2001 From: William JCM Date: Tue, 12 Oct 2021 16:53:19 +0200 Subject: [PATCH 063/128] SaveTool: change a drag widget to a slider. --- src/SaveTool/SaveTool_MainManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SaveTool/SaveTool_MainManager.cpp b/src/SaveTool/SaveTool_MainManager.cpp index 52868c2..96a470c 100644 --- a/src/SaveTool/SaveTool_MainManager.cpp +++ b/src/SaveTool/SaveTool_MainManager.cpp @@ -107,8 +107,8 @@ auto SaveTool::drawIntEditPopup(int* value_to_edit, int max) -> bool { drawHelpMarker("You can either drag the widget left or right to change the value,\n" "or click on it while holding Ctrl to edit the value directly."); ImGui::SameLine(); - drawUnsafeWidget([](auto... args){ return ImGui::DragInt("", args...); }, - value_to_edit, 1.0f, 0, max, "%d", ImGuiSliderFlags_AlwaysClamp); + drawUnsafeWidget([](auto... args){ return ImGui::SliderInt("", args...); }, + value_to_edit, 0, max, "%d", ImGuiSliderFlags_AlwaysClamp); ImGui::SameLine(); if(drawUnsafeWidget([]{ return ImGui::Button("Apply"); })) { apply = true; -- 2.39.2 From dd460b4313697cf73c2ace0f7032dbb41b03abdc Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 13 Oct 2021 14:49:31 +0200 Subject: [PATCH 064/128] MassManager: fix a bug with importing. --- src/MassManager/MassManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MassManager/MassManager.cpp b/src/MassManager/MassManager.cpp index 1327781..4e94fca 100644 --- a/src/MassManager/MassManager.cpp +++ b/src/MassManager/MassManager.cpp @@ -90,7 +90,7 @@ auto MassManager::importMass(const std::string& staged_fn, int hangar) -> bool { Utility::Directory::rm(_hangars[hangar].filename()); } - if(!Utility::Directory::move(source + ".tmp", _hangars[hangar].filename())) { + if(!Utility::Directory::move(source + ".tmp", Utility::Directory::join(_saveDirectory, _hangars[hangar].filename()))) { _lastError = Utility::formatString("Couldn't move {} to hangar {:.2d}", staged_fn, hangar + 1); return false; } -- 2.39.2 From b92c37e4b6f05e9e24279a26277318f3dfbad6e1 Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 14 Oct 2021 15:06:03 +0200 Subject: [PATCH 065/128] Mass: fix another bug with importing. --- src/Mass/Mass.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 031846d..8e9e417 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -864,19 +864,13 @@ auto Mass::writeGlobalStyle(UnsignedLong index) -> bool { auto Mass::updateSteamId(const std::string& steam_id) -> bool { _steamId = steam_id; - auto unit_data = _mass->at("UnitData"); - if(!unit_data) { + auto account = _mass->at("Account"); + if(!account) { _state = State::Invalid; return false; } - auto account_prop = unit_data->at("Account"); - if(!account_prop) { - _state = State::Invalid; - return false; - } - - account_prop->value = steam_id; + account->value = steam_id; return _mass->saveToFile(); } -- 2.39.2 From 4df90efd67319f4700bd5ed8bdd702662966fe80 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 16 Oct 2021 11:36:31 +0200 Subject: [PATCH 066/128] Mass: rename a field. --- src/Mass/Mass.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 6ab0218..2423f93 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -204,7 +204,7 @@ class Mass { } _armour; struct { - Containers::StaticArray<8, Weapon> meleeWeapons; + Containers::StaticArray<8, Weapon> melee; Containers::StaticArray<1, Weapon> shields; Containers::StaticArray<4, Weapon> bulletShooters; Containers::StaticArray<4, Weapon> energyShooters; -- 2.39.2 From 80bb85c0d82b4590163408da1273d528017ad9b4 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sun, 17 Oct 2021 08:37:12 +0200 Subject: [PATCH 067/128] Mass: add weapon reading. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Writing will come SOONâ„¢. --- src/Mass/Mass.cpp | 183 +++++++++++++++++++++++++++++++++++++++++++++- src/Mass/Mass.h | 27 +++++++ 2 files changed, 209 insertions(+), 1 deletion(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 8e9e417..1ab5df0 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -144,7 +144,35 @@ void Mass::refreshValues() { return; } - // TODO: weapons + getMeleeWeapons(); + if(_state == State::Invalid) { + return; + } + + getShields(); + if(_state == State::Invalid) { + return; + } + + getBulletShooters(); + if(_state == State::Invalid) { + return; + } + + getEnergyShooters(); + if(_state == State::Invalid) { + return; + } + + getBulletLaunchers(); + if(_state == State::Invalid) { + return; + } + + getEnergyLaunchers(); + if(_state == State::Invalid) { + return; + } if(!_demo) { @@ -806,6 +834,54 @@ auto Mass::writeArmourCustomStyle(UnsignedLong index) -> bool { return setCustomStyle(_armour.customStyles[index], index, "ArmorStyle_42_E2F6AC3647788CB366BD469B3B7E899E"); } +auto Mass::meleeWeapons() -> Containers::StaticArrayView<8, Weapon> { + return _weapons.melee; +} + +void Mass::getMeleeWeapons() { + getWeaponType("WeaponCC_22_0BBEC58C4A0EA1DB9E037B9339EE26A7", _weapons.melee); +} + +auto Mass::shields() -> Containers::StaticArrayView<1, Weapon> { + return _weapons.shields; +} + +void Mass::getShields() { + getWeaponType("Shield_53_839BFD7945481BAEA3E43A9C5CA8E92E", _weapons.shields); +} + +auto Mass::bulletShooters() -> Containers::StaticArrayView<4, Weapon> { + return _weapons.bulletShooters; +} + +void Mass::getBulletShooters() { + getWeaponType("WeaponBS_35_6EF6E0104FD7A138DF47F88CB57A83ED", _weapons.bulletShooters); +} + +auto Mass::energyShooters() -> Containers::StaticArrayView<4, Weapon> { + return _weapons.energyShooters; +} + +void Mass::getEnergyShooters() { + getWeaponType("WeaponES_37_1A295D544528623880A0B1AC2C7DEE99", _weapons.energyShooters); +} + +auto Mass::bulletLaunchers() -> Containers::StaticArrayView<4, Weapon> { + return _weapons.bulletLaunchers; +} + +void Mass::getBulletLaunchers() { + getWeaponType("WeaponBL_36_5FD7C41E4613A75B44AB0E90B362846E", _weapons.bulletLaunchers); +} + +auto Mass::energyLaunchers() -> Containers::StaticArrayView<4, Weapon> { + return _weapons.energyLaunchers; +} + +void Mass::getEnergyLaunchers() { + getWeaponType("WeaponEL_38_9D23F3884ACA15902C9E6CA6E4995995", _weapons.energyLaunchers); +} + auto Mass::globalStyles() -> Containers::StaticArrayView<16, CustomStyle> { return _globalStyles; } @@ -908,3 +984,108 @@ auto Mass::setCustomStyle(const CustomStyle& style, UnsignedLong index, const ch return _mass->saveToFile(); } + +void Mass::getWeaponType(const char* prop_name, Containers::ArrayView weapon_array) { + auto unit_data = _mass->at("UnitData"); + if(!unit_data) { + _state = State::Invalid; + return; + } + + auto prop = unit_data->at(prop_name); + if(!prop) { + _state = State::Invalid; + return; + } + + if(prop->items.size() != weapon_array.size()) { + _state = State::Invalid; + return; + } + + for(UnsignedInt i = 0; i < weapon_array.size(); i++) { + auto weapon_prop = prop->at(i); + auto& weapon = weapon_array[i]; + + weapon.name = weapon_prop->at("Name_13_7BF0D31F4E50C50C47231BB36A485D92")->value; + weapon.type = weapon_prop->at("Type_2_35ABA8C3406F8D9BBF14A89CD6BCE976")->enumValue; + + auto parts_prop = weapon_prop->at("Element_6_8E4617CC4B2C1F1490435599784EC6E0"); + weapon.parts = Containers::Array{ValueInit, parts_prop->items.size()}; + + for(UnsignedInt j = 0; j < parts_prop->items.size(); j++) { + auto part_prop = parts_prop->at(j); + auto& part = weapon.parts[j]; + + part.id = part_prop->at("ID_2_A74D75434308158E5926178822DD28EE")->value; + + auto part_styles = part_prop->at("Styles_17_994C97C34A90667BE5B716BFD0B97588"); + for(UnsignedInt k = 0; k < part_styles->items.size(); k++) { + part.styles[k] = part_styles->at(k)->value; + } + + auto part_decals = part_prop->at("Decals_13_8B81112B453D7230C0CDE982185E14F1"); + if(part_decals->items.size() != 8) { + _demo = true; + part.demoDecals = part_decals->items.size(); + } + + for(UnsignedInt k = 0; k < part_decals->items.size(); k++) { + auto decal_prop = part_decals->at(k); + auto& decal = part.decals[k]; + + decal.id = decal_prop->at("ID_3_694C0B35404D8A3168AEC89026BC8CF9")->value; + auto colour_prop = decal_prop->at("Color_8_1B0B9D2B43DA6AAB9FA549B374D3E606"); + decal.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; + auto pos_prop = decal_prop->at("Position_41_022C8FE84E1AAFE587261E88F2C72250"); + decal.position = Vector3{pos_prop->x, pos_prop->y, pos_prop->z}; + auto u_prop = decal_prop->at("UAxis_37_EBEB715F45491AECACCC07A1AE4646D1"); + decal.uAxis = Vector3{u_prop->x, u_prop->y, u_prop->z}; + auto v_prop = decal_prop->at("VAxis_39_C31EB2664EE202CAECFBBB84100B5E35"); + decal.vAxis = Vector3{v_prop->x, v_prop->y, v_prop->z}; + auto offset_prop = decal_prop->at("Offset_29_B02BBBB74FC60F5EDBEBAB8020738020"); + decal.offset = Vector2{offset_prop->x, offset_prop->y}; + decal.scale = decal_prop->at("Scale_32_959D1C2747AFD8D62808468235CBBA40")->value; + decal.rotation = decal_prop->at("Rotation_27_12D7C314493D203D5C2326A03C5F910F")->value; + decal.flip = decal_prop->at("Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1")->value; + decal.wrap = decal_prop->at("Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62")->value; + } + + if(!_demo) { + auto part_accs = part_prop->at("Accessories_21_3878DE8B4ED0EA0DB725E98BCDC20E0C"); + if(!part_accs) { + _demo = true; + } + + if(!_demo) { + if(part_accs->items.size() != 8) { + _state = State::Invalid; + return; + } + + for(UnsignedInt k = 0; k < part_accs->items.size(); k++) { + auto acc_prop = part_accs->at(k); + auto& accessory = part.accessories[k]; + + accessory.attachIndex = acc_prop->at("AttachIndex_2_4AFCF6024E4BA7426C6B9F80B8179D20")->value; + accessory.id = acc_prop->at("ID_4_5757B32647BAE263266259B8A7DFFFC1")->value; + auto acc_styles = acc_prop->at("Styles_7_91DEB0F24E24D13FC9472882C11D0DFD"); + for(UnsignedInt l = 0; l < acc_styles->items.size(); l++) { + accessory.styles[l] = acc_styles->at(l)->value; + } + auto rel_pos_prop = acc_prop->at("RelativePosition_14_BE8FB2A94074F34B3EDA6683B227D3A1"); + accessory.relativePosition = Vector3{rel_pos_prop->x, rel_pos_prop->y, rel_pos_prop->z}; + auto rel_pos_offset_prop = acc_prop->at("RelativePositionOffset_15_98FD0CE74E44BBAFC2D46FB4CA4E0ED6"); + accessory.relativePositionOffset = Vector3{rel_pos_offset_prop->x, rel_pos_offset_prop->y, rel_pos_offset_prop->z}; + auto rel_rot_prop = acc_prop->at("RelativeRotation_20_C78C73274E6E78E7878F8C98ECA342C0"); + accessory.relativeRotation = Vector3{rel_rot_prop->x, rel_rot_prop->y, rel_rot_prop->z}; + auto rel_rot_offset_prop = acc_prop->at("RelativeRotationOffset_21_E07FA0EC46728B7BA763C6861249ABAA"); + accessory.relativeRotationOffset = Vector3{rel_rot_offset_prop->x, rel_rot_offset_prop->y, rel_rot_offset_prop->z}; + auto local_scale_prop = acc_prop->at("LocalScale_24_DC2D93A742A41A46E7E61D988F15ED53"); + accessory.localScale = Vector3{local_scale_prop->x, local_scale_prop->y, local_scale_prop->z}; + } + } + } + } + } +} diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 2423f93..719845f 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -93,6 +93,7 @@ struct ArmourPart { 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}; }; @@ -165,6 +166,30 @@ class Mass { void getArmourCustomStyles(); auto writeArmourCustomStyle(UnsignedLong index) -> bool; + auto meleeWeapons() -> Containers::StaticArrayView<8, Weapon>; + void getMeleeWeapons(); + auto writeMeleeWeapons() -> bool; + + auto shields() -> Containers::StaticArrayView<1, Weapon>; + void getShields(); + auto writeShields() -> bool; + + auto bulletShooters() -> Containers::StaticArrayView<4, Weapon>; + void getBulletShooters(); + auto writeBulletShooters() -> bool; + + auto energyShooters() -> Containers::StaticArrayView<4, Weapon>; + void getEnergyShooters(); + auto writeEnergyShooters() -> bool; + + auto bulletLaunchers() -> Containers::StaticArrayView<4, Weapon>; + void getBulletLaunchers(); + auto writeBulletLaunchers() -> bool; + + auto energyLaunchers() -> Containers::StaticArrayView<4, Weapon>; + void getEnergyLaunchers(); + auto writeEnergyLaunchers() -> bool; + auto globalStyles() -> Containers::StaticArrayView<16, CustomStyle>; void getGlobalStyles(); auto writeGlobalStyle(UnsignedLong index) -> bool; @@ -174,6 +199,8 @@ class Mass { private: auto setCustomStyle(const CustomStyle& style, UnsignedLong index, const char* prop_name) -> bool; + void getWeaponType(const char* prop_name, Containers::ArrayView weapon_array); + Containers::Optional _mass; static std::string _lastError; -- 2.39.2 From 7e452db3a413d784dab6b066d6d3d98792394558 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sun, 17 Oct 2021 08:44:23 +0200 Subject: [PATCH 068/128] Mass: improve readability. --- src/Mass/Mass.cpp | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 1ab5df0..7bcfbf2 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -1055,35 +1055,34 @@ void Mass::getWeaponType(const char* prop_name, Containers::ArrayView we auto part_accs = part_prop->at("Accessories_21_3878DE8B4ED0EA0DB725E98BCDC20E0C"); if(!part_accs) { _demo = true; + continue; } - if(!_demo) { - if(part_accs->items.size() != 8) { - _state = State::Invalid; - return; - } + if(part_accs->items.size() != 8) { + _state = State::Invalid; + return; + } - for(UnsignedInt k = 0; k < part_accs->items.size(); k++) { - auto acc_prop = part_accs->at(k); - auto& accessory = part.accessories[k]; + for(UnsignedInt k = 0; k < part_accs->items.size(); k++) { + auto acc_prop = part_accs->at(k); + auto& accessory = part.accessories[k]; - accessory.attachIndex = acc_prop->at("AttachIndex_2_4AFCF6024E4BA7426C6B9F80B8179D20")->value; - accessory.id = acc_prop->at("ID_4_5757B32647BAE263266259B8A7DFFFC1")->value; - auto acc_styles = acc_prop->at("Styles_7_91DEB0F24E24D13FC9472882C11D0DFD"); - for(UnsignedInt l = 0; l < acc_styles->items.size(); l++) { - accessory.styles[l] = acc_styles->at(l)->value; - } - auto rel_pos_prop = acc_prop->at("RelativePosition_14_BE8FB2A94074F34B3EDA6683B227D3A1"); - accessory.relativePosition = Vector3{rel_pos_prop->x, rel_pos_prop->y, rel_pos_prop->z}; - auto rel_pos_offset_prop = acc_prop->at("RelativePositionOffset_15_98FD0CE74E44BBAFC2D46FB4CA4E0ED6"); - accessory.relativePositionOffset = Vector3{rel_pos_offset_prop->x, rel_pos_offset_prop->y, rel_pos_offset_prop->z}; - auto rel_rot_prop = acc_prop->at("RelativeRotation_20_C78C73274E6E78E7878F8C98ECA342C0"); - accessory.relativeRotation = Vector3{rel_rot_prop->x, rel_rot_prop->y, rel_rot_prop->z}; - auto rel_rot_offset_prop = acc_prop->at("RelativeRotationOffset_21_E07FA0EC46728B7BA763C6861249ABAA"); - accessory.relativeRotationOffset = Vector3{rel_rot_offset_prop->x, rel_rot_offset_prop->y, rel_rot_offset_prop->z}; - auto local_scale_prop = acc_prop->at("LocalScale_24_DC2D93A742A41A46E7E61D988F15ED53"); - accessory.localScale = Vector3{local_scale_prop->x, local_scale_prop->y, local_scale_prop->z}; + accessory.attachIndex = acc_prop->at("AttachIndex_2_4AFCF6024E4BA7426C6B9F80B8179D20")->value; + accessory.id = acc_prop->at("ID_4_5757B32647BAE263266259B8A7DFFFC1")->value; + auto acc_styles = acc_prop->at("Styles_7_91DEB0F24E24D13FC9472882C11D0DFD"); + for(UnsignedInt l = 0; l < acc_styles->items.size(); l++) { + accessory.styles[l] = acc_styles->at(l)->value; } + auto rel_pos_prop = acc_prop->at("RelativePosition_14_BE8FB2A94074F34B3EDA6683B227D3A1"); + accessory.relativePosition = Vector3{rel_pos_prop->x, rel_pos_prop->y, rel_pos_prop->z}; + auto rel_pos_offset_prop = acc_prop->at("RelativePositionOffset_15_98FD0CE74E44BBAFC2D46FB4CA4E0ED6"); + accessory.relativePositionOffset = Vector3{rel_pos_offset_prop->x, rel_pos_offset_prop->y, rel_pos_offset_prop->z}; + auto rel_rot_prop = acc_prop->at("RelativeRotation_20_C78C73274E6E78E7878F8C98ECA342C0"); + accessory.relativeRotation = Vector3{rel_rot_prop->x, rel_rot_prop->y, rel_rot_prop->z}; + auto rel_rot_offset_prop = acc_prop->at("RelativeRotationOffset_21_E07FA0EC46728B7BA763C6861249ABAA"); + accessory.relativeRotationOffset = Vector3{rel_rot_offset_prop->x, rel_rot_offset_prop->y, rel_rot_offset_prop->z}; + auto local_scale_prop = acc_prop->at("LocalScale_24_DC2D93A742A41A46E7E61D988F15ED53"); + accessory.localScale = Vector3{local_scale_prop->x, local_scale_prop->y, local_scale_prop->z}; } } } -- 2.39.2 From 83fe02a8dc8b1853b8215187dd6379f395497352 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sun, 17 Oct 2021 09:21:48 +0200 Subject: [PATCH 069/128] Mass: improve readability and reliability. --- src/Mass/Mass.cpp | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 7bcfbf2..246006e 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -578,15 +578,14 @@ void Mass::getArmourParts() { return; } - if(armour_array->items.size() != 38) { + if(armour_array->items.size() != _armour.parts.size()) { _state = State::Invalid; return; } for(UnsignedInt i = 0; i < armour_array->items.size(); i++) { auto part_prop = armour_array->at(i); - - ArmourPart part; + auto& part = _armour.parts[i]; part.slot = part_prop->at("Slot_3_408BA56F4C9605C7E805CF91B642249C")->enumValue; part.id = part_prop->at("ID_5_ACD101864D3481DE96EDACACC09BDD25")->value; @@ -597,7 +596,7 @@ void Mass::getArmourParts() { return; } - if(part_styles->items.size() != 4) { + if(part_styles->items.size() != part.styles.size()) { _state = State::Invalid; return; } @@ -612,15 +611,14 @@ void Mass::getArmourParts() { return; } - if(decals_array->items.size() != 8) { + if(decals_array->items.size() != part.decals.size()) { part.demoDecals = decals_array->items.size(); _demo = true; } for(UnsignedInt j = 0; j < decals_array->items.size(); j++) { auto decal_prop = decals_array->at(j); - - Decal decal; + auto& decal = part.decals[j]; decal.id = decal_prop->at("ID_3_694C0B35404D8A3168AEC89026BC8CF9")->value; auto colour_prop = decal_prop->at("Color_8_1B0B9D2B43DA6AAB9FA549B374D3E606"); @@ -637,25 +635,23 @@ void Mass::getArmourParts() { decal.rotation = decal_prop->at("Rotation_27_12D7C314493D203D5C2326A03C5F910F")->value; decal.flip = decal_prop->at("Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1")->value; decal.wrap = decal_prop->at("Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62")->value; - - part.decals[j] = decal; } if(!_demo) { auto accs_array = part_prop->at("Accessories_52_D902DD4241FA0050C2529596255153F3"); if(!accs_array) { _demo = true; + continue; } - if(accs_array->items.size() != 8) { + if(accs_array->items.size() != part.accessories.size()) { _state = State::Invalid; return; } for(UnsignedInt j = 0; j < accs_array->items.size(); j++) { auto acc_prop = accs_array->at(j); - - Accessory accessory; + auto& accessory = part.accessories[j]; accessory.attachIndex = acc_prop->at("AttachIndex_2_4AFCF6024E4BA7426C6B9F80B8179D20")->value; accessory.id = acc_prop->at("ID_4_5757B32647BAE263266259B8A7DFFFC1")->value; @@ -673,12 +669,8 @@ void Mass::getArmourParts() { accessory.relativeRotationOffset = Vector3{rel_rot_offset_prop->x, rel_rot_offset_prop->y, rel_rot_offset_prop->z}; auto local_scale_prop = acc_prop->at("LocalScale_24_DC2D93A742A41A46E7E61D988F15ED53"); accessory.localScale = Vector3{local_scale_prop->x, local_scale_prop->y, local_scale_prop->z}; - - part.accessories[j] = std::move(accessory); } } - - _armour.parts[i] = std::move(part); } } -- 2.39.2 From d79debe69fd084346126997e184a4da03d6a6394 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sun, 17 Oct 2021 10:52:57 +0200 Subject: [PATCH 070/128] Mass: change how setCustomStyle() works. This'll allow usage with weapon styles. --- src/Mass/Mass.cpp | 56 +++++++++++++++++++++++++++++++++++------------ src/Mass/Mass.h | 4 +++- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 246006e..0e181d4 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -558,7 +558,19 @@ auto Mass::writeFrameCustomStyle(UnsignedLong index) -> bool { return false; } - return setCustomStyle(_frame.customStyles[index], index, "FrameStyle_44_04A44C9440363CCEC5443D98BFAF22AA"); + auto unit_data = _mass->at("UnitData"); + if(!unit_data) { + _state = State::Invalid; + return false; + } + + auto frame_styles = unit_data->at("FrameStyle_44_04A44C9440363CCEC5443D98BFAF22AA"); + if(!frame_styles) { + _state = State::Invalid; + return false; + } + + return setCustomStyle(_frame.customStyles[index], index, frame_styles); } auto Mass::armourParts() -> Containers::StaticArrayView<38, ArmourPart> { @@ -823,7 +835,19 @@ auto Mass::writeArmourCustomStyle(UnsignedLong index) -> bool { return false; } - return setCustomStyle(_armour.customStyles[index], index, "ArmorStyle_42_E2F6AC3647788CB366BD469B3B7E899E"); + auto unit_data = _mass->at("UnitData"); + if(!unit_data) { + _state = State::Invalid; + return false; + } + + auto armour_styles = unit_data->at("ArmorStyle_42_E2F6AC3647788CB366BD469B3B7E899E"); + if(!armour_styles) { + _state = State::Invalid; + return false; + } + + return setCustomStyle(_armour.customStyles[index], index, armour_styles); } auto Mass::meleeWeapons() -> Containers::StaticArrayView<8, Weapon> { @@ -926,7 +950,19 @@ auto Mass::writeGlobalStyle(UnsignedLong index) -> bool { return false; } - return setCustomStyle(_globalStyles[index], index, "GlobalStyles_57_6A681C114035241F7BDAAE9B43A8BF1B"); + auto unit_data = _mass->at("UnitData"); + if(!unit_data) { + _state = State::Invalid; + return false; + } + + auto global_styles = unit_data->at("GlobalStyles_57_6A681C114035241F7BDAAE9B43A8BF1B"); + if(!global_styles) { + _state = State::Invalid; + return false; + } + + return setCustomStyle(_globalStyles[index], index, global_styles); } auto Mass::updateSteamId(const std::string& steam_id) -> bool { @@ -943,20 +979,12 @@ auto Mass::updateSteamId(const std::string& steam_id) -> bool { return _mass->saveToFile(); } -auto Mass::setCustomStyle(const CustomStyle& style, UnsignedLong index, const char* prop_name) -> bool { - auto unit_data = _mass->at("UnitData"); - if(!unit_data) { - _state = State::Invalid; +auto Mass::setCustomStyle(const CustomStyle& style, UnsignedLong index, ArrayProperty* style_array) -> bool { + if(!style_array) { return false; } - auto frame_styles = unit_data->at(prop_name); - if(!frame_styles) { - _state = State::Invalid; - return false; - } - - auto style_prop = frame_styles->at(index); + auto style_prop = style_array->at(index); style_prop->at("Name_27_1532115A46EF2B2FA283908DF561A86B")->value = style.name; auto colour_prop = style_prop->at("Color_5_F0D383DF40474C9464AE48A0984A212E"); diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 719845f..57a289e 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -32,6 +32,8 @@ using namespace Corrade; using namespace Magnum; +struct ArrayProperty; + struct Joints { Float neck = 0.0f; Float body = 0.0f; @@ -197,7 +199,7 @@ class Mass { auto updateSteamId(const std::string& steam_id) -> bool; private: - auto setCustomStyle(const CustomStyle& style, UnsignedLong index, const char* prop_name) -> bool; + auto setCustomStyle(const CustomStyle& style, UnsignedLong index, ArrayProperty* style_array) -> bool; void getWeaponType(const char* prop_name, Containers::ArrayView weapon_array); -- 2.39.2 From e461d5a505d1d15db715827b64fb1ce373de6dd7 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sun, 17 Oct 2021 15:28:54 +0200 Subject: [PATCH 071/128] Mass: add getCustomStyles(); --- src/Mass/Mass.cpp | 95 +++++++++++++---------------------------------- src/Mass/Mass.h | 1 + 2 files changed, 27 insertions(+), 69 deletions(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 0e181d4..00df6a0 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -528,29 +528,7 @@ void Mass::getFrameCustomStyles() { return; } - for(UnsignedInt i = 0; i < frame_styles->items.size(); i++) { - auto style_prop = frame_styles->at(i); - - CustomStyle style; - - style.name = style_prop->at("Name_27_1532115A46EF2B2FA283908DF561A86B")->value; - auto colour_prop = style_prop->at("Color_5_F0D383DF40474C9464AE48A0984A212E"); - style.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; - style.metallic = style_prop->at("Metallic_10_0A4CD1E4482CBF41CA61D0A856DE90B9")->value; - style.gloss = style_prop->at("Gloss_11_9769599842CC275A401C4282A236E240")->value; - style.glow = colour_prop->a == 0.0f ? false : true; - - style.patternId = style_prop->at("PatternID_14_516DB85641DAF8ECFD2920BE2BDF1311")->value; - style.opacity = style_prop->at("Opacity_30_53BD060B4DFCA1C92302D6A0F7831131")->value; - style.offset = Vector2{ - style_prop->at("OffsetX_23_70FC2E814C64BBB82452748D2AF9CD48")->value, - style_prop->at("OffsetY_24_5E1F866C4C054D9B2EE337ADC180C17F")->value - }; - style.rotation = style_prop->at("Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D")->value; - style.scale = style_prop->at("Scale_26_19DF0708409262183E1247B317137671")->value; - - _frame.customStyles[i] = std::move(style); - } + getCustomStyles(_frame.customStyles, frame_styles); } auto Mass::writeFrameCustomStyle(UnsignedLong index) -> bool { @@ -805,29 +783,7 @@ void Mass::getArmourCustomStyles() { return; } - for(UnsignedInt i = 0; i < armour_styles->items.size(); i++) { - auto style_prop = armour_styles->at(i); - - CustomStyle style; - - style.name = style_prop->at("Name_27_1532115A46EF2B2FA283908DF561A86B")->value; - auto colour_prop = style_prop->at("Color_5_F0D383DF40474C9464AE48A0984A212E"); - style.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; - style.metallic = style_prop->at("Metallic_10_0A4CD1E4482CBF41CA61D0A856DE90B9")->value; - style.gloss = style_prop->at("Gloss_11_9769599842CC275A401C4282A236E240")->value; - style.glow = colour_prop->a == 0.0f ? false : true; - - style.patternId = style_prop->at("PatternID_14_516DB85641DAF8ECFD2920BE2BDF1311")->value; - style.opacity = style_prop->at("Opacity_30_53BD060B4DFCA1C92302D6A0F7831131")->value; - style.offset = Vector2{ - style_prop->at("OffsetX_23_70FC2E814C64BBB82452748D2AF9CD48")->value, - style_prop->at("OffsetY_24_5E1F866C4C054D9B2EE337ADC180C17F")->value - }; - style.rotation = style_prop->at("Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D")->value; - style.scale = style_prop->at("Scale_26_19DF0708409262183E1247B317137671")->value; - - _armour.customStyles[i] = std::move(style); - } + getCustomStyles(_armour.customStyles, armour_styles); } auto Mass::writeArmourCustomStyle(UnsignedLong index) -> bool { @@ -920,29 +876,7 @@ void Mass::getGlobalStyles() { return; } - for(UnsignedInt i = 0; i < global_styles->items.size(); i++) { - auto style_prop = global_styles->at(i); - - CustomStyle style; - - style.name = style_prop->at("Name_27_1532115A46EF2B2FA283908DF561A86B")->value; - auto colour_prop = style_prop->at("Color_5_F0D383DF40474C9464AE48A0984A212E"); - style.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; - style.metallic = style_prop->at("Metallic_10_0A4CD1E4482CBF41CA61D0A856DE90B9")->value; - style.gloss = style_prop->at("Gloss_11_9769599842CC275A401C4282A236E240")->value; - style.glow = colour_prop->a == 0.0f ? false : true; - - style.patternId = style_prop->at("PatternID_14_516DB85641DAF8ECFD2920BE2BDF1311")->value; - style.opacity = style_prop->at("Opacity_30_53BD060B4DFCA1C92302D6A0F7831131")->value; - style.offset = Vector2{ - style_prop->at("OffsetX_23_70FC2E814C64BBB82452748D2AF9CD48")->value, - style_prop->at("OffsetY_24_5E1F866C4C054D9B2EE337ADC180C17F")->value - }; - style.rotation = style_prop->at("Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D")->value; - style.scale = style_prop->at("Scale_26_19DF0708409262183E1247B317137671")->value; - - _globalStyles[i] = std::move(style); - } + getCustomStyles(_globalStyles, global_styles); } auto Mass::writeGlobalStyle(UnsignedLong index) -> bool { @@ -979,6 +913,29 @@ auto Mass::updateSteamId(const std::string& steam_id) -> bool { return _mass->saveToFile(); } +void Mass::getCustomStyles(Containers::ArrayView styles, ArrayProperty* style_array) { + for(UnsignedInt i = 0; i < style_array->items.size(); i++) { + auto style_prop = style_array->at(i); + auto& style = styles[i]; + + style.name = style_prop->at("Name_27_1532115A46EF2B2FA283908DF561A86B")->value; + auto colour_prop = style_prop->at("Color_5_F0D383DF40474C9464AE48A0984A212E"); + style.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; + style.metallic = style_prop->at("Metallic_10_0A4CD1E4482CBF41CA61D0A856DE90B9")->value; + style.gloss = style_prop->at("Gloss_11_9769599842CC275A401C4282A236E240")->value; + style.glow = colour_prop->a == 0.0f ? false : true; + + style.patternId = style_prop->at("PatternID_14_516DB85641DAF8ECFD2920BE2BDF1311")->value; + style.opacity = style_prop->at("Opacity_30_53BD060B4DFCA1C92302D6A0F7831131")->value; + style.offset = Vector2{ + style_prop->at("OffsetX_23_70FC2E814C64BBB82452748D2AF9CD48")->value, + style_prop->at("OffsetY_24_5E1F866C4C054D9B2EE337ADC180C17F")->value + }; + style.rotation = style_prop->at("Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D")->value; + style.scale = style_prop->at("Scale_26_19DF0708409262183E1247B317137671")->value; + } +} + auto Mass::setCustomStyle(const CustomStyle& style, UnsignedLong index, ArrayProperty* style_array) -> bool { if(!style_array) { return false; diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 57a289e..793f620 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -199,6 +199,7 @@ class Mass { auto updateSteamId(const std::string& steam_id) -> bool; private: + void getCustomStyles(Containers::ArrayView styles, ArrayProperty* style_array); auto setCustomStyle(const CustomStyle& style, UnsignedLong index, ArrayProperty* style_array) -> bool; void getWeaponType(const char* prop_name, Containers::ArrayView weapon_array); -- 2.39.2 From 4ca6f62d9b46380a7492c808e54a5d8f52cb8cd3 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sun, 17 Oct 2021 15:29:16 +0200 Subject: [PATCH 072/128] Mass: remove as many hardcoded values as possible. --- src/Mass/Mass.cpp | 32 ++++++++++++++++---------------- src/Mass/Mass.h | 22 +++++++++++----------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 00df6a0..8143e35 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -386,7 +386,7 @@ auto Mass::writeJointSliders() -> bool { return true; } -auto Mass::frameStyles() -> Containers::StaticArrayView<4, Int> { +auto Mass::frameStyles() -> Containers::ArrayView { return _frame.styles; } @@ -409,12 +409,12 @@ void Mass::getFrameStyles() { return; } - if(frame_styles->items.size() != 4) { + if(frame_styles->items.size() != _frame.styles.size()) { _state = State::Invalid; return; } - for(UnsignedInt i = 0; i < 4; i++) { + for(UnsignedInt i = 0; i < frame_styles->items.size(); i++) { _frame.styles[i] = frame_styles->at(i)->value; } } @@ -506,7 +506,7 @@ auto Mass::writeEyeFlareColour() -> bool { return _mass->saveToFile(); } -auto Mass::frameCustomStyles() -> Containers::StaticArrayView<16, CustomStyle> { +auto Mass::frameCustomStyles() -> Containers::ArrayView { return _frame.customStyles; } @@ -523,7 +523,7 @@ void Mass::getFrameCustomStyles() { return; } - if(frame_styles->items.size() != 16) { + if(frame_styles->items.size() != _frame.customStyles.size()) { _state = State::Invalid; return; } @@ -551,7 +551,7 @@ auto Mass::writeFrameCustomStyle(UnsignedLong index) -> bool { return setCustomStyle(_frame.customStyles[index], index, frame_styles); } -auto Mass::armourParts() -> Containers::StaticArrayView<38, ArmourPart> { +auto Mass::armourParts() -> Containers::ArrayView { return _armour.parts; } @@ -761,7 +761,7 @@ auto Mass::writeArmourPart(UnsignedLong index) -> bool { return _mass->saveToFile(); } -auto Mass::armourCustomStyles() -> Containers::StaticArrayView<16, CustomStyle> { +auto Mass::armourCustomStyles() -> Containers::ArrayView { return _armour.customStyles; } @@ -778,7 +778,7 @@ void Mass::getArmourCustomStyles() { return; } - if(armour_styles->items.size() != 16) { + if(armour_styles->items.size() != _armour.customStyles.size()) { _state = State::Invalid; return; } @@ -806,7 +806,7 @@ auto Mass::writeArmourCustomStyle(UnsignedLong index) -> bool { return setCustomStyle(_armour.customStyles[index], index, armour_styles); } -auto Mass::meleeWeapons() -> Containers::StaticArrayView<8, Weapon> { +auto Mass::meleeWeapons() -> Containers::ArrayView { return _weapons.melee; } @@ -814,7 +814,7 @@ void Mass::getMeleeWeapons() { getWeaponType("WeaponCC_22_0BBEC58C4A0EA1DB9E037B9339EE26A7", _weapons.melee); } -auto Mass::shields() -> Containers::StaticArrayView<1, Weapon> { +auto Mass::shields() -> Containers::ArrayView { return _weapons.shields; } @@ -822,7 +822,7 @@ void Mass::getShields() { getWeaponType("Shield_53_839BFD7945481BAEA3E43A9C5CA8E92E", _weapons.shields); } -auto Mass::bulletShooters() -> Containers::StaticArrayView<4, Weapon> { +auto Mass::bulletShooters() -> Containers::ArrayView { return _weapons.bulletShooters; } @@ -830,7 +830,7 @@ void Mass::getBulletShooters() { getWeaponType("WeaponBS_35_6EF6E0104FD7A138DF47F88CB57A83ED", _weapons.bulletShooters); } -auto Mass::energyShooters() -> Containers::StaticArrayView<4, Weapon> { +auto Mass::energyShooters() -> Containers::ArrayView { return _weapons.energyShooters; } @@ -838,7 +838,7 @@ void Mass::getEnergyShooters() { getWeaponType("WeaponES_37_1A295D544528623880A0B1AC2C7DEE99", _weapons.energyShooters); } -auto Mass::bulletLaunchers() -> Containers::StaticArrayView<4, Weapon> { +auto Mass::bulletLaunchers() -> Containers::ArrayView { return _weapons.bulletLaunchers; } @@ -846,7 +846,7 @@ void Mass::getBulletLaunchers() { getWeaponType("WeaponBL_36_5FD7C41E4613A75B44AB0E90B362846E", _weapons.bulletLaunchers); } -auto Mass::energyLaunchers() -> Containers::StaticArrayView<4, Weapon> { +auto Mass::energyLaunchers() -> Containers::ArrayView { return _weapons.energyLaunchers; } @@ -854,7 +854,7 @@ void Mass::getEnergyLaunchers() { getWeaponType("WeaponEL_38_9D23F3884ACA15902C9E6CA6E4995995", _weapons.energyLaunchers); } -auto Mass::globalStyles() -> Containers::StaticArrayView<16, CustomStyle> { +auto Mass::globalStyles() -> Containers::ArrayView { return _globalStyles; } @@ -871,7 +871,7 @@ void Mass::getGlobalStyles() { return; } - if(global_styles->items.size() != 16) { + if(global_styles->items.size() != _globalStyles.size()) { _state = State::Invalid; return; } diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 793f620..2c24195 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -148,7 +148,7 @@ class Mass { void getJointSliders(); auto writeJointSliders() -> bool; - auto frameStyles() -> Containers::StaticArrayView<4, Int>; + auto frameStyles() -> Containers::ArrayView; void getFrameStyles(); auto writeFrameStyles() -> bool; @@ -156,43 +156,43 @@ class Mass { void getEyeFlareColour(); auto writeEyeFlareColour() -> bool; - auto frameCustomStyles() -> Containers::StaticArrayView<16, CustomStyle>; + auto frameCustomStyles() -> Containers::ArrayView; void getFrameCustomStyles(); auto writeFrameCustomStyle(UnsignedLong index) -> bool; - auto armourParts() -> Containers::StaticArrayView<38, ArmourPart>; + auto armourParts() -> Containers::ArrayView; void getArmourParts(); auto writeArmourPart(UnsignedLong index) -> bool; - auto armourCustomStyles() -> Containers::StaticArrayView<16, CustomStyle>; + auto armourCustomStyles() -> Containers::ArrayView; void getArmourCustomStyles(); auto writeArmourCustomStyle(UnsignedLong index) -> bool; - auto meleeWeapons() -> Containers::StaticArrayView<8, Weapon>; + auto meleeWeapons() -> Containers::ArrayView; void getMeleeWeapons(); auto writeMeleeWeapons() -> bool; - auto shields() -> Containers::StaticArrayView<1, Weapon>; + auto shields() -> Containers::ArrayView; void getShields(); auto writeShields() -> bool; - auto bulletShooters() -> Containers::StaticArrayView<4, Weapon>; + auto bulletShooters() -> Containers::ArrayView; void getBulletShooters(); auto writeBulletShooters() -> bool; - auto energyShooters() -> Containers::StaticArrayView<4, Weapon>; + auto energyShooters() -> Containers::ArrayView; void getEnergyShooters(); auto writeEnergyShooters() -> bool; - auto bulletLaunchers() -> Containers::StaticArrayView<4, Weapon>; + auto bulletLaunchers() -> Containers::ArrayView; void getBulletLaunchers(); auto writeBulletLaunchers() -> bool; - auto energyLaunchers() -> Containers::StaticArrayView<4, Weapon>; + auto energyLaunchers() -> Containers::ArrayView; void getEnergyLaunchers(); auto writeEnergyLaunchers() -> bool; - auto globalStyles() -> Containers::StaticArrayView<16, CustomStyle>; + auto globalStyles() -> Containers::ArrayView; void getGlobalStyles(); auto writeGlobalStyle(UnsignedLong index) -> bool; -- 2.39.2 From 247578a3867eba476466bfb1b25f4c9e0a1433ba Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 18 Oct 2021 14:53:35 +0200 Subject: [PATCH 073/128] Mass: add getDecals() and getAccessories(). --- src/Mass/Mass.cpp | 137 ++++++++++++++++++---------------------------- src/Mass/Mass.h | 3 + 2 files changed, 56 insertions(+), 84 deletions(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 8143e35..86aa2d3 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -606,26 +606,7 @@ void Mass::getArmourParts() { _demo = true; } - for(UnsignedInt j = 0; j < decals_array->items.size(); j++) { - auto decal_prop = decals_array->at(j); - auto& decal = part.decals[j]; - - decal.id = decal_prop->at("ID_3_694C0B35404D8A3168AEC89026BC8CF9")->value; - auto colour_prop = decal_prop->at("Color_8_1B0B9D2B43DA6AAB9FA549B374D3E606"); - decal.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; - auto pos_prop = decal_prop->at("Position_41_022C8FE84E1AAFE587261E88F2C72250"); - decal.position = Vector3{pos_prop->x, pos_prop->y, pos_prop->z}; - auto u_prop = decal_prop->at("UAxis_37_EBEB715F45491AECACCC07A1AE4646D1"); - decal.uAxis = Vector3{u_prop->x, u_prop->y, u_prop->z}; - auto v_prop = decal_prop->at("VAxis_39_C31EB2664EE202CAECFBBB84100B5E35"); - decal.vAxis = Vector3{v_prop->x, v_prop->y, v_prop->z}; - auto offset_prop = decal_prop->at("Offset_29_B02BBBB74FC60F5EDBEBAB8020738020"); - decal.offset = Vector2{offset_prop->x, offset_prop->y}; - decal.scale = decal_prop->at("Scale_32_959D1C2747AFD8D62808468235CBBA40")->value; - decal.rotation = decal_prop->at("Rotation_27_12D7C314493D203D5C2326A03C5F910F")->value; - decal.flip = decal_prop->at("Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1")->value; - decal.wrap = decal_prop->at("Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62")->value; - } + getDecals(part.decals, decals_array); if(!_demo) { auto accs_array = part_prop->at("Accessories_52_D902DD4241FA0050C2529596255153F3"); @@ -639,27 +620,7 @@ void Mass::getArmourParts() { return; } - for(UnsignedInt j = 0; j < accs_array->items.size(); j++) { - auto acc_prop = accs_array->at(j); - auto& accessory = part.accessories[j]; - - accessory.attachIndex = acc_prop->at("AttachIndex_2_4AFCF6024E4BA7426C6B9F80B8179D20")->value; - accessory.id = acc_prop->at("ID_4_5757B32647BAE263266259B8A7DFFFC1")->value; - auto acc_styles = acc_prop->at("Styles_7_91DEB0F24E24D13FC9472882C11D0DFD"); - for(UnsignedInt k = 0; k < acc_styles->items.size(); k++) { - accessory.styles[k] = acc_styles->at(k)->value; - } - auto rel_pos_prop = acc_prop->at("RelativePosition_14_BE8FB2A94074F34B3EDA6683B227D3A1"); - accessory.relativePosition = Vector3{rel_pos_prop->x, rel_pos_prop->y, rel_pos_prop->z}; - auto rel_pos_offset_prop = acc_prop->at("RelativePositionOffset_15_98FD0CE74E44BBAFC2D46FB4CA4E0ED6"); - accessory.relativePositionOffset = Vector3{rel_pos_offset_prop->x, rel_pos_offset_prop->y, rel_pos_offset_prop->z}; - auto rel_rot_prop = acc_prop->at("RelativeRotation_20_C78C73274E6E78E7878F8C98ECA342C0"); - accessory.relativeRotation = Vector3{rel_rot_prop->x, rel_rot_prop->y, rel_rot_prop->z}; - auto rel_rot_offset_prop = acc_prop->at("RelativeRotationOffset_21_E07FA0EC46728B7BA763C6861249ABAA"); - accessory.relativeRotationOffset = Vector3{rel_rot_offset_prop->x, rel_rot_offset_prop->y, rel_rot_offset_prop->z}; - auto local_scale_prop = acc_prop->at("LocalScale_24_DC2D93A742A41A46E7E61D988F15ED53"); - accessory.localScale = Vector3{local_scale_prop->x, local_scale_prop->y, local_scale_prop->z}; - } + getAccessories(part.accessories, accs_array); } } } @@ -962,6 +923,53 @@ auto Mass::setCustomStyle(const CustomStyle& style, UnsignedLong index, ArrayPro return _mass->saveToFile(); } +void Mass::getDecals(Containers::ArrayView decals, ArrayProperty* decal_array) { + for(UnsignedInt i = 0; i < decal_array->items.size(); i++) { + auto decal_prop = decal_array->at(i); + auto& decal = decals[i]; + + decal.id = decal_prop->at("ID_3_694C0B35404D8A3168AEC89026BC8CF9")->value; + auto colour_prop = decal_prop->at("Color_8_1B0B9D2B43DA6AAB9FA549B374D3E606"); + decal.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; + auto pos_prop = decal_prop->at("Position_41_022C8FE84E1AAFE587261E88F2C72250"); + decal.position = Vector3{pos_prop->x, pos_prop->y, pos_prop->z}; + auto u_prop = decal_prop->at("UAxis_37_EBEB715F45491AECACCC07A1AE4646D1"); + decal.uAxis = Vector3{u_prop->x, u_prop->y, u_prop->z}; + auto v_prop = decal_prop->at("VAxis_39_C31EB2664EE202CAECFBBB84100B5E35"); + decal.vAxis = Vector3{v_prop->x, v_prop->y, v_prop->z}; + auto offset_prop = decal_prop->at("Offset_29_B02BBBB74FC60F5EDBEBAB8020738020"); + decal.offset = Vector2{offset_prop->x, offset_prop->y}; + decal.scale = decal_prop->at("Scale_32_959D1C2747AFD8D62808468235CBBA40")->value; + decal.rotation = decal_prop->at("Rotation_27_12D7C314493D203D5C2326A03C5F910F")->value; + decal.flip = decal_prop->at("Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1")->value; + decal.wrap = decal_prop->at("Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62")->value; + } +} + +void Mass::getAccessories(Containers::ArrayView accessories, ArrayProperty* accessory_array) { + for(UnsignedInt i = 0; i < accessory_array->items.size(); i++) { + auto acc_prop = accessory_array->at(i); + auto& accessory = accessories[i]; + + accessory.attachIndex = acc_prop->at("AttachIndex_2_4AFCF6024E4BA7426C6B9F80B8179D20")->value; + accessory.id = acc_prop->at("ID_4_5757B32647BAE263266259B8A7DFFFC1")->value; + auto acc_styles = acc_prop->at("Styles_7_91DEB0F24E24D13FC9472882C11D0DFD"); + for(UnsignedInt j = 0; j < acc_styles->items.size(); j++) { + accessory.styles[j] = acc_styles->at(j)->value; + } + auto rel_pos_prop = acc_prop->at("RelativePosition_14_BE8FB2A94074F34B3EDA6683B227D3A1"); + accessory.relativePosition = Vector3{rel_pos_prop->x, rel_pos_prop->y, rel_pos_prop->z}; + auto rel_pos_offset_prop = acc_prop->at("RelativePositionOffset_15_98FD0CE74E44BBAFC2D46FB4CA4E0ED6"); + accessory.relativePositionOffset = Vector3{rel_pos_offset_prop->x, rel_pos_offset_prop->y, rel_pos_offset_prop->z}; + auto rel_rot_prop = acc_prop->at("RelativeRotation_20_C78C73274E6E78E7878F8C98ECA342C0"); + accessory.relativeRotation = Vector3{rel_rot_prop->x, rel_rot_prop->y, rel_rot_prop->z}; + auto rel_rot_offset_prop = acc_prop->at("RelativeRotationOffset_21_E07FA0EC46728B7BA763C6861249ABAA"); + accessory.relativeRotationOffset = Vector3{rel_rot_offset_prop->x, rel_rot_offset_prop->y, rel_rot_offset_prop->z}; + auto local_scale_prop = acc_prop->at("LocalScale_24_DC2D93A742A41A46E7E61D988F15ED53"); + accessory.localScale = Vector3{local_scale_prop->x, local_scale_prop->y, local_scale_prop->z}; + } +} + void Mass::getWeaponType(const char* prop_name, Containers::ArrayView weapon_array) { auto unit_data = _mass->at("UnitData"); if(!unit_data) { @@ -1002,31 +1010,12 @@ void Mass::getWeaponType(const char* prop_name, Containers::ArrayView we } auto part_decals = part_prop->at("Decals_13_8B81112B453D7230C0CDE982185E14F1"); - if(part_decals->items.size() != 8) { + if(part_decals->items.size() != part.decals.size()) { _demo = true; part.demoDecals = part_decals->items.size(); } - for(UnsignedInt k = 0; k < part_decals->items.size(); k++) { - auto decal_prop = part_decals->at(k); - auto& decal = part.decals[k]; - - decal.id = decal_prop->at("ID_3_694C0B35404D8A3168AEC89026BC8CF9")->value; - auto colour_prop = decal_prop->at("Color_8_1B0B9D2B43DA6AAB9FA549B374D3E606"); - decal.colour = Color4{colour_prop->r, colour_prop->g, colour_prop->b, colour_prop->a}; - auto pos_prop = decal_prop->at("Position_41_022C8FE84E1AAFE587261E88F2C72250"); - decal.position = Vector3{pos_prop->x, pos_prop->y, pos_prop->z}; - auto u_prop = decal_prop->at("UAxis_37_EBEB715F45491AECACCC07A1AE4646D1"); - decal.uAxis = Vector3{u_prop->x, u_prop->y, u_prop->z}; - auto v_prop = decal_prop->at("VAxis_39_C31EB2664EE202CAECFBBB84100B5E35"); - decal.vAxis = Vector3{v_prop->x, v_prop->y, v_prop->z}; - auto offset_prop = decal_prop->at("Offset_29_B02BBBB74FC60F5EDBEBAB8020738020"); - decal.offset = Vector2{offset_prop->x, offset_prop->y}; - decal.scale = decal_prop->at("Scale_32_959D1C2747AFD8D62808468235CBBA40")->value; - decal.rotation = decal_prop->at("Rotation_27_12D7C314493D203D5C2326A03C5F910F")->value; - decal.flip = decal_prop->at("Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1")->value; - decal.wrap = decal_prop->at("Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62")->value; - } + getDecals(part.decals, part_decals); if(!_demo) { auto part_accs = part_prop->at("Accessories_21_3878DE8B4ED0EA0DB725E98BCDC20E0C"); @@ -1035,32 +1024,12 @@ void Mass::getWeaponType(const char* prop_name, Containers::ArrayView we continue; } - if(part_accs->items.size() != 8) { + if(part_accs->items.size() != part.accessories.size()) { _state = State::Invalid; return; } - for(UnsignedInt k = 0; k < part_accs->items.size(); k++) { - auto acc_prop = part_accs->at(k); - auto& accessory = part.accessories[k]; - - accessory.attachIndex = acc_prop->at("AttachIndex_2_4AFCF6024E4BA7426C6B9F80B8179D20")->value; - accessory.id = acc_prop->at("ID_4_5757B32647BAE263266259B8A7DFFFC1")->value; - auto acc_styles = acc_prop->at("Styles_7_91DEB0F24E24D13FC9472882C11D0DFD"); - for(UnsignedInt l = 0; l < acc_styles->items.size(); l++) { - accessory.styles[l] = acc_styles->at(l)->value; - } - auto rel_pos_prop = acc_prop->at("RelativePosition_14_BE8FB2A94074F34B3EDA6683B227D3A1"); - accessory.relativePosition = Vector3{rel_pos_prop->x, rel_pos_prop->y, rel_pos_prop->z}; - auto rel_pos_offset_prop = acc_prop->at("RelativePositionOffset_15_98FD0CE74E44BBAFC2D46FB4CA4E0ED6"); - accessory.relativePositionOffset = Vector3{rel_pos_offset_prop->x, rel_pos_offset_prop->y, rel_pos_offset_prop->z}; - auto rel_rot_prop = acc_prop->at("RelativeRotation_20_C78C73274E6E78E7878F8C98ECA342C0"); - accessory.relativeRotation = Vector3{rel_rot_prop->x, rel_rot_prop->y, rel_rot_prop->z}; - auto rel_rot_offset_prop = acc_prop->at("RelativeRotationOffset_21_E07FA0EC46728B7BA763C6861249ABAA"); - accessory.relativeRotationOffset = Vector3{rel_rot_offset_prop->x, rel_rot_offset_prop->y, rel_rot_offset_prop->z}; - auto local_scale_prop = acc_prop->at("LocalScale_24_DC2D93A742A41A46E7E61D988F15ED53"); - accessory.localScale = Vector3{local_scale_prop->x, local_scale_prop->y, local_scale_prop->z}; - } + getAccessories(part.accessories, part_accs); } } } diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 2c24195..022c6c9 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -202,6 +202,9 @@ class Mass { void getCustomStyles(Containers::ArrayView styles, ArrayProperty* style_array); auto setCustomStyle(const CustomStyle& style, UnsignedLong index, ArrayProperty* style_array) -> bool; + void getDecals(Containers::ArrayView decals, ArrayProperty* decal_array); + void getAccessories(Containers::ArrayView accessories, ArrayProperty* accessory_array); + void getWeaponType(const char* prop_name, Containers::ArrayView weapon_array); Containers::Optional _mass; -- 2.39.2 From af71806e13b8e650aa4fd9c89e66ca5e6859da0e Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 18 Oct 2021 14:54:28 +0200 Subject: [PATCH 074/128] Mass: finish getWeaponType(). --- src/Mass/Mass.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 86aa2d3..904c557 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -1032,5 +1032,25 @@ void Mass::getWeaponType(const char* prop_name, Containers::ArrayView we getAccessories(part.accessories, part_accs); } } + + auto custom_styles = weapon_prop->at("Styles_10_8C3C82444B986AD7A99595AD4985912D"); + if(!custom_styles) { + _state = State::Invalid; + return; + } + + if(custom_styles->items.size() != weapon.customStyles.size()) { + _state = State::Invalid; + return; + } + + getCustomStyles(weapon.customStyles, custom_styles); + + weapon.attached = weapon_prop->at("Attach_15_D00AABBD4AD6A04778D56D81E51927B3")->value; + weapon.damageType = weapon_prop->at("DamageType_18_E1FFA53540591A9087EC698117A65C83")->enumValue; + weapon.dualWield = weapon_prop->at("DualWield_20_B2EB2CEA4A6A233DC7575996B6DD1222")->value; + weapon.effectColourMode = weapon_prop->at("ColorEfxMode_24_D254BCF943E852BF9ADB8AAA8FD80014")->enumValue; + auto effect_colour = weapon_prop->at("ColorEfx_26_D921B62946C493E487455A831F4520AC"); + weapon.effectColour = Color4{effect_colour->r, effect_colour->g, effect_colour->b, effect_colour->a}; } } -- 2.39.2 From 0900f92b9f39044e13a5a69856b16e9a3e8db2a2 Mon Sep 17 00:00:00 2001 From: William JCM Date: Fri, 29 Oct 2021 10:23:34 +0200 Subject: [PATCH 075/128] Mass: finish implementing the saving feature. Oh, and also remove a blank line, but that's not important. --- src/Mass/Mass.cpp | 144 +++++++++++++++++++++++++++++++++++++++++++++- src/Mass/Mass.h | 1 + 2 files changed, 144 insertions(+), 1 deletion(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 904c557..017a957 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -652,7 +652,6 @@ auto Mass::writeArmourPart(UnsignedLong index) -> bool { auto decals_array = part_prop->at("Decals_42_F358794A4F18497970F56BA9627D3603"); for(UnsignedInt i = 0; i < decals_array->items.size(); i++) { auto decal_prop = decals_array->at(i); - auto& decal = part.decals[i]; decal_prop->at("ID_3_694C0B35404D8A3168AEC89026BC8CF9")->value = decal.id; @@ -775,6 +774,10 @@ void Mass::getMeleeWeapons() { getWeaponType("WeaponCC_22_0BBEC58C4A0EA1DB9E037B9339EE26A7", _weapons.melee); } +auto Mass::writeMeleeWeapons() -> bool { + return writeWeaponType("WeaponCC_22_0BBEC58C4A0EA1DB9E037B9339EE26A7", _weapons.melee); +} + auto Mass::shields() -> Containers::ArrayView { return _weapons.shields; } @@ -783,6 +786,10 @@ void Mass::getShields() { getWeaponType("Shield_53_839BFD7945481BAEA3E43A9C5CA8E92E", _weapons.shields); } +auto Mass::writeShields() -> bool { + return writeWeaponType("Shield_53_839BFD7945481BAEA3E43A9C5CA8E92E", _weapons.shields); +} + auto Mass::bulletShooters() -> Containers::ArrayView { return _weapons.bulletShooters; } @@ -791,6 +798,10 @@ void Mass::getBulletShooters() { getWeaponType("WeaponBS_35_6EF6E0104FD7A138DF47F88CB57A83ED", _weapons.bulletShooters); } +auto Mass::writeBulletShooters() -> bool { + return writeWeaponType("WeaponBS_35_6EF6E0104FD7A138DF47F88CB57A83ED", _weapons.bulletShooters); +} + auto Mass::energyShooters() -> Containers::ArrayView { return _weapons.energyShooters; } @@ -799,6 +810,10 @@ void Mass::getEnergyShooters() { getWeaponType("WeaponES_37_1A295D544528623880A0B1AC2C7DEE99", _weapons.energyShooters); } +auto Mass::writeEnergyShooters() -> bool { + return writeWeaponType("WeaponES_37_1A295D544528623880A0B1AC2C7DEE99", _weapons.energyShooters); +} + auto Mass::bulletLaunchers() -> Containers::ArrayView { return _weapons.bulletLaunchers; } @@ -807,6 +822,10 @@ void Mass::getBulletLaunchers() { getWeaponType("WeaponBL_36_5FD7C41E4613A75B44AB0E90B362846E", _weapons.bulletLaunchers); } +auto Mass::writeBulletLaunchers() -> bool { + return writeWeaponType("WeaponBL_36_5FD7C41E4613A75B44AB0E90B362846E", _weapons.bulletLaunchers); +} + auto Mass::energyLaunchers() -> Containers::ArrayView { return _weapons.energyLaunchers; } @@ -815,6 +834,10 @@ void Mass::getEnergyLaunchers() { getWeaponType("WeaponEL_38_9D23F3884ACA15902C9E6CA6E4995995", _weapons.energyLaunchers); } +auto Mass::writeEnergyLaunchers() -> bool { + return writeWeaponType("WeaponEL_38_9D23F3884ACA15902C9E6CA6E4995995", _weapons.energyLaunchers); +} + auto Mass::globalStyles() -> Containers::ArrayView { return _globalStyles; } @@ -1054,3 +1077,122 @@ void Mass::getWeaponType(const char* prop_name, Containers::ArrayView we weapon.effectColour = Color4{effect_colour->r, effect_colour->g, effect_colour->b, effect_colour->a}; } } + +auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView weapon_array) -> bool { + auto unit_data = _mass->at("UnitData"); + if(!unit_data) { + _state = State::Invalid; + return false; + } + + auto prop = unit_data->at(prop_name); + if(!prop) { + _state = State::Invalid; + return false; + } + + if(prop->items.size() != weapon_array.size()) { + _state = State::Invalid; + return false; + } + + for(UnsignedInt i = 0; i < weapon_array.size(); i++) { + auto weapon_prop = prop->at(i); + auto& weapon = weapon_array[i]; + + weapon_prop->at("Name_13_7BF0D31F4E50C50C47231BB36A485D92")->value = weapon.name; + weapon_prop->at("Type_2_35ABA8C3406F8D9BBF14A89CD6BCE976")->enumValue = weapon.type; + + auto parts_prop = weapon_prop->at("Element_6_8E4617CC4B2C1F1490435599784EC6E0"); + if(parts_prop->items.size() != weapon_array.size()) { + _state = State::Invalid; + return false; + } + + for(UnsignedInt j = 0; j < parts_prop->items.size(); j++) { + auto part_prop = parts_prop->at(j); + auto& part = weapon.parts[j]; + + part_prop->at("ID_2_A74D75434308158E5926178822DD28EE")->value = part.id; + + auto part_styles = part_prop->at("Styles_17_994C97C34A90667BE5B716BFD0B97588"); + for(UnsignedInt k = 0; k < part_styles->items.size(); k++) { + part_styles->at(k)->value = part.styles[k]; + } + + auto part_decals = part_prop->at("Decals_13_8B81112B453D7230C0CDE982185E14F1"); + for(UnsignedInt k = 0; k < part_decals->items.size(); k++) { + auto decal_prop = part_decals->at(k); + auto& decal = part.decals[k]; + + decal_prop->at("ID_3_694C0B35404D8A3168AEC89026BC8CF9")->value = decal.id; + auto colour_prop = decal_prop->at("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("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("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("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("Offset_29_B02BBBB74FC60F5EDBEBAB8020738020"); + offset_prop->x = decal.offset.x(); + offset_prop->y = decal.offset.y(); + decal_prop->at("Scale_32_959D1C2747AFD8D62808468235CBBA40")->value = decal.scale; + decal_prop->at("Rotation_27_12D7C314493D203D5C2326A03C5F910F")->value = decal.rotation; + decal_prop->at("Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1")->value = decal.flip; + decal_prop->at("Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62")->value = decal.wrap; + } + + if(!_demo) { + auto part_accs = part_prop->at("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); + } + } + + auto custom_styles = weapon_prop->at("Styles_10_8C3C82444B986AD7A99595AD4985912D"); + if(!custom_styles) { + _state = State::Invalid; + return false; + } + + if(custom_styles->items.size() != weapon.customStyles.size()) { + _state = State::Invalid; + return false; + } + + for(UnsignedInt j = 0; j < weapon.customStyles.size(); j++) { + setCustomStyle(weapon.customStyles[j], j, custom_styles); + } + + weapon_prop->at("Attach_15_D00AABBD4AD6A04778D56D81E51927B3")->value = weapon.attached; + weapon_prop->at("DamageType_18_E1FFA53540591A9087EC698117A65C83")->enumValue = weapon.damageType; + weapon_prop->at("DualWield_20_B2EB2CEA4A6A233DC7575996B6DD1222")->value = weapon.dualWield; + weapon_prop->at("ColorEfxMode_24_D254BCF943E852BF9ADB8AAA8FD80014")->enumValue = weapon.effectColourMode; + auto effect_colour = weapon_prop->at("ColorEfx_26_D921B62946C493E487455A831F4520AC"); + effect_colour->r = weapon.effectColour.r(); + effect_colour->g = weapon.effectColour.g(); + effect_colour->b = weapon.effectColour.b(); + effect_colour->a = weapon.effectColour.a(); + } + + return _mass->saveToFile(); +} diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 022c6c9..a161046 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -206,6 +206,7 @@ class Mass { void getAccessories(Containers::ArrayView accessories, ArrayProperty* accessory_array); void getWeaponType(const char* prop_name, Containers::ArrayView weapon_array); + auto writeWeaponType(const char* prop_name, Containers::ArrayView weapon_array) -> bool; Containers::Optional _mass; -- 2.39.2 From 8d87cdd61984d2abac9d1a94bc1ea1a18d99dc63 Mon Sep 17 00:00:00 2001 From: William JCM Date: Fri, 29 Oct 2021 14:41:52 +0200 Subject: [PATCH 076/128] Mass: fix a check. --- src/Mass/Mass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 017a957..f07bf65 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -1104,7 +1104,7 @@ auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView weapon_prop->at("Type_2_35ABA8C3406F8D9BBF14A89CD6BCE976")->enumValue = weapon.type; auto parts_prop = weapon_prop->at("Element_6_8E4617CC4B2C1F1490435599784EC6E0"); - if(parts_prop->items.size() != weapon_array.size()) { + if(parts_prop->items.size() != weapon.parts.size()) { _state = State::Invalid; return false; } -- 2.39.2 From 83fa5822bfaad71bcfe6d69553cc699f53bf6f78 Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 1 Nov 2021 09:40:33 +0100 Subject: [PATCH 077/128] MassManager: fix bugs in path handling. --- src/MassManager/MassManager.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/MassManager/MassManager.cpp b/src/MassManager/MassManager.cpp index 4e94fca..74a6148 100644 --- a/src/MassManager/MassManager.cpp +++ b/src/MassManager/MassManager.cpp @@ -86,8 +86,8 @@ auto MassManager::importMass(const std::string& staged_fn, int hangar) -> bool { return false; } - if(Utility::Directory::exists(_hangars[hangar].filename())) { - Utility::Directory::rm(_hangars[hangar].filename()); + if(Utility::Directory::exists(Utility::Directory::join(_saveDirectory, _hangars[hangar].filename()))) { + Utility::Directory::rm(Utility::Directory::join(_saveDirectory, _hangars[hangar].filename())); } if(!Utility::Directory::move(source + ".tmp", Utility::Directory::join(_saveDirectory, _hangars[hangar].filename()))) { @@ -132,8 +132,8 @@ auto MassManager::moveMass(int source, int destination) -> bool { return false; } - std::string source_file = _hangars[source].filename(); - std::string dest_file = _hangars[destination].filename(); + std::string source_file = Utility::Directory::join(_saveDirectory, _hangars[source].filename()); + std::string dest_file = Utility::Directory::join(_saveDirectory, _hangars[destination].filename()); Mass::State dest_state = _hangars[destination].state(); switch(dest_state) { @@ -162,7 +162,7 @@ auto MassManager::deleteMass(int hangar) -> bool { return false; } - if(!Utility::Directory::rm(_hangars[hangar].filename())) { + if(!Utility::Directory::rm(Utility::Directory::join(_saveDirectory, _hangars[hangar].filename()))) { _lastError = "Deletion failed. Maybe the file was already deleted, or it's locked by another application."; return false; } -- 2.39.2 From bd8ff47f1e3a917ec61ff2a91083ea9074aa8265 Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 1 Nov 2021 11:19:34 +0100 Subject: [PATCH 078/128] Mass: fix data ordering issue with joint sliders. --- src/Mass/Mass.cpp | 132 +++++++++---------- src/UESaveFile/Types/GenericStructProperty.h | 11 ++ 2 files changed, 77 insertions(+), 66 deletions(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index f07bf65..7b4a028 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -290,94 +290,94 @@ auto Mass::writeJointSliders() -> bool { return false; } - auto length = frame_prop->at("NeckLength_6_ED6AF79849C27CD1A9D523A09E2BFE58"); - if(!length && _frame.joints.neck != 0.0f) { - length = new FloatProperty; - auto length_prop = FloatProperty::ptr{length}; - length_prop->name.emplace("NeckLength_6_ED6AF79849C27CD1A9D523A09E2BFE58"); - arrayAppend(frame_prop->properties, std::move(length_prop)); - } - if(length) { + Containers::Array temp; + + auto length = frame_prop->atMove("NeckLength_6_ED6AF79849C27CD1A9D523A09E2BFE58"); + if(_frame.joints.neck != 0.0f) { + if(!length) { + length.emplace(); + length->name.emplace("NeckLength_6_ED6AF79849C27CD1A9D523A09E2BFE58"); + } length->value = _frame.joints.neck; + arrayAppend(temp, std::move(length)); } - length = frame_prop->at("BodyLength_7_C16287754CBA96C93BAE36A5C154996A"); - if(!length && _frame.joints.body != 0.0f) { - length = new FloatProperty; - auto length_prop = FloatProperty::ptr{length}; - length_prop->name.emplace("BodyLength_7_C16287754CBA96C93BAE36A5C154996A"); - arrayAppend(frame_prop->properties, std::move(length_prop)); - } - if(length) { + length = frame_prop->atMove("BodyLength_7_C16287754CBA96C93BAE36A5C154996A"); + if(_frame.joints.body != 0.0f) { + if(!length) { + length.emplace(); + length->name.emplace("BodyLength_7_C16287754CBA96C93BAE36A5C154996A"); + } length->value = _frame.joints.body; + arrayAppend(temp, std::move(length)); } - length = frame_prop->at("ShoulderLength_8_220EDF304F1C1226F0D8D39117FB3883"); - if(!length && _frame.joints.shoulders != 0.0f) { - length = new FloatProperty; - auto length_prop = FloatProperty::ptr{length}; - length_prop->name.emplace("ShoulderLength_8_220EDF304F1C1226F0D8D39117FB3883"); - arrayAppend(frame_prop->properties, std::move(length_prop)); - } - if(length) { + length = frame_prop->atMove("ShoulderLength_8_220EDF304F1C1226F0D8D39117FB3883"); + if(_frame.joints.shoulders != 0.0f) { + if(!length) { + length.emplace(); + length->name.emplace("ShoulderLength_8_220EDF304F1C1226F0D8D39117FB3883"); + } length->value = _frame.joints.shoulders; + arrayAppend(temp, std::move(length)); } - length = frame_prop->at("HipLength_14_02AEEEAC4376087B9C51F0AA7CC92818"); - if(!length && _frame.joints.hips != 0.0f) { - length = new FloatProperty; - auto length_prop = FloatProperty::ptr{length}; - length_prop->name.emplace("HipLength_14_02AEEEAC4376087B9C51F0AA7CC92818"); - arrayAppend(frame_prop->properties, std::move(length_prop)); - } - if(length) { - length->value = _frame.joints.hips; - } - - length = frame_prop->at("ArmUpperLength_10_249FDA3E4F3B399E7B9E5C9B7C765EAE"); - if(!length && _frame.joints.upperArms != 0.0f) { - length = new FloatProperty; - auto length_prop = FloatProperty::ptr{length}; - length_prop->name.emplace("ArmUpperLength_10_249FDA3E4F3B399E7B9E5C9B7C765EAE"); - arrayAppend(frame_prop->properties, std::move(length_prop)); - } - if(length) { + length = frame_prop->atMove("ArmUpperLength_10_249FDA3E4F3B399E7B9E5C9B7C765EAE"); + if(_frame.joints.upperArms != 0.0f) { + if(!length) { + length.emplace(); + length->name.emplace("ArmUpperLength_10_249FDA3E4F3B399E7B9E5C9B7C765EAE"); + } length->value = _frame.joints.upperArms; + arrayAppend(temp, std::move(length)); } - length = frame_prop->at("ArmLowerLength_12_ACD0F02745C28882619376926292FB36"); - if(!length && _frame.joints.lowerArms != 0.0f) { - length = new FloatProperty; - auto length_prop = FloatProperty::ptr{length}; - length_prop->name.emplace("ArmLowerLength_12_ACD0F02745C28882619376926292FB36"); - arrayAppend(frame_prop->properties, std::move(length_prop)); - } - if(length) { + length = frame_prop->atMove("ArmLowerLength_12_ACD0F02745C28882619376926292FB36"); + if(_frame.joints.lowerArms != 0.0f) { + if(!length) { + length.emplace(); + length->name.emplace("ArmLowerLength_12_ACD0F02745C28882619376926292FB36"); + } length->value = _frame.joints.lowerArms; + arrayAppend(temp, std::move(length)); } - length = frame_prop->at("LegUpperLength_16_A7C4C71249A3776F7A543D96819C0C61"); - if(!length && _frame.joints.upperLegs != 0.0f) { - length = new FloatProperty; - auto length_prop = FloatProperty::ptr{length}; - length_prop->name.emplace("LegUpperLength_16_A7C4C71249A3776F7A543D96819C0C61"); - arrayAppend(frame_prop->properties, std::move(length_prop)); + length = frame_prop->atMove("HipLength_14_02AEEEAC4376087B9C51F0AA7CC92818"); + if(_frame.joints.hips != 0.0f) { + if(!length) { + length.emplace(); + length->name.emplace("HipLength_14_02AEEEAC4376087B9C51F0AA7CC92818"); + } + length->value = _frame.joints.hips; + arrayAppend(temp, std::move(length)); } - if(length) { + + length = frame_prop->atMove("LegUpperLength_16_A7C4C71249A3776F7A543D96819C0C61"); + if(_frame.joints.upperLegs != 0.0f) { + if(!length) { + length.emplace(); + length->name.emplace("LegUpperLength_16_A7C4C71249A3776F7A543D96819C0C61"); + } length->value = _frame.joints.upperLegs; + arrayAppend(temp, std::move(length)); } - length = frame_prop->at("LegLowerLength_18_D2DF39964EA0F2A2129D0491B08A032F"); - if(!length && _frame.joints.lowerLegs != 0.0f) { - length = new FloatProperty; - auto length_prop = FloatProperty::ptr{length}; - length_prop->name.emplace("LegLowerLength_18_D2DF39964EA0F2A2129D0491B08A032F"); - arrayAppend(frame_prop->properties, std::move(length_prop)); - } - if(length) { + length = frame_prop->atMove("LegLowerLength_18_D2DF39964EA0F2A2129D0491B08A032F"); + if(_frame.joints.lowerLegs != 0.0f) { + if(!length) { + length.emplace(); + length->name.emplace("LegLowerLength_18_D2DF39964EA0F2A2129D0491B08A032F"); + } length->value = _frame.joints.lowerLegs; + arrayAppend(temp, std::move(length)); } + arrayAppend(temp, std::move(frame_prop->properties[frame_prop->properties.size() - 3])); + arrayAppend(temp, std::move(frame_prop->properties[frame_prop->properties.size() - 2])); + arrayAppend(temp, std::move(frame_prop->properties[frame_prop->properties.size() - 1])); + + frame_prop->properties = std::move(temp); + if(!_mass->saveToFile()) { _lastError = "Couldn't write data to " + _filename; return false; diff --git a/src/UESaveFile/Types/GenericStructProperty.h b/src/UESaveFile/Types/GenericStructProperty.h index 2e5b8bb..7ff3cfb 100644 --- a/src/UESaveFile/Types/GenericStructProperty.h +++ b/src/UESaveFile/Types/GenericStructProperty.h @@ -36,5 +36,16 @@ struct GenericStructProperty : public StructProperty { return nullptr; } + template + std::enable_if_t::value, typename T::ptr> + atMove(const std::string& name) { + for(auto& item : properties) { + if(item && item->name == name) { + return Containers::Pointer{static_cast(item.release())}; + } + } + return nullptr; + } + Containers::Array properties; }; -- 2.39.2 From fe0db983ceac6fda3b019f703209166af0d3a4a8 Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 2 Dec 2021 15:27:00 +0100 Subject: [PATCH 079/128] Mass: add tuning reading support. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Writing support SOONâ„¢. --- src/Mass/Mass.cpp | 77 ++++++++++++++++++++++++++++++++++++++++++++++- src/Mass/Mass.h | 14 +++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 7b4a028..41464e3 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -177,9 +177,15 @@ void Mass::refreshValues() { if(!_demo) { getGlobalStyles(); + if(_state == State::Invalid) { + return; + } } - // TODO: tuning + getTuning(); + if(_state == State::Invalid) { + return; + } auto account_prop = _mass->at("Account"); if(!account_prop) { @@ -883,6 +889,47 @@ auto Mass::writeGlobalStyle(UnsignedLong index) -> bool { return setCustomStyle(_globalStyles[index], index, global_styles); } +void Mass::getTuning() { + getTuningCategory("Engine", _tuning.engineId, "Gears", _tuning.gearIds); + if(_state == State::Invalid) { + return; + } + + getTuningCategory("OS", _tuning.osId, "Modules", _tuning.moduleIds); + if(_state == State::Invalid) { + return; + } + + getTuningCategory("Architect", _tuning.archId, "Techs", _tuning.techIds); + if(_state == State::Invalid) { + return; + } +} + +auto Mass::engine() -> Int& { + return _tuning.engineId; +} + +auto Mass::gears() -> Containers::ArrayView { + return _tuning.gearIds; +} + +auto Mass::os() -> Int& { + return _tuning.osId; +} + +auto Mass::modules() -> Containers::ArrayView { + return _tuning.moduleIds; +} + +auto Mass::architecture() -> Int& { + return _tuning.archId; +} + +auto Mass::techs() -> Containers::ArrayView { + return _tuning.techIds; +} + auto Mass::updateSteamId(const std::string& steam_id) -> bool { _steamId = steam_id; @@ -1196,3 +1243,31 @@ auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView return _mass->saveToFile(); } + +void Mass::getTuningCategory(const char* big_node_prop_name, Int& big_node_id, + const char* small_nodes_prop_name, Containers::ArrayView small_nodes_ids) +{ + auto node_id = _mass->at(big_node_prop_name); + if(!node_id) { + _state = State::Invalid; + return; + } + big_node_id = node_id->value; + + auto node_ids = _mass->at(small_nodes_prop_name); + if(!node_ids) { + _state = State::Invalid; + return; + } + + if(node_ids->items.size() != small_nodes_ids.size()) { + _state = State::Invalid; + return; + } + + for(UnsignedInt i = 0; i < small_nodes_ids.size(); i++) { + auto small_node_id = node_ids->at(i); + CORRADE_INTERNAL_ASSERT(small_node_id); + small_nodes_ids[i] = small_node_id->value; + } +} diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index a161046..8ff8989 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -196,6 +196,17 @@ class Mass { void getGlobalStyles(); auto writeGlobalStyle(UnsignedLong index) -> bool; + void getTuning(); + + auto engine() -> Int&; + auto gears() -> Containers::ArrayView; + + auto os() -> Int&; + auto modules() -> Containers::ArrayView; + + auto architecture() -> Int&; + auto techs() -> Containers::ArrayView; + auto updateSteamId(const std::string& steam_id) -> bool; private: @@ -208,6 +219,9 @@ class Mass { void getWeaponType(const char* prop_name, Containers::ArrayView weapon_array); auto writeWeaponType(const char* prop_name, Containers::ArrayView weapon_array) -> bool; + void getTuningCategory(const char* big_node_prop_name, Int& big_node_id, + const char* small_nodes_prop_name, Containers::ArrayView small_nodes_ids); + Containers::Optional _mass; static std::string _lastError; -- 2.39.2 From c6de9c19402b7645990cfeec0c61ddae38ab81f4 Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 2 Dec 2021 19:23:28 +0100 Subject: [PATCH 080/128] SaveTool: finish most of the M.A.S.S. viewer. Some parts are very unfinished, but do work. --- src/CMakeLists.txt | 2 + src/Maps/Accessories.h | 74 ++ src/Maps/WeaponTypes.h | 29 + src/SaveTool/SaveTool.h | 21 +- src/SaveTool/SaveTool_MassViewer.cpp | 1498 +++++++++++++++++++++----- 5 files changed, 1382 insertions(+), 242 deletions(-) create mode 100644 src/Maps/Accessories.h create mode 100644 src/Maps/WeaponTypes.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3cd84a0..a9b0e20 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -126,11 +126,13 @@ add_executable(MassBuilderSaveTool WIN32 MassManager/MassManager.cpp Mass/Mass.h Mass/Mass.cpp + Maps/Accessories.h Maps/ArmourSets.h Maps/ArmourSlots.h Maps/LastMissionId.h Maps/StoryProgress.h Maps/StyleNames.h + Maps/WeaponTypes.h ToastQueue/ToastQueue.h ToastQueue/ToastQueue.cpp FontAwesome/IconsFontAwesome5.h diff --git a/src/Maps/Accessories.h b/src/Maps/Accessories.h new file mode 100644 index 0000000..dc50ded --- /dev/null +++ b/src/Maps/Accessories.h @@ -0,0 +1,74 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include + +using namespace Magnum; + +static const std::map accessories { + // Primitives + {1, "Cube (S)"}, + {2, "Pentagon (S)"}, + {3, "Hexagon (S)"}, + {4, "Cylinder (S)"}, + {5, "Sphere (S)"}, + {6, "TriPyramid (S)"}, + {7, "SquPyramid (S)"}, + {8, "PenPyramid (S)"}, + {9, "HexPyramid (S)"}, + {10, "Cone (S)"}, + {11, "SquStick (S)"}, + {12, "PenStick (S)"}, + {13, "HexStick (S)"}, + {14, "CycStick (S)"}, + {15, "Capsule (S)"}, + {16, "Decal Pad 01 (S)"}, + {17, "Decal Pad 02 (S)"}, + {18, "Decal Pad 03 (S)"}, + {19, "Decal Pad 04 (S)"}, + {20, "Decal Pad 05 (S)"}, + + {51, "SquBevel (S)"}, + {52, "TriBevel (S)"}, + {53, "PenBevel (S)"}, + {54, "HexBevel (S)"}, + {55, "CycBevel (S)"}, + {56, "RecBevel (S)"}, + {57, "DaiBevel (S)"}, + {58, "MonBevel (S)"}, + {59, "CofBevel (S)"}, + {60, "JevBevel (S)"}, + {61, "SquEmboss (S)"}, + {62, "TriEmboss (S)"}, + {63, "PenEmboss (S)"}, + {64, "HexEmboss (S)"}, + {65, "CycEmboss (S)"}, + {66, "RecEmboss (S)"}, + {67, "DaiEmboss (S)"}, + {68, "MonEmboss (S)"}, + {69, "CofEmboss (S)"}, + {70, "JevEmboss (S)"}, + + // Armours + + // Components + + // Connectors +}; diff --git a/src/Maps/WeaponTypes.h b/src/Maps/WeaponTypes.h new file mode 100644 index 0000000..2d0a0b1 --- /dev/null +++ b/src/Maps/WeaponTypes.h @@ -0,0 +1,29 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include +#include + +static const std::unordered_map 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"}, +}; diff --git a/src/SaveTool/SaveTool.h b/src/SaveTool/SaveTool.h index 7ff8409..d390be1 100644 --- a/src/SaveTool/SaveTool.h +++ b/src/SaveTool/SaveTool.h @@ -112,8 +112,25 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener void drawMassViewer(); void drawFrameInfo(); void drawJointSliders(); - void drawFramePaint(); - void drawCustomStyles(Containers::ArrayView styles); + void drawFrameStyles(); + void drawEyeColourPicker(); + void drawCustomFrameStyles(); + void drawArmour(); + void drawCustomArmourStyles(); + void drawWeapons(); + void drawWeaponEditor(Weapon& weapon); + void drawGlobalStyles(); + void drawTuning(); + void drawDecalEditor(Decal& decal); + void drawAccessoryEditor(Accessory& accessory, Containers::ArrayView style_view); + auto getStyleName(Int id, Containers::ArrayView view) -> const char*; + + enum DCSResult { + DCS_Fail, + DCS_ResetStyle, + DCS_Save + }; + auto drawCustomStyle(CustomStyle& style) -> DCSResult; void drawAbout(); void drawGameState(); diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index 5b1fb86..7d3ff4e 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -14,17 +14,26 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include + #include +#include "../Maps/Accessories.h" +#include "../Maps/ArmourSets.h" +#include "../Maps/ArmourSlots.h" #include "../Maps/StyleNames.h" +#include "../Maps/WeaponTypes.h" #include "../FontAwesome/IconsFontAwesome5.h" #include "SaveTool.h" +static Weapon* current_weapon = nullptr; + void SaveTool::drawMassViewer() { if(!_currentMass || _currentMass->state() != Mass::State::Valid) { _currentMass = nullptr; + current_weapon = nullptr; _uiState = UiState::MainManager; _queue.addToast(Toast::Type::Error, "The selected M.A.S.S. isn't valid anymore."); return; @@ -41,61 +50,93 @@ void SaveTool::drawMassViewer() { return; } - ImGui::Text("Current M.A.S.S.: %s (%s)", - (*_currentMass->name()).c_str(), - _currentMass->filename().c_str()); - ImGui::SameLine(); - if(ImGui::SmallButton(ICON_FA_ARROW_LEFT " Back to main manager")) { - _currentMass = nullptr; - _uiState = UiState::MainManager; - } - - ImGui::TextColored(ImColor(255, 255, 0), ICON_FA_EXCLAMATION_TRIANGLE); - ImGui::SameLine(); - ImGui::TextWrapped("WARNING: Colours in this app may look different from in-game colours, due to unavoidable differences in the rendering pipeline."); - - ImGui::TextColored(ImColor(255, 255, 0), ICON_FA_EXCLAMATION_TRIANGLE); - ImGui::SameLine(); - ImGui::TextWrapped("Real-time updates are disabled while on this screen. %s", _currentMass->dirty() ? "The save file has been changed." : ""); - if(_currentMass->dirty()) { - ImGui::SameLine(); - if(ImGui::SmallButton(ICON_FA_SYNC_ALT " Refresh")) { - _currentMass->refreshValues(); - _currentMass->setDirty(false); - } - } - if(ImGui::BeginChild("##MassInfo", - {ImGui::GetContentRegionAvailWidth() * 0.60f, 0.0f}, + {0.0f, 0.0f}, true, ImGuiWindowFlags_MenuBar)) { if(ImGui::BeginMenuBar()) { - ImGui::TextUnformatted("M.A.S.S. Information"); - ImGui::EndMenuBar(); - } + if(ImGui::BeginTable("##MassViewerMenuTable", 4)) { + ImGui::TableSetupColumn("##MassName"); + ImGui::TableSetupColumn("##Spacer", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("##Updates"); + ImGui::TableSetupColumn("##Close", ImGuiTableColumnFlags_WidthFixed); - if(ImGui::BeginTabBar("##MassTabBar")) { - if(ImGui::BeginTabItem("Frame")) { - drawFrameInfo(); - ImGui::EndTabItem(); + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex(0); + ImGui::Text("M.A.S.S.: %s", (*_currentMass->name()).c_str()); + drawTooltip(_currentMass->filename().c_str()); + + ImGui::TableSetColumnIndex(2); + if(_currentMass->dirty()) { + ImGui::TextUnformatted("External changes detected"); + ImGui::SameLine(); + if(ImGui::SmallButton(ICON_FA_SYNC_ALT " Refresh")) { + _currentMass->refreshValues(); + _currentMass->setDirty(false); + } + } + + ImGui::TableSetColumnIndex(3); + if(ImGui::SmallButton(ICON_FA_TIMES)) { + current_weapon = nullptr; + _currentMass = nullptr; + _uiState = UiState::MainManager; + } + + ImGui::EndTable(); } - ImGui::EndTabBar(); - } - } - ImGui::EndChild(); - - ImGui::SameLine(); - - if(ImGui::BeginChild("##GlobalStyles", {0.0f, 0.0f}, - true, ImGuiWindowFlags_MenuBar)) - { - if(ImGui::BeginMenuBar()) { - ImGui::TextUnformatted("Global styles"); ImGui::EndMenuBar(); } - ImGui::TextUnformatted("Placeholder"); + ImGui::TextColored(ImColor(255, 255, 0), ICON_FA_EXCLAMATION_TRIANGLE); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::TextWrapped("WARNING: Colours in this app may look different from in-game colours, due to unavoidable differences in the rendering pipeline."); + ImGui::TextColored(ImColor(255, 255, 0), ICON_FA_EXCLAMATION_TRIANGLE); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::TextWrapped("Real-time updates are disabled on this screen."); + + if(_currentMass) { + if(ImGui::BeginTabBar("##MassTabBar")) { + if(ImGui::BeginTabItem("Frame")) { + drawFrameInfo(); + ImGui::EndTabItem(); + } + + if(ImGui::BeginTabItem("Custom frame styles")) { + drawCustomFrameStyles(); + ImGui::EndTabItem(); + } + + if(ImGui::BeginTabItem("Armour parts")) { + drawArmour(); + ImGui::EndTabItem(); + } + + if(ImGui::BeginTabItem("Custom armour styles")) { + drawCustomArmourStyles(); + ImGui::EndTabItem(); + } + + if(ImGui::BeginTabItem("Weapons (WIP)")) { + drawWeapons(); + ImGui::EndTabItem(); + } + + if(!_currentMass->demo() && ImGui::BeginTabItem("Global styles")) { + drawGlobalStyles(); + ImGui::EndTabItem(); + } + + if(ImGui::BeginTabItem("Tuning (WIP)")) { + drawTuning(); + ImGui::EndTabItem(); + } + + ImGui::EndTabBar(); + } + } } ImGui::EndChild(); @@ -107,20 +148,59 @@ void SaveTool::drawFrameInfo() { return; } - ImGui::TextUnformatted("Frame type: Skeleton"); // Placeholder for now, replace with actual code once other frames are implemented. - - drawJointSliders(); - - drawFramePaint(); -} - -void SaveTool::drawJointSliders() { - if(!ImGui::CollapsingHeader("Joint sliders")) { + 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; } - static Joints sliders = _currentMass->jointSliders(); - static bool joints_edit = false; static bool joints_dirty = false; ImGui::TextWrapped("In-game values are multiplied by 100.\nFor example, 0.500 here is equal to 50 in-game."); @@ -134,15 +214,9 @@ void SaveTool::drawJointSliders() { ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Neck"); ImGui::TableSetColumnIndex(1); - if(joints_edit) { - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##NeckSlider", &sliders.neck, 0.0f, 1.0f)) { - joints_dirty = true; - } - } - else { - ImGui::AlignTextToFramePadding(); - ImGui::Text("%.3f", Double(_currentMass->jointSliders().neck)); + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##NeckSlider", &_currentMass->jointSliders().neck, 0.0f, 1.0f)) { + joints_dirty = true; } ImGui::TableNextRow(); @@ -150,15 +224,9 @@ void SaveTool::drawJointSliders() { ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Body"); ImGui::TableSetColumnIndex(1); - if(joints_edit) { - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##BodySlider", &sliders.body, 0.0f, 1.0f)) { - joints_dirty = true; - } - } - else { - ImGui::AlignTextToFramePadding(); - ImGui::Text("%.3f", Double(_currentMass->jointSliders().body)); + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##BodySlider", &_currentMass->jointSliders().body, 0.0f, 1.0f)) { + joints_dirty = true; } ImGui::TableNextRow(); @@ -166,15 +234,9 @@ void SaveTool::drawJointSliders() { ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Shoulders"); ImGui::TableSetColumnIndex(1); - if(joints_edit) { - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##ShouldersSlider", &sliders.shoulders, 0.0f, 1.0f)) { - joints_dirty = true; - } - } - else { - ImGui::AlignTextToFramePadding(); - ImGui::Text("%.3f", Double(_currentMass->jointSliders().shoulders)); + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##ShouldersSlider", &_currentMass->jointSliders().shoulders, 0.0f, 1.0f)) { + joints_dirty = true; } ImGui::TableNextRow(); @@ -182,31 +244,9 @@ void SaveTool::drawJointSliders() { ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Hips"); ImGui::TableSetColumnIndex(1); - if(joints_edit) { - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##HipsSlider", &sliders.hips, 0.0f, 1.0f)) { - joints_dirty = true; - } - } - else { - ImGui::AlignTextToFramePadding(); - ImGui::Text("%.3f", Double(_currentMass->jointSliders().hips)); - } - - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(1); - if(ImGui::BeginTable("##UpperLowerLayoutTable", 2, ImGuiTableFlags_BordersInnerV)) { - ImGui::TableSetupColumn("##Upper", ImGuiTableColumnFlags_WidthStretch); - ImGui::TableSetupColumn("##Lower", ImGuiTableColumnFlags_WidthStretch); - - ImGui::TableNextColumn(); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Upper"); - ImGui::TableNextColumn(); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Lower"); - - ImGui::EndTable(); + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##HipsSlider", &_currentMass->jointSliders().hips, 0.0f, 1.0f)) { + joints_dirty = true; } ImGui::TableNextRow(); @@ -214,210 +254,1188 @@ void SaveTool::drawJointSliders() { ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Arms"); ImGui::TableSetColumnIndex(1); - if(ImGui::BeginTable("##UpperLowerArmsLayoutTable", 2, ImGuiTableFlags_BordersInnerV)) { + 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(); - if(joints_edit) { - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##UpperArmsSlider", &sliders.upperArms, 0.0f, 1.0f)) { - joints_dirty = true; - } - } - else { - ImGui::AlignTextToFramePadding(); - ImGui::Text("%.3f", Double(_currentMass->jointSliders().upperArms)); + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##UpperArmsSlider", &_currentMass->jointSliders().upperArms, 0.0f, 1.0f, "Upper: %.3f")) { + joints_dirty = true; } ImGui::TableNextColumn(); - if(joints_edit) { - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##LowerArmsSlider", &sliders.lowerArms, 0.0f, 1.0f)) { - joints_dirty = true; - } - } - else { - ImGui::AlignTextToFramePadding(); - ImGui::Text("%.3f", Double(_currentMass->jointSliders().lowerArms)); + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##LowerArmsSlider", &_currentMass->jointSliders().lowerArms, 0.0f, 1.0f, "Lower: %.3f")) { + joints_dirty = true; } ImGui::EndTable(); } + ImGui::PopStyleVar(); ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Legs"); ImGui::TableSetColumnIndex(1); - if(ImGui::BeginTable("##UpperLowerLegsLayoutTable", 2, ImGuiTableFlags_BordersInnerV)) { + 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(); - if(joints_edit) { - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##UpperLegsSlider", &sliders.upperLegs, 0.0f, 1.0f)) { - joints_dirty = true; - } - } - else { - ImGui::AlignTextToFramePadding(); - ImGui::Text("%.3f", Double(_currentMass->jointSliders().upperLegs)); + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##UpperLegsSlider", &_currentMass->jointSliders().upperLegs, 0.0f, 1.0f, "Upper: %.3f")) { + joints_dirty = true; } ImGui::TableNextColumn(); - if(joints_edit) { - ImGui::SetNextItemWidth(-1.0f); - if(ImGui::SliderFloat("##LowerLegsSlider", &sliders.lowerLegs, 0.0f, 1.0f)) { - joints_dirty = true; - } - } - else { - ImGui::AlignTextToFramePadding(); - ImGui::Text("%.3f", Double(_currentMass->jointSliders().lowerLegs)); + ImGui::SetNextItemWidth(-1.0f); + if(ImGui::SliderFloat("##LowerLegsSlider", &_currentMass->jointSliders().lowerLegs, 0.0f, 1.0f, "Lower: %.3f")) { + joints_dirty = true; } ImGui::EndTable(); } + ImGui::PopStyleVar(); ImGui::EndTable(); } - if(joints_edit) { - if(!joints_dirty) { - ImGui::BeginDisabled(); - ImGui::Button(ICON_FA_SAVE " Save changes"); - ImGui::SameLine(); - ImGui::Button(ICON_FA_UNDO " Reset sliders"); - ImGui::EndDisabled(); - } - else { - if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save changes"); })) { - if(!_currentMass->setSliders(sliders)) { - _queue.addToast(Toast::Type::Error, "Error writing the joint sliders."); - } - joints_dirty = false; - joints_edit = false; - } - ImGui::SameLine(); - if(ImGui::Button(ICON_FA_UNDO " Reset sliders")) { - sliders = _currentMass->jointSliders(); - joints_dirty = false; - } - } + if(!joints_dirty) { + ImGui::BeginDisabled(); + ImGui::Button(ICON_FA_SAVE " Save"); ImGui::SameLine(); - if(ImGui::Button(ICON_FA_TIMES " Cancel editing")) { - sliders = _currentMass->jointSliders(); - joints_dirty = false; - joints_edit = false; - } - - ImGui::TextUnformatted("To input out-of-range values, hold Ctrl and click on a slider."); + ImGui::Button(ICON_FA_UNDO " Reset"); + ImGui::EndDisabled(); } else { - if(ImGui::Button(ICON_FA_EDIT " Edit sliders")) { - sliders = _currentMass->jointSliders(); - joints_edit = true; + if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { + if(!_currentMass->writeJointSliders()) { + _queue.addToast(Toast::Type::Error, "Error: " + Mass::lastError()); + } + joints_dirty = false; + } + ImGui::SameLine(); + if(ImGui::Button(ICON_FA_UNDO " Reset")) { + _currentMass->getJointSliders(); + joints_dirty = false; } } } -void SaveTool::drawFramePaint() { - if(!ImGui::CollapsingHeader("Paint")) { +void SaveTool::drawFrameStyles() { + if(!_currentMass || _currentMass->state() != Mass::State::Valid) { return; } - ImGui::TextUnformatted("Frame styles:"); + static bool styles_dirty = false; + for(Int i = 0; i < 4; i++) { ImGui::AlignTextToFramePadding(); ImGui::Text("Slot %d:", i + 1); + ImGui::SameLine(); + ImGui::PushID(i); - GameState game_state = _gameState; - if(!_unsafeMode && game_state != GameState::NotRunning) { - ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true); - ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.5f); - } - if(ImGui::BeginCombo("##Style", style_names.at(_currentMass->frameStyles()[i]))) { + + if(ImGui::BeginCombo("##Style", getStyleName(_currentMass->frameStyles()[i], _currentMass->frameCustomStyles()))) { for(const auto& style : style_names) { - if(ImGui::Selectable(style.second, _currentMass->frameStyles()[i] == style.first)) { - if(!_currentMass->setFrameStyle(i, style.first)) { - _queue.addToast(Toast::Type::Error, Mass::lastError()); - } - else { - _currentMass->refreshValues(); - } + if(ImGui::Selectable(getStyleName(style.first, _currentMass->frameCustomStyles()), _currentMass->frameStyles()[i] == style.first)) { + _currentMass->frameStyles()[i] = style.first; + styles_dirty = true; } } ImGui::EndCombo(); } - if(!_unsafeMode && game_state != GameState::NotRunning) { - ImGui::PopItemFlag(); - ImGui::PopStyleVar(); + + ImGui::PopID(); + } + + if(!styles_dirty) { + 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, "Error: " + Mass::lastError()); + } + styles_dirty = false; + } + ImGui::SameLine(); + if(ImGui::Button(ICON_FA_UNDO " Reset")) { + _currentMass->getFrameStyles(); + styles_dirty = false; + } + } +} + +void SaveTool::drawEyeColourPicker() { + if(!_currentMass || _currentMass->state() != Mass::State::Valid) { + return; + } + + static bool eye_flare_dirty = false; + + if(ImGui::ColorPicker3("##EyeFlarePicker", &_currentMass->eyeFlareColour().x())) { + eye_flare_dirty = true; + } + + if(!eye_flare_dirty) { + 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, "Error writing the eye flare colour."); + } + eye_flare_dirty = false; + } + ImGui::SameLine(); + if(ImGui::Button(ICON_FA_UNDO " Reset")) { + _currentMass->getEyeFlareColour(); + eye_flare_dirty = 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: + _currentMass->writeFrameCustomStyle(i); + break; + default: + break; } ImGui::PopID(); } + ImGui::EndChild(); +} + +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 Containers::StaticArray<38, Int> selected_decals{ValueInit}; + static Containers::StaticArray<38, Int> selected_accessories{ValueInit}; + + 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###%s", armour_slots.at(part.slot), armour_sets.at(part.id).name, part.slot.c_str()); + } + else { + std::snprintf(header, 128, "%s: %i###%s", armour_slots.at(part.slot), part.id, part.slot.c_str()); + } + + if(ImGui::CollapsingHeader(header)) { + ImGui::BeginGroup(); + + ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth() * 0.491f); + if(ImGui::BeginListBox("##ChangePart")) { + if(std::strncmp("Neck", armour_slots.at(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++) { + ImGui::AlignTextToFramePadding(); + ImGui::Text("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"); + + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Showing/editing decal"); + for(UnsignedLong j = 0; j < part.demoDecals; j++) { + ImGui::SameLine(); + ImGui::RadioButton(std::to_string(j + 1).c_str(), &selected_decals[i], j); + } + + drawDecalEditor(part.decals[selected_decals[i]]); + + ImGui::PopID(); + + if(!_currentMass->demo()) { + ImGui::Separator(); + + ImGui::PushID("Accessory"); + + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Showing/editing accessory"); + for(UnsignedInt j = 0; j < part.accessories.size(); j++) { + ImGui::SameLine(); + ImGui::RadioButton(std::string{char(65 + j)}.c_str(), &selected_accessories[i], j); + } + + drawAccessoryEditor(part.accessories[selected_accessories[i]], _currentMass->armourCustomStyles()); + + ImGui::PopID(); + } + + ImGui::Separator(); + + if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { + _currentMass->writeArmourPart(i); + } + } + + 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: + _currentMass->writeArmourCustomStyle(i); + break; + default: + break; + } + ImGui::PopID(); + } + + ImGui::EndChild(); +} + +void SaveTool::drawWeapons() { + if(!_currentMass || _currentMass->state() != Mass::State::Valid) { + current_weapon = nullptr; + return; + } + + if(!ImGui::BeginTable("##WeaponsList", 1, ImGuiTableFlags_ScrollY|ImGuiTableFlags_BordersOuter|ImGuiTableFlags_BordersInnerH, {ImGui::GetContentRegionAvailWidth() * 0.2f, 0.0f})) { + return; + } + + ImGui::TableSetupColumn("Weapon"); + + int id = 0; + +#define weapcat(header, array, payload_type, payload_tooltip) \ + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); \ + ImGui::TableNextColumn(); \ + ImGui::TextUnformatted(header); \ + \ + for(UnsignedInt i = 0; i < _currentMass->array().size(); i++) { \ + auto& weapon = _currentMass->array()[i]; \ + \ + ImGui::TableNextRow(); \ + ImGui::TableNextColumn(); \ + ImGui::PushID(id); \ + if(ImGui::Selectable(weapon.name.c_str(), current_weapon == &weapon)) { \ + current_weapon = &weapon; \ + } \ + if(ImGui::BeginDragDropSource()) { \ + ImGui::SetDragDropPayload(payload_type, &i, sizeof(UnsignedInt)); \ + ImGui::Text(payload_tooltip " %i - %s", i + 1, weapon.name.c_str()); \ + ImGui::EndDragDropSource(); \ + } \ + if(ImGui::BeginDragDropTarget()) { \ + if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(payload_type)) { \ + int index = *static_cast(payload->Data); \ + \ + if(current_weapon == &_currentMass->array()[index]) { \ + current_weapon = &_currentMass->array()[i]; \ + } \ + else if (current_weapon == &_currentMass->array()[i]) { \ + current_weapon = &_currentMass->array()[index]; \ + } \ + \ + std::swap(_currentMass->array()[index], _currentMass->array()[i]); \ + } \ + \ + ImGui::EndDragDropTarget(); \ + } \ + \ + ImGui::PopID(); \ + id++; \ + \ + if(weapon.attached == true) { \ + ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, 0x1F008CFFu); \ + } \ + } + + weapcat("Melee weapons", meleeWeapons, "MeleeWeapon", "Melee weapon") + weapcat("Shield", shields, "Shield", "Shield") + weapcat("Bullet shooters", bulletShooters, "BShooter", "Bullet shooter") + weapcat("Energy shooters", energyShooters, "EShooter", "Energy shooter") + weapcat("Bullet launchers", bulletLaunchers, "BLauncher", "Bullet launcher") + weapcat("Energy launchers", energyLaunchers, "ELauncher", "Energy launcher") + +#undef weapcat + + ImGui::EndTable(); + + ImGui::SameLine(); + + if(!current_weapon) { + ImGui::TextUnformatted("No weapon selected."); + return; + } + + ImGui::BeginGroup(); + + const Float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); + if(!ImGui::BeginChild("##WeaponChild", {0.0f, -footer_height_to_reserve})) { + ImGui::EndChild(); + return; + } + + drawWeaponEditor(*current_weapon); + + ImGui::EndChild(); + ImGui::Separator(); - static bool eye_flare_edit = false; - static bool eye_flare_dirty = false; + if(drawUnsafeWidget([](){ return ImGui::Button(ICON_FA_SAVE " Save changes to weapon category"); })) { + if(current_weapon->type == "enuWeaponTypes::NewEnumerator0") { + if(!_currentMass->writeMeleeWeapons()) { + _queue.addToast(Toast::Type::Error, "Couldn't save melee weapons"); + } + } + else if(current_weapon->type == "enuWeaponTypes::NewEnumerator5") { + if(!_currentMass->writeShields()) { + _queue.addToast(Toast::Type::Error, "Couldn't save shields"); + } + } + else if(current_weapon->type == "enuWeaponTypes::NewEnumerator1") { + if(!_currentMass->writeBulletShooters()) { + _queue.addToast(Toast::Type::Error, "Couldn't save bullet shooters"); + } + } + else if(current_weapon->type == "enuWeaponTypes::NewEnumerator2") { + if(!_currentMass->writeEnergyShooters()) { + _queue.addToast(Toast::Type::Error, "Couldn't save energy shooters"); + } + } + else if(current_weapon->type == "enuWeaponTypes::NewEnumerator3") { + if(!_currentMass->writeBulletLaunchers()) { + _queue.addToast(Toast::Type::Error, "Couldn't save bullet launchers"); + } + } + else if(current_weapon->type == "enuWeaponTypes::NewEnumerator4") { + if(!_currentMass->writeEnergyLaunchers()) { + _queue.addToast(Toast::Type::Error, "Couldn't save energy launchers"); + } + } + else { + _queue.addToast(Toast::Type::Error, "Unknown weapon type"); + } + } - static Color4 eye_flare = _currentMass->eyeFlareColour(); + ImGui::SameLine(); + + if(ImGui::Button(ICON_FA_UNDO_ALT " Reset weapon category")) { + if(current_weapon->type == "enuWeaponTypes::NewEnumerator0") { + _currentMass->getMeleeWeapons(); + } + else if(current_weapon->type == "enuWeaponTypes::NewEnumerator5") { + _currentMass->getShields(); + } + else if(current_weapon->type == "enuWeaponTypes::NewEnumerator1") { + _currentMass->getBulletShooters(); + } + else if(current_weapon->type == "enuWeaponTypes::NewEnumerator2") { + _currentMass->getEnergyShooters(); + } + else if(current_weapon->type == "enuWeaponTypes::NewEnumerator3") { + _currentMass->getBulletLaunchers(); + } + else if(current_weapon->type == "enuWeaponTypes::NewEnumerator4") { + _currentMass->getEnergyLaunchers(); + } + else { + _queue.addToast(Toast::Type::Error, "Unknown weapon type"); + } + } + + ImGui::EndGroup(); +} + +void SaveTool::drawWeaponEditor(Weapon& weapon) { + if(!_currentMass || _currentMass->state() != Mass::State::Valid || !current_weapon) { + return; + } ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Eye flare colour:"); + ImGui::Text("%s: %s", weapon_types.at(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(); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Equipped:"); + + if(weapon.type != "enuWeaponTypes::NewEnumerator5") { + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Damage type:"); + } + + if(weapon.type == "enuWeaponTypes::NewEnumerator0") { + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Dual-wield:"); + + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Custom effect mode:"); + + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Custom effect colour:"); + } + ImGui::EndGroup(); ImGui::SameLine(); ImGui::BeginGroup(); - if(eye_flare_edit) { - if(ImGui::ColorEdit3("##EyeFlarePicker", &eye_flare.x())) { - eye_flare_dirty = true; + ImGui::Checkbox("##EquippedCheckbox", &weapon.attached); + + if(weapon.type != "enuWeaponTypes::NewEnumerator5") { + if(weapon.type == "enuWeaponTypes::NewEnumerator0" && + ImGui::RadioButton("Physical##NoElement", weapon.damageType == "enuDamageProperty::NewEnumerator0")) + { + weapon.damageType = "enuDamageProperty::NewEnumerator0"; + } + else if((weapon.type == "enuWeaponTypes::NewEnumerator1" || weapon.type == "enuWeaponTypes::NewEnumerator3") && + ImGui::RadioButton("Piercing##NoElement", weapon.damageType == "enuDamageProperty::NewEnumerator1")) + { + weapon.damageType = "enuDamageProperty::NewEnumerator1"; + } + else if((weapon.type == "enuWeaponTypes::NewEnumerator2" || weapon.type == "enuWeaponTypes::NewEnumerator4") && + ImGui::RadioButton("Plasma##NoElement", weapon.damageType == "enuDamageProperty::NewEnumerator5")) + { + weapon.damageType = "enuDamageProperty::NewEnumerator5"; } ImGui::SameLine(); - drawHelpMarker("Right-click for more option, click the coloured square for the full picker.", 250.0f); - - if(!eye_flare_dirty) { - ImGui::BeginDisabled(); - ImGui::Button(ICON_FA_SAVE " Save"); - ImGui::SameLine(); - ImGui::Button(ICON_FA_UNDO " Reset"); - ImGui::EndDisabled(); + if(ImGui::RadioButton("Heat##Heat", weapon.damageType == "enuDamageProperty::NewEnumerator2")) { + weapon.damageType = "enuDamageProperty::NewEnumerator2"; } - else { - if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { - if(!_currentMass->setEyeFlareColour(eye_flare)) { - _queue.addToast(Toast::Type::Error, "Error writing the eye flare colour."); - } - eye_flare_dirty = false; - eye_flare_edit = false; - } - ImGui::SameLine(); - if(ImGui::Button(ICON_FA_UNDO " Reset")) { - eye_flare = _currentMass->eyeFlareColour(); - eye_flare_dirty = false; - } - } - ImGui::SameLine(); - - if(ImGui::Button(ICON_FA_TIMES " Cancel")) { - eye_flare = _currentMass->eyeFlareColour(); - eye_flare_dirty = false; - eye_flare_edit = false; + if(ImGui::RadioButton("Freeze##Freeze", weapon.damageType == "enuDamageProperty::NewEnumerator3")) { + weapon.damageType = "enuDamageProperty::NewEnumerator3"; + } + ImGui::SameLine(); + if(ImGui::RadioButton("Shock##Shock", weapon.damageType == "enuDamageProperty::NewEnumerator4")) { + weapon.damageType = "enuDamageProperty::NewEnumerator4"; } } - else { - ImGui::BeginDisabled(); - ImColor colour{_currentMass->eyeFlareColour()}; - ImGui::ColorEdit3("##EyeFlarePicker", &colour.Value.x); - ImGui::EndDisabled(); - if(ImGui::Button(ICON_FA_EDIT " Edit")) { - eye_flare = _currentMass->eyeFlareColour(); - eye_flare_edit = true; + if(weapon.type == "enuWeaponTypes::NewEnumerator0") { + ImGui::Checkbox("##DualWield", &weapon.dualWield); + + if(ImGui::RadioButton("Default##Default", weapon.effectColourMode == "enuWeaponEffectColorMode::NewEnumerator0")) { + weapon.effectColourMode = "enuWeaponEffectColorMode::NewEnumerator0"; + } + ImGui::SameLine(); + if(ImGui::RadioButton("Custom##Custom", weapon.effectColourMode == "enuWeaponEffectColorMode::NewEnumerator1")) { + weapon.effectColourMode = "enuWeaponEffectColorMode::NewEnumerator1"; + } + + bool custom_effect = (weapon.effectColourMode == "enuWeaponEffectColorMode::NewEnumerator1"); + 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")) { + static Int selected_part = 0; + + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Viewing/editing part:"); + for(Int i = 0; UnsignedLong(i) < weapon.parts.size(); i++) { + if(UnsignedLong(selected_part) >= weapon.parts.size()) { + selected_part = 0; + } + ImGui::SameLine(); + ImGui::RadioButton(std::to_string(i + 1).c_str(), &selected_part, i); + } + + auto& part = weapon.parts[selected_part]; + + 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++) { + ImGui::AlignTextToFramePadding(); + ImGui::Text("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"); + + static Int selected_decal = 0; + + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Showing/editing decal"); + for(UnsignedLong i = 0; i < part.demoDecals; i++) { + ImGui::SameLine(); + ImGui::RadioButton(std::to_string(i + 1).c_str(), &selected_decal, i); + } + + drawDecalEditor(part.decals[selected_decal]); + + ImGui::PopID(); + + if(_currentMass->demo() == false) { + ImGui::Separator(); + + ImGui::PushID("Accessory"); + + static Int selected_accessory = 0; + + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Showing/editing accessory"); + for(UnsignedLong i = 0; i < part.accessories.size(); i++) { + ImGui::SameLine(); + ImGui::RadioButton(std::string{char(65 + i)}.c_str(), &selected_accessory, i); + } + + drawAccessoryEditor(part.accessories[selected_accessory], weapon.customStyles); + + ImGui::PopID(); + } + } + + ImGui::EndChild(); + } +} + +void SaveTool::drawGlobalStyles() { + if(!_currentMass || _currentMass->state() != Mass::State::Valid) { + return; + } + + if(!ImGui::BeginChild("##GlobalStyles")) { + ImGui::EndChild(); + return; + } + + ImGui::TextWrapped("In-game values are multiplied by 100. For example, 0.500 here is equal to 50 in-game."); + + for(UnsignedInt i = 0; i < _currentMass->globalStyles().size(); i++) { + ImGui::PushID(i); + DCSResult result; + result = drawCustomStyle(_currentMass->globalStyles()[i]); + switch(result) { + case DCS_ResetStyle: + _currentMass->getGlobalStyles(); + break; + case DCS_Save: + _currentMass->writeGlobalStyle(i); + break; + default: + break; + } + ImGui::PopID(); + } + + ImGui::EndChild(); +} + +void SaveTool::drawTuning() { + if(!_currentMass || _currentMass->state() != Mass::State::Valid) { + return; + } + + if(!ImGui::BeginTable("##TuningTable", 3)) { + return; + } + + ImGui::TableSetupColumn("##EngineColumn"); + ImGui::TableSetupColumn("##OSColumn"); + ImGui::TableSetupColumn("##ArchitectureColumn"); + + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex(0); + + if(ImGui::BeginTable("##EngineTable", 1, ImGuiTableFlags_Borders)) { + ImGui::TableSetupColumn("##Engine"); + + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::TextUnformatted("Engine"); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("%i", _currentMass->engine()); + + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::TextUnformatted("Gears"); + + for(UnsignedInt i = 0; i < _currentMass->gears().size(); i++) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("%i", _currentMass->gears()[i]); + } + + ImGui::EndTable(); + } + + ImGui::TableSetColumnIndex(1); + + if(ImGui::BeginTable("##OSTable", 1, ImGuiTableFlags_Borders)) { + ImGui::TableSetupColumn("##OS"); + + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::TextUnformatted("OS"); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("%i", _currentMass->os()); + + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::TextUnformatted("Modules"); + + for(UnsignedInt i = 0; i < _currentMass->modules().size(); i++) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("%i", _currentMass->modules()[i]); + } + + ImGui::EndTable(); + } + + ImGui::TableSetColumnIndex(2); + + if(ImGui::BeginTable("##ArchTable", 1, ImGuiTableFlags_Borders)) { + ImGui::TableSetupColumn("##Arch"); + + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::TextUnformatted("Architecture"); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("%i", _currentMass->architecture()); + + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); + ImGui::TableNextColumn(); + ImGui::TextUnformatted("Techs"); + + for(UnsignedInt i = 0; i < _currentMass->techs().size(); i++) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Text("%i", _currentMass->techs()[i]); + } + + ImGui::EndTable(); + } + + ImGui::EndTable(); +} + +auto SaveTool::drawCustomStyle(CustomStyle& style) -> DCSResult { + if(!_currentMass || _currentMass->state() != Mass::State::Valid) { + return DCS_Fail; + } + + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {8.0f, 0.0f}); + + Containers::ScopeGuard guard{[]{ ImGui::PopStyleVar(); }}; + + DCSResult return_value = DCS_Fail; + + if(!ImGui::BeginChild("##CustomStyle", {0.0f, 244.0f}, true, ImGuiWindowFlags_MenuBar)) { + ImGui::EndChild(); + return DCS_Fail; + } + + if(ImGui::BeginMenuBar()) { + ImGui::TextUnformatted(style.name.c_str()); + + static Containers::StaticArray<33, char> name_buf{ValueInit}; + if(ImGui::SmallButton(ICON_FA_EDIT " Rename")) { + for(auto& c : name_buf) { + c = '\0'; + } + std::strncpy(name_buf.data(), style.name.c_str(), 32); + ImGui::OpenPopup("name_edit"); + } + if(drawRenamePopup(name_buf)) { + style.name = name_buf.data(); + } + + ImGui::EndMenuBar(); + } + + if(ImGui::BeginTable("##StyleTable", 2, ImGuiTableFlags_BordersInnerV)) { + ImGui::TableSetupColumn("##Colour", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("##Pattern", ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextRow(); + + ImGui::TableNextColumn(); + + ImGui::BeginGroup(); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Colour:"); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Metallic:"); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Gloss:"); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Glow:"); + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + ImGui::ColorEdit3("##Picker", &style.colour.r()); + ImGui::SameLine(); + drawHelpMarker("Right-click for more option, click the coloured square for the full picker."); + + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::SliderFloat("##SliderMetallic", &style.metallic, 0.0f, 1.0f); + + ImGui::SetNextItemWidth(-FLT_MIN); + ImGui::SliderFloat("##SliderGloss", &style.gloss,0.0f, 1.0f); + + ImGui::Checkbox("##Glow", &style.glow); + ImGui::EndGroup(); + + ImGui::TableNextColumn(); + + ImGui::BeginGroup(); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Pattern:"); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Opacity:"); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("X offset:"); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Y offset:"); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Rotation:"); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Scale:"); + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + ImGui::AlignTextToFramePadding(); + ImGui::Text("%i", style.patternId); + ImGui::PushItemWidth(-FLT_MIN); + ImGui::SliderFloat("##SliderOpacity", &style.opacity, 0.0f, 1.0f); + ImGui::SliderFloat("##SliderOffsetX", &style.offset.x(), 0.0f, 1.0f); + ImGui::SliderFloat("##SliderOffsetY", &style.offset.y(), 0.0f, 1.0f); + ImGui::SliderFloat("##SliderRotation", &style.rotation, 0.0f, 1.0f); + ImGui::SliderFloat("##SliderScale", &style.scale, 0.0f, 1.0f); + ImGui::PopItemWidth(); + ImGui::EndGroup(); + + ImGui::EndTable(); + } + + if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { + return_value = DCS_Save; + } + ImGui::SameLine(); + if(ImGui::Button(ICON_FA_UNDO " Reset")) { + return_value = DCS_ResetStyle; + } + + ImGui::EndChild(); + + return return_value; +} + +void SaveTool::drawDecalEditor(Decal& decal) { + ImGui::Text("ID: %i", decal.id); + + if(ImGui::BeginTable("##DecalTable", 2, ImGuiTableFlags_BordersInnerV)) { + ImGui::TableSetupColumn("##Normal", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("##Advanced", ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextRow(); + + ImGui::TableNextColumn(); + + ImGui::BeginGroup(); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Colour:"); + + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Offset:"); + + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Rotation:"); + + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Scale:"); + + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Flip X:"); + + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Surface wrap:"); + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + ImGui::ColorEdit3("##Picker", &decal.colour.r()); + ImGui::SameLine(); + drawHelpMarker("Right-click for more option, click the coloured square for the full picker."); + + ImGui::PushMultiItemsWidths(2, ImGui::CalcItemWidth()); + ImGui::SliderFloat("##OffsetX", &decal.offset.x(), 0.0f, 1.0f, "X: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderFloat("##OffsetY", &decal.offset.y(), 0.0f, 1.0f, "Y: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(); + drawHelpMarker("0.0 = -100 in-game\n1.0 = 100 in-game"); + + ImGui::SliderFloat("##Rotation", &decal.rotation, 0.0f, 1.0f); + ImGui::SameLine(); + drawHelpMarker("0.0 = 0 in-game\n1.0 = 360 in-game"); + + ImGui::SliderFloat("##Scale", &decal.scale, 0.0f, 1.0f); + ImGui::SameLine(); + drawHelpMarker("0.0 = 1 in-game\n1.0 = 100 in-game"); + + ImGui::Checkbox("##Flip", &decal.flip); + + ImGui::Checkbox("##Wrap", &decal.wrap); + ImGui::EndGroup(); + + + ImGui::TableNextColumn(); + + ImGui::TextColored(ImColor(255, 255, 0), ICON_FA_EXCLAMATION_TRIANGLE); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::TextUnformatted("Advanced settings. Touch these at your own risk."); + + ImGui::BeginGroup(); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Position:"); + + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("U axis:"); + + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("V axis:"); + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); + ImGui::DragFloat("##PosX", &decal.position.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::DragFloat("##PosY", &decal.position.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::DragFloat("##PosZ", &decal.position.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f"); + ImGui::PopItemWidth(); + + ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); + ImGui::DragFloat("##UX", &decal.uAxis.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::DragFloat("##UY", &decal.uAxis.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::DragFloat("##UZ", &decal.uAxis.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f"); + ImGui::PopItemWidth(); + + ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); + ImGui::DragFloat("##VX", &decal.vAxis.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::DragFloat("##VY", &decal.vAxis.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::DragFloat("##VZ", &decal.vAxis.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f"); + ImGui::PopItemWidth(); + ImGui::EndGroup(); + + ImGui::EndTable(); + } +} + +void SaveTool::drawAccessoryEditor(Accessory& accessory, Containers::ArrayView style_view) { + if(accessory.id < 1) { + ImGui::TextUnformatted("Accessory: "); + } + else if(accessories.find(accessory.id) != accessories.cend()) { + ImGui::Text("Accessory #%i - %s", accessory.id, accessories.at(accessory.id)); + } + else { + ImGui::Text("Accessory #%i", accessory.id); + drawTooltip("WARNING: accessory mapping is a WIP."); + } + +#ifdef SAVETOOL_DEBUG_BUILD + ImGui::SameLine(0.0f, ImGui::GetStyle().FramePadding.x * 5.0f); + ImGui::Text("Attach index: %i", accessory.attachIndex); +#endif + + ImGui::BeginGroup(); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Styles:"); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Base position:"); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Position offset:"); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Base rotation:"); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Rotation offset:"); + ImGui::AlignTextToFramePadding(); + ImGui::TextUnformatted("Scale:"); + ImGui::EndGroup(); + + ImGui::SameLine(); + + ImGui::BeginGroup(); + ImGui::PushMultiItemsWidths(2, ImGui::CalcItemWidth()); + if(ImGui::BeginCombo("##Style1", getStyleName(accessory.styles[0], style_view))) { + for(const auto& style : style_names) { + if(ImGui::Selectable(getStyleName(style.first, style_view), accessory.styles[0] == style.first)) { + accessory.styles[0] = style.first; + } + } + + ImGui::EndCombo(); + } + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + if(ImGui::BeginCombo("##Style2", getStyleName(accessory.styles[1], style_view))) { + for(const auto& style : style_names) { + if(ImGui::Selectable(getStyleName(style.first, style_view), accessory.styles[1] == style.first)) { + accessory.styles[1] = style.first; + } + } + + ImGui::EndCombo(); + } + ImGui::PopItemWidth(); + + ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); + ImGui::DragFloat("##PosX", &accessory.relativePosition.x(), 1.0f, -FLT_MAX, +FLT_MAX, "X: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::DragFloat("##PosY", &accessory.relativePosition.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Y: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::DragFloat("##PosZ", &accessory.relativePosition.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Z: %.3f"); + ImGui::PopItemWidth(); + + ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); + ImGui::SliderFloat("##PosOffsetX", &accessory.relativePositionOffset.x(), -500.0f, +500.0f, "X: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderFloat("##PosOffsetY", &accessory.relativePositionOffset.y(), -500.0f, +500.0f, "Y: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderFloat("##PosOffsetZ", &accessory.relativePositionOffset.z(), -500.0f, +500.0f, "Z: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(); + drawHelpMarker("+/-500.0 = +/-250 in-game"); + + ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); + ImGui::DragFloat("##RotX", &accessory.relativeRotation.x(), 1.0f, -FLT_MAX, +FLT_MAX, "Roll: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::DragFloat("##RotY", &accessory.relativeRotation.y(), 1.0f, -FLT_MAX, +FLT_MAX, "Yaw: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::DragFloat("##RotZ", &accessory.relativeRotation.z(), 1.0f, -FLT_MAX, +FLT_MAX, "Pitch: %.3f"); + ImGui::PopItemWidth(); + + ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); + ImGui::SliderFloat("##RotOffsetX", &accessory.relativeRotationOffset.x(), -180.0f, +180.0f, "Roll: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderFloat("##RotOffsetY", &accessory.relativeRotationOffset.y(), -180.0f, +180.0f, "Yaw: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderFloat("##RotOffsetZ", &accessory.relativeRotationOffset.z(), -180.0f, +180.0f, "Pitch: %.3f"); + ImGui::PopItemWidth(); + + ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth()); + ImGui::SliderFloat("##ScaleX", &accessory.localScale.x(), -3.0f, +3.0f, "X: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderFloat("##ScaleY", &accessory.localScale.y(), -3.0f, +3.0f, "Y: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::SliderFloat("##ScaleZ", &accessory.localScale.z(), -3.0f, +3.0f, "Z: %.3f"); + ImGui::PopItemWidth(); + ImGui::SameLine(); + drawHelpMarker("+/-3.0 = +/-150 in-game"); + ImGui::EndGroup(); +} + +auto SaveTool::getStyleName(Int id, Containers::ArrayView view) -> const char* { + if(id >= 0 && id <= 15) { + return view[id].name.c_str(); + } + else if(id >= 50 && id <= 65) { + return _currentMass->globalStyles()[id - 50].name.c_str(); + } + else { + return style_names.at(id); + } } -- 2.39.2 From 321e8feed0abb7708b9e9f04fd8be57fa83a77c6 Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 2 Dec 2021 19:52:26 +0100 Subject: [PATCH 081/128] SaveTool: change how versions are evaluated. This allows pre-releases and beta versions to be considered out of date once complete versions are released. --- src/SaveTool/SaveTool.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SaveTool/SaveTool.cpp b/src/SaveTool/SaveTool.cpp index 662d821..d9d6599 100644 --- a/src/SaveTool/SaveTool.cpp +++ b/src/SaveTool/SaveTool.cpp @@ -404,10 +404,7 @@ void SaveTool::updateCheckEvent(SDL_Event& event) { static const Version current_ver{SAVETOOL_VERSION}; Version latest_ver{response[0]["tag_name"]}; - if(latest_ver == current_ver) { - _queue.addToast(Toast::Type::Success, "The application is already up to date."); - } - else if(latest_ver > current_ver) { + if(latest_ver > current_ver || (latest_ver == current_ver && Utility::String::endsWith(SAVETOOL_VERSION, "-pre"))) { _queue.addToast(Toast::Type::Warning, "Your version is out of date.\nCheck the settings for more information.", std::chrono::milliseconds{5000}); _updateAvailable = true; @@ -415,6 +412,9 @@ void SaveTool::updateCheckEvent(SDL_Event& event) { _releaseLink = response[0]["html_url"]; _downloadLink = response[0]["assets"][0]["browser_download_url"]; } + else if(latest_ver == current_ver) { + _queue.addToast(Toast::Type::Success, "The application is already up to date."); + } else if(current_ver > latest_ver) { _queue.addToast(Toast::Type::Warning, "Your version is more recent than the latest one in the repo. How???"); } -- 2.39.2 From a05f3eeed09740222d66561be5d66da1c891f20e Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 2 Dec 2021 20:12:54 +0100 Subject: [PATCH 082/128] Prepare the pre-release. --- src/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a9b0e20..a479bae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,7 +18,7 @@ set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -set(SAVETOOL_PROJECT_VERSION 1.2.0) +set(SAVETOOL_PROJECT_VERSION 1.3.0-pre) find_package(Corrade REQUIRED Main Containers Utility Interconnect) find_package(Magnum REQUIRED GL Sdl2Application) @@ -144,7 +144,7 @@ if(CMAKE_BUILD_TYPE STREQUAL Debug) add_compile_definitions(SAVETOOL_DEBUG_BUILD) endif() add_compile_definitions(SAVETOOL_VERSION="${SAVETOOL_PROJECT_VERSION}" - SAVETOOL_CODENAME="Cute Quindolia" + SAVETOOL_CODENAME="Dickish Cyclops" SUPPORTED_GAME_VERSION="0.7.6") if(CMAKE_BUILD_TYPE STREQUAL Release) -- 2.39.2 From 065e63f27caa7ec1cb42815d0ee5e4bd25a8aeda Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 2 Dec 2021 20:37:47 +0100 Subject: [PATCH 083/128] SaveTool: skip prereleases in update check. --- src/SaveTool/SaveTool.cpp | 41 ++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/src/SaveTool/SaveTool.cpp b/src/SaveTool/SaveTool.cpp index d9d6599..e21a3b0 100644 --- a/src/SaveTool/SaveTool.cpp +++ b/src/SaveTool/SaveTool.cpp @@ -402,22 +402,32 @@ void SaveTool::updateCheckEvent(SDL_Event& event) { }; static const Version current_ver{SAVETOOL_VERSION}; - Version latest_ver{response[0]["tag_name"]}; - if(latest_ver > current_ver || (latest_ver == current_ver && Utility::String::endsWith(SAVETOOL_VERSION, "-pre"))) { - _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"]; - } - else if(latest_ver == current_ver) { - _queue.addToast(Toast::Type::Success, "The application is already up to date."); - } - else if(current_ver > latest_ver) { - _queue.addToast(Toast::Type::Warning, "Your version is more recent than the latest one in the repo. How???"); + for(auto& release : response) { + if(release["prerelease"] == true) { + continue; + } + + Version latest_ver{release["tag_name"]}; + + if(latest_ver > current_ver || (latest_ver == current_ver && Utility::String::endsWith(SAVETOOL_VERSION, "-pre"))) { + _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"]; + } + else if(latest_ver == current_ver) { + _queue.addToast(Toast::Type::Success, "The application is already up to date."); + } + else if(current_ver > latest_ver) { + _queue.addToast(Toast::Type::Warning, "Your version is more recent than the latest one in the repo. How???"); + } + + break; } + } void SaveTool::initialiseConfiguration() { @@ -762,8 +772,7 @@ void SaveTool::checkGameState() { } void SaveTool::checkForUpdates() { - cpr::Response r = cpr::Get(cpr::Url{"https://williamjcm.ovh/git/api/v1/repos/williamjcm/MassBuilderSaveTool/releases"}, - cpr::Parameters{{"limit", "1"}}, cpr::Timeout{10000}); + cpr::Response r = cpr::Get(cpr::Url{"https://williamjcm.ovh/git/api/v1/repos/williamjcm/MassBuilderSaveTool/releases"}, cpr::Timeout{10000}); SDL_Event event; SDL_zero(event); -- 2.39.2 From 7059295cb30d9a46178fb67af018a6a18c7a1841 Mon Sep 17 00:00:00 2001 From: William JCM Date: Fri, 3 Dec 2021 00:14:22 +0100 Subject: [PATCH 084/128] 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. --- src/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a479bae..a0087a4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -153,6 +153,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 -- 2.39.2 From bd05a98820cb81279bb4e315be0fbae21b71e56a Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 3 Jan 2022 11:56:34 +0100 Subject: [PATCH 085/128] SaveTool: make pre-releases considered up-to-date if they're more recent than the latest stable. --- src/SaveTool/SaveTool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SaveTool/SaveTool.cpp b/src/SaveTool/SaveTool.cpp index e21a3b0..88dee79 100644 --- a/src/SaveTool/SaveTool.cpp +++ b/src/SaveTool/SaveTool.cpp @@ -418,7 +418,7 @@ void SaveTool::updateCheckEvent(SDL_Event& event) { _releaseLink = response[0]["html_url"]; _downloadLink = response[0]["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) { -- 2.39.2 From 96768c1aab8a2cc439333ce7dbf9b87cb0666bae Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 3 Jan 2022 12:10:20 +0100 Subject: [PATCH 086/128] SaveTool: add a conversion operator. --- src/SaveTool/SaveTool.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/SaveTool/SaveTool.cpp b/src/SaveTool/SaveTool.cpp index 88dee79..c56e96d 100644 --- a/src/SaveTool/SaveTool.cpp +++ b/src/SaveTool/SaveTool.cpp @@ -399,6 +399,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,7 +417,7 @@ 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); + _latestVersion = latest_ver; _releaseLink = response[0]["html_url"]; _downloadLink = response[0]["assets"][0]["browser_download_url"]; } @@ -427,7 +430,6 @@ void SaveTool::updateCheckEvent(SDL_Event& event) { break; } - } void SaveTool::initialiseConfiguration() { -- 2.39.2 From 1378676bbcbb3128ee3ddcf75a301da0c4ca4d85 Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 3 Jan 2022 12:46:13 +0100 Subject: [PATCH 087/128] SaveTool: fix old code. --- src/SaveTool/SaveTool.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SaveTool/SaveTool.cpp b/src/SaveTool/SaveTool.cpp index c56e96d..c9276cc 100644 --- a/src/SaveTool/SaveTool.cpp +++ b/src/SaveTool/SaveTool.cpp @@ -418,8 +418,8 @@ void SaveTool::updateCheckEvent(SDL_Event& event) { std::chrono::milliseconds{5000}); _updateAvailable = true; _latestVersion = latest_ver; - _releaseLink = response[0]["html_url"]; - _downloadLink = response[0]["assets"][0]["browser_download_url"]; + _releaseLink = release["html_url"]; + _downloadLink = release["assets"][0]["browser_download_url"]; } else if(latest_ver == current_ver || (current_ver > latest_ver && Utility::String::endsWith(SAVETOOL_VERSION, "-pre"))) { _queue.addToast(Toast::Type::Success, "The application is already up to date."); -- 2.39.2 From 7fb269f86237d6cc25ed0ce716331055de158a57 Mon Sep 17 00:00:00 2001 From: William JCM Date: Fri, 7 Jan 2022 09:35:32 +0100 Subject: [PATCH 088/128] SaveTool: change viewer window ID. --- src/SaveTool/SaveTool_MassViewer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index 7d3ff4e..8b639ea 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -42,7 +42,7 @@ void SaveTool::drawMassViewer() { ImGui::SetNextWindowPos({0.0f, ImGui::GetItemRectSize().y}, ImGuiCond_Always); ImGui::SetNextWindowSize({Float(windowSize().x()), Float(windowSize().y()) - ImGui::GetItemRectSize().y}, ImGuiCond_Always); - if(!ImGui::Begin("##MainWindow", nullptr, + if(!ImGui::Begin("##MassViewer", nullptr, ImGuiWindowFlags_NoDecoration|ImGuiWindowFlags_NoMove| ImGuiWindowFlags_NoBackground|ImGuiWindowFlags_NoBringToFrontOnFocus)) { -- 2.39.2 From 1621a4dbd5b01e1aaefb8ce0a7fc3634d686c0bf Mon Sep 17 00:00:00 2001 From: William JCM Date: Fri, 14 Jan 2022 13:22:51 +0100 Subject: [PATCH 089/128] 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. --- src/SaveTool/SaveTool.h | 9 +++ src/SaveTool/SaveTool_MassViewer.cpp | 90 +++++++++++++--------------- 2 files changed, 52 insertions(+), 47 deletions(-) diff --git a/src/SaveTool/SaveTool.h b/src/SaveTool/SaveTool.h index d390be1..7338049 100644 --- a/src/SaveTool/SaveTool.h +++ b/src/SaveTool/SaveTool.h @@ -248,5 +248,14 @@ 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 _cheatMode{false}; }; diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index 8b639ea..fccd93d 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -74,6 +74,9 @@ void SaveTool::drawMassViewer() { if(ImGui::SmallButton(ICON_FA_SYNC_ALT " Refresh")) { _currentMass->refreshValues(); _currentMass->setDirty(false); + _jointsDirty = false; + _stylesDirty = false; + _eyeFlareDirty = false; } } @@ -82,7 +85,15 @@ void SaveTool::drawMassViewer() { current_weapon = nullptr; _currentMass = nullptr; _uiState = UiState::MainManager; - } + _jointsDirty = false; + _stylesDirty = false; + _eyeFlareDirty = false; + _selectedArmourDecals = Containers::StaticArray<38, Int>{ValueInit}; + _selectedArmourAccessories = Containers::StaticArray<38, Int>{ValueInit}; + _selectedWeaponPart = 0; + _selectedWeaponDecal = 0; + _selectedWeaponAccessory = 0; + }; ImGui::EndTable(); } @@ -201,8 +212,6 @@ void SaveTool::drawJointSliders() { return; } - static bool joints_dirty = false; - 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)) { @@ -216,7 +225,7 @@ void SaveTool::drawJointSliders() { ImGui::TableSetColumnIndex(1); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##NeckSlider", &_currentMass->jointSliders().neck, 0.0f, 1.0f)) { - joints_dirty = true; + _jointsDirty = true; } ImGui::TableNextRow(); @@ -226,7 +235,7 @@ void SaveTool::drawJointSliders() { ImGui::TableSetColumnIndex(1); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##BodySlider", &_currentMass->jointSliders().body, 0.0f, 1.0f)) { - joints_dirty = true; + _jointsDirty = true; } ImGui::TableNextRow(); @@ -236,7 +245,7 @@ void SaveTool::drawJointSliders() { ImGui::TableSetColumnIndex(1); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##ShouldersSlider", &_currentMass->jointSliders().shoulders, 0.0f, 1.0f)) { - joints_dirty = true; + _jointsDirty = true; } ImGui::TableNextRow(); @@ -246,7 +255,7 @@ void SaveTool::drawJointSliders() { ImGui::TableSetColumnIndex(1); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##HipsSlider", &_currentMass->jointSliders().hips, 0.0f, 1.0f)) { - joints_dirty = true; + _jointsDirty = true; } ImGui::TableNextRow(); @@ -262,12 +271,12 @@ void SaveTool::drawJointSliders() { ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##UpperArmsSlider", &_currentMass->jointSliders().upperArms, 0.0f, 1.0f, "Upper: %.3f")) { - joints_dirty = true; + _jointsDirty = true; } ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##LowerArmsSlider", &_currentMass->jointSliders().lowerArms, 0.0f, 1.0f, "Lower: %.3f")) { - joints_dirty = true; + _jointsDirty = true; } ImGui::EndTable(); @@ -287,12 +296,12 @@ void SaveTool::drawJointSliders() { ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##UpperLegsSlider", &_currentMass->jointSliders().upperLegs, 0.0f, 1.0f, "Upper: %.3f")) { - joints_dirty = true; + _jointsDirty = true; } ImGui::TableNextColumn(); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##LowerLegsSlider", &_currentMass->jointSliders().lowerLegs, 0.0f, 1.0f, "Lower: %.3f")) { - joints_dirty = true; + _jointsDirty = true; } ImGui::EndTable(); @@ -302,7 +311,7 @@ void SaveTool::drawJointSliders() { ImGui::EndTable(); } - if(!joints_dirty) { + if(!_jointsDirty) { ImGui::BeginDisabled(); ImGui::Button(ICON_FA_SAVE " Save"); ImGui::SameLine(); @@ -314,12 +323,12 @@ void SaveTool::drawJointSliders() { if(!_currentMass->writeJointSliders()) { _queue.addToast(Toast::Type::Error, "Error: " + Mass::lastError()); } - joints_dirty = false; + _jointsDirty = false; } ImGui::SameLine(); if(ImGui::Button(ICON_FA_UNDO " Reset")) { _currentMass->getJointSliders(); - joints_dirty = false; + _jointsDirty = false; } } } @@ -329,8 +338,6 @@ void SaveTool::drawFrameStyles() { return; } - static bool styles_dirty = false; - for(Int i = 0; i < 4; i++) { ImGui::AlignTextToFramePadding(); ImGui::Text("Slot %d:", i + 1); @@ -343,7 +350,7 @@ void SaveTool::drawFrameStyles() { for(const auto& style : style_names) { if(ImGui::Selectable(getStyleName(style.first, _currentMass->frameCustomStyles()), _currentMass->frameStyles()[i] == style.first)) { _currentMass->frameStyles()[i] = style.first; - styles_dirty = true; + _stylesDirty = true; } } @@ -353,7 +360,7 @@ void SaveTool::drawFrameStyles() { ImGui::PopID(); } - if(!styles_dirty) { + if(!_stylesDirty) { ImGui::BeginDisabled(); ImGui::Button(ICON_FA_SAVE " Save"); ImGui::SameLine(); @@ -365,12 +372,12 @@ void SaveTool::drawFrameStyles() { if(!_currentMass->writeFrameStyles()) { _queue.addToast(Toast::Type::Error, "Error: " + Mass::lastError()); } - styles_dirty = false; + _stylesDirty = false; } ImGui::SameLine(); if(ImGui::Button(ICON_FA_UNDO " Reset")) { _currentMass->getFrameStyles(); - styles_dirty = false; + _stylesDirty = false; } } } @@ -380,13 +387,11 @@ void SaveTool::drawEyeColourPicker() { return; } - static bool eye_flare_dirty = false; - if(ImGui::ColorPicker3("##EyeFlarePicker", &_currentMass->eyeFlareColour().x())) { - eye_flare_dirty = true; + _eyeFlareDirty = true; } - if(!eye_flare_dirty) { + if(!_eyeFlareDirty) { ImGui::BeginDisabled(); ImGui::Button(ICON_FA_SAVE " Save"); ImGui::SameLine(); @@ -398,12 +403,12 @@ void SaveTool::drawEyeColourPicker() { if(!_currentMass->writeEyeFlareColour()) { _queue.addToast(Toast::Type::Error, "Error writing the eye flare colour."); } - eye_flare_dirty = false; + _eyeFlareDirty = false; } ImGui::SameLine(); if(ImGui::Button(ICON_FA_UNDO " Reset")) { _currentMass->getEyeFlareColour(); - eye_flare_dirty = false; + _eyeFlareDirty = false; } } } @@ -454,9 +459,6 @@ void SaveTool::drawArmour() { return; } - static Containers::StaticArray<38, Int> selected_decals{ValueInit}; - static Containers::StaticArray<38, Int> selected_accessories{ValueInit}; - for(UnsignedInt i = 0; i < _currentMass->armourParts().size(); i++) { ImGui::PushID(i); @@ -543,10 +545,10 @@ void SaveTool::drawArmour() { ImGui::TextUnformatted("Showing/editing decal"); for(UnsignedLong j = 0; j < part.demoDecals; j++) { ImGui::SameLine(); - ImGui::RadioButton(std::to_string(j + 1).c_str(), &selected_decals[i], j); + ImGui::RadioButton(std::to_string(j + 1).c_str(), &_selectedArmourDecals[i], j); } - drawDecalEditor(part.decals[selected_decals[i]]); + drawDecalEditor(part.decals[_selectedArmourDecals[i]]); ImGui::PopID(); @@ -559,10 +561,10 @@ void SaveTool::drawArmour() { ImGui::TextUnformatted("Showing/editing accessory"); for(UnsignedInt j = 0; j < part.accessories.size(); j++) { ImGui::SameLine(); - ImGui::RadioButton(std::string{char(65 + j)}.c_str(), &selected_accessories[i], j); + ImGui::RadioButton(std::string{char(65 + j)}.c_str(), &_selectedArmourAccessories[i], j); } - drawAccessoryEditor(part.accessories[selected_accessories[i]], _currentMass->armourCustomStyles()); + drawAccessoryEditor(part.accessories[_selectedArmourAccessories[i]], _currentMass->armourCustomStyles()); ImGui::PopID(); } @@ -872,19 +874,17 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { ImGui::Separator(); if(ImGui::CollapsingHeader("Weapon parts")) { - static Int selected_part = 0; - ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Viewing/editing part:"); for(Int i = 0; UnsignedLong(i) < weapon.parts.size(); i++) { - if(UnsignedLong(selected_part) >= weapon.parts.size()) { - selected_part = 0; + if(UnsignedLong(_selectedWeaponPart) >= weapon.parts.size()) { + _selectedWeaponPart = 0; } ImGui::SameLine(); - ImGui::RadioButton(std::to_string(i + 1).c_str(), &selected_part, i); + ImGui::RadioButton(std::to_string(i + 1).c_str(), &_selectedWeaponPart, i); } - auto& part = weapon.parts[selected_part]; + auto& part = weapon.parts[_selectedWeaponPart]; ImGui::Text("ID: %i", part.id); @@ -917,16 +917,14 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { ImGui::PushID("Decal"); - static Int selected_decal = 0; - ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Showing/editing decal"); for(UnsignedLong i = 0; i < part.demoDecals; i++) { ImGui::SameLine(); - ImGui::RadioButton(std::to_string(i + 1).c_str(), &selected_decal, i); + ImGui::RadioButton(std::to_string(i + 1).c_str(), &_selectedWeaponDecal, i); } - drawDecalEditor(part.decals[selected_decal]); + drawDecalEditor(part.decals[_selectedWeaponDecal]); ImGui::PopID(); @@ -935,16 +933,14 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { ImGui::PushID("Accessory"); - static Int selected_accessory = 0; - ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Showing/editing accessory"); for(UnsignedLong i = 0; i < part.accessories.size(); i++) { ImGui::SameLine(); - ImGui::RadioButton(std::string{char(65 + i)}.c_str(), &selected_accessory, i); + ImGui::RadioButton(std::string{char(65 + i)}.c_str(), &_selectedWeaponAccessory, i); } - drawAccessoryEditor(part.accessories[selected_accessory], weapon.customStyles); + drawAccessoryEditor(part.accessories[_selectedWeaponAccessory], weapon.customStyles); ImGui::PopID(); } -- 2.39.2 From 51602c713a4e8adf6589350e846f3272fec689b9 Mon Sep 17 00:00:00 2001 From: William JCM Date: Fri, 14 Jan 2022 13:42:09 +0100 Subject: [PATCH 090/128] SaveTool: update layout. --- src/SaveTool/SaveTool_MainManager.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/SaveTool/SaveTool_MainManager.cpp b/src/SaveTool/SaveTool_MainManager.cpp index 96a470c..ce38c70 100644 --- a/src/SaveTool/SaveTool_MainManager.cpp +++ b/src/SaveTool/SaveTool_MainManager.cpp @@ -409,7 +409,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); @@ -504,6 +504,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); -- 2.39.2 From 9a9c08391ae1ba1422c977345c27828483c05190 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 15 Jan 2022 10:48:49 +0100 Subject: [PATCH 091/128] SaveTool: change how story progress is handled. --- src/Maps/StoryProgress.h | 2 +- src/SaveTool/SaveTool_MainManager.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Maps/StoryProgress.h b/src/Maps/StoryProgress.h index 4643b92..054fbbf 100644 --- a/src/Maps/StoryProgress.h +++ b/src/Maps/StoryProgress.h @@ -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 story_progress diff --git a/src/SaveTool/SaveTool_MainManager.cpp b/src/SaveTool/SaveTool_MainManager.cpp index ce38c70..7b20524 100644 --- a/src/SaveTool/SaveTool_MainManager.cpp +++ b/src/SaveTool/SaveTool_MainManager.cpp @@ -188,7 +188,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 { @@ -261,7 +261,7 @@ 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", -- 2.39.2 From 8102d1d83afb4a73f932de86dbf16ba80ffb575f Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 15 Jan 2022 11:30:12 +0100 Subject: [PATCH 092/128] SaveTool: improve the ShellExecuteW call. --- src/SaveTool/SaveTool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SaveTool/SaveTool.cpp b/src/SaveTool/SaveTool.cpp index c9276cc..beea4e0 100644 --- a/src/SaveTool/SaveTool.cpp +++ b/src/SaveTool/SaveTool.cpp @@ -748,7 +748,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() { -- 2.39.2 From ed0c4a73bbd3a9ac96c206dcc7a12e78dc8bf1eb Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 15 Jan 2022 11:31:06 +0100 Subject: [PATCH 093/128] SaveTool: mark profiles as "legacy" in the manager. I'll probably have to redesign that whole part of the UI anyway, so... --- src/SaveTool/SaveTool_ProfileManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SaveTool/SaveTool_ProfileManager.cpp b/src/SaveTool/SaveTool_ProfileManager.cpp index 9bdf20c..6421c7d 100644 --- a/src/SaveTool/SaveTool_ProfileManager.cpp +++ b/src/SaveTool/SaveTool_ProfileManager.cpp @@ -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)) { -- 2.39.2 From 13d09e4aa08b674d42f982481a22b887cc5097fe Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 15 Jan 2022 11:31:33 +0100 Subject: [PATCH 094/128] SaveTool: prepare for the better import/export system. --- src/SaveTool/SaveTool.cpp | 20 ++++++++++++++++++++ src/SaveTool/SaveTool.h | 4 ++++ 2 files changed, 24 insertions(+) diff --git a/src/SaveTool/SaveTool.cpp b/src/SaveTool/SaveTool.cpp index beea4e0..1864630 100644 --- a/src/SaveTool/SaveTool.cpp +++ b/src/SaveTool/SaveTool.cpp @@ -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); diff --git a/src/SaveTool/SaveTool.h b/src/SaveTool/SaveTool.h index 7338049..ed76717 100644 --- a/src/SaveTool/SaveTool.h +++ b/src/SaveTool/SaveTool.h @@ -212,6 +212,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 -- 2.39.2 From 1ec4522baffa0b0ca63b23ba57bd0de33df88c84 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 15 Jan 2022 13:26:12 +0100 Subject: [PATCH 095/128] SaveTool: make the clickthrough hint have priority. --- src/SaveTool/SaveTool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SaveTool/SaveTool.cpp b/src/SaveTool/SaveTool.cpp index 1864630..84cdc8f 100644 --- a/src/SaveTool/SaveTool.cpp +++ b/src/SaveTool/SaveTool.cpp @@ -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 { -- 2.39.2 From a4045e8e9b58650805ce454ab73fe483fc9d6f93 Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 20 Jan 2022 11:39:36 +0100 Subject: [PATCH 096/128] Add a few IDs to maps. --- src/Maps/LastMissionId.h | 1 + src/Maps/StoryProgress.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/Maps/LastMissionId.h b/src/Maps/LastMissionId.h index 34dbdc0..a6b6621 100644 --- a/src/Maps/LastMissionId.h +++ b/src/Maps/LastMissionId.h @@ -33,6 +33,7 @@ static const std::map 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"}, diff --git a/src/Maps/StoryProgress.h b/src/Maps/StoryProgress.h index 054fbbf..37101a0 100644 --- a/src/Maps/StoryProgress.h +++ b/src/Maps/StoryProgress.h @@ -88,5 +88,7 @@ static const Corrade::Containers::Array 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"}, } }; -- 2.39.2 From 0ac1e759ca341cc4787035a1709c27a5c9c861a5 Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 20 Jan 2022 19:42:27 +0100 Subject: [PATCH 097/128] Profile(Manager),SaveTool: prepare for legacy/normal distinction. --- src/Profile/Profile.cpp | 18 ++++++++++++++---- src/Profile/Profile.h | 13 ++++++++++--- src/ProfileManager/ProfileManager.cpp | 4 ++-- src/SaveTool/SaveTool.cpp | 10 +++++----- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/Profile/Profile.cpp b/src/Profile/Profile.cpp index d1998ae..0cc993e 100644 --- a/src/Profile/Profile.cpp +++ b/src/Profile/Profile.cpp @@ -36,7 +36,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,7 +45,14 @@ 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"}); + _account = Utility::String::ltrim(Utility::String::rtrim(_filename, ".sav"), (_type == ProfileType::Demo ? "Demo" : "") + std::string{"Profile"}); + + if(Utility::String::beginsWith(_account, "PMCSlot")) { + _version = ProfileVersion::Normal; + } + else { + _version = ProfileVersion::Legacy; + } refreshValues(); @@ -69,8 +75,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() { diff --git a/src/Profile/Profile.h b/src/Profile/Profile.h index 1759e36..9a4c900 100644 --- a/src/Profile/Profile.h +++ b/src/Profile/Profile.h @@ -29,6 +29,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 +46,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(); @@ -122,10 +129,10 @@ class Profile { auto getResource(const char* container, Int id) -> Int; auto setResource(const char* container, Int id, Int amount) -> bool; - std::string _profileDirectory; std::string _filename; ProfileType _type; + ProfileVersion _version; UESaveFile _profile; @@ -159,7 +166,7 @@ class Profile { Int _mineralExoskeletology = 0; Int _carbonisedSkin = 0; - std::string _steamId; + std::string _account; bool _valid = false; std::string _lastError; diff --git a/src/ProfileManager/ProfileManager.cpp b/src/ProfileManager/ProfileManager.cpp index 57da93a..e86cecb 100644 --- a/src/ProfileManager/ProfileManager.cpp +++ b/src/ProfileManager/ProfileManager.cpp @@ -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; diff --git a/src/SaveTool/SaveTool.cpp b/src/SaveTool/SaveTool.cpp index 84cdc8f..7689f34 100644 --- a/src/SaveTool/SaveTool.cpp +++ b/src/SaveTool/SaveTool.cpp @@ -227,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); @@ -241,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); @@ -255,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); @@ -273,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; @@ -582,7 +582,7 @@ auto SaveTool::findGameDataDirectory() -> bool { void SaveTool::initialiseMassManager() { _massManager.emplace(_saveDir, - _currentProfile->steamId(), + _currentProfile->account(), _currentProfile->type() == ProfileType::Demo, _stagingDir); -- 2.39.2 From 8fb837bfc0d77e78ad8c3ddcab4cb67071ad53ca Mon Sep 17 00:00:00 2001 From: William JCM Date: Sun, 30 Jan 2022 09:48:14 +0100 Subject: [PATCH 098/128] SaveTool: finish implementing weapon reordering. --- src/SaveTool/SaveTool.h | 6 ++ src/SaveTool/SaveTool_MassViewer.cpp | 93 +++++++++++++++++++++++++--- 2 files changed, 90 insertions(+), 9 deletions(-) diff --git a/src/SaveTool/SaveTool.h b/src/SaveTool/SaveTool.h index ed76717..00999c3 100644 --- a/src/SaveTool/SaveTool.h +++ b/src/SaveTool/SaveTool.h @@ -260,6 +260,12 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener 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}; }; diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index fccd93d..2b70a68 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -620,7 +620,14 @@ void SaveTool::drawWeapons() { return; } - if(!ImGui::BeginTable("##WeaponsList", 1, ImGuiTableFlags_ScrollY|ImGuiTableFlags_BordersOuter|ImGuiTableFlags_BordersInnerH, {ImGui::GetContentRegionAvailWidth() * 0.2f, 0.0f})) { + 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})) + { return; } @@ -628,7 +635,7 @@ void SaveTool::drawWeapons() { int id = 0; -#define weapcat(header, array, payload_type, payload_tooltip) \ +#define weapcat(header, array, dirty, payload_type, payload_tooltip) \ ImGui::TableNextRow(ImGuiTableRowFlags_Headers); \ ImGui::TableNextColumn(); \ ImGui::TextUnformatted(header); \ @@ -659,6 +666,7 @@ void SaveTool::drawWeapons() { } \ \ std::swap(_currentMass->array()[index], _currentMass->array()[i]); \ + (dirty) = true; \ } \ \ ImGui::EndDragDropTarget(); \ @@ -672,17 +680,85 @@ void SaveTool::drawWeapons() { } \ } - weapcat("Melee weapons", meleeWeapons, "MeleeWeapon", "Melee weapon") - weapcat("Shield", shields, "Shield", "Shield") - weapcat("Bullet shooters", bulletShooters, "BShooter", "Bullet shooter") - weapcat("Energy shooters", energyShooters, "EShooter", "Energy shooter") - weapcat("Bullet launchers", bulletLaunchers, "BLauncher", "Bullet launcher") - weapcat("Energy launchers", energyLaunchers, "ELauncher", "Energy launcher") + weapcat("Melee weapons", meleeWeapons, _meleeDirty, "MeleeWeapon", "Melee weapon") + weapcat("Shield", shields, _shieldsDirty, "Shield", "Shield") + weapcat("Bullet shooters", bulletShooters, _bShootersDirty, "BShooter", "Bullet shooter") + weapcat("Energy shooters", energyShooters, _eShootersDirty, "EShooter", "Energy shooter") + weapcat("Bullet launchers", bulletLaunchers, _bLaunchersDirty, "BLauncher", "Bullet launcher") + weapcat("Energy launchers", energyLaunchers, _eLaunchersDirty, "ELauncher", "Energy launcher") #undef weapcat 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) { + _currentMass->writeMeleeWeapons(); + _meleeDirty = false; + } + if(_shieldsDirty) { + _currentMass->writeShields(); + _shieldsDirty = false; + } + if(_bShootersDirty) { + _currentMass->writeBulletShooters(); + _bShootersDirty = false; + } + if(_eShootersDirty) { + _currentMass->writeEnergyShooters(); + _eShootersDirty = false; + } + if(_bLaunchersDirty) { + _currentMass->writeBulletLaunchers(); + _bLaunchersDirty = false; + } + if(_eLaunchersDirty) { + _currentMass->writeEnergyLaunchers(); + _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(!current_weapon) { @@ -692,7 +768,6 @@ void SaveTool::drawWeapons() { ImGui::BeginGroup(); - const Float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); if(!ImGui::BeginChild("##WeaponChild", {0.0f, -footer_height_to_reserve})) { ImGui::EndChild(); return; -- 2.39.2 From 51faed72105c2dafdab70dc932696c1319c17641 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sun, 30 Jan 2022 11:36:56 +0100 Subject: [PATCH 099/128] Update copyright years. I should have done that earlier... and of course I forgot a few files... --- CMakeLists.txt | 2 +- src/CMakeLists.txt | 2 +- src/Maps/Accessories.h | 2 +- src/Maps/ArmourSets.h | 2 +- src/Maps/ArmourSlots.h | 2 +- src/Maps/LastMissionId.h | 2 +- src/Maps/StoryProgress.h | 2 +- src/Maps/StyleNames.h | 2 +- src/Maps/WeaponTypes.h | 2 +- src/Mass/Mass.cpp | 2 +- src/Mass/Mass.h | 2 +- src/MassManager/MassManager.cpp | 2 +- src/MassManager/MassManager.h | 2 +- src/Profile/Profile.cpp | 2 +- src/Profile/Profile.h | 2 +- src/Profile/ResourceIDs.h | 2 +- src/ProfileManager/ProfileManager.cpp | 2 +- src/ProfileManager/ProfileManager.h | 2 +- src/SaveTool/SaveTool.cpp | 2 +- src/SaveTool/SaveTool.h | 2 +- src/SaveTool/SaveTool_MainManager.cpp | 2 +- src/SaveTool/SaveTool_MassViewer.cpp | 2 +- src/SaveTool/SaveTool_ProfileManager.cpp | 2 +- src/SaveTool/SaveTool_drawAbout.cpp | 2 +- src/SaveTool/SaveTool_drawMainMenu.cpp | 2 +- src/ToastQueue/ToastQueue.cpp | 2 +- src/ToastQueue/ToastQueue.h | 2 +- src/UESaveFile/BinaryReader.cpp | 2 +- src/UESaveFile/BinaryReader.h | 2 +- src/UESaveFile/BinaryWriter.cpp | 2 +- src/UESaveFile/BinaryWriter.h | 2 +- src/UESaveFile/Debug.cpp | 2 +- src/UESaveFile/Debug.h | 2 +- src/UESaveFile/PropertySerialiser.cpp | 2 +- src/UESaveFile/PropertySerialiser.h | 2 +- .../Serialisers/AbstractUnrealCollectionPropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/AbstractUnrealPropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/AbstractUnrealStructSerialiser.h | 2 +- src/UESaveFile/Serialisers/ArrayPropertySerialiser.cpp | 2 +- src/UESaveFile/Serialisers/ArrayPropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/BoolPropertySerialiser.cpp | 2 +- src/UESaveFile/Serialisers/BoolPropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/BytePropertySerialiser.cpp | 2 +- src/UESaveFile/Serialisers/BytePropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/ColourPropertySerialiser.cpp | 2 +- src/UESaveFile/Serialisers/ColourPropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/DateTimePropertySerialiser.cpp | 2 +- src/UESaveFile/Serialisers/DateTimePropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/EnumPropertySerialiser.cpp | 2 +- src/UESaveFile/Serialisers/EnumPropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/FloatPropertySerialiser.cpp | 2 +- src/UESaveFile/Serialisers/FloatPropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/GuidPropertySerialiser.cpp | 2 +- src/UESaveFile/Serialisers/GuidPropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/IntPropertySerialiser.cpp | 2 +- src/UESaveFile/Serialisers/IntPropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/MapPropertySerialiser.cpp | 2 +- src/UESaveFile/Serialisers/MapPropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/ResourcePropertySerialiser.cpp | 2 +- src/UESaveFile/Serialisers/ResourcePropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/RotatorPropertySerialiser.cpp | 2 +- src/UESaveFile/Serialisers/RotatorPropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/SetPropertySerialiser.cpp | 2 +- src/UESaveFile/Serialisers/SetPropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/StringPropertySerialiser.cpp | 2 +- src/UESaveFile/Serialisers/StringPropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/StructSerialiser.cpp | 2 +- src/UESaveFile/Serialisers/StructSerialiser.h | 2 +- src/UESaveFile/Serialisers/TextPropertySerialiser.cpp | 2 +- src/UESaveFile/Serialisers/TextPropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/UnrealPropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/Vector2DPropertySerialiser.cpp | 2 +- src/UESaveFile/Serialisers/Vector2DPropertySerialiser.h | 2 +- src/UESaveFile/Serialisers/VectorPropertySerialiser.cpp | 2 +- src/UESaveFile/Serialisers/VectorPropertySerialiser.h | 2 +- src/UESaveFile/Types/ArrayProperty.h | 2 +- src/UESaveFile/Types/BoolProperty.h | 2 +- src/UESaveFile/Types/ByteProperty.h | 2 +- src/UESaveFile/Types/ColourStructProperty.h | 2 +- src/UESaveFile/Types/DateTimeStructProperty.h | 2 +- src/UESaveFile/Types/EnumProperty.h | 2 +- src/UESaveFile/Types/FloatProperty.h | 2 +- src/UESaveFile/Types/GenericStructProperty.h | 2 +- src/UESaveFile/Types/GuidStructProperty.h | 2 +- src/UESaveFile/Types/IntProperty.h | 2 +- src/UESaveFile/Types/MapProperty.h | 2 +- src/UESaveFile/Types/NoneProperty.h | 2 +- src/UESaveFile/Types/ResourceItemValue.h | 2 +- src/UESaveFile/Types/RotatorStructProperty.h | 2 +- src/UESaveFile/Types/SetProperty.h | 2 +- src/UESaveFile/Types/StringProperty.h | 2 +- src/UESaveFile/Types/StructProperty.h | 2 +- src/UESaveFile/Types/TextProperty.h | 2 +- src/UESaveFile/Types/UnrealProperty.h | 2 +- src/UESaveFile/Types/UnrealPropertyBase.h | 2 +- src/UESaveFile/Types/Vector2DStructProperty.h | 2 +- src/UESaveFile/Types/VectorStructProperty.h | 2 +- src/UESaveFile/UESaveFile.cpp | 2 +- src/UESaveFile/UESaveFile.h | 2 +- src/main.cpp | 2 +- src/resource.rc | 2 +- 101 files changed, 101 insertions(+), 101 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5281f8d..e54cc44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a0087a4..06b6d29 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/Maps/Accessories.h b/src/Maps/Accessories.h index dc50ded..3f41ccc 100644 --- a/src/Maps/Accessories.h +++ b/src/Maps/Accessories.h @@ -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 diff --git a/src/Maps/ArmourSets.h b/src/Maps/ArmourSets.h index e1fbda0..24d46bd 100644 --- a/src/Maps/ArmourSets.h +++ b/src/Maps/ArmourSets.h @@ -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 diff --git a/src/Maps/ArmourSlots.h b/src/Maps/ArmourSlots.h index cfee3a7..629959a 100644 --- a/src/Maps/ArmourSlots.h +++ b/src/Maps/ArmourSlots.h @@ -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 diff --git a/src/Maps/LastMissionId.h b/src/Maps/LastMissionId.h index a6b6621..165fbb1 100644 --- a/src/Maps/LastMissionId.h +++ b/src/Maps/LastMissionId.h @@ -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 diff --git a/src/Maps/StoryProgress.h b/src/Maps/StoryProgress.h index 37101a0..6bb1742 100644 --- a/src/Maps/StoryProgress.h +++ b/src/Maps/StoryProgress.h @@ -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 diff --git a/src/Maps/StyleNames.h b/src/Maps/StyleNames.h index 16c3988..960ecb1 100644 --- a/src/Maps/StyleNames.h +++ b/src/Maps/StyleNames.h @@ -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 diff --git a/src/Maps/WeaponTypes.h b/src/Maps/WeaponTypes.h index 2d0a0b1..170aecc 100644 --- a/src/Maps/WeaponTypes.h +++ b/src/Maps/WeaponTypes.h @@ -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 diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 41464e3..93c3c64 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -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 diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 8ff8989..5caa2ec 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -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 diff --git a/src/MassManager/MassManager.cpp b/src/MassManager/MassManager.cpp index 74a6148..a385df9 100644 --- a/src/MassManager/MassManager.cpp +++ b/src/MassManager/MassManager.cpp @@ -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 diff --git a/src/MassManager/MassManager.h b/src/MassManager/MassManager.h index a30c7e8..fbfee37 100644 --- a/src/MassManager/MassManager.h +++ b/src/MassManager/MassManager.h @@ -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 diff --git a/src/Profile/Profile.cpp b/src/Profile/Profile.cpp index 0cc993e..16a655b 100644 --- a/src/Profile/Profile.cpp +++ b/src/Profile/Profile.cpp @@ -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 diff --git a/src/Profile/Profile.h b/src/Profile/Profile.h index 9a4c900..af361e3 100644 --- a/src/Profile/Profile.h +++ b/src/Profile/Profile.h @@ -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 diff --git a/src/Profile/ResourceIDs.h b/src/Profile/ResourceIDs.h index 085bf40..d122e23 100644 --- a/src/Profile/ResourceIDs.h +++ b/src/Profile/ResourceIDs.h @@ -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 diff --git a/src/ProfileManager/ProfileManager.cpp b/src/ProfileManager/ProfileManager.cpp index e86cecb..db1f289 100644 --- a/src/ProfileManager/ProfileManager.cpp +++ b/src/ProfileManager/ProfileManager.cpp @@ -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 diff --git a/src/ProfileManager/ProfileManager.h b/src/ProfileManager/ProfileManager.h index aeb91ae..db9887a 100644 --- a/src/ProfileManager/ProfileManager.h +++ b/src/ProfileManager/ProfileManager.h @@ -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 diff --git a/src/SaveTool/SaveTool.cpp b/src/SaveTool/SaveTool.cpp index 7689f34..3b2a23f 100644 --- a/src/SaveTool/SaveTool.cpp +++ b/src/SaveTool/SaveTool.cpp @@ -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 diff --git a/src/SaveTool/SaveTool.h b/src/SaveTool/SaveTool.h index 00999c3..c8e0f83 100644 --- a/src/SaveTool/SaveTool.h +++ b/src/SaveTool/SaveTool.h @@ -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 diff --git a/src/SaveTool/SaveTool_MainManager.cpp b/src/SaveTool/SaveTool_MainManager.cpp index 7b20524..4534c32 100644 --- a/src/SaveTool/SaveTool_MainManager.cpp +++ b/src/SaveTool/SaveTool_MainManager.cpp @@ -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 diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index 2b70a68..7992f43 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -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 diff --git a/src/SaveTool/SaveTool_ProfileManager.cpp b/src/SaveTool/SaveTool_ProfileManager.cpp index 6421c7d..07656c7 100644 --- a/src/SaveTool/SaveTool_ProfileManager.cpp +++ b/src/SaveTool/SaveTool_ProfileManager.cpp @@ -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 diff --git a/src/SaveTool/SaveTool_drawAbout.cpp b/src/SaveTool/SaveTool_drawAbout.cpp index e84b44d..0e1e92a 100644 --- a/src/SaveTool/SaveTool_drawAbout.cpp +++ b/src/SaveTool/SaveTool_drawAbout.cpp @@ -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 diff --git a/src/SaveTool/SaveTool_drawMainMenu.cpp b/src/SaveTool/SaveTool_drawMainMenu.cpp index d51074e..28bda90 100644 --- a/src/SaveTool/SaveTool_drawMainMenu.cpp +++ b/src/SaveTool/SaveTool_drawMainMenu.cpp @@ -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 diff --git a/src/ToastQueue/ToastQueue.cpp b/src/ToastQueue/ToastQueue.cpp index 1f367c6..ec2113d 100644 --- a/src/ToastQueue/ToastQueue.cpp +++ b/src/ToastQueue/ToastQueue.cpp @@ -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 diff --git a/src/ToastQueue/ToastQueue.h b/src/ToastQueue/ToastQueue.h index 3c2d4ce..d309833 100644 --- a/src/ToastQueue/ToastQueue.h +++ b/src/ToastQueue/ToastQueue.h @@ -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 diff --git a/src/UESaveFile/BinaryReader.cpp b/src/UESaveFile/BinaryReader.cpp index 6a9b942..2a4eeac 100644 --- a/src/UESaveFile/BinaryReader.cpp +++ b/src/UESaveFile/BinaryReader.cpp @@ -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 diff --git a/src/UESaveFile/BinaryReader.h b/src/UESaveFile/BinaryReader.h index c2a52b5..881d1fd 100644 --- a/src/UESaveFile/BinaryReader.h +++ b/src/UESaveFile/BinaryReader.h @@ -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 diff --git a/src/UESaveFile/BinaryWriter.cpp b/src/UESaveFile/BinaryWriter.cpp index 76c2b03..0522eb8 100644 --- a/src/UESaveFile/BinaryWriter.cpp +++ b/src/UESaveFile/BinaryWriter.cpp @@ -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 diff --git a/src/UESaveFile/BinaryWriter.h b/src/UESaveFile/BinaryWriter.h index 50ea2b7..299f40c 100644 --- a/src/UESaveFile/BinaryWriter.h +++ b/src/UESaveFile/BinaryWriter.h @@ -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 diff --git a/src/UESaveFile/Debug.cpp b/src/UESaveFile/Debug.cpp index 6d60d35..c11568e 100644 --- a/src/UESaveFile/Debug.cpp +++ b/src/UESaveFile/Debug.cpp @@ -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 diff --git a/src/UESaveFile/Debug.h b/src/UESaveFile/Debug.h index 016d7e0..4a85ca1 100644 --- a/src/UESaveFile/Debug.h +++ b/src/UESaveFile/Debug.h @@ -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 diff --git a/src/UESaveFile/PropertySerialiser.cpp b/src/UESaveFile/PropertySerialiser.cpp index cb65cea..830e78a 100644 --- a/src/UESaveFile/PropertySerialiser.cpp +++ b/src/UESaveFile/PropertySerialiser.cpp @@ -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 diff --git a/src/UESaveFile/PropertySerialiser.h b/src/UESaveFile/PropertySerialiser.h index 8757aea..546cd72 100644 --- a/src/UESaveFile/PropertySerialiser.h +++ b/src/UESaveFile/PropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/AbstractUnrealCollectionPropertySerialiser.h b/src/UESaveFile/Serialisers/AbstractUnrealCollectionPropertySerialiser.h index df004d4..c672495 100644 --- a/src/UESaveFile/Serialisers/AbstractUnrealCollectionPropertySerialiser.h +++ b/src/UESaveFile/Serialisers/AbstractUnrealCollectionPropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/AbstractUnrealPropertySerialiser.h b/src/UESaveFile/Serialisers/AbstractUnrealPropertySerialiser.h index 6605550..21c42eb 100644 --- a/src/UESaveFile/Serialisers/AbstractUnrealPropertySerialiser.h +++ b/src/UESaveFile/Serialisers/AbstractUnrealPropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/AbstractUnrealStructSerialiser.h b/src/UESaveFile/Serialisers/AbstractUnrealStructSerialiser.h index e52175b..0d8d003 100644 --- a/src/UESaveFile/Serialisers/AbstractUnrealStructSerialiser.h +++ b/src/UESaveFile/Serialisers/AbstractUnrealStructSerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/ArrayPropertySerialiser.cpp b/src/UESaveFile/Serialisers/ArrayPropertySerialiser.cpp index 70cff56..7bb6196 100644 --- a/src/UESaveFile/Serialisers/ArrayPropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/ArrayPropertySerialiser.cpp @@ -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 diff --git a/src/UESaveFile/Serialisers/ArrayPropertySerialiser.h b/src/UESaveFile/Serialisers/ArrayPropertySerialiser.h index f05db71..b5c2530 100644 --- a/src/UESaveFile/Serialisers/ArrayPropertySerialiser.h +++ b/src/UESaveFile/Serialisers/ArrayPropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/BoolPropertySerialiser.cpp b/src/UESaveFile/Serialisers/BoolPropertySerialiser.cpp index 10e761f..ee1c4fa 100644 --- a/src/UESaveFile/Serialisers/BoolPropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/BoolPropertySerialiser.cpp @@ -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 diff --git a/src/UESaveFile/Serialisers/BoolPropertySerialiser.h b/src/UESaveFile/Serialisers/BoolPropertySerialiser.h index 1b0bb3d..373fca0 100644 --- a/src/UESaveFile/Serialisers/BoolPropertySerialiser.h +++ b/src/UESaveFile/Serialisers/BoolPropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/BytePropertySerialiser.cpp b/src/UESaveFile/Serialisers/BytePropertySerialiser.cpp index 7bfec24..96ad39f 100644 --- a/src/UESaveFile/Serialisers/BytePropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/BytePropertySerialiser.cpp @@ -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 diff --git a/src/UESaveFile/Serialisers/BytePropertySerialiser.h b/src/UESaveFile/Serialisers/BytePropertySerialiser.h index 3fd882d..cdf5fbe 100644 --- a/src/UESaveFile/Serialisers/BytePropertySerialiser.h +++ b/src/UESaveFile/Serialisers/BytePropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/ColourPropertySerialiser.cpp b/src/UESaveFile/Serialisers/ColourPropertySerialiser.cpp index befd069..a263311 100644 --- a/src/UESaveFile/Serialisers/ColourPropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/ColourPropertySerialiser.cpp @@ -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 diff --git a/src/UESaveFile/Serialisers/ColourPropertySerialiser.h b/src/UESaveFile/Serialisers/ColourPropertySerialiser.h index f858739..5a6cbab 100644 --- a/src/UESaveFile/Serialisers/ColourPropertySerialiser.h +++ b/src/UESaveFile/Serialisers/ColourPropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/DateTimePropertySerialiser.cpp b/src/UESaveFile/Serialisers/DateTimePropertySerialiser.cpp index 615449e..b06db6e 100644 --- a/src/UESaveFile/Serialisers/DateTimePropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/DateTimePropertySerialiser.cpp @@ -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 diff --git a/src/UESaveFile/Serialisers/DateTimePropertySerialiser.h b/src/UESaveFile/Serialisers/DateTimePropertySerialiser.h index b0e43ac..abaf5e2 100644 --- a/src/UESaveFile/Serialisers/DateTimePropertySerialiser.h +++ b/src/UESaveFile/Serialisers/DateTimePropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/EnumPropertySerialiser.cpp b/src/UESaveFile/Serialisers/EnumPropertySerialiser.cpp index cd7364c..7ee43d8 100644 --- a/src/UESaveFile/Serialisers/EnumPropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/EnumPropertySerialiser.cpp @@ -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 diff --git a/src/UESaveFile/Serialisers/EnumPropertySerialiser.h b/src/UESaveFile/Serialisers/EnumPropertySerialiser.h index ab4335f..d41c628 100644 --- a/src/UESaveFile/Serialisers/EnumPropertySerialiser.h +++ b/src/UESaveFile/Serialisers/EnumPropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/FloatPropertySerialiser.cpp b/src/UESaveFile/Serialisers/FloatPropertySerialiser.cpp index c13a72e..06ce073 100644 --- a/src/UESaveFile/Serialisers/FloatPropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/FloatPropertySerialiser.cpp @@ -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 diff --git a/src/UESaveFile/Serialisers/FloatPropertySerialiser.h b/src/UESaveFile/Serialisers/FloatPropertySerialiser.h index c4e45ab..34a96b0 100644 --- a/src/UESaveFile/Serialisers/FloatPropertySerialiser.h +++ b/src/UESaveFile/Serialisers/FloatPropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/GuidPropertySerialiser.cpp b/src/UESaveFile/Serialisers/GuidPropertySerialiser.cpp index 9fe235b..8576c34 100644 --- a/src/UESaveFile/Serialisers/GuidPropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/GuidPropertySerialiser.cpp @@ -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 diff --git a/src/UESaveFile/Serialisers/GuidPropertySerialiser.h b/src/UESaveFile/Serialisers/GuidPropertySerialiser.h index 7dd1d67..c8ade5e 100644 --- a/src/UESaveFile/Serialisers/GuidPropertySerialiser.h +++ b/src/UESaveFile/Serialisers/GuidPropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/IntPropertySerialiser.cpp b/src/UESaveFile/Serialisers/IntPropertySerialiser.cpp index 5c70d45..c5b3e4e 100644 --- a/src/UESaveFile/Serialisers/IntPropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/IntPropertySerialiser.cpp @@ -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 diff --git a/src/UESaveFile/Serialisers/IntPropertySerialiser.h b/src/UESaveFile/Serialisers/IntPropertySerialiser.h index 8274299..1301f05 100644 --- a/src/UESaveFile/Serialisers/IntPropertySerialiser.h +++ b/src/UESaveFile/Serialisers/IntPropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/MapPropertySerialiser.cpp b/src/UESaveFile/Serialisers/MapPropertySerialiser.cpp index daa6ce9..b1a6046 100644 --- a/src/UESaveFile/Serialisers/MapPropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/MapPropertySerialiser.cpp @@ -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 diff --git a/src/UESaveFile/Serialisers/MapPropertySerialiser.h b/src/UESaveFile/Serialisers/MapPropertySerialiser.h index aba537b..cbe1402 100644 --- a/src/UESaveFile/Serialisers/MapPropertySerialiser.h +++ b/src/UESaveFile/Serialisers/MapPropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/ResourcePropertySerialiser.cpp b/src/UESaveFile/Serialisers/ResourcePropertySerialiser.cpp index a92406f..44a83d0 100644 --- a/src/UESaveFile/Serialisers/ResourcePropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/ResourcePropertySerialiser.cpp @@ -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 diff --git a/src/UESaveFile/Serialisers/ResourcePropertySerialiser.h b/src/UESaveFile/Serialisers/ResourcePropertySerialiser.h index b085667..e32439b 100644 --- a/src/UESaveFile/Serialisers/ResourcePropertySerialiser.h +++ b/src/UESaveFile/Serialisers/ResourcePropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/RotatorPropertySerialiser.cpp b/src/UESaveFile/Serialisers/RotatorPropertySerialiser.cpp index 4be77bf..b69633e 100644 --- a/src/UESaveFile/Serialisers/RotatorPropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/RotatorPropertySerialiser.cpp @@ -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 diff --git a/src/UESaveFile/Serialisers/RotatorPropertySerialiser.h b/src/UESaveFile/Serialisers/RotatorPropertySerialiser.h index 9a7192e..1f05573 100644 --- a/src/UESaveFile/Serialisers/RotatorPropertySerialiser.h +++ b/src/UESaveFile/Serialisers/RotatorPropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/SetPropertySerialiser.cpp b/src/UESaveFile/Serialisers/SetPropertySerialiser.cpp index 7b81683..69a20af 100644 --- a/src/UESaveFile/Serialisers/SetPropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/SetPropertySerialiser.cpp @@ -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 diff --git a/src/UESaveFile/Serialisers/SetPropertySerialiser.h b/src/UESaveFile/Serialisers/SetPropertySerialiser.h index a19f560..44f1a6a 100644 --- a/src/UESaveFile/Serialisers/SetPropertySerialiser.h +++ b/src/UESaveFile/Serialisers/SetPropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/StringPropertySerialiser.cpp b/src/UESaveFile/Serialisers/StringPropertySerialiser.cpp index c455fd5..047b25e 100644 --- a/src/UESaveFile/Serialisers/StringPropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/StringPropertySerialiser.cpp @@ -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 diff --git a/src/UESaveFile/Serialisers/StringPropertySerialiser.h b/src/UESaveFile/Serialisers/StringPropertySerialiser.h index 0c9e69b..a4a1924 100644 --- a/src/UESaveFile/Serialisers/StringPropertySerialiser.h +++ b/src/UESaveFile/Serialisers/StringPropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/StructSerialiser.cpp b/src/UESaveFile/Serialisers/StructSerialiser.cpp index 8ff7d06..cecfb7a 100644 --- a/src/UESaveFile/Serialisers/StructSerialiser.cpp +++ b/src/UESaveFile/Serialisers/StructSerialiser.cpp @@ -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 diff --git a/src/UESaveFile/Serialisers/StructSerialiser.h b/src/UESaveFile/Serialisers/StructSerialiser.h index e219360..671290e 100644 --- a/src/UESaveFile/Serialisers/StructSerialiser.h +++ b/src/UESaveFile/Serialisers/StructSerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/TextPropertySerialiser.cpp b/src/UESaveFile/Serialisers/TextPropertySerialiser.cpp index 72a0f54..8e379f4 100644 --- a/src/UESaveFile/Serialisers/TextPropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/TextPropertySerialiser.cpp @@ -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 diff --git a/src/UESaveFile/Serialisers/TextPropertySerialiser.h b/src/UESaveFile/Serialisers/TextPropertySerialiser.h index 3b99676..b408e83 100644 --- a/src/UESaveFile/Serialisers/TextPropertySerialiser.h +++ b/src/UESaveFile/Serialisers/TextPropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/UnrealPropertySerialiser.h b/src/UESaveFile/Serialisers/UnrealPropertySerialiser.h index 0364908..1c963c9 100644 --- a/src/UESaveFile/Serialisers/UnrealPropertySerialiser.h +++ b/src/UESaveFile/Serialisers/UnrealPropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.cpp b/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.cpp index f272d6f..402f4d4 100644 --- a/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.cpp @@ -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 diff --git a/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.h b/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.h index c754bda..0351243 100644 --- a/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.h +++ b/src/UESaveFile/Serialisers/Vector2DPropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Serialisers/VectorPropertySerialiser.cpp b/src/UESaveFile/Serialisers/VectorPropertySerialiser.cpp index feaa803..ee11136 100644 --- a/src/UESaveFile/Serialisers/VectorPropertySerialiser.cpp +++ b/src/UESaveFile/Serialisers/VectorPropertySerialiser.cpp @@ -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 diff --git a/src/UESaveFile/Serialisers/VectorPropertySerialiser.h b/src/UESaveFile/Serialisers/VectorPropertySerialiser.h index 3afb0dc..a3c9e75 100644 --- a/src/UESaveFile/Serialisers/VectorPropertySerialiser.h +++ b/src/UESaveFile/Serialisers/VectorPropertySerialiser.h @@ -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 diff --git a/src/UESaveFile/Types/ArrayProperty.h b/src/UESaveFile/Types/ArrayProperty.h index f453720..5cda507 100644 --- a/src/UESaveFile/Types/ArrayProperty.h +++ b/src/UESaveFile/Types/ArrayProperty.h @@ -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 diff --git a/src/UESaveFile/Types/BoolProperty.h b/src/UESaveFile/Types/BoolProperty.h index f80a3b9..ad80c62 100644 --- a/src/UESaveFile/Types/BoolProperty.h +++ b/src/UESaveFile/Types/BoolProperty.h @@ -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 diff --git a/src/UESaveFile/Types/ByteProperty.h b/src/UESaveFile/Types/ByteProperty.h index 8587d2e..26d5398 100644 --- a/src/UESaveFile/Types/ByteProperty.h +++ b/src/UESaveFile/Types/ByteProperty.h @@ -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 diff --git a/src/UESaveFile/Types/ColourStructProperty.h b/src/UESaveFile/Types/ColourStructProperty.h index 6df355d..56c898f 100644 --- a/src/UESaveFile/Types/ColourStructProperty.h +++ b/src/UESaveFile/Types/ColourStructProperty.h @@ -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 diff --git a/src/UESaveFile/Types/DateTimeStructProperty.h b/src/UESaveFile/Types/DateTimeStructProperty.h index 86da227..4e3ecea 100644 --- a/src/UESaveFile/Types/DateTimeStructProperty.h +++ b/src/UESaveFile/Types/DateTimeStructProperty.h @@ -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 diff --git a/src/UESaveFile/Types/EnumProperty.h b/src/UESaveFile/Types/EnumProperty.h index e9418af..9494856 100644 --- a/src/UESaveFile/Types/EnumProperty.h +++ b/src/UESaveFile/Types/EnumProperty.h @@ -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 diff --git a/src/UESaveFile/Types/FloatProperty.h b/src/UESaveFile/Types/FloatProperty.h index 1594959..bdc44b0 100644 --- a/src/UESaveFile/Types/FloatProperty.h +++ b/src/UESaveFile/Types/FloatProperty.h @@ -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 diff --git a/src/UESaveFile/Types/GenericStructProperty.h b/src/UESaveFile/Types/GenericStructProperty.h index 7ff3cfb..a14c7d2 100644 --- a/src/UESaveFile/Types/GenericStructProperty.h +++ b/src/UESaveFile/Types/GenericStructProperty.h @@ -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 diff --git a/src/UESaveFile/Types/GuidStructProperty.h b/src/UESaveFile/Types/GuidStructProperty.h index e5a88dc..cf5d1ca 100644 --- a/src/UESaveFile/Types/GuidStructProperty.h +++ b/src/UESaveFile/Types/GuidStructProperty.h @@ -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 diff --git a/src/UESaveFile/Types/IntProperty.h b/src/UESaveFile/Types/IntProperty.h index d03f77b..55774d1 100644 --- a/src/UESaveFile/Types/IntProperty.h +++ b/src/UESaveFile/Types/IntProperty.h @@ -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 diff --git a/src/UESaveFile/Types/MapProperty.h b/src/UESaveFile/Types/MapProperty.h index 3f8768b..d52994f 100644 --- a/src/UESaveFile/Types/MapProperty.h +++ b/src/UESaveFile/Types/MapProperty.h @@ -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 diff --git a/src/UESaveFile/Types/NoneProperty.h b/src/UESaveFile/Types/NoneProperty.h index e52ec61..91e8ab9 100644 --- a/src/UESaveFile/Types/NoneProperty.h +++ b/src/UESaveFile/Types/NoneProperty.h @@ -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 diff --git a/src/UESaveFile/Types/ResourceItemValue.h b/src/UESaveFile/Types/ResourceItemValue.h index d5f5221..4692296 100644 --- a/src/UESaveFile/Types/ResourceItemValue.h +++ b/src/UESaveFile/Types/ResourceItemValue.h @@ -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 diff --git a/src/UESaveFile/Types/RotatorStructProperty.h b/src/UESaveFile/Types/RotatorStructProperty.h index 89add8b..e1bdf21 100644 --- a/src/UESaveFile/Types/RotatorStructProperty.h +++ b/src/UESaveFile/Types/RotatorStructProperty.h @@ -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 diff --git a/src/UESaveFile/Types/SetProperty.h b/src/UESaveFile/Types/SetProperty.h index b426dc6..6c490ed 100644 --- a/src/UESaveFile/Types/SetProperty.h +++ b/src/UESaveFile/Types/SetProperty.h @@ -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 diff --git a/src/UESaveFile/Types/StringProperty.h b/src/UESaveFile/Types/StringProperty.h index 07463ac..2d871b7 100644 --- a/src/UESaveFile/Types/StringProperty.h +++ b/src/UESaveFile/Types/StringProperty.h @@ -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 diff --git a/src/UESaveFile/Types/StructProperty.h b/src/UESaveFile/Types/StructProperty.h index 80b9efc..c38a048 100644 --- a/src/UESaveFile/Types/StructProperty.h +++ b/src/UESaveFile/Types/StructProperty.h @@ -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 diff --git a/src/UESaveFile/Types/TextProperty.h b/src/UESaveFile/Types/TextProperty.h index 46bd99e..bffaaad 100644 --- a/src/UESaveFile/Types/TextProperty.h +++ b/src/UESaveFile/Types/TextProperty.h @@ -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 diff --git a/src/UESaveFile/Types/UnrealProperty.h b/src/UESaveFile/Types/UnrealProperty.h index 08290fe..2192028 100644 --- a/src/UESaveFile/Types/UnrealProperty.h +++ b/src/UESaveFile/Types/UnrealProperty.h @@ -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 diff --git a/src/UESaveFile/Types/UnrealPropertyBase.h b/src/UESaveFile/Types/UnrealPropertyBase.h index 4ef94f8..d538428 100644 --- a/src/UESaveFile/Types/UnrealPropertyBase.h +++ b/src/UESaveFile/Types/UnrealPropertyBase.h @@ -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 diff --git a/src/UESaveFile/Types/Vector2DStructProperty.h b/src/UESaveFile/Types/Vector2DStructProperty.h index 1795b05..9372881 100644 --- a/src/UESaveFile/Types/Vector2DStructProperty.h +++ b/src/UESaveFile/Types/Vector2DStructProperty.h @@ -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 diff --git a/src/UESaveFile/Types/VectorStructProperty.h b/src/UESaveFile/Types/VectorStructProperty.h index 529522e..367435b 100644 --- a/src/UESaveFile/Types/VectorStructProperty.h +++ b/src/UESaveFile/Types/VectorStructProperty.h @@ -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 diff --git a/src/UESaveFile/UESaveFile.cpp b/src/UESaveFile/UESaveFile.cpp index e696a95..5835769 100644 --- a/src/UESaveFile/UESaveFile.cpp +++ b/src/UESaveFile/UESaveFile.cpp @@ -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 diff --git a/src/UESaveFile/UESaveFile.h b/src/UESaveFile/UESaveFile.h index d3b095d..856e12b 100644 --- a/src/UESaveFile/UESaveFile.h +++ b/src/UESaveFile/UESaveFile.h @@ -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 diff --git a/src/main.cpp b/src/main.cpp index ca38b71..6545425 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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 diff --git a/src/resource.rc b/src/resource.rc index 7491807..df99b1e 100644 --- a/src/resource.rc +++ b/src/resource.rc @@ -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 -- 2.39.2 From d74a7bc219e5d0acf6c64f38579171bc46da5358 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sun, 30 Jan 2022 14:02:30 +0100 Subject: [PATCH 100/128] Mass: make Weapon copyable. This is necessary to add weapon copying. --- src/Mass/Mass.h | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 5caa2ec..815455d 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -101,6 +101,42 @@ struct WeaponPart { }; struct Weapon { + Weapon() = default; + + Weapon(const Weapon& other) { + name = other.name; + type = other.type; + parts = Containers::Array{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& operator=(const Weapon& other) { + name = other.name; + type = other.type; + parts = Containers::Array{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; + } + + Weapon(Weapon&& other) = default; + Weapon& operator=(Weapon&& other) = default; + std::string name; std::string type; Containers::Array parts; -- 2.39.2 From 940fe3feeeeaa03918c86a7904c7b82de66af08a Mon Sep 17 00:00:00 2001 From: William JCM Date: Sun, 30 Jan 2022 14:04:22 +0100 Subject: [PATCH 101/128] SaveTool: implement weapon copying. --- src/SaveTool/SaveTool_MassViewer.cpp | 96 +++++++++++++++------------- 1 file changed, 53 insertions(+), 43 deletions(-) diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index 7992f43..3078d60 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -635,49 +635,59 @@ void SaveTool::drawWeapons() { int id = 0; -#define weapcat(header, array, dirty, payload_type, payload_tooltip) \ - ImGui::TableNextRow(ImGuiTableRowFlags_Headers); \ - ImGui::TableNextColumn(); \ - ImGui::TextUnformatted(header); \ - \ - for(UnsignedInt i = 0; i < _currentMass->array().size(); i++) { \ - auto& weapon = _currentMass->array()[i]; \ - \ - ImGui::TableNextRow(); \ - ImGui::TableNextColumn(); \ - ImGui::PushID(id); \ - if(ImGui::Selectable(weapon.name.c_str(), current_weapon == &weapon)) { \ - current_weapon = &weapon; \ - } \ - if(ImGui::BeginDragDropSource()) { \ - ImGui::SetDragDropPayload(payload_type, &i, sizeof(UnsignedInt)); \ - ImGui::Text(payload_tooltip " %i - %s", i + 1, weapon.name.c_str()); \ - ImGui::EndDragDropSource(); \ - } \ - if(ImGui::BeginDragDropTarget()) { \ - if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(payload_type)) { \ - int index = *static_cast(payload->Data); \ - \ - if(current_weapon == &_currentMass->array()[index]) { \ - current_weapon = &_currentMass->array()[i]; \ - } \ - else if (current_weapon == &_currentMass->array()[i]) { \ - current_weapon = &_currentMass->array()[index]; \ - } \ - \ - std::swap(_currentMass->array()[index], _currentMass->array()[i]); \ - (dirty) = true; \ - } \ - \ - ImGui::EndDragDropTarget(); \ - } \ - \ - ImGui::PopID(); \ - id++; \ - \ - if(weapon.attached == true) { \ - ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, 0x1F008CFFu); \ - } \ +#define weapcat(header, array, dirty, payload_type, payload_tooltip) \ + ImGui::TableNextRow(ImGuiTableRowFlags_Headers); \ + ImGui::TableNextColumn(); \ + ImGui::TextUnformatted(header); \ + \ + for(UnsignedInt i = 0; i < _currentMass->array().size(); i++) { \ + auto& weapon = _currentMass->array()[i]; \ + \ + ImGui::TableNextRow(); \ + ImGui::TableNextColumn(); \ + ImGui::PushID(id); \ + if(ImGui::Selectable(weapon.name.c_str(), current_weapon == &weapon)) { \ + current_weapon = &weapon; \ + } \ + if(ImGui::BeginDragDropSource()) { \ + ImGui::SetDragDropPayload(payload_type, &i, sizeof(UnsignedInt)); \ + if(ImGui::GetIO().KeyCtrl) { \ + ImGui::Text(payload_tooltip " %i - %s (copy)", i + 1, weapon.name.c_str()); \ + } \ + else { \ + ImGui::Text(payload_tooltip " %i - %s", i + 1, weapon.name.c_str()); \ + } \ + ImGui::EndDragDropSource(); \ + } \ + if(ImGui::BeginDragDropTarget()) { \ + if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(payload_type)) { \ + int index = *static_cast(payload->Data); \ + \ + if(!ImGui::GetIO().KeyCtrl) { \ + if(current_weapon == &_currentMass->array()[index]) { \ + current_weapon = &_currentMass->array()[i]; \ + } \ + else if (current_weapon == &_currentMass->array()[i]) { \ + current_weapon = &_currentMass->array()[index]; \ + } \ + \ + std::swap(_currentMass->array()[index], _currentMass->array()[i]); \ + } \ + else { \ + _currentMass->array()[i] = _currentMass->array()[index]; \ + } \ + (dirty) = true; \ + } \ + \ + ImGui::EndDragDropTarget(); \ + } \ + \ + ImGui::PopID(); \ + id++; \ + \ + if(weapon.attached == true) { \ + ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, 0x1F008CFFu); \ + } \ } weapcat("Melee weapons", meleeWeapons, _meleeDirty, "MeleeWeapon", "Melee weapon") -- 2.39.2 From 41cd92352d635e2d588d1c4fc9b3c904be1e4f8b Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 9 Feb 2022 13:36:33 +0100 Subject: [PATCH 102/128] Mass: refactor and optimise some parts. --- src/CMakeLists.txt | 9 ++ src/Maps/WeaponTypes.hpp | 24 +++++ src/Mass/Accessory.h | 36 ++++++++ src/Mass/ArmourPart.h | 38 ++++++++ src/Mass/CustomStyle.h | 39 ++++++++ src/Mass/Decal.h | 38 ++++++++ src/Mass/Joints.h | 32 +++++++ src/Mass/Mass.cpp | 17 +++- src/Mass/Mass.h | 122 ++----------------------- src/Mass/Weapon.cpp | 49 ++++++++++ src/Mass/Weapon.h | 61 +++++++++++++ src/Mass/WeaponPart.h | 35 ++++++++ src/SaveTool/SaveTool_MassViewer.cpp | 128 ++++++++++++++------------- 13 files changed, 452 insertions(+), 176 deletions(-) create mode 100644 src/Maps/WeaponTypes.hpp create mode 100644 src/Mass/Accessory.h create mode 100644 src/Mass/ArmourPart.h create mode 100644 src/Mass/CustomStyle.h create mode 100644 src/Mass/Decal.h create mode 100644 src/Mass/Joints.h create mode 100644 src/Mass/Weapon.cpp create mode 100644 src/Mass/Weapon.h create mode 100644 src/Mass/WeaponPart.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 06b6d29..90c2be5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -124,8 +124,16 @@ 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 @@ -133,6 +141,7 @@ add_executable(MassBuilderSaveTool WIN32 Maps/StoryProgress.h Maps/StyleNames.h Maps/WeaponTypes.h + Maps/WeaponTypes.hpp ToastQueue/ToastQueue.h ToastQueue/ToastQueue.cpp FontAwesome/IconsFontAwesome5.h diff --git a/src/Maps/WeaponTypes.hpp b/src/Maps/WeaponTypes.hpp new file mode 100644 index 0000000..baa31d6 --- /dev/null +++ b/src/Maps/WeaponTypes.hpp @@ -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 . + +#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 diff --git a/src/Mass/Accessory.h b/src/Mass/Accessory.h new file mode 100644 index 0000000..cca271d --- /dev/null +++ b/src/Mass/Accessory.h @@ -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 . + +#include + +#include +#include + +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}; +}; diff --git a/src/Mass/ArmourPart.h b/src/Mass/ArmourPart.h new file mode 100644 index 0000000..833f768 --- /dev/null +++ b/src/Mass/ArmourPart.h @@ -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 . + +#include + +#include + +#include + +#include "Decal.h" +#include "Accessory.h" + +using namespace Corrade; +using namespace Magnum; + +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}; +}; diff --git a/src/Mass/CustomStyle.h b/src/Mass/CustomStyle.h new file mode 100644 index 0000000..cee5cf9 --- /dev/null +++ b/src/Mass/CustomStyle.h @@ -0,0 +1,39 @@ +#pragma once + +// MassBuilderSaveTool +// Copyright (C) 2021-2022 Guillaume Jacquemin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include + +#include +#include +#include + +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; +}; diff --git a/src/Mass/Decal.h b/src/Mass/Decal.h new file mode 100644 index 0000000..487f813 --- /dev/null +++ b/src/Mass/Decal.h @@ -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 . + +#include + +#include +#include +#include + +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; +}; diff --git a/src/Mass/Joints.h b/src/Mass/Joints.h new file mode 100644 index 0000000..8cc58c2 --- /dev/null +++ b/src/Mass/Joints.h @@ -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 . + +#include + +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; +}; diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 93c3c64..eff9fdd 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -1063,7 +1063,14 @@ void Mass::getWeaponType(const char* prop_name, Containers::ArrayView we auto& weapon = weapon_array[i]; weapon.name = weapon_prop->at("Name_13_7BF0D31F4E50C50C47231BB36A485D92")->value; - weapon.type = weapon_prop->at("Type_2_35ABA8C3406F8D9BBF14A89CD6BCE976")->enumValue; + std::string& weapon_type = weapon_prop->at("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("Element_6_8E4617CC4B2C1F1490435599784EC6E0"); weapon.parts = Containers::Array{ValueInit, parts_prop->items.size()}; @@ -1148,7 +1155,13 @@ auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView auto& weapon = weapon_array[i]; weapon_prop->at("Name_13_7BF0D31F4E50C50C47231BB36A485D92")->value = weapon.name; - weapon_prop->at("Type_2_35ABA8C3406F8D9BBF14A89CD6BCE976")->enumValue = weapon.type; + switch(weapon.type) { + #define c(enumerator, strenum, name) case WeaponType::enumerator: weapon_prop->at("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("Element_6_8E4617CC4B2C1F1490435599784EC6E0"); if(parts_prop->items.size() != weapon.parts.size()) { diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 815455d..450680e 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -27,6 +27,14 @@ #include #include +#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,120 +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 { - Weapon() = default; - - Weapon(const Weapon& other) { - name = other.name; - type = other.type; - parts = Containers::Array{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& operator=(const Weapon& other) { - name = other.name; - type = other.type; - parts = Containers::Array{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; - } - - Weapon(Weapon&& other) = default; - Weapon& operator=(Weapon&& other) = default; - - std::string name; - std::string type; - Containers::Array 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 { diff --git a/src/Mass/Weapon.cpp b/src/Mass/Weapon.cpp new file mode 100644 index 0000000..5864282 --- /dev/null +++ b/src/Mass/Weapon.cpp @@ -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 . + +#include "Weapon.h" + +Weapon::Weapon(const Weapon& other) { + name = other.name; + type = other.type; + parts = Containers::Array{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{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; +} diff --git a/src/Mass/Weapon.h b/src/Mass/Weapon.h new file mode 100644 index 0000000..ee7eddd --- /dev/null +++ b/src/Mass/Weapon.h @@ -0,0 +1,61 @@ +#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 . + +#include + +#include +#include + +#include +#include + +#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, +}; + +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 parts; + Containers::StaticArray<16, CustomStyle> customStyles{ValueInit}; + bool attached = false; + std::string damageType; + bool dualWield = false; + std::string effectColourMode; + Color4 effectColour{0.0f}; +}; diff --git a/src/Mass/WeaponPart.h b/src/Mass/WeaponPart.h new file mode 100644 index 0000000..a6cb1a4 --- /dev/null +++ b/src/Mass/WeaponPart.h @@ -0,0 +1,35 @@ +#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 . + +#include + +#include + +#include "Decal.h" +#include "Accessory.h" + +using namespace Corrade; +using namespace Magnum; + +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}; +}; diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index 3078d60..9dd28f9 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -790,64 +790,67 @@ void SaveTool::drawWeapons() { ImGui::Separator(); if(drawUnsafeWidget([](){ return ImGui::Button(ICON_FA_SAVE " Save changes to weapon category"); })) { - if(current_weapon->type == "enuWeaponTypes::NewEnumerator0") { - if(!_currentMass->writeMeleeWeapons()) { - _queue.addToast(Toast::Type::Error, "Couldn't save melee weapons"); - } - } - else if(current_weapon->type == "enuWeaponTypes::NewEnumerator5") { - if(!_currentMass->writeShields()) { - _queue.addToast(Toast::Type::Error, "Couldn't save shields"); - } - } - else if(current_weapon->type == "enuWeaponTypes::NewEnumerator1") { - if(!_currentMass->writeBulletShooters()) { - _queue.addToast(Toast::Type::Error, "Couldn't save bullet shooters"); - } - } - else if(current_weapon->type == "enuWeaponTypes::NewEnumerator2") { - if(!_currentMass->writeEnergyShooters()) { - _queue.addToast(Toast::Type::Error, "Couldn't save energy shooters"); - } - } - else if(current_weapon->type == "enuWeaponTypes::NewEnumerator3") { - if(!_currentMass->writeBulletLaunchers()) { - _queue.addToast(Toast::Type::Error, "Couldn't save bullet launchers"); - } - } - else if(current_weapon->type == "enuWeaponTypes::NewEnumerator4") { - if(!_currentMass->writeEnergyLaunchers()) { - _queue.addToast(Toast::Type::Error, "Couldn't save energy launchers"); - } - } - else { - _queue.addToast(Toast::Type::Error, "Unknown weapon type"); + switch(current_weapon->type) { + case WeaponType::Melee: + if(!_currentMass->writeMeleeWeapons()) { + _queue.addToast(Toast::Type::Error, "Couldn't save melee weapons"); + } + break; + case WeaponType::Shield: + if(!_currentMass->writeShields()) { + _queue.addToast(Toast::Type::Error, "Couldn't save shields"); + } + break; + case WeaponType::BulletShooter: + if(!_currentMass->writeBulletShooters()) { + _queue.addToast(Toast::Type::Error, "Couldn't save bullet shooters"); + } + break; + case WeaponType::EnergyShooter: + if(!_currentMass->writeEnergyShooters()) { + _queue.addToast(Toast::Type::Error, "Couldn't save energy shooters"); + } + break; + case WeaponType::BulletLauncher: + if(!_currentMass->writeBulletLaunchers()) { + _queue.addToast(Toast::Type::Error, "Couldn't save bullet launchers"); + } + break; + case WeaponType::EnergyLauncher: + if(!_currentMass->writeEnergyLaunchers()) { + _queue.addToast(Toast::Type::Error, "Couldn't save energy launchers"); + } + break; + default: + _queue.addToast(Toast::Type::Error, "Unknown weapon type"); + } } ImGui::SameLine(); if(ImGui::Button(ICON_FA_UNDO_ALT " Reset weapon category")) { - if(current_weapon->type == "enuWeaponTypes::NewEnumerator0") { - _currentMass->getMeleeWeapons(); - } - else if(current_weapon->type == "enuWeaponTypes::NewEnumerator5") { - _currentMass->getShields(); - } - else if(current_weapon->type == "enuWeaponTypes::NewEnumerator1") { - _currentMass->getBulletShooters(); - } - else if(current_weapon->type == "enuWeaponTypes::NewEnumerator2") { - _currentMass->getEnergyShooters(); - } - else if(current_weapon->type == "enuWeaponTypes::NewEnumerator3") { - _currentMass->getBulletLaunchers(); - } - else if(current_weapon->type == "enuWeaponTypes::NewEnumerator4") { - _currentMass->getEnergyLaunchers(); - } - else { - _queue.addToast(Toast::Type::Error, "Unknown weapon type"); + switch(current_weapon->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"); } } @@ -860,7 +863,12 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { } ImGui::AlignTextToFramePadding(); - ImGui::Text("%s: %s", weapon_types.at(weapon.type), weapon.name.c_str()); + const char* labels[] { + #define c(enumerator, strenum, name) name, + #include "../Maps/WeaponTypes.hpp" + #undef c + }; + ImGui::Text("%s: %s", labels[UnsignedInt(weapon.type)], weapon.name.c_str()); ImGui::SameLine(); @@ -880,12 +888,12 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Equipped:"); - if(weapon.type != "enuWeaponTypes::NewEnumerator5") { + if(weapon.type != WeaponType::Shield) { ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Damage type:"); } - if(weapon.type == "enuWeaponTypes::NewEnumerator0") { + if(weapon.type == WeaponType::Melee) { ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Dual-wield:"); @@ -902,18 +910,18 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { ImGui::BeginGroup(); ImGui::Checkbox("##EquippedCheckbox", &weapon.attached); - if(weapon.type != "enuWeaponTypes::NewEnumerator5") { - if(weapon.type == "enuWeaponTypes::NewEnumerator0" && + if(weapon.type != WeaponType::Shield) { + if(weapon.type == WeaponType::Melee && ImGui::RadioButton("Physical##NoElement", weapon.damageType == "enuDamageProperty::NewEnumerator0")) { weapon.damageType = "enuDamageProperty::NewEnumerator0"; } - else if((weapon.type == "enuWeaponTypes::NewEnumerator1" || weapon.type == "enuWeaponTypes::NewEnumerator3") && + else if((weapon.type == WeaponType::BulletShooter || weapon.type == WeaponType::BulletLauncher) && ImGui::RadioButton("Piercing##NoElement", weapon.damageType == "enuDamageProperty::NewEnumerator1")) { weapon.damageType = "enuDamageProperty::NewEnumerator1"; } - else if((weapon.type == "enuWeaponTypes::NewEnumerator2" || weapon.type == "enuWeaponTypes::NewEnumerator4") && + else if((weapon.type == WeaponType::EnergyShooter || weapon.type == WeaponType::EnergyLauncher) && ImGui::RadioButton("Plasma##NoElement", weapon.damageType == "enuDamageProperty::NewEnumerator5")) { weapon.damageType = "enuDamageProperty::NewEnumerator5"; @@ -932,7 +940,7 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { } } - if(weapon.type == "enuWeaponTypes::NewEnumerator0") { + if(weapon.type == WeaponType::Melee) { ImGui::Checkbox("##DualWield", &weapon.dualWield); if(ImGui::RadioButton("Default##Default", weapon.effectColourMode == "enuWeaponEffectColorMode::NewEnumerator0")) { -- 2.39.2 From 9f324c30fd97dcd272c641c526095b9610d7e478 Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 9 Feb 2022 13:54:44 +0100 Subject: [PATCH 103/128] Delete WeaponTypes.h. It's not needed anymore. --- src/CMakeLists.txt | 1 - src/Maps/WeaponTypes.h | 29 ---------------------------- src/SaveTool/SaveTool_MassViewer.cpp | 1 - 3 files changed, 31 deletions(-) delete mode 100644 src/Maps/WeaponTypes.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 90c2be5..500b132 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -140,7 +140,6 @@ add_executable(MassBuilderSaveTool WIN32 Maps/LastMissionId.h Maps/StoryProgress.h Maps/StyleNames.h - Maps/WeaponTypes.h Maps/WeaponTypes.hpp ToastQueue/ToastQueue.h ToastQueue/ToastQueue.cpp diff --git a/src/Maps/WeaponTypes.h b/src/Maps/WeaponTypes.h deleted file mode 100644 index 170aecc..0000000 --- a/src/Maps/WeaponTypes.h +++ /dev/null @@ -1,29 +0,0 @@ -#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 . - -#include -#include - -static const std::unordered_map 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"}, -}; diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index 9dd28f9..0b4fb8f 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -22,7 +22,6 @@ #include "../Maps/ArmourSets.h" #include "../Maps/ArmourSlots.h" #include "../Maps/StyleNames.h" -#include "../Maps/WeaponTypes.h" #include "../FontAwesome/IconsFontAwesome5.h" -- 2.39.2 From 28db82c8a9db68ef9ee0dec5e1fb0e4570b6efea Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 9 Feb 2022 14:17:05 +0100 Subject: [PATCH 104/128] Weapon,Mass,SaveTool: refactor some more code. --- src/CMakeLists.txt | 1 + src/Maps/DamageTypes.hpp | 24 ++++++++++++++++++++++++ src/Mass/Mass.cpp | 19 ++++++++++++++++--- src/Mass/Weapon.h | 11 ++++++++++- src/SaveTool/SaveTool_MassViewer.cpp | 24 ++++++++++++------------ 5 files changed, 63 insertions(+), 16 deletions(-) create mode 100644 src/Maps/DamageTypes.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 500b132..e6bb24e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -137,6 +137,7 @@ add_executable(MassBuilderSaveTool WIN32 Maps/Accessories.h Maps/ArmourSets.h Maps/ArmourSlots.h + Maps/DamageTypes.hpp Maps/LastMissionId.h Maps/StoryProgress.h Maps/StyleNames.h diff --git a/src/Maps/DamageTypes.hpp b/src/Maps/DamageTypes.hpp new file mode 100644 index 0000000..5665f65 --- /dev/null +++ b/src/Maps/DamageTypes.hpp @@ -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 . + +#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 diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index eff9fdd..9371ade 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -1063,7 +1063,7 @@ void Mass::getWeaponType(const char* prop_name, Containers::ArrayView we auto& weapon = weapon_array[i]; weapon.name = weapon_prop->at("Name_13_7BF0D31F4E50C50C47231BB36A485D92")->value; - std::string& weapon_type = weapon_prop->at("Type_2_35ABA8C3406F8D9BBF14A89CD6BCE976")->enumValue; + auto& weapon_type = weapon_prop->at("Type_2_35ABA8C3406F8D9BBF14A89CD6BCE976")->enumValue; #define c(enumerator, strenum, name) if(weapon_type == (strenum)) { weapon.type = WeaponType::enumerator; } else #include "../Maps/WeaponTypes.hpp" #undef c @@ -1124,7 +1124,14 @@ void Mass::getWeaponType(const char* prop_name, Containers::ArrayView we getCustomStyles(weapon.customStyles, custom_styles); weapon.attached = weapon_prop->at("Attach_15_D00AABBD4AD6A04778D56D81E51927B3")->value; - weapon.damageType = weapon_prop->at("DamageType_18_E1FFA53540591A9087EC698117A65C83")->enumValue; + auto& damage_type = weapon_prop->at("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("DualWield_20_B2EB2CEA4A6A233DC7575996B6DD1222")->value; weapon.effectColourMode = weapon_prop->at("ColorEfxMode_24_D254BCF943E852BF9ADB8AAA8FD80014")->enumValue; auto effect_colour = weapon_prop->at("ColorEfx_26_D921B62946C493E487455A831F4520AC"); @@ -1244,7 +1251,13 @@ auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView } weapon_prop->at("Attach_15_D00AABBD4AD6A04778D56D81E51927B3")->value = weapon.attached; - weapon_prop->at("DamageType_18_E1FFA53540591A9087EC698117A65C83")->enumValue = weapon.damageType; + switch(weapon.damageType) { + #define c(enumerator, strenum) case DamageType::enumerator: weapon_prop->at("DamageType_18_E1FFA53540591A9087EC698117A65C83")->enumValue = strenum; break; + #include "../Maps/DamageTypes.hpp" + #undef c + default: + Utility::Warning{} << "Invalid damage type enum value in writeWeaponType()."; + } weapon_prop->at("DualWield_20_B2EB2CEA4A6A233DC7575996B6DD1222")->value = weapon.dualWield; weapon_prop->at("ColorEfxMode_24_D254BCF943E852BF9ADB8AAA8FD80014")->enumValue = weapon.effectColourMode; auto effect_colour = weapon_prop->at("ColorEfx_26_D921B62946C493E487455A831F4520AC"); diff --git a/src/Mass/Weapon.h b/src/Mass/Weapon.h index ee7eddd..25785b4 100644 --- a/src/Mass/Weapon.h +++ b/src/Mass/Weapon.h @@ -39,6 +39,15 @@ enum class WeaponType { EnergyLauncher = 4, }; +enum class DamageType { + Physical = 0, + Piercing = 1, + Plasma = 5, + Heat = 2, + Freeze = 3, + Shock = 4, +}; + struct Weapon { Weapon() = default; @@ -54,7 +63,7 @@ struct Weapon { Containers::Array parts; Containers::StaticArray<16, CustomStyle> customStyles{ValueInit}; bool attached = false; - std::string damageType; + DamageType damageType = DamageType::Physical; bool dualWield = false; std::string effectColourMode; Color4 effectColour{0.0f}; diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index 0b4fb8f..ffaa054 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -911,31 +911,31 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { if(weapon.type != WeaponType::Shield) { if(weapon.type == WeaponType::Melee && - ImGui::RadioButton("Physical##NoElement", weapon.damageType == "enuDamageProperty::NewEnumerator0")) + ImGui::RadioButton("Physical##NoElement", weapon.damageType == DamageType::Physical)) { - weapon.damageType = "enuDamageProperty::NewEnumerator0"; + weapon.damageType = DamageType::Physical; } else if((weapon.type == WeaponType::BulletShooter || weapon.type == WeaponType::BulletLauncher) && - ImGui::RadioButton("Piercing##NoElement", weapon.damageType == "enuDamageProperty::NewEnumerator1")) + ImGui::RadioButton("Piercing##NoElement", weapon.damageType == DamageType::Piercing)) { - weapon.damageType = "enuDamageProperty::NewEnumerator1"; + weapon.damageType = DamageType::Piercing; } else if((weapon.type == WeaponType::EnergyShooter || weapon.type == WeaponType::EnergyLauncher) && - ImGui::RadioButton("Plasma##NoElement", weapon.damageType == "enuDamageProperty::NewEnumerator5")) + ImGui::RadioButton("Plasma##NoElement", weapon.damageType == DamageType::Plasma)) { - weapon.damageType = "enuDamageProperty::NewEnumerator5"; + weapon.damageType = DamageType::Plasma; } ImGui::SameLine(); - if(ImGui::RadioButton("Heat##Heat", weapon.damageType == "enuDamageProperty::NewEnumerator2")) { - weapon.damageType = "enuDamageProperty::NewEnumerator2"; + if(ImGui::RadioButton("Heat##Heat", weapon.damageType == DamageType::Heat)) { + weapon.damageType = DamageType::Heat; } ImGui::SameLine(); - if(ImGui::RadioButton("Freeze##Freeze", weapon.damageType == "enuDamageProperty::NewEnumerator3")) { - weapon.damageType = "enuDamageProperty::NewEnumerator3"; + if(ImGui::RadioButton("Freeze##Freeze", weapon.damageType == DamageType::Freeze)) { + weapon.damageType = DamageType::Freeze; } ImGui::SameLine(); - if(ImGui::RadioButton("Shock##Shock", weapon.damageType == "enuDamageProperty::NewEnumerator4")) { - weapon.damageType = "enuDamageProperty::NewEnumerator4"; + if(ImGui::RadioButton("Shock##Shock", weapon.damageType == DamageType::Freeze)) { + weapon.damageType = DamageType::Freeze; } } -- 2.39.2 From bbc40d7c935e4aa64f7150207b8e1494921a20f9 Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 9 Feb 2022 18:12:11 +0100 Subject: [PATCH 105/128] Weapon: update formatting. --- src/Mass/Weapon.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Mass/Weapon.h b/src/Mass/Weapon.h index 25785b4..102299c 100644 --- a/src/Mass/Weapon.h +++ b/src/Mass/Weapon.h @@ -31,10 +31,10 @@ using namespace Corrade; using namespace Magnum; enum class WeaponType { - Melee = 0, - Shield = 5, - BulletShooter = 1, - EnergyShooter = 2, + Melee = 0, + Shield = 5, + BulletShooter = 1, + EnergyShooter = 2, BulletLauncher = 3, EnergyLauncher = 4, }; -- 2.39.2 From 353a71d8abe0f8c039b1df5242121a4c45dc53e6 Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 9 Feb 2022 18:28:23 +0100 Subject: [PATCH 106/128] Weapon: remove an unneeded blank line. --- src/Mass/Weapon.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Mass/Weapon.h b/src/Mass/Weapon.h index 102299c..a2c4b00 100644 --- a/src/Mass/Weapon.h +++ b/src/Mass/Weapon.h @@ -49,7 +49,6 @@ enum class DamageType { }; struct Weapon { - Weapon() = default; Weapon(const Weapon& other); -- 2.39.2 From afc163f3449a115f123e6cb6ffd83a251b674b67 Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 9 Feb 2022 20:16:14 +0100 Subject: [PATCH 107/128] Mass: rename a member. SteamIDs aren't used anymore in 0.8+ save files. --- src/Mass/Mass.cpp | 8 ++++---- src/Mass/Mass.h | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 9371ade..21f9a0e 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -193,7 +193,7 @@ void Mass::refreshValues() { return; } - _steamId = account_prop->value; + _account = account_prop->value; _state = State::Valid; } @@ -930,8 +930,8 @@ auto Mass::techs() -> Containers::ArrayView { return _tuning.techIds; } -auto Mass::updateSteamId(const std::string& steam_id) -> bool { - _steamId = steam_id; +auto Mass::updateAccount(const std::string& new_account) -> bool { + _account = new_account; auto account = _mass->at("Account"); if(!account) { @@ -939,7 +939,7 @@ auto Mass::updateSteamId(const std::string& steam_id) -> bool { return false; } - account->value = steam_id; + account->value = new_account; return _mass->saveToFile(); } diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index 450680e..c89c9f3 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -137,7 +137,8 @@ class Mass { auto architecture() -> Int&; auto techs() -> Containers::ArrayView; - 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 styles, ArrayProperty* style_array); @@ -203,5 +204,5 @@ class Mass { Containers::StaticArray<7, Int> techIds; } _tuning; - std::string _steamId; + std::string _account; }; -- 2.39.2 From 2cabe6a3ba3a1c12a85fdb4e2c42c0f3be5ab28a Mon Sep 17 00:00:00 2001 From: William JCM Date: Fri, 11 Feb 2022 18:31:45 +0100 Subject: [PATCH 108/128] MassManager: adapt to Mass changes. Should have caught it earlier. Ugh. --- src/MassManager/MassManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MassManager/MassManager.cpp b/src/MassManager/MassManager.cpp index a385df9..d6338c3 100644 --- a/src/MassManager/MassManager.cpp +++ b/src/MassManager/MassManager.cpp @@ -79,7 +79,7 @@ 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)) + if(!Mass{source + ".tmp"}.updateAccount(_steamId)) { _lastError = "The M.A.S.S. file at " + source + " seems to be corrupt."; Utility::Directory::rm(source + ".tmp"); -- 2.39.2 From a6c061497913ad3e1e01a32098b2f97801a03003 Mon Sep 17 00:00:00 2001 From: William JCM Date: Fri, 11 Feb 2022 19:44:16 +0100 Subject: [PATCH 109/128] BinaryWriter: make non-copyable. --- src/UESaveFile/BinaryWriter.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/UESaveFile/BinaryWriter.h b/src/UESaveFile/BinaryWriter.h index 299f40c..0d7a726 100644 --- a/src/UESaveFile/BinaryWriter.h +++ b/src/UESaveFile/BinaryWriter.h @@ -33,6 +33,12 @@ 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(); -- 2.39.2 From 8f1e3668a3318ad46aaf3b15e6f92be3c04ab848 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 12 Feb 2022 11:21:23 +0100 Subject: [PATCH 110/128] BinaryWriter: allow writing string literals. --- src/UESaveFile/BinaryWriter.cpp | 2 +- src/UESaveFile/BinaryWriter.h | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/UESaveFile/BinaryWriter.cpp b/src/UESaveFile/BinaryWriter.cpp index 0522eb8..9cb991e 100644 --- a/src/UESaveFile/BinaryWriter.cpp +++ b/src/UESaveFile/BinaryWriter.cpp @@ -102,7 +102,7 @@ auto BinaryWriter::writeDouble(Double value) -> bool { return std::fwrite(&value, sizeof(Double), 1, _file) == 1; } -auto BinaryWriter::writeArray(Containers::ArrayView array) -> bool { +auto BinaryWriter::writeArray(Containers::ArrayView array) -> bool { if(array.size() == 0) { return false; } diff --git a/src/UESaveFile/BinaryWriter.h b/src/UESaveFile/BinaryWriter.h index 0d7a726..18013da 100644 --- a/src/UESaveFile/BinaryWriter.h +++ b/src/UESaveFile/BinaryWriter.h @@ -59,7 +59,11 @@ class BinaryWriter { auto writeUnsignedLong(UnsignedLong value) -> bool; auto writeFloat(Float value) -> bool; auto writeDouble(Double value) -> bool; - auto writeArray(Containers::ArrayView array) -> bool; + auto writeArray(Containers::ArrayView array) -> bool; + template + auto writeString(const char(&str)[size]) -> bool { + return writeArray({str, size - 1}); + } template auto writeStaticArray(Containers::StaticArrayView array) -> bool { -- 2.39.2 From 4000421a8c1dd0986d9511dbe174cd6aa3935966 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sun, 13 Feb 2022 10:31:55 +0100 Subject: [PATCH 111/128] Mass,SaveTool: refactor even more code. --- src/CMakeLists.txt | 2 +- src/Maps/ArmourSlots.h | 61 ---------------------------- src/Maps/ArmourSlots.hpp | 56 +++++++++++++++++++++++++ src/Mass/ArmourPart.h | 43 +++++++++++++++++++- src/Mass/Mass.cpp | 22 ++++++++-- src/SaveTool/SaveTool_MassViewer.cpp | 15 ++++--- 6 files changed, 128 insertions(+), 71 deletions(-) delete mode 100644 src/Maps/ArmourSlots.h create mode 100644 src/Maps/ArmourSlots.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e6bb24e..bb05b35 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -136,7 +136,7 @@ add_executable(MassBuilderSaveTool WIN32 Mass/WeaponPart.h Maps/Accessories.h Maps/ArmourSets.h - Maps/ArmourSlots.h + Maps/ArmourSlots.hpp Maps/DamageTypes.hpp Maps/LastMissionId.h Maps/StoryProgress.h diff --git a/src/Maps/ArmourSlots.h b/src/Maps/ArmourSlots.h deleted file mode 100644 index 629959a..0000000 --- a/src/Maps/ArmourSlots.h +++ /dev/null @@ -1,61 +0,0 @@ -#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 . - -#include -#include - -static const std::unordered_map 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"}, -}; diff --git a/src/Maps/ArmourSlots.hpp b/src/Maps/ArmourSlots.hpp new file mode 100644 index 0000000..3004a65 --- /dev/null +++ b/src/Maps/ArmourSlots.hpp @@ -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 . + +#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 diff --git a/src/Mass/ArmourPart.h b/src/Mass/ArmourPart.h index 833f768..4374764 100644 --- a/src/Mass/ArmourPart.h +++ b/src/Mass/ArmourPart.h @@ -28,8 +28,49 @@ 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 { - std::string slot; + ArmourSlot slot = ArmourSlot::Face; Int id = 0; Containers::StaticArray<4, Int> styles{ValueInit}; UnsignedInt demoDecals = 8; diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 21f9a0e..9c8a18a 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -583,7 +583,15 @@ void Mass::getArmourParts() { auto part_prop = armour_array->at(i); auto& part = _armour.parts[i]; - part.slot = part_prop->at("Slot_3_408BA56F4C9605C7E805CF91B642249C")->enumValue; + auto& armour_slot = part_prop->at("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("ID_5_ACD101864D3481DE96EDACACC09BDD25")->value; auto part_styles = part_prop->at("Styles_47_3E31870441DFD7DB8BEE5C85C26B365B"); @@ -644,8 +652,16 @@ auto Mass::writeArmourPart(UnsignedLong index) -> bool { auto& part = _armour.parts[index]; - if(part_prop->at("Slot_3_408BA56F4C9605C7E805CF91B642249C")->enumValue != part.slot) { - return false; + auto& armour_slot = part_prop->at("Slot_3_408BA56F4C9605C7E805CF91B642249C")->enumValue; + switch(part.slot) { + #define c(enumerator, strenum, name) case ArmourSlot::enumerator: \ + if((strenum) != armour_slot) { \ + _lastError = "Armour part slot doesn't match save file."; \ + return false; \ + } \ + break; + #include "../Maps/ArmourSlots.hpp" + #undef c } part_prop->at("ID_5_ACD101864D3481DE96EDACACC09BDD25")->value = part.id; diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index ffaa054..942c68c 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -20,7 +20,6 @@ #include "../Maps/Accessories.h" #include "../Maps/ArmourSets.h" -#include "../Maps/ArmourSlots.h" #include "../Maps/StyleNames.h" #include "../FontAwesome/IconsFontAwesome5.h" @@ -458,6 +457,12 @@ void SaveTool::drawArmour() { 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); @@ -468,10 +473,10 @@ void SaveTool::drawArmour() { std::memset(header, '\0', 129); if(armour_sets.find(part.id) != armour_sets.cend()) { - std::snprintf(header, 128, "%s: %s###%s", armour_slots.at(part.slot), armour_sets.at(part.id).name, part.slot.c_str()); + 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###%s", armour_slots.at(part.slot), part.id, part.slot.c_str()); + std::snprintf(header, 128, "%s: %i###%u", slot_labels[UnsignedInt(part.slot)], part.id, UnsignedInt(part.slot)); } if(ImGui::CollapsingHeader(header)) { @@ -479,7 +484,7 @@ void SaveTool::drawArmour() { ImGui::SetNextItemWidth(ImGui::GetContentRegionAvailWidth() * 0.491f); if(ImGui::BeginListBox("##ChangePart")) { - if(std::strncmp("Neck", armour_slots.at(part.slot), 4) != 0) { + 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; @@ -862,7 +867,7 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { } ImGui::AlignTextToFramePadding(); - const char* labels[] { + static const char* labels[] { #define c(enumerator, strenum, name) name, #include "../Maps/WeaponTypes.hpp" #undef c -- 2.39.2 From 2ff32c4c78d6b66285c40aa3db133c6c8b8329ce Mon Sep 17 00:00:00 2001 From: William JCM Date: Sun, 13 Feb 2022 15:02:08 +0100 Subject: [PATCH 112/128] Add a CRC32 algorithm. --- src/CMakeLists.txt | 1 + src/Utilities/Crc32.h | 62 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 src/Utilities/Crc32.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bb05b35..0afc71d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -144,6 +144,7 @@ add_executable(MassBuilderSaveTool WIN32 Maps/WeaponTypes.hpp ToastQueue/ToastQueue.h ToastQueue/ToastQueue.cpp + Utilities/Crc32.h FontAwesome/IconsFontAwesome5.h FontAwesome/IconsFontAwesome5Brands.h resource.rc diff --git a/src/Utilities/Crc32.h b/src/Utilities/Crc32.h new file mode 100644 index 0000000..50d037f --- /dev/null +++ b/src/Utilities/Crc32.h @@ -0,0 +1,62 @@ +#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 . + +#include +#include + +#include + +using namespace Corrade; +using namespace Magnum; + +struct Crc32 { + static const Containers::StaticArray<256, UnsignedInt> table; + + static auto update(UnsignedInt initial, Containers::ArrayView data) -> UnsignedInt { + UnsignedInt c = initial ^ 0xFFFFFFFF; + auto u = Containers::arrayCast(data); + + for(std::size_t i = 0; i < data.size(); ++i) { + c = table[(c ^ u[i]) & 0xFF] ^ (c >> 8); + } + + return c ^ 0xFFFFFFFF; + } +}; + +const Containers::StaticArray<256, UnsignedInt> Crc32::table = []{ + UnsignedInt polynomial = 0xEDB88320; + Containers::StaticArray<256, UnsignedInt> temp{ValueInit}; + + for(UnsignedInt i = 0; i < 256; i++) { + UnsignedInt c = i; + + for(std::size_t j = 0; j < 8; j++) { + if(c & 1) { + c = polynomial ^ (c >> 1); + } + else { + c >>= 1; + } + } + + temp[i] = c; + } + + return temp; +}(); -- 2.39.2 From 5e06c484922e102ad51b41861e3f23062841738f Mon Sep 17 00:00:00 2001 From: William JCM Date: Sun, 13 Feb 2022 15:03:45 +0100 Subject: [PATCH 113/128] BinaryWriter: fix an issue with writeValueToArray(). The view needs to be of type T, not U (which can potentially be T&). --- src/UESaveFile/BinaryWriter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/UESaveFile/BinaryWriter.h b/src/UESaveFile/BinaryWriter.h index 18013da..1d12acd 100644 --- a/src/UESaveFile/BinaryWriter.h +++ b/src/UESaveFile/BinaryWriter.h @@ -74,7 +74,7 @@ class BinaryWriter { template::value, T, T&>> auto writeValueToArray(U value) -> UnsignedLong { - Containers::ArrayView view{&value, 1}; + Containers::ArrayView view{&value, 1}; return writeDataToArray(view); } -- 2.39.2 From 76210e147aedef4455fc48339c044020557563d6 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sun, 13 Feb 2022 15:09:07 +0100 Subject: [PATCH 114/128] BinaryWriter: add a way to access the temp array. That way, I'll be able to easily compute the CRC32 of it. --- src/UESaveFile/BinaryWriter.cpp | 4 ++++ src/UESaveFile/BinaryWriter.h | 1 + 2 files changed, 5 insertions(+) diff --git a/src/UESaveFile/BinaryWriter.cpp b/src/UESaveFile/BinaryWriter.cpp index 9cb991e..64e6b24 100644 --- a/src/UESaveFile/BinaryWriter.cpp +++ b/src/UESaveFile/BinaryWriter.cpp @@ -46,6 +46,10 @@ auto BinaryWriter::position() -> Long { return _ftelli64(_file); } +auto BinaryWriter::array() const -> Containers::ArrayView { + return _data; +} + auto BinaryWriter::arrayPosition() const -> UnsignedLong { return _index; } diff --git a/src/UESaveFile/BinaryWriter.h b/src/UESaveFile/BinaryWriter.h index 1d12acd..ac73f1a 100644 --- a/src/UESaveFile/BinaryWriter.h +++ b/src/UESaveFile/BinaryWriter.h @@ -45,6 +45,7 @@ class BinaryWriter { auto position() -> Long; + auto array() const -> Containers::ArrayView; auto arrayPosition() const -> UnsignedLong; auto flushToFile() -> bool; -- 2.39.2 From 975f471a6814c1f7e87a0c92d2c28e0a51c8c8cd Mon Sep 17 00:00:00 2001 From: William JCM Date: Mon, 14 Feb 2022 09:31:20 +0100 Subject: [PATCH 115/128] BinaryReader: add a way to seek into the file. --- src/UESaveFile/BinaryReader.cpp | 4 ++++ src/UESaveFile/BinaryReader.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/UESaveFile/BinaryReader.cpp b/src/UESaveFile/BinaryReader.cpp index 2a4eeac..a34486e 100644 --- a/src/UESaveFile/BinaryReader.cpp +++ b/src/UESaveFile/BinaryReader.cpp @@ -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; diff --git a/src/UESaveFile/BinaryReader.h b/src/UESaveFile/BinaryReader.h index 881d1fd..253d14f 100644 --- a/src/UESaveFile/BinaryReader.h +++ b/src/UESaveFile/BinaryReader.h @@ -36,6 +36,8 @@ class BinaryReader { auto eof() -> bool; auto position() -> Long; + auto seek(Long position) -> bool; + void closeFile(); auto readChar(char& value) -> bool; -- 2.39.2 From 7cb9ea28b270e49ba7563a99d305942c3e01fd2d Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 16 Feb 2022 11:47:49 +0100 Subject: [PATCH 116/128] BinaryReader: add a way to read arbitrary types. --- src/UESaveFile/BinaryReader.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/UESaveFile/BinaryReader.h b/src/UESaveFile/BinaryReader.h index 253d14f..5a11280 100644 --- a/src/UESaveFile/BinaryReader.h +++ b/src/UESaveFile/BinaryReader.h @@ -53,6 +53,11 @@ class BinaryReader { auto readDouble(Double& value) -> bool; auto readArray(Containers::Array& array, std::size_t count) -> bool; + template + auto readValue(T& value) -> bool { + return fread(&value, sizeof(T), 1, _file) == sizeof(T); + } + template auto readStaticArray(Containers::StaticArray& array) -> bool { return std::fread(array.data(), sizeof(char), S, _file) == S; -- 2.39.2 From 955ec010b8e75f6ae64fb26aed7e4ec801bba813 Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 16 Feb 2022 11:48:39 +0100 Subject: [PATCH 117/128] Crc32: make the polynomial literal explicitly unsigned. --- src/Utilities/Crc32.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utilities/Crc32.h b/src/Utilities/Crc32.h index 50d037f..e6babd6 100644 --- a/src/Utilities/Crc32.h +++ b/src/Utilities/Crc32.h @@ -40,7 +40,7 @@ struct Crc32 { }; const Containers::StaticArray<256, UnsignedInt> Crc32::table = []{ - UnsignedInt polynomial = 0xEDB88320; + UnsignedInt polynomial = 0xEDB88320u; Containers::StaticArray<256, UnsignedInt> temp{ValueInit}; for(UnsignedInt i = 0; i < 256; i++) { -- 2.39.2 From 88abf91047ef96935204e0cd4990096fcd66cb33 Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 17 Feb 2022 20:01:59 +0100 Subject: [PATCH 118/128] Mass,SaveTool: add some future-proofing. --- src/Mass/ArmourPart.h | 6 +- src/Mass/Mass.cpp | 292 ++++++++++++--------------- src/Mass/Mass.h | 10 +- src/Mass/WeaponPart.h | 37 +++- src/SaveTool/SaveTool_MassViewer.cpp | 12 +- 5 files changed, 176 insertions(+), 181 deletions(-) diff --git a/src/Mass/ArmourPart.h b/src/Mass/ArmourPart.h index 4374764..7246b89 100644 --- a/src/Mass/ArmourPart.h +++ b/src/Mass/ArmourPart.h @@ -18,6 +18,7 @@ #include +#include #include #include @@ -73,7 +74,6 @@ struct ArmourPart { ArmourSlot slot = ArmourSlot::Face; Int id = 0; Containers::StaticArray<4, Int> styles{ValueInit}; - UnsignedInt demoDecals = 8; - Containers::StaticArray<8, Decal> decals{ValueInit}; - Containers::StaticArray<8, Accessory> accessories{ValueInit}; + Containers::Array decals; + Containers::Array accessories; }; diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 9c8a18a..3cc7bd4 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -174,12 +174,9 @@ void Mass::refreshValues() { return; } - if(!_demo) - { - getGlobalStyles(); - if(_state == State::Invalid) { - return; - } + getGlobalStyles(); + if(_state == State::Invalid) { + return; } getTuning(); @@ -232,10 +229,6 @@ auto Mass::state() -> State { return _state; } -auto Mass::demo() const -> bool { - return _demo; -} - auto Mass::dirty() const -> bool { return _dirty; } @@ -615,55 +608,53 @@ void Mass::getArmourParts() { return; } - if(decals_array->items.size() != part.decals.size()) { - part.demoDecals = decals_array->items.size(); - _demo = true; - } + part.decals = Containers::Array{decals_array->items.size()}; getDecals(part.decals, decals_array); - if(!_demo) { - auto accs_array = part_prop->at("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("Accessories_52_D902DD4241FA0050C2529596255153F3"); + if(!accs_array) { + part.accessories = Containers::Array{}; + continue; } + + if(part.accessories.size() != accs_array->items.size()) { + part.accessories = Containers::Array{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("UnitData"); auto armour_array = unit_data->at("Armor_10_12E266C44116DDAF57E99ABB575A4B3C"); - auto part_prop = armour_array->at(index); - - auto& part = _armour.parts[index]; - - auto& armour_slot = part_prop->at("Slot_3_408BA56F4C9605C7E805CF91B642249C")->enumValue; - switch(part.slot) { - #define c(enumerator, strenum, name) case ArmourSlot::enumerator: \ - if((strenum) != armour_slot) { \ - _lastError = "Armour part slot doesn't match save file."; \ - return false; \ - } \ + 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 } + GenericStructProperty* part_prop = nullptr; + + for(UnsignedInt i = 0; i < armour_array->items.size(); i++) { + part_prop = armour_array->at(i); + if(slot_str != part_prop->at("Slot_3_408BA56F4C9605C7E805CF91B642249C")->value) { + part_prop = nullptr; + } + } + + if(!part_prop) { + return false; + } + part_prop->at("ID_5_ACD101864D3481DE96EDACACC09BDD25")->value = part.id; auto part_styles = part_prop->at("Styles_47_3E31870441DFD7DB8BEE5C85C26B365B"); @@ -672,72 +663,11 @@ auto Mass::writeArmourPart(UnsignedLong index) -> bool { } auto decals_array = part_prop->at("Decals_42_F358794A4F18497970F56BA9627D3603"); - for(UnsignedInt i = 0; i < decals_array->items.size(); i++) { - auto decal_prop = decals_array->at(i); - auto& decal = part.decals[i]; + writeDecals(part.decals, decals_array); - decal_prop->at("ID_3_694C0B35404D8A3168AEC89026BC8CF9")->value = decal.id; - auto colour_prop = decal_prop->at("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("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("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("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("Offset_29_B02BBBB74FC60F5EDBEBAB8020738020"); - offset_prop->x = decal.offset.x(); - offset_prop->y = decal.offset.y(); - decal_prop->at("Scale_32_959D1C2747AFD8D62808468235CBBA40")->value = decal.scale; - decal_prop->at("Rotation_27_12D7C314493D203D5C2326A03C5F910F")->value = decal.rotation; - decal_prop->at("Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1")->value = decal.flip; - decal_prop->at("Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62")->value = decal.wrap; - } - - if(!_demo) { + if(part.accessories.size() != 0) { auto accs_array = part_prop->at("Accessories_52_D902DD4241FA0050C2529596255153F3"); - - for(UnsignedInt i = 0; i < accs_array->items.size(); i++) { - auto acc_prop = accs_array->at(i); - - auto& accessory = part.accessories[i]; - - acc_prop->at("AttachIndex_2_4AFCF6024E4BA7426C6B9F80B8179D20")->value = accessory.attachIndex; - acc_prop->at("ID_4_5757B32647BAE263266259B8A7DFFFC1")->value = accessory.id; - auto acc_styles = acc_prop->at("Styles_7_91DEB0F24E24D13FC9472882C11D0DFD"); - for(UnsignedInt j = 0; j < acc_styles->items.size(); j++) { - acc_styles->at(j)->value = accessory.styles[j]; - } - auto rel_pos_prop = acc_prop->at("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("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("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("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("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(); @@ -873,12 +803,12 @@ void Mass::getGlobalStyles() { auto global_styles = unit_data->at("GlobalStyles_57_6A681C114035241F7BDAAE9B43A8BF1B"); if(!global_styles) { - _state = State::Invalid; + _globalStyles = Containers::Array{0}; return; } if(global_styles->items.size() != _globalStyles.size()) { - _state = State::Invalid; + _globalStyles = Containers::Array{global_styles->items.size()}; return; } @@ -946,6 +876,10 @@ auto Mass::techs() -> Containers::ArrayView { return _tuning.techIds; } +auto Mass::account() -> const std::string& { + return _account; +} + auto Mass::updateAccount(const std::string& new_account) -> bool { _account = new_account; @@ -1032,6 +966,39 @@ void Mass::getDecals(Containers::ArrayView decals, ArrayProperty* decal_a } } +void Mass::writeDecals(Containers::ArrayView decals, ArrayProperty* decal_array) { + for(UnsignedInt i = 0; i < decal_array->items.size(); i++) { + auto decal_prop = decal_array->at(i); + auto& decal = decals[i]; + + decal_prop->at("ID_3_694C0B35404D8A3168AEC89026BC8CF9")->value = decal.id; + auto colour_prop = decal_prop->at("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("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("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("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("Offset_29_B02BBBB74FC60F5EDBEBAB8020738020"); + offset_prop->x = decal.offset.x(); + offset_prop->y = decal.offset.y(); + decal_prop->at("Scale_32_959D1C2747AFD8D62808468235CBBA40")->value = decal.scale; + decal_prop->at("Rotation_27_12D7C314493D203D5C2326A03C5F910F")->value = decal.rotation; + decal_prop->at("Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1")->value = decal.flip; + decal_prop->at("Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62")->value = decal.wrap; + } +} + void Mass::getAccessories(Containers::ArrayView accessories, ArrayProperty* accessory_array) { for(UnsignedInt i = 0; i < accessory_array->items.size(); i++) { auto acc_prop = accessory_array->at(i); @@ -1056,6 +1023,40 @@ void Mass::getAccessories(Containers::ArrayView accessories, ArrayPro } } +void Mass::writeAccessories(Containers::ArrayView accessories, ArrayProperty* accs_array) { + for(UnsignedInt i = 0; i < accs_array->items.size(); i++) { + auto acc_prop = accs_array->at(i); + auto& accessory = accessories[i]; + + acc_prop->at("AttachIndex_2_4AFCF6024E4BA7426C6B9F80B8179D20")->value = accessory.attachIndex; + acc_prop->at("ID_4_5757B32647BAE263266259B8A7DFFFC1")->value = accessory.id; + auto acc_styles = acc_prop->at("Styles_7_91DEB0F24E24D13FC9472882C11D0DFD"); + for(UnsignedInt j = 0; j < acc_styles->items.size(); j++) { + acc_styles->at(j)->value = accessory.styles[j]; + } + auto rel_pos_prop = acc_prop->at("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("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("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("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("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_array) { auto unit_data = _mass->at("UnitData"); if(!unit_data) { @@ -1104,26 +1105,21 @@ void Mass::getWeaponType(const char* prop_name, Containers::ArrayView we auto part_decals = part_prop->at("Decals_13_8B81112B453D7230C0CDE982185E14F1"); if(part_decals->items.size() != part.decals.size()) { - _demo = true; - part.demoDecals = part_decals->items.size(); + part.decals = Containers::Array{part_decals->items.size()}; } getDecals(part.decals, part_decals); - if(!_demo) { - auto part_accs = part_prop->at("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("Accessories_21_3878DE8B4ED0EA0DB725E98BCDC20E0C"); + if(!part_accs) { + part.accessories = Containers::Array{0}; + continue; } + + if(part_accs->items.size() != part.accessories.size()) { + part.accessories = Containers::Array{part_accs->items.size()}; + } + getAccessories(part.accessories, part_accs); } auto custom_styles = weapon_prop->at("Styles_10_8C3C82444B986AD7A99595AD4985912D"); @@ -1204,51 +1200,19 @@ auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView } auto part_decals = part_prop->at("Decals_13_8B81112B453D7230C0CDE982185E14F1"); - for(UnsignedInt k = 0; k < part_decals->items.size(); k++) { - auto decal_prop = part_decals->at(k); - auto& decal = part.decals[k]; + writeDecals(part.decals, part_decals); - decal_prop->at("ID_3_694C0B35404D8A3168AEC89026BC8CF9")->value = decal.id; - auto colour_prop = decal_prop->at("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("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("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("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("Offset_29_B02BBBB74FC60F5EDBEBAB8020738020"); - offset_prop->x = decal.offset.x(); - offset_prop->y = decal.offset.y(); - decal_prop->at("Scale_32_959D1C2747AFD8D62808468235CBBA40")->value = decal.scale; - decal_prop->at("Rotation_27_12D7C314493D203D5C2326A03C5F910F")->value = decal.rotation; - decal_prop->at("Flip_35_CECCFB184CCD9412BD93FE9A8B656BE1")->value = decal.flip; - decal_prop->at("Wrap_43_A7C68CDF4A92AF2ECDA53F953EE7CA62")->value = decal.wrap; + auto part_accs = part_prop->at("Accessories_21_3878DE8B4ED0EA0DB725E98BCDC20E0C"); + if(!part_accs) { + continue; } - if(!_demo) { - auto part_accs = part_prop->at("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; + return false; } + + writeAccessories(part.accessories, part_accs); } auto custom_styles = weapon_prop->at("Styles_10_8C3C82444B986AD7A99595AD4985912D"); diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index c89c9f3..f46a4e9 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -69,8 +69,6 @@ class Mass { auto state() -> State; - auto demo() const -> bool; - auto dirty() const -> bool; void setDirty(bool dirty = true); @@ -92,7 +90,7 @@ class Mass { auto armourParts() -> Containers::ArrayView; void getArmourParts(); - auto writeArmourPart(UnsignedLong index) -> bool; + auto writeArmourPart(ArmourSlot slot) -> bool; auto armourCustomStyles() -> Containers::ArrayView; void getArmourCustomStyles(); @@ -145,7 +143,10 @@ class Mass { auto setCustomStyle(const CustomStyle& style, UnsignedLong index, ArrayProperty* style_array) -> bool; void getDecals(Containers::ArrayView decals, ArrayProperty* decal_array); + void writeDecals(Containers::ArrayView decals, ArrayProperty* decal_array); + void getAccessories(Containers::ArrayView accessories, ArrayProperty* accessory_array); + void writeAccessories(Containers::ArrayView accessories, ArrayProperty* accs_array); void getWeaponType(const char* prop_name, Containers::ArrayView weapon_array); auto writeWeaponType(const char* prop_name, Containers::ArrayView weapon_array) -> bool; @@ -160,7 +161,6 @@ class Mass { std::string _folder; std::string _filename; State _state = State::Empty; - bool _demo = false; bool _dirty = false; @@ -191,7 +191,7 @@ class Mass { Containers::StaticArray<4, Weapon> energyLaunchers; } _weapons; - Containers::StaticArray<16, CustomStyle> _globalStyles; + Containers::Array _globalStyles; struct { Int engineId; diff --git a/src/Mass/WeaponPart.h b/src/Mass/WeaponPart.h index a6cb1a4..227b5af 100644 --- a/src/Mass/WeaponPart.h +++ b/src/Mass/WeaponPart.h @@ -16,6 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#include #include #include @@ -27,9 +28,39 @@ using namespace Corrade; using namespace Magnum; struct WeaponPart { + WeaponPart() = default; + + WeaponPart(const WeaponPart& other) { + id = other.id; + styles = other.styles; + decals = Containers::Array{other.decals.size()}; + for(UnsignedInt i = 0; i < decals.size(); i++) { + decals[i] = other.decals[i]; + } + accessories = Containers::Array{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{other.decals.size()}; + for(UnsignedInt i = 0; i < decals.size(); i++) { + decals[i] = other.decals[i]; + } + accessories = Containers::Array{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}; - UnsignedInt demoDecals = 8; - Containers::StaticArray<8, Decal> decals{ValueInit}; - Containers::StaticArray<8, Accessory> accessories{ValueInit}; + Containers::Array decals{}; + Containers::Array accessories{}; }; diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index 942c68c..a73a866 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -133,7 +133,7 @@ void SaveTool::drawMassViewer() { ImGui::EndTabItem(); } - if(!_currentMass->demo() && ImGui::BeginTabItem("Global styles")) { + if(!_currentMass->globalStyles().size() != 0 && ImGui::BeginTabItem("Global styles")) { drawGlobalStyles(); ImGui::EndTabItem(); } @@ -547,7 +547,7 @@ void SaveTool::drawArmour() { ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Showing/editing decal"); - for(UnsignedLong j = 0; j < part.demoDecals; j++) { + for(UnsignedLong j = 0; j < part.decals.size(); j++) { ImGui::SameLine(); ImGui::RadioButton(std::to_string(j + 1).c_str(), &_selectedArmourDecals[i], j); } @@ -556,7 +556,7 @@ void SaveTool::drawArmour() { ImGui::PopID(); - if(!_currentMass->demo()) { + if(!part.accessories.size()) { ImGui::Separator(); ImGui::PushID("Accessory"); @@ -576,7 +576,7 @@ void SaveTool::drawArmour() { ImGui::Separator(); if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { - _currentMass->writeArmourPart(i); + _currentMass->writeArmourPart(part.slot); } } @@ -1016,7 +1016,7 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { ImGui::AlignTextToFramePadding(); ImGui::TextUnformatted("Showing/editing decal"); - for(UnsignedLong i = 0; i < part.demoDecals; i++) { + for(UnsignedLong i = 0; i < part.decals.size(); i++) { ImGui::SameLine(); ImGui::RadioButton(std::to_string(i + 1).c_str(), &_selectedWeaponDecal, i); } @@ -1025,7 +1025,7 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { ImGui::PopID(); - if(_currentMass->demo() == false) { + if(part.accessories.size() != 0) { ImGui::Separator(); ImGui::PushID("Accessory"); -- 2.39.2 From 82170b30782b0ee996eeea2d568b4c662ac9d0fd Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 17 Feb 2022 20:22:33 +0100 Subject: [PATCH 119/128] Mass,SaveTool: optimise the effect colour mode. --- src/CMakeLists.txt | 1 + src/Maps/EffectColourModes.hpp | 20 ++++++++++++++++++++ src/Mass/Mass.cpp | 17 +++++++++++++++-- src/Mass/Weapon.h | 7 ++++++- src/SaveTool/SaveTool_MassViewer.cpp | 10 +++++----- 5 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 src/Maps/EffectColourModes.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0afc71d..afc8ac7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -138,6 +138,7 @@ add_executable(MassBuilderSaveTool WIN32 Maps/ArmourSets.h Maps/ArmourSlots.hpp Maps/DamageTypes.hpp + Maps/EffectColourModes.hpp Maps/LastMissionId.h Maps/StoryProgress.h Maps/StyleNames.h diff --git a/src/Maps/EffectColourModes.hpp b/src/Maps/EffectColourModes.hpp new file mode 100644 index 0000000..7df0077 --- /dev/null +++ b/src/Maps/EffectColourModes.hpp @@ -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 . + +#ifdef c +c(Default, "enuWeaponEffectColorMode::NewEnumerator0") +c(Custom, "enuWeaponEffectColorMode::NewEnumerator1") +#endif diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 3cc7bd4..84b6b45 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -1145,7 +1145,14 @@ void Mass::getWeaponType(const char* prop_name, Containers::ArrayView we Utility::Warning{} << "Invalid damage type enum value in getWeaponType()."; } weapon.dualWield = weapon_prop->at("DualWield_20_B2EB2CEA4A6A233DC7575996B6DD1222")->value; - weapon.effectColourMode = weapon_prop->at("ColorEfxMode_24_D254BCF943E852BF9ADB8AAA8FD80014")->enumValue; + auto& effect_colour_mode = weapon_prop->at("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("ColorEfx_26_D921B62946C493E487455A831F4520AC"); weapon.effectColour = Color4{effect_colour->r, effect_colour->g, effect_colour->b, effect_colour->a}; } @@ -1239,7 +1246,13 @@ auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView Utility::Warning{} << "Invalid damage type enum value in writeWeaponType()."; } weapon_prop->at("DualWield_20_B2EB2CEA4A6A233DC7575996B6DD1222")->value = weapon.dualWield; - weapon_prop->at("ColorEfxMode_24_D254BCF943E852BF9ADB8AAA8FD80014")->enumValue = weapon.effectColourMode; + switch(weapon.effectColourMode) { + #define c(enumerator, enumstr) case EffectColourMode::enumerator: \ + weapon_prop->at("ColorEfxMode_24_D254BCF943E852BF9ADB8AAA8FD80014")->enumValue = enumstr; \ + break; + #include "../Maps/EffectColourModes.hpp" + #undef c + } auto effect_colour = weapon_prop->at("ColorEfx_26_D921B62946C493E487455A831F4520AC"); effect_colour->r = weapon.effectColour.r(); effect_colour->g = weapon.effectColour.g(); diff --git a/src/Mass/Weapon.h b/src/Mass/Weapon.h index a2c4b00..660de20 100644 --- a/src/Mass/Weapon.h +++ b/src/Mass/Weapon.h @@ -48,6 +48,11 @@ enum class DamageType { Shock = 4, }; +enum class EffectColourMode { + Default = 0, + Custom = 1, +}; + struct Weapon { Weapon() = default; @@ -64,6 +69,6 @@ struct Weapon { bool attached = false; DamageType damageType = DamageType::Physical; bool dualWield = false; - std::string effectColourMode; + EffectColourMode effectColourMode = EffectColourMode::Default; Color4 effectColour{0.0f}; }; diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index a73a866..7dba4e1 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -947,15 +947,15 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { if(weapon.type == WeaponType::Melee) { ImGui::Checkbox("##DualWield", &weapon.dualWield); - if(ImGui::RadioButton("Default##Default", weapon.effectColourMode == "enuWeaponEffectColorMode::NewEnumerator0")) { - weapon.effectColourMode = "enuWeaponEffectColorMode::NewEnumerator0"; + if(ImGui::RadioButton("Default##Default", weapon.effectColourMode == EffectColourMode::Default)) { + weapon.effectColourMode = EffectColourMode::Default; } ImGui::SameLine(); - if(ImGui::RadioButton("Custom##Custom", weapon.effectColourMode == "enuWeaponEffectColorMode::NewEnumerator1")) { - weapon.effectColourMode = "enuWeaponEffectColorMode::NewEnumerator1"; + if(ImGui::RadioButton("Custom##Custom", weapon.effectColourMode == EffectColourMode::Custom)) { + weapon.effectColourMode = EffectColourMode::Custom; } - bool custom_effect = (weapon.effectColourMode == "enuWeaponEffectColorMode::NewEnumerator1"); + bool custom_effect = (weapon.effectColourMode == EffectColourMode::Custom); if(!custom_effect) { ImGui::BeginDisabled(); } -- 2.39.2 From 77d7eaefad70de134bb3bb4491fa7a224b566433 Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 23 Feb 2022 10:18:58 +0100 Subject: [PATCH 120/128] SaveTool: fix a condition. --- src/SaveTool/SaveTool_MassViewer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index 7dba4e1..2bd8730 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -133,7 +133,7 @@ void SaveTool::drawMassViewer() { ImGui::EndTabItem(); } - if(!_currentMass->globalStyles().size() != 0 && ImGui::BeginTabItem("Global styles")) { + if(_currentMass->globalStyles().size() != 0 && ImGui::BeginTabItem("Global styles")) { drawGlobalStyles(); ImGui::EndTabItem(); } -- 2.39.2 From 883d5d3f417bdf434eb0373ca3c8b16247a03998 Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 23 Feb 2022 15:47:28 +0100 Subject: [PATCH 121/128] Mass,SaveTool: improve error handling. --- src/Mass/Mass.cpp | 79 ++++++++++++++++++++----- src/Mass/Mass.h | 4 +- src/SaveTool/SaveTool_MassViewer.cpp | 87 ++++++++++++++++++++-------- 3 files changed, 130 insertions(+), 40 deletions(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 84b6b45..7b81c68 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -35,8 +35,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 +48,28 @@ auto Mass::lastError() -> std::string const& { auto Mass::getNameFromFile(const std::string& path) -> Containers::Optional { 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("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("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; } @@ -378,7 +376,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; } @@ -445,7 +443,7 @@ auto Mass::writeFrameStyles() -> bool { } if(!_mass->saveToFile()) { - _lastError = "Couldn't write data to " + _filename; + _lastError = _mass->lastError(); return false; } @@ -482,18 +480,21 @@ auto Mass::writeEyeFlareColour() -> bool { auto unit_data = _mass->at("UnitData"); if(!unit_data) { _state = State::Invalid; + _lastError = "No unit data in " + _filename; return false; } auto frame = unit_data->at("Frame_3_F92B0F6A44A15088AF7F41B9FF290653"); if(!frame) { _state = State::Invalid; + _lastError = "No frame data in " + _filename; return false; } auto eye_flare_prop = frame->at("EyeFlareColor_36_AF79999C40FCA0E88A2F9A84488A38CA"); if(!eye_flare_prop) { _state = State::Invalid; + _lastError = "No eye flare property in " + _filename; return false; } @@ -502,7 +503,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 { @@ -532,18 +538,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("UnitData"); if(!unit_data) { _state = State::Invalid; + _lastError = "No unit data in " + _filename; return false; } auto frame_styles = unit_data->at("FrameStyle_44_04A44C9440363CCEC5443D98BFAF22AA"); if(!frame_styles) { _state = State::Invalid; + _lastError = "No frame styles in " + _filename; return false; } @@ -652,6 +661,14 @@ auto Mass::writeArmourPart(ArmourSlot slot) -> bool { } 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; } @@ -670,7 +687,12 @@ auto Mass::writeArmourPart(ArmourSlot slot) -> bool { writeAccessories(part.accessories, accs_array); } - return _mass->saveToFile(); + if(!_mass->saveToFile()) { + _lastError = _mass->lastError(); + return false; + } + + return true; } auto Mass::armourCustomStyles() -> Containers::ArrayView { @@ -700,17 +722,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("UnitData"); if(!unit_data) { _state = State::Invalid; + _lastError = "Couldn't find unit data in " + _filename; return false; } auto armour_styles = unit_data->at("ArmorStyle_42_E2F6AC3647788CB366BD469B3B7E899E"); if(!armour_styles) { + _lastError = "Couldn't find armour custom styles in " + _filename; _state = State::Invalid; return false; } @@ -817,18 +842,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("UnitData"); if(!unit_data) { _state = State::Invalid; + _lastError = "No unit data found in " + _filename; return false; } auto global_styles = unit_data->at("GlobalStyles_57_6A681C114035241F7BDAAE9B43A8BF1B"); if(!global_styles) { _state = State::Invalid; + _lastError = "No global styles found in " + _filename; return false; } @@ -919,11 +947,17 @@ void Mass::getCustomStyles(Containers::ArrayView 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(index); + if(!style_prop) { + _lastError = "Style index is out of range in " + _filename; + return false; + } + style_prop->at("Name_27_1532115A46EF2B2FA283908DF561A86B")->value = style.name; auto colour_prop = style_prop->at("Color_5_F0D383DF40474C9464AE48A0984A212E"); colour_prop->r = style.colour.r(); @@ -940,7 +974,12 @@ auto Mass::setCustomStyle(const CustomStyle& style, UnsignedLong index, ArrayPro style_prop->at("Rotation_25_EC2DFAD84AD0A6BD3FA841ACD52EDD6D")->value = style.rotation; style_prop->at("Scale_26_19DF0708409262183E1247B317137671")->value = style.scale; - return _mass->saveToFile(); + if(!_mass->saveToFile()) { + _lastError = _mass->lastError(); + return false; + } + + return true; } void Mass::getDecals(Containers::ArrayView decals, ArrayProperty* decal_array) { @@ -1162,17 +1201,20 @@ auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView auto unit_data = _mass->at("UnitData"); if(!unit_data) { _state = State::Invalid; + _lastError = "No unit data in " + _filename; return false; } auto prop = unit_data->at(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; } @@ -1192,6 +1234,7 @@ auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView auto parts_prop = weapon_prop->at("Element_6_8E4617CC4B2C1F1490435599784EC6E0"); if(parts_prop->items.size() != weapon.parts.size()) { _state = State::Invalid; + _lastError = "Weapon parts array size mismatch."; return false; } @@ -1216,6 +1259,7 @@ auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView if(part_accs->items.size() != part.accessories.size()) { _state = State::Invalid; + _lastError = "Accessories array size mismatch."; return false; } @@ -1225,11 +1269,13 @@ auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView auto custom_styles = weapon_prop->at("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; } @@ -1243,7 +1289,7 @@ auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView #include "../Maps/DamageTypes.hpp" #undef c default: - Utility::Warning{} << "Invalid damage type enum value in writeWeaponType()."; + Utility::Warning{} << "Unknown damage type enum value in writeWeaponType()."; } weapon_prop->at("DualWield_20_B2EB2CEA4A6A233DC7575996B6DD1222")->value = weapon.dualWield; switch(weapon.effectColourMode) { @@ -1252,6 +1298,8 @@ auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView break; #include "../Maps/EffectColourModes.hpp" #undef c + default: + Utility::Warning{} << "Unknown effect colour mode in writeWeaponType()."; } auto effect_colour = weapon_prop->at("ColorEfx_26_D921B62946C493E487455A831F4520AC"); effect_colour->r = weapon.effectColour.r(); @@ -1260,7 +1308,12 @@ auto Mass::writeWeaponType(const char* prop_name, Containers::ArrayView 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, diff --git a/src/Mass/Mass.h b/src/Mass/Mass.h index f46a4e9..3edfc3c 100644 --- a/src/Mass/Mass.h +++ b/src/Mass/Mass.h @@ -56,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; @@ -156,7 +156,7 @@ class Mass { Containers::Optional _mass; - static std::string _lastError; + std::string _lastError; std::string _folder; std::string _filename; diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index 2bd8730..b19ba9b 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -319,7 +319,7 @@ void SaveTool::drawJointSliders() { else { if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { if(!_currentMass->writeJointSliders()) { - _queue.addToast(Toast::Type::Error, "Error: " + Mass::lastError()); + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } _jointsDirty = false; } @@ -368,7 +368,7 @@ void SaveTool::drawFrameStyles() { else { if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { if(!_currentMass->writeFrameStyles()) { - _queue.addToast(Toast::Type::Error, "Error: " + Mass::lastError()); + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } _stylesDirty = false; } @@ -399,7 +399,7 @@ void SaveTool::drawEyeColourPicker() { else { if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { if(!_currentMass->writeEyeFlareColour()) { - _queue.addToast(Toast::Type::Error, "Error writing the eye flare colour."); + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } _eyeFlareDirty = false; } @@ -432,7 +432,9 @@ void SaveTool::drawCustomFrameStyles() { _currentMass->getFrameCustomStyles(); break; case DCS_Save: - _currentMass->writeFrameCustomStyle(i); + if(!_currentMass->writeFrameCustomStyle(i)) { + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); + } break; default: break; @@ -576,7 +578,9 @@ void SaveTool::drawArmour() { ImGui::Separator(); if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { - _currentMass->writeArmourPart(part.slot); + if(!_currentMass->writeArmourPart(part.slot)) { + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); + } } } @@ -607,7 +611,9 @@ void SaveTool::drawCustomArmourStyles() { _currentMass->getArmourCustomStyles(); break; case DCS_Save: - _currentMass->writeArmourCustomStyle(i); + if(_currentMass->writeArmourCustomStyle(i)) { + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); + } break; default: break; @@ -713,28 +719,57 @@ void SaveTool::drawWeapons() { if(drawUnsafeWidget([]{ return ImGui::Button(ICON_FA_SAVE " Save"); })) { if(_meleeDirty) { - _currentMass->writeMeleeWeapons(); - _meleeDirty = false; + if(!_currentMass->writeMeleeWeapons()) { + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); + } + else { + _meleeDirty = false; + } } + if(_shieldsDirty) { - _currentMass->writeShields(); - _shieldsDirty = false; + if(!_currentMass->writeShields()) { + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); + } + else { + _shieldsDirty = false; + } } + if(_bShootersDirty) { - _currentMass->writeBulletShooters(); - _bShootersDirty = false; + if(!_currentMass->writeBulletShooters()) { + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); + } + else { + _bShootersDirty = false; + } } + if(_eShootersDirty) { - _currentMass->writeEnergyShooters(); - _eShootersDirty = false; + if(_currentMass->writeEnergyShooters()) { + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); + } + else { + _eShootersDirty = false; + } } + if(_bLaunchersDirty) { - _currentMass->writeBulletLaunchers(); - _bLaunchersDirty = false; + if(_currentMass->writeBulletLaunchers()) { + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); + } + else { + _bLaunchersDirty = false; + } } + if(_eLaunchersDirty) { - _currentMass->writeEnergyLaunchers(); - _eLaunchersDirty = false; + if(_currentMass->writeEnergyLaunchers()) { + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); + } + else { + _eLaunchersDirty = false; + } } } @@ -797,32 +832,32 @@ void SaveTool::drawWeapons() { switch(current_weapon->type) { case WeaponType::Melee: if(!_currentMass->writeMeleeWeapons()) { - _queue.addToast(Toast::Type::Error, "Couldn't save melee weapons"); + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } break; case WeaponType::Shield: if(!_currentMass->writeShields()) { - _queue.addToast(Toast::Type::Error, "Couldn't save shields"); + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } break; case WeaponType::BulletShooter: if(!_currentMass->writeBulletShooters()) { - _queue.addToast(Toast::Type::Error, "Couldn't save bullet shooters"); + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } break; case WeaponType::EnergyShooter: if(!_currentMass->writeEnergyShooters()) { - _queue.addToast(Toast::Type::Error, "Couldn't save energy shooters"); + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } break; case WeaponType::BulletLauncher: if(!_currentMass->writeBulletLaunchers()) { - _queue.addToast(Toast::Type::Error, "Couldn't save bullet launchers"); + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } break; case WeaponType::EnergyLauncher: if(!_currentMass->writeEnergyLaunchers()) { - _queue.addToast(Toast::Type::Error, "Couldn't save energy launchers"); + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); } break; default: @@ -1068,7 +1103,9 @@ void SaveTool::drawGlobalStyles() { _currentMass->getGlobalStyles(); break; case DCS_Save: - _currentMass->writeGlobalStyle(i); + if(!_currentMass->writeGlobalStyle(i)) { + _queue.addToast(Toast::Type::Error, _currentMass->lastError()); + } break; default: break; -- 2.39.2 From 350ad59f8eb2b9a76dda6a78063d580065297d54 Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 23 Feb 2022 21:59:00 +0100 Subject: [PATCH 122/128] SaveTool: add a convenience wrapper over ImGui stuff. --- src/SaveTool/SaveTool.h | 6 + src/SaveTool/SaveTool_MainManager.cpp | 7 +- src/SaveTool/SaveTool_MassViewer.cpp | 146 ++++++++----------------- src/SaveTool/SaveTool_drawAbout.cpp | 33 ++---- src/SaveTool/SaveTool_drawMainMenu.cpp | 6 +- 5 files changed, 69 insertions(+), 129 deletions(-) diff --git a/src/SaveTool/SaveTool.h b/src/SaveTool/SaveTool.h index c8e0f83..3530558 100644 --- a/src/SaveTool/SaveTool.h +++ b/src/SaveTool/SaveTool.h @@ -168,6 +168,12 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener } } + template + void drawAlignedText(const char* text, Args... args) { + ImGui::AlignTextToFramePadding(); + ImGui::Text(text, std::forward(args)...); + } + void openUri(const std::string& uri); void checkGameState(); diff --git a/src/SaveTool/SaveTool_MainManager.cpp b/src/SaveTool/SaveTool_MainManager.cpp index 4534c32..b834640 100644 --- a/src/SaveTool/SaveTool_MainManager.cpp +++ b/src/SaveTool/SaveTool_MainManager.cpp @@ -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; diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index b19ba9b..fa4675f 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -218,8 +218,7 @@ void SaveTool::drawJointSliders() { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Neck"); + drawAlignedText("Neck"); ImGui::TableSetColumnIndex(1); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##NeckSlider", &_currentMass->jointSliders().neck, 0.0f, 1.0f)) { @@ -228,8 +227,7 @@ void SaveTool::drawJointSliders() { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Body"); + drawAlignedText("Body"); ImGui::TableSetColumnIndex(1); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##BodySlider", &_currentMass->jointSliders().body, 0.0f, 1.0f)) { @@ -238,8 +236,7 @@ void SaveTool::drawJointSliders() { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Shoulders"); + drawAlignedText("Shoulders"); ImGui::TableSetColumnIndex(1); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##ShouldersSlider", &_currentMass->jointSliders().shoulders, 0.0f, 1.0f)) { @@ -248,8 +245,7 @@ void SaveTool::drawJointSliders() { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Hips"); + drawAlignedText("Hips"); ImGui::TableSetColumnIndex(1); ImGui::SetNextItemWidth(-1.0f); if(ImGui::SliderFloat("##HipsSlider", &_currentMass->jointSliders().hips, 0.0f, 1.0f)) { @@ -258,8 +254,7 @@ void SaveTool::drawJointSliders() { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Arms"); + drawAlignedText("Arms"); ImGui::TableSetColumnIndex(1); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2{2.0f, 1.0f}); if(ImGui::BeginTable("##UpperLowerArmsLayoutTable", 2)) { @@ -283,8 +278,7 @@ void SaveTool::drawJointSliders() { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Legs"); + drawAlignedText("Legs"); ImGui::TableSetColumnIndex(1); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2{2.0f, 1.0f}); if(ImGui::BeginTable("##UpperLowerLegsLayoutTable", 2)) { @@ -337,8 +331,7 @@ void SaveTool::drawFrameStyles() { } for(Int i = 0; i < 4; i++) { - ImGui::AlignTextToFramePadding(); - ImGui::Text("Slot %d:", i + 1); + drawAlignedText("Slot %d:", i + 1); ImGui::SameLine(); @@ -520,8 +513,7 @@ void SaveTool::drawArmour() { ImGui::TextUnformatted("Styles:"); for(Int j = 0; j < 4; j++) { - ImGui::AlignTextToFramePadding(); - ImGui::Text("Slot %d:", j + 1); + drawAlignedText("Slot %d:", j + 1); ImGui::SameLine(); @@ -547,8 +539,7 @@ void SaveTool::drawArmour() { ImGui::PushID("Decal"); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Showing/editing decal"); + drawAlignedText("Showing/editing decal"); for(UnsignedLong j = 0; j < part.decals.size(); j++) { ImGui::SameLine(); ImGui::RadioButton(std::to_string(j + 1).c_str(), &_selectedArmourDecals[i], j); @@ -563,8 +554,7 @@ void SaveTool::drawArmour() { ImGui::PushID("Accessory"); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Showing/editing 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); @@ -901,13 +891,13 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { return; } - ImGui::AlignTextToFramePadding(); static const char* labels[] { #define c(enumerator, strenum, name) name, #include "../Maps/WeaponTypes.hpp" #undef c }; - ImGui::Text("%s: %s", labels[UnsignedInt(weapon.type)], weapon.name.c_str()); + + drawAlignedText("%s: %s", labels[UnsignedInt(weapon.type)], weapon.name.c_str()); ImGui::SameLine(); @@ -924,23 +914,18 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { } ImGui::BeginGroup(); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Equipped:"); + drawAlignedText("Equipped:"); if(weapon.type != WeaponType::Shield) { - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Damage type:"); + drawAlignedText("Damage type:"); } if(weapon.type == WeaponType::Melee) { - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Dual-wield:"); + drawAlignedText("Dual-wield:"); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Custom effect mode:"); + drawAlignedText("Custom effect mode:"); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Custom effect colour:"); + drawAlignedText("Custom effect colour:"); } ImGui::EndGroup(); @@ -1006,8 +991,7 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { ImGui::Separator(); if(ImGui::CollapsingHeader("Weapon parts")) { - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Viewing/editing part:"); + drawAlignedText("Viewing/editing part:"); for(Int i = 0; UnsignedLong(i) < weapon.parts.size(); i++) { if(UnsignedLong(_selectedWeaponPart) >= weapon.parts.size()) { _selectedWeaponPart = 0; @@ -1024,8 +1008,7 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { ImGui::TextUnformatted("Styles:"); for(Int i = 0; i < 4; i++) { - ImGui::AlignTextToFramePadding(); - ImGui::Text("Slot %d:", i + 1); + drawAlignedText("Slot %d:", i + 1); ImGui::SameLine(); @@ -1049,8 +1032,7 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { ImGui::PushID("Decal"); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Showing/editing 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); @@ -1065,8 +1047,7 @@ void SaveTool::drawWeaponEditor(Weapon& weapon) { ImGui::PushID("Accessory"); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Showing/editing 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); @@ -1255,14 +1236,10 @@ auto SaveTool::drawCustomStyle(CustomStyle& style) -> DCSResult { ImGui::TableNextColumn(); ImGui::BeginGroup(); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Colour:"); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Metallic:"); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Gloss:"); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Glow:"); + drawAlignedText("Colour:"); + drawAlignedText("Metallic:"); + drawAlignedText("Gloss:"); + drawAlignedText("Glow:"); ImGui::EndGroup(); ImGui::SameLine(); @@ -1284,25 +1261,18 @@ auto SaveTool::drawCustomStyle(CustomStyle& style) -> DCSResult { ImGui::TableNextColumn(); ImGui::BeginGroup(); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Pattern:"); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Opacity:"); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("X offset:"); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Y offset:"); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Rotation:"); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Scale:"); + drawAlignedText("Pattern:"); + drawAlignedText("Opacity:"); + drawAlignedText("X offset:"); + drawAlignedText("Y offset:"); + drawAlignedText("Rotation:"); + drawAlignedText("Scale:"); ImGui::EndGroup(); ImGui::SameLine(); ImGui::BeginGroup(); - ImGui::AlignTextToFramePadding(); - ImGui::Text("%i", style.patternId); + drawAlignedText("%i", style.patternId); ImGui::PushItemWidth(-FLT_MIN); ImGui::SliderFloat("##SliderOpacity", &style.opacity, 0.0f, 1.0f); ImGui::SliderFloat("##SliderOffsetX", &style.offset.x(), 0.0f, 1.0f); @@ -1340,23 +1310,12 @@ void SaveTool::drawDecalEditor(Decal& decal) { ImGui::TableNextColumn(); ImGui::BeginGroup(); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Colour:"); - - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Offset:"); - - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Rotation:"); - - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Scale:"); - - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Flip X:"); - - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Surface wrap:"); + drawAlignedText("Colour:"); + drawAlignedText("Offset:"); + drawAlignedText("Rotation:"); + drawAlignedText("Scale:"); + drawAlignedText("Flip X:"); + drawAlignedText("Surface wrap:"); ImGui::EndGroup(); ImGui::SameLine(); @@ -1396,14 +1355,9 @@ void SaveTool::drawDecalEditor(Decal& decal) { ImGui::TextUnformatted("Advanced settings. Touch these at your own risk."); ImGui::BeginGroup(); - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("Position:"); - - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("U axis:"); - - ImGui::AlignTextToFramePadding(); - ImGui::TextUnformatted("V axis:"); + drawAlignedText("Position:"); + drawAlignedText("U axis:"); + drawAlignedText("V axis:"); ImGui::EndGroup(); ImGui::SameLine(); @@ -1462,18 +1416,12 @@ void SaveTool::drawAccessoryEditor(Accessory& accessory, Containers::ArrayView(_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); } -- 2.39.2 From a1c17b713829bcdfa7752b0e38b6b95b1d440168 Mon Sep 17 00:00:00 2001 From: William JCM Date: Thu, 24 Feb 2022 14:00:47 +0100 Subject: [PATCH 123/128] Profile(Manager),SaveTool: improve error handling and fix bugs. --- src/Profile/Profile.cpp | 43 ++++++++++++----- src/Profile/Profile.h | 4 +- src/ProfileManager/ProfileManager.cpp | 2 +- src/SaveTool/SaveTool_MainManager.cpp | 69 +++++++++++++-------------- 4 files changed, 67 insertions(+), 51 deletions(-) diff --git a/src/Profile/Profile.cpp b/src/Profile/Profile.cpp index 16a655b..d7ba426 100644 --- a/src/Profile/Profile.cpp +++ b/src/Profile/Profile.cpp @@ -45,7 +45,13 @@ Profile::Profile(const std::string& path): _type = ProfileType::FullGame; } - _account = Utility::String::ltrim(Utility::String::rtrim(_filename, ".sav"), (_type == ProfileType::Demo ? "Demo" : "") + std::string{"Profile"}); + auto account_prop = _profile.at("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; @@ -55,8 +61,6 @@ Profile::Profile(const std::string& path): } refreshValues(); - - _valid = _profile.valid(); } auto Profile::valid() const -> bool { @@ -90,7 +94,13 @@ void Profile::refreshValues() { return; } - _name = _profile.at("CompanyName")->value; + auto name_prop = _profile.at("CompanyName"); + if(!name_prop) { + _lastError = "No company name in " + _filename; + _valid = false; + return; + } + _name = name_prop->value; auto prop = _profile.at("ActiveFrameSlot"); _activeFrameSlot = prop ? prop->value : 0; @@ -127,6 +137,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& { @@ -135,11 +147,16 @@ auto Profile::companyName() const -> std::string const& { auto Profile::renameCompany(const std::string& new_name) -> bool { auto name_prop = _profile.at("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; } @@ -159,13 +176,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; } @@ -181,13 +199,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; } @@ -358,7 +377,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(container); if(!mats_prop) { @@ -374,14 +393,16 @@ auto Profile::getResource(const char* container, Int id) -> Int { return it != mats_prop->items.end() ? static_cast(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(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(prop.get()); return res_prop->id == id; }; @@ -406,5 +427,5 @@ auto Profile::setResource(const char* container, Int id, Int amount) -> bool { return false; } - return _profile.saveToFile(); + return true; } diff --git a/src/Profile/Profile.h b/src/Profile/Profile.h index af361e3..d68bd44 100644 --- a/src/Profile/Profile.h +++ b/src/Profile/Profile.h @@ -126,8 +126,8 @@ 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 _filename; diff --git a/src/ProfileManager/ProfileManager.cpp b/src/ProfileManager/ProfileManager.cpp index db1f289..38d69cf 100644 --- a/src/ProfileManager/ProfileManager.cpp +++ b/src/ProfileManager/ProfileManager.cpp @@ -76,7 +76,7 @@ auto ProfileManager::refreshProfiles() -> bool { } if(_profiles.empty()) { - _lastError = "No profiles were found."; + _lastError = "No valid profiles were found."; return false; } diff --git a/src/SaveTool/SaveTool_MainManager.cpp b/src/SaveTool/SaveTool_MainManager.cpp index b834640..8dc5e2f 100644 --- a/src/SaveTool/SaveTool_MainManager.cpp +++ b/src/SaveTool/SaveTool_MainManager.cpp @@ -225,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()); } } @@ -243,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()); } } @@ -263,8 +261,7 @@ void SaveTool::drawGeneralInfo() { 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()); } } } @@ -272,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(); @@ -300,34 +296,33 @@ void SaveTool::drawResearchInventory() { 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"); \ + #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(ImGui::SmallButton, ICON_FA_EDIT)) { \ + (var) = _currentProfile->getter(); \ + ImGui::OpenPopup("int_edit"); \ + } \ + if(drawIntEditPopup(&(var), 9999)) { \ + if(!_currentProfile->set##setter((var))) { \ + _queue.addToast(Toast::Type::Error, _currentProfile->lastError()); \ + } \ + } \ + ImGui::PopID(); \ + } \ + } \ + else { \ + ImGui::TextDisabled("Not found in the save file"); \ } if(ImGui::BeginTable("##ResearchInventoryTable", 4, @@ -454,7 +449,7 @@ void SaveTool::drawMassManager() { _massManager->lastError().c_str(), window()); } } - 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()", -- 2.39.2 From d0ddc73852796c827b92bb9880851921b86f9d06 Mon Sep 17 00:00:00 2001 From: William JCM Date: Fri, 25 Feb 2022 21:00:32 +0100 Subject: [PATCH 124/128] Profile: fix a compile error. --- src/Profile/Profile.cpp | 2 -- src/Profile/Profile.h | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Profile/Profile.cpp b/src/Profile/Profile.cpp index d7ba426..6913153 100644 --- a/src/Profile/Profile.cpp +++ b/src/Profile/Profile.cpp @@ -27,8 +27,6 @@ #include "../UESaveFile/Types/IntProperty.h" #include "../UESaveFile/Types/StringProperty.h" -#include "ResourceIDs.h" - #include "Profile.h" using namespace Corrade; diff --git a/src/Profile/Profile.h b/src/Profile/Profile.h index d68bd44..b65ea6a 100644 --- a/src/Profile/Profile.h +++ b/src/Profile/Profile.h @@ -22,6 +22,8 @@ #include "../UESaveFile/UESaveFile.h" +#include "ResourceIDs.h" + using namespace Magnum; enum class ProfileType : UnsignedByte { -- 2.39.2 From de2ba9ce7ffb2e5fe32d88d86e3a7c8865ebbec9 Mon Sep 17 00:00:00 2001 From: William JCM Date: Sat, 26 Feb 2022 14:48:45 +0100 Subject: [PATCH 125/128] Mass(Manager),SaveTool: improve error handling. --- src/Mass/Mass.cpp | 10 +++++-- src/MassManager/MassManager.cpp | 36 +++++++++++++----------- src/SaveTool/SaveTool_MainManager.cpp | 17 ++++------- src/SaveTool/SaveTool_ProfileManager.cpp | 9 ++---- 4 files changed, 35 insertions(+), 37 deletions(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index 7b81c68..ff8b6cd 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#include - #include #include @@ -914,12 +912,18 @@ auto Mass::updateAccount(const std::string& new_account) -> bool { auto account = _mass->at("Account"); if(!account) { _state = State::Invalid; + _lastError = "Couldn't find the account property."; return false; } account->value = new_account; - return _mass->saveToFile(); + if(!_mass->saveToFile()) { + _lastError = _mass->lastError(); + return false; + } + + return true; } void Mass::getCustomStyles(Containers::ArrayView styles, ArrayProperty* style_array) { diff --git a/src/MassManager/MassManager.cpp b/src/MassManager/MassManager.cpp index d6338c3..22b18bf 100644 --- a/src/MassManager/MassManager.cpp +++ b/src/MassManager/MassManager.cpp @@ -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"}.updateAccount(_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; } diff --git a/src/SaveTool/SaveTool_MainManager.cpp b/src/SaveTool/SaveTool_MainManager.cpp index 8dc5e2f..fdb53a0 100644 --- a/src/SaveTool/SaveTool_MainManager.cpp +++ b/src/SaveTool/SaveTool_MainManager.cpp @@ -445,8 +445,7 @@ void SaveTool::drawMassManager() { std::string file = *(static_cast(payload->Data)); if(!_massManager->importMass(file, i)) { - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error importing M.A.S.S.", - _massManager->lastError().c_str(), window()); + _queue.addToast(Toast::Type::Error, _massManager->lastError()); } } else if((payload = ImGui::AcceptDragDropPayload("Mass"))) { @@ -460,9 +459,7 @@ void SaveTool::drawMassManager() { int index = *(static_cast(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()); } } @@ -575,9 +572,7 @@ void SaveTool::drawMassManager() { int index = *(static_cast(payload->Data)); if(!_massManager->exportMass(index)) { - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", - _massManager->lastError().c_str(), - window()); + _queue.addToast(Toast::Type::Error, _massManager->lastError()); } } @@ -626,8 +621,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(); } @@ -665,8 +659,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(); } diff --git a/src/SaveTool/SaveTool_ProfileManager.cpp b/src/SaveTool/SaveTool_ProfileManager.cpp index 07656c7..d501d1b 100644 --- a/src/SaveTool/SaveTool_ProfileManager.cpp +++ b/src/SaveTool/SaveTool_ProfileManager.cpp @@ -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(); } -- 2.39.2 From 572585e648211518f205046b3b091233c6aaedcd Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 2 Mar 2022 11:46:31 +0100 Subject: [PATCH 126/128] SaveTool: optimise code readability. ...mostly for Clang/CLion. :D --- src/SaveTool/SaveTool.h | 3 + src/SaveTool/SaveTool_MainManager.cpp | 179 ++++++++++++++++---------- 2 files changed, 114 insertions(+), 68 deletions(-) diff --git a/src/SaveTool/SaveTool.h b/src/SaveTool/SaveTool.h index 3530558..5341c7a 100644 --- a/src/SaveTool/SaveTool.h +++ b/src/SaveTool/SaveTool.h @@ -105,6 +105,9 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener auto drawRenamePopup(Containers::ArrayView name_view) -> bool; void drawGeneralInfo(); void drawResearchInventory(); + template + 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; diff --git a/src/SaveTool/SaveTool_MainManager.cpp b/src/SaveTool/SaveTool_MainManager.cpp index fdb53a0..d949984 100644 --- a/src/SaveTool/SaveTool_MainManager.cpp +++ b/src/SaveTool/SaveTool_MainManager.cpp @@ -287,44 +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(ImGui::SmallButton, ICON_FA_EDIT)) { \ - (var) = _currentProfile->getter(); \ - ImGui::OpenPopup("int_edit"); \ - } \ - if(drawIntEditPopup(&(var), 9999)) { \ - if(!_currentProfile->set##setter((var))) { \ - _queue.addToast(Toast::Type::Error, _currentProfile->lastError()); \ - } \ - } \ - ImGui::PopID(); \ - } \ - } \ - else { \ - ImGui::TextDisabled("Not found in the save file"); \ - } - if(ImGui::BeginTable("##ResearchInventoryTable", 4, ImGuiTableFlags_BordersOuter|ImGuiTableFlags_ScrollY|ImGuiTableFlags_BordersInnerH)) { @@ -337,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 +void SaveTool::drawMaterialRow(const char* name, Int tier, Getter getter, Setter setter) { + static_assert(std::is_same::value, "getter doesn't return an Int, and/or doesn't take zero arguments."); + static_assert(std::is_same::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() { -- 2.39.2 From 6208825aa62c8f229bbbfe773014410d8ea12a94 Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 2 Mar 2022 14:10:13 +0100 Subject: [PATCH 127/128] Mass: fix a bug that prevented global styles from being read. --- src/Mass/Mass.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Mass/Mass.cpp b/src/Mass/Mass.cpp index ff8b6cd..38bdf2c 100644 --- a/src/Mass/Mass.cpp +++ b/src/Mass/Mass.cpp @@ -832,7 +832,6 @@ void Mass::getGlobalStyles() { if(global_styles->items.size() != _globalStyles.size()) { _globalStyles = Containers::Array{global_styles->items.size()}; - return; } getCustomStyles(_globalStyles, global_styles); -- 2.39.2 From f522d20dd40df9db44fc52770cae2ec0a2723ecf Mon Sep 17 00:00:00 2001 From: William JCM Date: Wed, 2 Mar 2022 14:43:02 +0100 Subject: [PATCH 128/128] SaveTool: fragment SaveTool_MassViewer.cpp. This will make maintenance easier. I hope. --- src/CMakeLists.txt | 3 + src/Maps/StyleNames.h | 8 +- src/SaveTool/SaveTool.h | 3 + src/SaveTool/SaveTool_MassViewer.cpp | 920 +------------------ src/SaveTool/SaveTool_MassViewer_Armour.cpp | 198 ++++ src/SaveTool/SaveTool_MassViewer_Frame.cpp | 307 +++++++ src/SaveTool/SaveTool_MassViewer_Weapons.cpp | 475 ++++++++++ 7 files changed, 996 insertions(+), 918 deletions(-) create mode 100644 src/SaveTool/SaveTool_MassViewer_Armour.cpp create mode 100644 src/SaveTool/SaveTool_MassViewer_Frame.cpp create mode 100644 src/SaveTool/SaveTool_MassViewer_Weapons.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index afc8ac7..6e8d616 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/Maps/StyleNames.h b/src/Maps/StyleNames.h index 960ecb1..048509a 100644 --- a/src/Maps/StyleNames.h +++ b/src/Maps/StyleNames.h @@ -22,7 +22,9 @@ using namespace Magnum; -static const std::map style_names { +extern const std::map 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 style_names { {225, "Dark Brown Camo"}, {226, "Blue Camo"}, {227, "Dark Blue Camo"}, -}; +} +#endif +; diff --git a/src/SaveTool/SaveTool.h b/src/SaveTool/SaveTool.h index 5341c7a..b871df8 100644 --- a/src/SaveTool/SaveTool.h +++ b/src/SaveTool/SaveTool.h @@ -121,6 +121,7 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener void drawArmour(); void drawCustomArmourStyles(); void drawWeapons(); + void drawWeaponCategory(const char* name, Containers::ArrayView weapons_view, bool& dirty, const char* payload_type, const char* payload_tooltip); void drawWeaponEditor(Weapon& weapon); void drawGlobalStyles(); void drawTuning(); @@ -238,6 +239,8 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener Containers::Pointer _massManager; Mass* _currentMass{nullptr}; + Weapon* _currentWeapon = nullptr; + Containers::Pointer _fileWatcher; enum watchID { SaveDir = 0, diff --git a/src/SaveTool/SaveTool_MassViewer.cpp b/src/SaveTool/SaveTool_MassViewer.cpp index fa4675f..578b2e9 100644 --- a/src/SaveTool/SaveTool_MassViewer.cpp +++ b/src/SaveTool/SaveTool_MassViewer.cpp @@ -19,19 +19,18 @@ #include #include "../Maps/Accessories.h" -#include "../Maps/ArmourSets.h" + +#define STYLENAMES_DEFINITION #include "../Maps/StyleNames.h" #include "../FontAwesome/IconsFontAwesome5.h" #include "SaveTool.h" -static Weapon* current_weapon = nullptr; - void SaveTool::drawMassViewer() { if(!_currentMass || _currentMass->state() != Mass::State::Valid) { _currentMass = nullptr; - current_weapon = nullptr; + _currentWeapon = nullptr; _uiState = UiState::MainManager; _queue.addToast(Toast::Type::Error, "The selected M.A.S.S. isn't valid anymore."); return; @@ -80,7 +79,7 @@ void SaveTool::drawMassViewer() { ImGui::TableSetColumnIndex(3); if(ImGui::SmallButton(ICON_FA_TIMES)) { - current_weapon = nullptr; + _currentWeapon = nullptr; _currentMass = nullptr; _uiState = UiState::MainManager; _jointsDirty = false; @@ -152,917 +151,6 @@ void SaveTool::drawMassViewer() { ImGui::End(); } -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(); -} - -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(UnsignedLong 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(); -} - -void SaveTool::drawWeapons() { - if(!_currentMass || _currentMass->state() != Mass::State::Valid) { - current_weapon = 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})) - { - return; - } - - ImGui::TableSetupColumn("Weapon"); - - int id = 0; - -#define weapcat(header, array, dirty, payload_type, payload_tooltip) \ - ImGui::TableNextRow(ImGuiTableRowFlags_Headers); \ - ImGui::TableNextColumn(); \ - ImGui::TextUnformatted(header); \ - \ - for(UnsignedInt i = 0; i < _currentMass->array().size(); i++) { \ - auto& weapon = _currentMass->array()[i]; \ - \ - ImGui::TableNextRow(); \ - ImGui::TableNextColumn(); \ - ImGui::PushID(id); \ - if(ImGui::Selectable(weapon.name.c_str(), current_weapon == &weapon)) { \ - current_weapon = &weapon; \ - } \ - if(ImGui::BeginDragDropSource()) { \ - ImGui::SetDragDropPayload(payload_type, &i, sizeof(UnsignedInt)); \ - if(ImGui::GetIO().KeyCtrl) { \ - ImGui::Text(payload_tooltip " %i - %s (copy)", i + 1, weapon.name.c_str()); \ - } \ - else { \ - ImGui::Text(payload_tooltip " %i - %s", i + 1, weapon.name.c_str()); \ - } \ - ImGui::EndDragDropSource(); \ - } \ - if(ImGui::BeginDragDropTarget()) { \ - if(const ImGuiPayload* payload = ImGui::AcceptDragDropPayload(payload_type)) { \ - int index = *static_cast(payload->Data); \ - \ - if(!ImGui::GetIO().KeyCtrl) { \ - if(current_weapon == &_currentMass->array()[index]) { \ - current_weapon = &_currentMass->array()[i]; \ - } \ - else if (current_weapon == &_currentMass->array()[i]) { \ - current_weapon = &_currentMass->array()[index]; \ - } \ - \ - std::swap(_currentMass->array()[index], _currentMass->array()[i]); \ - } \ - else { \ - _currentMass->array()[i] = _currentMass->array()[index]; \ - } \ - (dirty) = true; \ - } \ - \ - ImGui::EndDragDropTarget(); \ - } \ - \ - ImGui::PopID(); \ - id++; \ - \ - if(weapon.attached == true) { \ - ImGui::TableSetBgColor(ImGuiTableBgTarget_CellBg, 0x1F008CFFu); \ - } \ - } - - weapcat("Melee weapons", meleeWeapons, _meleeDirty, "MeleeWeapon", "Melee weapon") - weapcat("Shield", shields, _shieldsDirty, "Shield", "Shield") - weapcat("Bullet shooters", bulletShooters, _bShootersDirty, "BShooter", "Bullet shooter") - weapcat("Energy shooters", energyShooters, _eShootersDirty, "EShooter", "Energy shooter") - weapcat("Bullet launchers", bulletLaunchers, _bLaunchersDirty, "BLauncher", "Bullet launcher") - weapcat("Energy launchers", energyLaunchers, _eLaunchersDirty, "ELauncher", "Energy launcher") - -#undef weapcat - - 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(!current_weapon) { - ImGui::TextUnformatted("No weapon selected."); - return; - } - - ImGui::BeginGroup(); - - if(!ImGui::BeginChild("##WeaponChild", {0.0f, -footer_height_to_reserve})) { - ImGui::EndChild(); - return; - } - - drawWeaponEditor(*current_weapon); - - ImGui::EndChild(); - - ImGui::Separator(); - - if(drawUnsafeWidget([](){ return ImGui::Button(ICON_FA_SAVE " Save changes to weapon category"); })) { - switch(current_weapon->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(current_weapon->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::drawWeaponEditor(Weapon& weapon) { - if(!_currentMass || _currentMass->state() != Mass::State::Valid || !current_weapon) { - 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(); - } -} - void SaveTool::drawGlobalStyles() { if(!_currentMass || _currentMass->state() != Mass::State::Valid) { return; diff --git a/src/SaveTool/SaveTool_MassViewer_Armour.cpp b/src/SaveTool/SaveTool_MassViewer_Armour.cpp new file mode 100644 index 0000000..a3120ad --- /dev/null +++ b/src/SaveTool/SaveTool_MassViewer_Armour.cpp @@ -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 . + +#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(); +} diff --git a/src/SaveTool/SaveTool_MassViewer_Frame.cpp b/src/SaveTool/SaveTool_MassViewer_Frame.cpp new file mode 100644 index 0000000..8aae76f --- /dev/null +++ b/src/SaveTool/SaveTool_MassViewer_Frame.cpp @@ -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 . + +#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(); +} diff --git a/src/SaveTool/SaveTool_MassViewer_Weapons.cpp b/src/SaveTool/SaveTool_MassViewer_Weapons.cpp new file mode 100644 index 0000000..afac126 --- /dev/null +++ b/src/SaveTool/SaveTool_MassViewer_Weapons.cpp @@ -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 . + +#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 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(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(); + } +} -- 2.39.2