Compare commits

..

No commits in common. "fa81d2428e56c4d0feb19f8dac5a1b52631897da" and "44656b32d50a1bc3def7ec7226b694051d895e48" have entirely different histories.

9 changed files with 124 additions and 158 deletions

View file

@ -143,7 +143,6 @@ add_executable(MassBuilderSaveTool WIN32
ProfileManager/ProfileManager.cpp ProfileManager/ProfileManager.cpp
Profile/Profile.h Profile/Profile.h
Profile/Profile.cpp Profile/Profile.cpp
Profile/PropertyNames.h
Profile/ResourceIDs.h Profile/ResourceIDs.h
MassManager/MassManager.h MassManager/MassManager.h
MassManager/MassManager.cpp MassManager/MassManager.cpp

View file

@ -19,20 +19,16 @@
#include <Corrade/Utility/Format.h> #include <Corrade/Utility/Format.h>
#include <Corrade/Utility/Path.h> #include <Corrade/Utility/Path.h>
#include "../Logger/Logger.h"
#include "MassManager.h" #include "MassManager.h"
using namespace Containers::Literals; using namespace Containers::Literals;
MassManager::MassManager(Containers::StringView save_path, Containers::StringView account, bool demo, MassManager::MassManager(Containers::StringView save_path, Containers::StringView account, bool demo, Containers::StringView staging_dir):
Containers::StringView staging_dir):
_saveDirectory{save_path}, _account{account}, _demo{demo}, _stagingAreaDirectory{staging_dir} _saveDirectory{save_path}, _account{account}, _demo{demo}, _stagingAreaDirectory{staging_dir}
{ {
Containers::String mass_filename = ""; Containers::String mass_filename = "";
for(UnsignedInt i = 0; i < _hangars.size(); i++) { for(UnsignedInt i = 0; i < _hangars.size(); i++) {
mass_filename = Utility::Path::join(_saveDirectory, mass_filename = Utility::Path::join(_saveDirectory, Utility::format("{}Unit{:.2d}{}.sav", demo ? "Demo"_s : ""_s, i, _account));
Utility::format("{}Unit{:.2d}{}.sav", demo ? "Demo"_s : ""_s, i, _account));
new(&_hangars[i]) Mass{mass_filename}; new(&_hangars[i]) Mass{mass_filename};
} }
@ -49,8 +45,6 @@ auto MassManager::hangar(Int hangar) -> Mass& {
void MassManager::refreshHangar(Int hangar) { void MassManager::refreshHangar(Int hangar) {
if(hangar < 0 || hangar >= 32) { if(hangar < 0 || hangar >= 32) {
_lastError = "Hangar index out of range.";
LOG_ERROR(_lastError);
return; return;
} }
@ -62,8 +56,7 @@ void MassManager::refreshHangar(Int hangar) {
auto MassManager::importMass(Containers::StringView staged_fn, Int hangar) -> bool { auto MassManager::importMass(Containers::StringView staged_fn, Int hangar) -> bool {
if(hangar < 0 || hangar >= 32) { if(hangar < 0 || hangar >= 32) {
_lastError = "Hangar index out of range."; _lastError = "Hangar out of range in MassManager::importMass()"_s;
LOG_ERROR(_lastError);
return false; return false;
} }
@ -71,7 +64,6 @@ auto MassManager::importMass(Containers::StringView staged_fn, Int hangar) -> bo
if(it == _stagedMasses.end()) { if(it == _stagedMasses.end()) {
_lastError = "Couldn't find "_s + staged_fn + " in the staged M.A.S.S.es."_s; _lastError = "Couldn't find "_s + staged_fn + " in the staged M.A.S.S.es."_s;
LOG_ERROR(_lastError);
return false; return false;
} }
@ -95,7 +87,6 @@ auto MassManager::importMass(Containers::StringView staged_fn, Int hangar) -> bo
if(!Utility::Path::move(source + ".tmp"_s, dest)) { if(!Utility::Path::move(source + ".tmp"_s, dest)) {
_lastError = Utility::format("Couldn't move {} to hangar {:.2d}", staged_fn, hangar + 1); _lastError = Utility::format("Couldn't move {} to hangar {:.2d}", staged_fn, hangar + 1);
LOG_ERROR(_lastError);
return false; return false;
} }
@ -104,14 +95,12 @@ auto MassManager::importMass(Containers::StringView staged_fn, Int hangar) -> bo
auto MassManager::exportMass(Int hangar) -> bool { auto MassManager::exportMass(Int hangar) -> bool {
if(hangar < 0 || hangar >= 32) { if(hangar < 0 || hangar >= 32) {
_lastError = "Hangar index out of range."_s; _lastError = "Hangar out of range in MassManager::exportMass()"_s;
LOG_ERROR(_lastError);
return false; return false;
} }
if(_hangars[hangar].state() != Mass::State::Valid) { if(_hangars[hangar].state() != Mass::State::Valid) {
_lastError = Utility::format("There is no valid data to export in hangar {:.2d}", hangar + 1); _lastError = Utility::format("There is no valid data to export in hangar {:.2d}", hangar + 1);
LOG_ERROR(_lastError);
return false; return false;
} }
@ -121,7 +110,6 @@ auto MassManager::exportMass(Int hangar) -> bool {
if(!Utility::Path::copy(source, dest)) { if(!Utility::Path::copy(source, dest)) {
_lastError = Utility::format("Couldn't export data from hangar {:.2d} to {}", hangar, dest); _lastError = Utility::format("Couldn't export data from hangar {:.2d} to {}", hangar, dest);
LOG_ERROR(_lastError);
return false; return false;
} }
@ -130,14 +118,12 @@ auto MassManager::exportMass(Int hangar) -> bool {
auto MassManager::moveMass(Int source, Int destination) -> bool { auto MassManager::moveMass(Int source, Int destination) -> bool {
if(source < 0 || source >= 32) { if(source < 0 || source >= 32) {
_lastError = "Source hangar index out of range."_s; _lastError = "Source hangar out of range."_s;
LOG_ERROR(_lastError);
return false; return false;
} }
if(destination < 0 || destination >= 32) { if(destination < 0 || destination >= 32) {
_lastError = "Destination hangar index out of range."_s; _lastError = "Destination hangar out of range."_s;
LOG_ERROR(_lastError);
return false; return false;
} }
@ -167,14 +153,12 @@ auto MassManager::moveMass(Int source, Int destination) -> bool {
auto MassManager::deleteMass(Int hangar) -> bool { auto MassManager::deleteMass(Int hangar) -> bool {
if(hangar < 0 || hangar >= 32) { if(hangar < 0 || hangar >= 32) {
_lastError = "Hangar index out of range."_s; _lastError = "Hangar out of range."_s;
LOG_ERROR(_lastError);
return false; return false;
} }
if(!Utility::Path::remove(Utility::Path::join(_saveDirectory, _hangars[hangar].filename()))) { if(!Utility::Path::remove(Utility::Path::join(_saveDirectory, _hangars[hangar].filename()))) {
_lastError = Utility::format("Deletion failed: {}", std::strerror(errno)); _lastError = Utility::format("Deletion failed: {}", std::strerror(errno));
LOG_ERROR(_lastError);
return false; return false;
} }
@ -189,11 +173,10 @@ void MassManager::refreshStagedMasses() {
_stagedMasses.clear(); _stagedMasses.clear();
using Utility::Path::ListFlag; using Utility::Path::ListFlag;
auto file_list = Utility::Path::list(_stagingAreaDirectory, auto file_list = Utility::Path::list(_stagingAreaDirectory, ListFlag::SkipSpecial|ListFlag::SkipDirectories|ListFlag::SkipDotAndDotDot);
ListFlag::SkipSpecial|ListFlag::SkipDirectories|ListFlag::SkipDotAndDotDot);
if(!file_list) { if(!file_list) {
LOG_ERROR_FORMAT("{} couldn't be opened.", _stagingAreaDirectory); Utility::Error{} << _stagingAreaDirectory << "couldn't be opened";
return; return;
} }
@ -240,13 +223,11 @@ void MassManager::refreshStagedMass(Containers::StringView filename) {
auto MassManager::deleteStagedMass(Containers::StringView filename) -> bool { auto MassManager::deleteStagedMass(Containers::StringView filename) -> bool {
if(_stagedMasses.find(filename) == _stagedMasses.cend()) { if(_stagedMasses.find(filename) == _stagedMasses.cend()) {
_lastError = "The file "_s + filename + " couldn't be found in the list of staged M.A.S.S.es."_s; _lastError = "The file "_s + filename + " couldn't be found in the list of staged M.A.S.S.es."_s;
LOG_ERROR(_lastError);
return false; return false;
} }
if(!Utility::Path::remove(Utility::Path::join(_stagingAreaDirectory, filename))) { if(!Utility::Path::remove(Utility::Path::join(_stagingAreaDirectory, filename))) {
_lastError = filename + " couldn't be deleted: " + std::strerror(errno); _lastError = filename + " couldn't be deleted: " + std::strerror(errno);
LOG_ERROR(_lastError);
return false; return false;
} }

View file

@ -16,11 +16,12 @@
#include <algorithm> #include <algorithm>
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/Pair.h> #include <Corrade/Containers/Pair.h>
#include <Corrade/Containers/StaticArray.h>
#include <Corrade/Utility/Path.h> #include <Corrade/Utility/Path.h>
#include <Corrade/Utility/Format.h>
#include "PropertyNames.h"
#include "../Logger/Logger.h"
#include "../UESaveFile/Types/ArrayProperty.h" #include "../UESaveFile/Types/ArrayProperty.h"
#include "../UESaveFile/Types/ResourceItemValue.h" #include "../UESaveFile/Types/ResourceItemValue.h"
#include "../UESaveFile/Types/IntProperty.h" #include "../UESaveFile/Types/IntProperty.h"
@ -34,11 +35,8 @@ using namespace Containers::Literals;
Profile::Profile(Containers::StringView path): Profile::Profile(Containers::StringView path):
_profile(path) _profile(path)
{ {
LOG_INFO_FORMAT("Reading profile at {}.", path);
if(!_profile.valid()) { if(!_profile.valid()) {
_lastError = _profile.lastError(); _lastError = _profile.lastError();
_valid = false;
return; return;
} }
@ -51,7 +49,7 @@ Profile::Profile(Containers::StringView path):
_type = ProfileType::FullGame; _type = ProfileType::FullGame;
} }
auto account_prop = _profile.at<StringProperty>(PROFILE_ACCOUNT); auto account_prop = _profile.at<StringProperty>("Account"_s);
if(!account_prop) { if(!account_prop) {
_lastError = "Couldn't find an account ID in "_s + _filename; _lastError = "Couldn't find an account ID in "_s + _filename;
_valid = false; _valid = false;
@ -59,6 +57,13 @@ Profile::Profile(Containers::StringView path):
} }
_account = account_prop->value; _account = account_prop->value;
if(_account.hasPrefix("PMCSlot"_s)) {
_version = ProfileVersion::Normal;
}
else {
_version = ProfileVersion::Legacy;
}
refreshValues(); refreshValues();
} }
@ -82,73 +87,72 @@ auto Profile::isDemo() const -> bool {
return _type == ProfileType::Demo; return _type == ProfileType::Demo;
} }
auto Profile::version() const -> ProfileVersion {
return _version;
}
auto Profile::isLegacy() const -> bool {
return _version == ProfileVersion::Legacy;
}
auto Profile::account() const -> Containers::StringView { auto Profile::account() const -> Containers::StringView {
return _account; return _account;
} }
void Profile::refreshValues() { void Profile::refreshValues() {
if(!_profile.reloadData()) { if(!_profile.reloadData()) {
LOG_ERROR(_profile.lastError()); _lastError = _profile.lastError();
_valid = false; _valid = false;
return; return;
} }
if(_profile.saveType() != "/Game/Core/Save/bpSaveGameProfile.bpSaveGameProfile_C"_s) { if(_profile.saveType() != "/Game/Core/Save/bpSaveGameProfile.bpSaveGameProfile_C"_s) {
LOG_ERROR_FORMAT("{} is not a valid profile save.", _filename); Utility::Error{} << _filename << "is not a valid profile save.";
_valid = false;
return;
} }
LOG_INFO("Getting the company name."); auto name_prop = _profile.at<StringProperty>("CompanyName"_s);
auto name_prop = _profile.at<StringProperty>(PROFILE_NAME);
if(!name_prop) { if(!name_prop) {
_lastError = "No company name in "_s + _filename; _lastError = "No company name in "_s + _filename;
LOG_ERROR(_lastError);
_valid = false; _valid = false;
return; return;
} }
_name = name_prop->value; _name = name_prop->value;
LOG_INFO("Getting the active frame slot."); auto prop = _profile.at<IntProperty>("ActiveFrameSlot"_s);
auto prop = _profile.at<IntProperty>(PROFILE_ACTIVE_FRAME_SLOT);
_activeFrameSlot = prop ? prop->value : 0; _activeFrameSlot = prop ? prop->value : 0;
LOG_INFO("Getting the credits."); prop = _profile.at<IntProperty>("Credit"_s);
prop = _profile.at<IntProperty>(PROFILE_CREDITS);
_credits = prop ? prop->value : 0; _credits = prop ? prop->value : 0;
LOG_INFO("Getting the story progress."); prop = _profile.at<IntProperty>("StoryProgress"_s);
prop = _profile.at<IntProperty>(PROFILE_STORY_PROGRESS);
_storyProgress = prop ? prop->value : 0; _storyProgress = prop ? prop->value : 0;
LOG_INFO("Getting the last mission ID."); prop = _profile.at<IntProperty>("LastMissionID"_s);
prop = _profile.at<IntProperty>(PROFILE_LAST_MISSION_ID);
_lastMissionId = prop ? prop->value : 0; _lastMissionId = prop ? prop->value : 0;
LOG_INFO("Getting the materials."); _verseSteel = getResource("ResourceMaterial"_s, VerseSteel);
_verseSteel = getResource(PROFILE_MATERIAL, VerseSteel); _undinium = getResource("ResourceMaterial"_s, Undinium);
_undinium = getResource(PROFILE_MATERIAL, Undinium); _necriumAlloy = getResource("ResourceMaterial"_s, NecriumAlloy);
_necriumAlloy = getResource(PROFILE_MATERIAL, NecriumAlloy); _lunarite = getResource("ResourceMaterial"_s, Lunarite);
_lunarite = getResource(PROFILE_MATERIAL, Lunarite); _asterite = getResource("ResourceMaterial"_s, Asterite);
_asterite = getResource(PROFILE_MATERIAL, Asterite);
_ednil = getResource(PROFILE_MATERIAL, Ednil); _ednil = getResource("ResourceMaterial"_s, Ednil);
_nuflalt = getResource(PROFILE_MATERIAL, Nuflalt); _nuflalt = getResource("ResourceMaterial"_s, Nuflalt);
_aurelene = getResource(PROFILE_MATERIAL, Aurelene); _aurelene = getResource("ResourceMaterial"_s, Aurelene);
_soldus = getResource(PROFILE_MATERIAL, Soldus); _soldus = getResource("ResourceMaterial"_s, Soldus);
_synthesisedN = getResource(PROFILE_MATERIAL, SynthesisedN); _synthesisedN = getResource("ResourceMaterial"_s, SynthesisedN);
_alcarbonite = getResource(PROFILE_MATERIAL, Alcarbonite); _alcarbonite = getResource("ResourceMaterial"_s, Alcarbonite);
_keriphene = getResource(PROFILE_MATERIAL, Keriphene); _keriphene = getResource("ResourceMaterial"_s, Keriphene);
_nitinolCM = getResource(PROFILE_MATERIAL, NitinolCM); _nitinolCM = getResource("ResourceMaterial"_s, NitinolCM);
_quarkium = getResource(PROFILE_MATERIAL, Quarkium); _quarkium = getResource("ResourceMaterial"_s, Quarkium);
_alterene = getResource(PROFILE_MATERIAL, Alterene); _alterene = getResource("ResourceMaterial"_s, Alterene);
_mixedComposition = getResource(PROFILE_QUARK_DATA, MixedComposition); _mixedComposition = getResource("ResourceQuarkData"_s, MixedComposition);
_voidResidue = getResource(PROFILE_QUARK_DATA, VoidResidue); _voidResidue = getResource("ResourceQuarkData"_s, VoidResidue);
_muscularConstruction = getResource(PROFILE_QUARK_DATA, MuscularConstruction); _muscularConstruction = getResource("ResourceQuarkData"_s, MuscularConstruction);
_mineralExoskeletology = getResource(PROFILE_QUARK_DATA, MineralExoskeletology); _mineralExoskeletology = getResource("ResourceQuarkData"_s, MineralExoskeletology);
_carbonisedSkin = getResource(PROFILE_QUARK_DATA, CarbonisedSkin); _carbonisedSkin = getResource("ResourceQuarkData"_s, CarbonisedSkin);
_valid = true; _valid = true;
} }
@ -158,10 +162,9 @@ auto Profile::companyName() const -> Containers::StringView {
} }
auto Profile::renameCompany(Containers::StringView new_name) -> bool { auto Profile::renameCompany(Containers::StringView new_name) -> bool {
auto name_prop = _profile.at<StringProperty>(PROFILE_NAME); auto name_prop = _profile.at<StringProperty>("CompanyName"_s);
if(!name_prop) { if(!name_prop) {
_lastError = "No company name in "_s + _filename; _lastError = "No company name in "_s + _filename;
LOG_ERROR(_lastError);
_valid = false; _valid = false;
return false; return false;
} }
@ -185,12 +188,11 @@ auto Profile::credits() const -> Int {
} }
auto Profile::setCredits(Int amount) -> bool { auto Profile::setCredits(Int amount) -> bool {
auto credits_prop = _profile.at<IntProperty>(PROFILE_CREDITS); auto credits_prop = _profile.at<IntProperty>("Credit"_s);
if(!credits_prop) { if(!credits_prop) {
credits_prop = new IntProperty; credits_prop = new IntProperty;
credits_prop->name.emplace("Credit"_s); credits_prop->name.emplace("Credit"_s);
credits_prop->valueLength = sizeof(Int);
_profile.appendProperty(IntProperty::ptr{credits_prop}); _profile.appendProperty(IntProperty::ptr{credits_prop});
} }
@ -214,7 +216,6 @@ auto Profile::setStoryProgress(Int progress) -> bool {
if(!story_progress_prop) { if(!story_progress_prop) {
story_progress_prop = new IntProperty; story_progress_prop = new IntProperty;
story_progress_prop->name.emplace("StoryProgress"_s); story_progress_prop->name.emplace("StoryProgress"_s);
story_progress_prop->valueLength = sizeof(Int);
_profile.appendProperty(IntProperty::ptr{story_progress_prop}); _profile.appendProperty(IntProperty::ptr{story_progress_prop});
} }
@ -237,7 +238,7 @@ auto Profile::verseSteel() const -> Int {
} }
auto Profile::setVerseSteel(Int amount) -> bool { auto Profile::setVerseSteel(Int amount) -> bool {
return setResource(PROFILE_MATERIAL, VerseSteel, amount); return setResource("ResourceMaterial"_s, VerseSteel, amount);
} }
auto Profile::undinium() const -> Int { auto Profile::undinium() const -> Int {
@ -245,7 +246,7 @@ auto Profile::undinium() const -> Int {
} }
auto Profile::setUndinium(Int amount) -> bool { auto Profile::setUndinium(Int amount) -> bool {
return setResource(PROFILE_MATERIAL, Undinium, amount); return setResource("ResourceMaterial"_s, Undinium, amount);
} }
auto Profile::necriumAlloy() const -> Int { auto Profile::necriumAlloy() const -> Int {
@ -253,7 +254,7 @@ auto Profile::necriumAlloy() const -> Int {
} }
auto Profile::setNecriumAlloy(Int amount) -> bool { auto Profile::setNecriumAlloy(Int amount) -> bool {
return setResource(PROFILE_MATERIAL, NecriumAlloy, amount); return setResource("ResourceMaterial"_s, NecriumAlloy, amount);
} }
auto Profile::lunarite() const -> Int { auto Profile::lunarite() const -> Int {
@ -261,7 +262,7 @@ auto Profile::lunarite() const -> Int {
} }
auto Profile::setLunarite(Int amount) -> bool { auto Profile::setLunarite(Int amount) -> bool {
return setResource(PROFILE_MATERIAL, Lunarite, amount); return setResource("ResourceMaterial"_s, Lunarite, amount);
} }
auto Profile::asterite() const -> Int { auto Profile::asterite() const -> Int {
@ -269,7 +270,7 @@ auto Profile::asterite() const -> Int {
} }
auto Profile::setAsterite(Int amount) -> bool { auto Profile::setAsterite(Int amount) -> bool {
return setResource(PROFILE_MATERIAL, Asterite, amount); return setResource("ResourceMaterial"_s, Asterite, amount);
} }
auto Profile::ednil() const -> Int { auto Profile::ednil() const -> Int {
@ -277,7 +278,7 @@ auto Profile::ednil() const -> Int {
} }
auto Profile::setEdnil(Int amount) -> bool { auto Profile::setEdnil(Int amount) -> bool {
return setResource(PROFILE_MATERIAL, Ednil, amount); return setResource("ResourceMaterial"_s, Ednil, amount);
} }
auto Profile::nuflalt() const -> Int { auto Profile::nuflalt() const -> Int {
@ -285,7 +286,7 @@ auto Profile::nuflalt() const -> Int {
} }
auto Profile::setNuflalt(Int amount) -> bool { auto Profile::setNuflalt(Int amount) -> bool {
return setResource(PROFILE_MATERIAL, Nuflalt, amount); return setResource("ResourceMaterial"_s, Nuflalt, amount);
} }
auto Profile::aurelene() const -> Int { auto Profile::aurelene() const -> Int {
@ -293,7 +294,7 @@ auto Profile::aurelene() const -> Int {
} }
auto Profile::setAurelene(Int amount) -> bool { auto Profile::setAurelene(Int amount) -> bool {
return setResource(PROFILE_MATERIAL, Aurelene, amount); return setResource("ResourceMaterial"_s, Aurelene, amount);
} }
auto Profile::soldus() const -> Int { auto Profile::soldus() const -> Int {
@ -301,7 +302,7 @@ auto Profile::soldus() const -> Int {
} }
auto Profile::setSoldus(Int amount) -> bool { auto Profile::setSoldus(Int amount) -> bool {
return setResource(PROFILE_MATERIAL, Soldus, amount); return setResource("ResourceMaterial"_s, Soldus, amount);
} }
auto Profile::synthesisedN() const -> Int { auto Profile::synthesisedN() const -> Int {
@ -309,7 +310,7 @@ auto Profile::synthesisedN() const -> Int {
} }
auto Profile::setSynthesisedN(Int amount) -> bool { auto Profile::setSynthesisedN(Int amount) -> bool {
return setResource(PROFILE_MATERIAL, SynthesisedN, amount); return setResource("ResourceMaterial"_s, SynthesisedN, amount);
} }
auto Profile::alcarbonite() const -> Int { auto Profile::alcarbonite() const -> Int {
@ -317,7 +318,7 @@ auto Profile::alcarbonite() const -> Int {
} }
auto Profile::setAlcarbonite(Int amount) -> bool { auto Profile::setAlcarbonite(Int amount) -> bool {
return setResource(PROFILE_MATERIAL, Alcarbonite, amount); return setResource("ResourceMaterial"_s, Alcarbonite, amount);
} }
auto Profile::keriphene() const -> Int { auto Profile::keriphene() const -> Int {
@ -325,7 +326,7 @@ auto Profile::keriphene() const -> Int {
} }
auto Profile::setKeriphene(Int amount) -> bool { auto Profile::setKeriphene(Int amount) -> bool {
return setResource(PROFILE_MATERIAL, Keriphene, amount); return setResource("ResourceMaterial"_s, Keriphene, amount);
} }
auto Profile::nitinolCM() const -> Int { auto Profile::nitinolCM() const -> Int {
@ -333,7 +334,7 @@ auto Profile::nitinolCM() const -> Int {
} }
auto Profile::setNitinolCM(Int amount) -> bool { auto Profile::setNitinolCM(Int amount) -> bool {
return setResource(PROFILE_MATERIAL, NitinolCM, amount); return setResource("ResourceMaterial"_s, NitinolCM, amount);
} }
auto Profile::quarkium() const -> Int { auto Profile::quarkium() const -> Int {
@ -341,7 +342,7 @@ auto Profile::quarkium() const -> Int {
} }
auto Profile::setQuarkium(Int amount) -> bool { auto Profile::setQuarkium(Int amount) -> bool {
return setResource(PROFILE_MATERIAL, Quarkium, amount); return setResource("ResourceMaterial"_s, Quarkium, amount);
} }
auto Profile::alterene() const -> Int { auto Profile::alterene() const -> Int {
@ -349,7 +350,7 @@ auto Profile::alterene() const -> Int {
} }
auto Profile::setAlterene(Int amount) -> bool { auto Profile::setAlterene(Int amount) -> bool {
return setResource(PROFILE_MATERIAL, Alterene, amount); return setResource("ResourceMaterial"_s, Alterene, amount);
} }
auto Profile::mixedComposition() const -> Int { auto Profile::mixedComposition() const -> Int {
@ -357,7 +358,7 @@ auto Profile::mixedComposition() const -> Int {
} }
auto Profile::setMixedComposition(Int amount) -> bool { auto Profile::setMixedComposition(Int amount) -> bool {
return setResource(PROFILE_QUARK_DATA, MixedComposition, amount); return setResource("ResourceQuarkData"_s, MixedComposition, amount);
} }
auto Profile::voidResidue() const -> Int { auto Profile::voidResidue() const -> Int {
@ -365,7 +366,7 @@ auto Profile::voidResidue() const -> Int {
} }
auto Profile::setVoidResidue(Int amount) -> bool { auto Profile::setVoidResidue(Int amount) -> bool {
return setResource(PROFILE_QUARK_DATA, VoidResidue, amount); return setResource("ResourceQuarkData"_s, VoidResidue, amount);
} }
auto Profile::muscularConstruction() const -> Int { auto Profile::muscularConstruction() const -> Int {
@ -373,7 +374,7 @@ auto Profile::muscularConstruction() const -> Int {
} }
auto Profile::setMuscularConstruction(Int amount) -> bool { auto Profile::setMuscularConstruction(Int amount) -> bool {
return setResource(PROFILE_QUARK_DATA, MuscularConstruction, amount); return setResource("ResourceQuarkData"_s, MuscularConstruction, amount);
} }
auto Profile::mineralExoskeletology() const -> Int { auto Profile::mineralExoskeletology() const -> Int {
@ -381,7 +382,7 @@ auto Profile::mineralExoskeletology() const -> Int {
} }
auto Profile::setMineralExoskeletology(Int amount) -> bool { auto Profile::setMineralExoskeletology(Int amount) -> bool {
return setResource(PROFILE_QUARK_DATA, MineralExoskeletology, amount); return setResource("ResourceQuarkData"_s, MineralExoskeletology, amount);
} }
auto Profile::carbonisedSkin() const -> Int { auto Profile::carbonisedSkin() const -> Int {
@ -389,7 +390,7 @@ auto Profile::carbonisedSkin() const -> Int {
} }
auto Profile::setCarbonisedSkin(Int amount) -> bool { auto Profile::setCarbonisedSkin(Int amount) -> bool {
return setResource(PROFILE_QUARK_DATA, CarbonisedSkin, amount); return setResource("ResourceQuarkData"_s, CarbonisedSkin, amount);
} }
auto Profile::getResource(Containers::StringView container, MaterialID id) -> Int { auto Profile::getResource(Containers::StringView container, MaterialID id) -> Int {
@ -412,9 +413,9 @@ auto Profile::setResource(Containers::StringView container, MaterialID id, Int a
auto mats_prop = _profile.at<ArrayProperty>(container); auto mats_prop = _profile.at<ArrayProperty>(container);
if(!mats_prop) { if(!mats_prop) {
mats_prop = new ArrayProperty; _lastError = "Couldn't find "_s + container + " in "_s + _filename;
mats_prop->name.emplace(container); _valid = false;
_profile.appendProperty(ArrayProperty::ptr{mats_prop}); return false;
} }
auto predicate = [&id](UnrealPropertyBase::ptr& prop){ auto predicate = [&id](UnrealPropertyBase::ptr& prop){

View file

@ -33,6 +33,11 @@ enum class ProfileType : UnsignedByte {
FullGame FullGame
}; };
enum class ProfileVersion : UnsignedByte {
Legacy, // pre-0.8
Normal // 0.8 and later
};
class Profile { class Profile {
public: public:
explicit Profile(Containers::StringView path); explicit Profile(Containers::StringView path);
@ -46,6 +51,9 @@ class Profile {
auto type() const -> ProfileType; auto type() const -> ProfileType;
auto isDemo() const -> bool; auto isDemo() const -> bool;
auto version() const -> ProfileVersion;
auto isLegacy() const -> bool;
auto account() const -> Containers::StringView; auto account() const -> Containers::StringView;
void refreshValues(); void refreshValues();
@ -130,6 +138,7 @@ class Profile {
Containers::String _filename; Containers::String _filename;
ProfileType _type; ProfileType _type;
ProfileVersion _version;
UESaveFile _profile; UESaveFile _profile;

View file

@ -1,10 +0,0 @@
#pragma once
#define PROFILE_NAME "CompanyName"
#define PROFILE_ACTIVE_FRAME_SLOT "ActiveFrameSlot"
#define PROFILE_CREDITS "Credit"
#define PROFILE_STORY_PROGRESS "StoryProgress"
#define PROFILE_LAST_MISSION_ID "LastMissionID"
#define PROFILE_MATERIAL "ResourceMaterial"
#define PROFILE_QUARK_DATA "ResourceQuarkData"
#define PROFILE_ACCOUNT "Account"

View file

@ -18,6 +18,8 @@
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
#include <iomanip>
#include <regex>
#include <Corrade/Containers/ScopeGuard.h> #include <Corrade/Containers/ScopeGuard.h>
#include <Corrade/Containers/StaticArray.h> #include <Corrade/Containers/StaticArray.h>
@ -27,8 +29,6 @@
#include <zip.h> #include <zip.h>
#include "../Logger/Logger.h"
#include "ProfileManager.h" #include "ProfileManager.h"
using namespace Containers::Literals; using namespace Containers::Literals;
@ -53,8 +53,6 @@ auto ProfileManager::profiles() -> Containers::ArrayView<Profile> {
} }
auto ProfileManager::refreshProfiles() -> bool { auto ProfileManager::refreshProfiles() -> bool {
LOG_INFO("Refreshing profiles.");
_profiles = Containers::Array<Profile>{}; _profiles = Containers::Array<Profile>{};
using Utility::Path::ListFlag; using Utility::Path::ListFlag;
@ -63,12 +61,14 @@ auto ProfileManager::refreshProfiles() -> bool {
if(!files) { if(!files) {
_lastError = _saveDirectory + " can't be opened."; _lastError = _saveDirectory + " can't be opened.";
LOG_ERROR(_lastError);
return false; return false;
} }
auto predicate = [](Containers::StringView file)->bool{ auto predicate = [](Containers::StringView file)->bool{
return !((file.hasPrefix("DemoProfile") || file.hasPrefix("Profile")) && file.hasSuffix(".sav")); std::regex legacy_regex("(Demo)?Profile[0-9]{17}\\.sav", std::regex::nosubs);
std::regex new_regex("(Demo)?ProfilePMCSlot[0-9]{3}\\.sav", std::regex::nosubs);
std::cmatch m;
return !std::regex_match(file.data(), m, legacy_regex) && !std::regex_match(file.data(), m, new_regex);
}; };
auto files_view = files->exceptSuffix(files->end() - std::remove_if(files->begin(), files->end(), predicate)); auto files_view = files->exceptSuffix(files->end() - std::remove_if(files->begin(), files->end(), predicate));
@ -77,7 +77,7 @@ auto ProfileManager::refreshProfiles() -> bool {
Profile profile{Utility::Path::join(_saveDirectory, file)}; Profile profile{Utility::Path::join(_saveDirectory, file)};
if(!profile.valid()) { if(!profile.valid()) {
LOG_WARNING_FORMAT("Profile {} is invalid: {}", file, profile.lastError()); Utility::Warning{} << "Profile"_s << file << "is invalid:"_s << profile.lastError();
continue; continue;
} }
@ -86,7 +86,6 @@ auto ProfileManager::refreshProfiles() -> bool {
if(_profiles.isEmpty()) { if(_profiles.isEmpty()) {
_lastError = "No valid profiles were found."_s; _lastError = "No valid profiles were found."_s;
LOG_ERROR(_lastError);
return false; return false;
} }
@ -102,7 +101,6 @@ auto ProfileManager::deleteProfile(std::size_t index, bool delete_builds) -> boo
_lastError = Utility::format("Couldn't delete {} (filename: {}).", _lastError = Utility::format("Couldn't delete {} (filename: {}).",
_profiles[index].companyName(), _profiles[index].companyName(),
_profiles[index].filename()); _profiles[index].filename());
LOG_ERROR(_lastError);
refreshProfiles(); refreshProfiles();
return false; return false;
} }
@ -131,7 +129,7 @@ auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> boo
std::tm* time = std::localtime(&timestamp); std::tm* time = std::localtime(&timestamp);
auto& profile = _profiles[index]; auto& profile = _profiles[index];
auto filename = Utility::format("{}_{}{:.2d}{:.2d}_{:.2d}{:.2d}{:.2d}.backup.mbst", auto filename = Utility::format("{}_{}{:.2d}{:.2d}_{:.2d}{:.2d}{:.2d}.mbprofbackup",
Utility::String::replaceAll(profile.companyName().data(), " ", "_").c_str(), Utility::String::replaceAll(profile.companyName().data(), " ", "_").c_str(),
time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
time->tm_hour, time->tm_min, time->tm_sec); time->tm_hour, time->tm_min, time->tm_sec);
@ -142,28 +140,26 @@ auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> boo
if(zip == nullptr) { if(zip == nullptr) {
zip_error_init_with_code(&error, error_code); zip_error_init_with_code(&error, error_code);
_lastError = zip_error_strerror(&error); _lastError = zip_error_strerror(&error);
LOG_ERROR(_lastError);
return false; return false;
} }
zip_source_t* profile_source = zip_source_file(zip, Utility::Path::toNativeSeparators(Utility::Path::join(_saveDirectory, profile.filename())).data(), 0, 0); zip_source_t* profile_source = zip_source_file(zip, Utility::Path::toNativeSeparators(Utility::Path::join(_saveDirectory, profile.filename())).data(), 0, 0);
if(profile_source == nullptr) { if(profile_source == nullptr) {
_lastError = zip_strerror(zip); _lastError = zip_strerror(zip);
LOG_ERROR(_lastError);
zip_source_free(profile_source); zip_source_free(profile_source);
return false; return false;
} }
if(zip_file_add(zip, profile.filename().data(), profile_source, ZIP_FL_ENC_UTF_8) == -1) { if(zip_file_add(zip, profile.filename().data(), profile_source, ZIP_FL_ENC_UTF_8) == -1) {
_lastError = zip_strerror(zip); _lastError = zip_strerror(zip);
LOG_ERROR(_lastError);
zip_source_free(profile_source); zip_source_free(profile_source);
return false; return false;
} }
auto comment = Utility::format("{}|{}|{}-{:.2d}-{:.2d}-{:.2d}-{:.2d}-{:.2d}", auto comment = Utility::format("{}|{}{}|{}-{:.2d}-{:.2d}-{:.2d}-{:.2d}-{:.2d}",
profile.companyName(), profile.companyName(),
profile.isDemo() ? "demo"_s : "full"_s, profile.isDemo() ? "demo"_s : "full"_s,
profile.isLegacy() ? ""_s : "_new"_s,
time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
time->tm_hour, time->tm_min, time->tm_sec); time->tm_hour, time->tm_min, time->tm_sec);
zip_set_archive_comment(zip, comment.data(), comment.size()); zip_set_archive_comment(zip, comment.data(), comment.size());
@ -193,7 +189,6 @@ auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> boo
if(zip_close(zip) == -1) { if(zip_close(zip) == -1) {
_lastError = zip_strerror(zip); _lastError = zip_strerror(zip);
LOG_ERROR(_lastError);
return false; return false;
} }
@ -215,12 +210,11 @@ void ProfileManager::refreshBackups() {
if(!files) { if(!files) {
_lastError = _backupsDirectory + " can't be opened."; _lastError = _backupsDirectory + " can't be opened.";
LOG_ERROR(_lastError);
return; return;
} }
auto predicate = [](Containers::StringView file)->bool{ auto predicate = [](Containers::StringView file)->bool{
return !(file.hasSuffix(".mbprofbackup"_s) || file.hasSuffix(".backup.mbst")); return !file.hasSuffix(".mbprofbackup"_s);
}; };
auto files_view = files->exceptSuffix(files->end() - std::remove_if(files->begin(), files->end(), predicate)); auto files_view = files->exceptSuffix(files->end() - std::remove_if(files->begin(), files->end(), predicate));
@ -258,11 +252,21 @@ void ProfileManager::refreshBackups() {
backup.company = info[0]; backup.company = info[0];
if(info[1].hasPrefix("full")) { if(info[1] == "full") {
backup.type = ProfileType::FullGame; backup.type = ProfileType::FullGame;
backup.version = ProfileVersion::Legacy;
} }
else if(info[1].hasPrefix("demo")) { else if(info[1] == "demo") {
backup.type = ProfileType::Demo; backup.type = ProfileType::Demo;
backup.version = ProfileVersion::Legacy;
}
else if(info[1] == "full_new") {
backup.type = ProfileType::FullGame;
backup.version = ProfileVersion::Normal;
}
else if(info[1] == "demo_new") {
backup.type = ProfileType::Demo;
backup.version = ProfileVersion::Normal;
} }
else { else {
continue; continue;
@ -293,7 +297,6 @@ void ProfileManager::refreshBackups() {
auto ProfileManager::deleteBackup(std::size_t index) -> bool { auto ProfileManager::deleteBackup(std::size_t index) -> bool {
if(!Utility::Path::remove(Utility::Path::join(_backupsDirectory, _backups[index].filename))) { if(!Utility::Path::remove(Utility::Path::join(_backupsDirectory, _backups[index].filename))) {
_lastError = "Couldn't delete " + _backups[index].filename; _lastError = "Couldn't delete " + _backups[index].filename;
LOG_ERROR(_lastError);
return false; return false;
} }
@ -320,7 +323,6 @@ auto ProfileManager::restoreBackup(std::size_t index) -> bool {
zip_error_t error; zip_error_t error;
zip_error_init_with_code(&error, error_code); zip_error_init_with_code(&error, error_code);
_lastError = zip_error_strerror(&error); _lastError = zip_error_strerror(&error);
LOG_ERROR(_lastError);
return false; return false;
} }
@ -330,7 +332,6 @@ auto ProfileManager::restoreBackup(std::size_t index) -> bool {
FILE* out = std::fopen(Utility::Path::join(_saveDirectory, file).data(), "wb"); FILE* out = std::fopen(Utility::Path::join(_saveDirectory, file).data(), "wb");
if(out == nullptr) { if(out == nullptr) {
_lastError = Utility::format(error_format.data(), file, std::strerror(errno)); _lastError = Utility::format(error_format.data(), file, std::strerror(errno));
LOG_ERROR(_lastError);
return false; return false;
} }
@ -339,7 +340,6 @@ auto ProfileManager::restoreBackup(std::size_t index) -> bool {
zip_file_t* zf = zip_fopen(zip, file.data(), ZIP_FL_ENC_GUESS); zip_file_t* zf = zip_fopen(zip, file.data(), ZIP_FL_ENC_GUESS);
if(zf == nullptr) { if(zf == nullptr) {
_lastError = Utility::format(error_format.data(), file, zip_strerror(zip)); _lastError = Utility::format(error_format.data(), file, zip_strerror(zip));
LOG_ERROR(_lastError);
return false; return false;
} }
@ -351,14 +351,12 @@ auto ProfileManager::restoreBackup(std::size_t index) -> bool {
while((bytes_read = zip_fread(zf, buf.data(), buf.size())) > 0) { while((bytes_read = zip_fread(zf, buf.data(), buf.size())) > 0) {
if(std::fwrite(buf.data(), sizeof(char), bytes_read, out) < static_cast<std::size_t>(bytes_read)) { if(std::fwrite(buf.data(), sizeof(char), bytes_read, out) < static_cast<std::size_t>(bytes_read)) {
_lastError = Utility::format(error_format.data(), file, "not enough bytes written."); _lastError = Utility::format(error_format.data(), file, "not enough bytes written.");
LOG_ERROR(_lastError);
return false; return false;
} }
} }
if(bytes_read == -1) { if(bytes_read == -1) {
_lastError = Utility::format(error_format.data(), file, "couldn't read bytes from archive."); _lastError = Utility::format(error_format.data(), file, "couldn't read bytes from archive.");
LOG_ERROR(_lastError);
return false; return false;
} }
} }

View file

@ -29,6 +29,7 @@ struct Backup {
Containers::String filename; Containers::String filename;
Containers::String company; Containers::String company;
ProfileType type; ProfileType type;
ProfileVersion version;
struct { struct {
int year; int year;
int month; int month;

View file

@ -14,12 +14,10 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <Corrade/Utility/Format.h>
#include <Corrade/Utility/String.h> #include <Corrade/Utility/String.h>
#include <Corrade/Utility/Unicode.h> #include <Corrade/Utility/Unicode.h>
#include <SDL_events.h> #include <SDL_events.h>
#include <SDL_messagebox.h>
#include <fileapi.h> #include <fileapi.h>
#include <handleapi.h> #include <handleapi.h>
@ -100,11 +98,7 @@ void SaveTool::fileUpdateEvent(SDL_Event& event) {
if(is_current_profile) { if(is_current_profile) {
_currentProfile = nullptr; _currentProfile = nullptr;
_uiState = UiState::ProfileManager; _uiState = UiState::ProfileManager;
if(!_profileManager->refreshProfiles()) { _profileManager->refreshProfiles();
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_profileManager->lastError().data(), window());
exit(EXIT_FAILURE);
}
} }
else if(is_unit) { else if(is_unit) {
if(!_currentMass || _currentMass != &(_massManager->hangar(index))) { if(!_currentMass || _currentMass != &(_massManager->hangar(index))) {

View file

@ -52,8 +52,7 @@ void SaveTool::drawProfileManager() {
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
if(ImGui::SmallButton("Refresh")) { if(ImGui::SmallButton("Refresh")) {
if(!_profileManager->refreshProfiles()) { if(!_profileManager->refreshProfiles()) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error in ProfileManager", _profileManager->lastError().data(), window());
_profileManager->lastError().data(), window());
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
@ -96,7 +95,9 @@ void SaveTool::drawProfileManager() {
} }
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
ImGui::TextUnformatted(profile.isDemo() ? "Demo" : "Full"); ImGui::Text("%s%s",
profile.isDemo() ? "Demo" : "Full",
profile.isLegacy() ? " (legacy)" : "");
ImGui::TableSetColumnIndex(2); ImGui::TableSetColumnIndex(2);
if(ImGui::SmallButton(ICON_FA_FILE_ARCHIVE)) { if(ImGui::SmallButton(ICON_FA_FILE_ARCHIVE)) {
@ -160,11 +161,7 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
if(!_profileManager->restoreBackup(backup_index)) { if(!_profileManager->restoreBackup(backup_index)) {
_queue.addToast(Toast::Type::Error, _profileManager->lastError()); _queue.addToast(Toast::Type::Error, _profileManager->lastError());
} }
if(!_profileManager->refreshProfiles()) { _profileManager->refreshProfiles();
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_profileManager->lastError().data(), window());
exit(EXIT_FAILURE);
}
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
ImGui::SameLine(); ImGui::SameLine();
@ -281,7 +278,9 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
backup.timestamp.second); backup.timestamp.second);
ImGui::TableSetColumnIndex(2); ImGui::TableSetColumnIndex(2);
ImGui::TextUnformatted(backup.type == ProfileType::Demo ? "Demo" : "Full"); ImGui::Text("%s%s",
backup.type == ProfileType::Demo ? "Demo" : "Full",
backup.version == ProfileVersion::Legacy ? " (legacy)" : "");
ImGui::TableSetColumnIndex(3); ImGui::TableSetColumnIndex(3);
ImGui::PushID(i); ImGui::PushID(i);
@ -341,18 +340,12 @@ auto SaveTool::drawBackupProfilePopup(std::size_t profile_index) -> ImGuiID {
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
if(ImGui::Button("Yes")) { if(ImGui::Button("Yes")) {
if(!_profileManager->backupProfile(profile_index, true)) { _profileManager->backupProfile(profile_index, true);
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_profileManager->lastError().data(), window());
}
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button("No", ImGui::GetItemRectSize())) { if(ImGui::Button("No", ImGui::GetItemRectSize())) {
if(!_profileManager->backupProfile(profile_index, false)) { _profileManager->backupProfile(profile_index, false);
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_profileManager->lastError().data(), window());
}
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
ImGui::SameLine(); ImGui::SameLine();