Compare commits

...

10 commits

9 changed files with 158 additions and 124 deletions

View file

@ -143,6 +143,7 @@ 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,16 +19,20 @@
#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, Containers::StringView staging_dir): MassManager::MassManager(Containers::StringView save_path, Containers::StringView account, bool demo,
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, Utility::format("{}Unit{:.2d}{}.sav", demo ? "Demo"_s : ""_s, i, _account)); mass_filename = Utility::Path::join(_saveDirectory,
Utility::format("{}Unit{:.2d}{}.sav", demo ? "Demo"_s : ""_s, i, _account));
new(&_hangars[i]) Mass{mass_filename}; new(&_hangars[i]) Mass{mass_filename};
} }
@ -45,6 +49,8 @@ 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;
} }
@ -56,7 +62,8 @@ 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 out of range in MassManager::importMass()"_s; _lastError = "Hangar index out of range.";
LOG_ERROR(_lastError);
return false; return false;
} }
@ -64,6 +71,7 @@ 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;
} }
@ -87,6 +95,7 @@ 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;
} }
@ -95,12 +104,14 @@ 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 out of range in MassManager::exportMass()"_s; _lastError = "Hangar index out of range."_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;
} }
@ -110,6 +121,7 @@ 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;
} }
@ -118,12 +130,14 @@ 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 out of range."_s; _lastError = "Source hangar index out of range."_s;
LOG_ERROR(_lastError);
return false; return false;
} }
if(destination < 0 || destination >= 32) { if(destination < 0 || destination >= 32) {
_lastError = "Destination hangar out of range."_s; _lastError = "Destination hangar index out of range."_s;
LOG_ERROR(_lastError);
return false; return false;
} }
@ -153,12 +167,14 @@ 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 out of range."_s; _lastError = "Hangar index 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;
} }
@ -173,10 +189,11 @@ void MassManager::refreshStagedMasses() {
_stagedMasses.clear(); _stagedMasses.clear();
using Utility::Path::ListFlag; using Utility::Path::ListFlag;
auto file_list = Utility::Path::list(_stagingAreaDirectory, ListFlag::SkipSpecial|ListFlag::SkipDirectories|ListFlag::SkipDotAndDotDot); auto file_list = Utility::Path::list(_stagingAreaDirectory,
ListFlag::SkipSpecial|ListFlag::SkipDirectories|ListFlag::SkipDotAndDotDot);
if(!file_list) { if(!file_list) {
Utility::Error{} << _stagingAreaDirectory << "couldn't be opened"; LOG_ERROR_FORMAT("{} couldn't be opened.", _stagingAreaDirectory);
return; return;
} }
@ -223,11 +240,13 @@ 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,12 +16,11 @@
#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"
@ -35,8 +34,11 @@ 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;
} }
@ -49,7 +51,7 @@ Profile::Profile(Containers::StringView path):
_type = ProfileType::FullGame; _type = ProfileType::FullGame;
} }
auto account_prop = _profile.at<StringProperty>("Account"_s); auto account_prop = _profile.at<StringProperty>(PROFILE_ACCOUNT);
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;
@ -57,13 +59,6 @@ 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();
} }
@ -87,72 +82,73 @@ 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()) {
_lastError = _profile.lastError(); LOG_ERROR(_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) {
Utility::Error{} << _filename << "is not a valid profile save."; LOG_ERROR_FORMAT("{} is not a valid profile save.", _filename);
_valid = false;
return;
} }
auto name_prop = _profile.at<StringProperty>("CompanyName"_s); LOG_INFO("Getting the company name.");
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;
auto prop = _profile.at<IntProperty>("ActiveFrameSlot"_s); LOG_INFO("Getting the active frame slot.");
auto prop = _profile.at<IntProperty>(PROFILE_ACTIVE_FRAME_SLOT);
_activeFrameSlot = prop ? prop->value : 0; _activeFrameSlot = prop ? prop->value : 0;
prop = _profile.at<IntProperty>("Credit"_s); LOG_INFO("Getting the credits.");
prop = _profile.at<IntProperty>(PROFILE_CREDITS);
_credits = prop ? prop->value : 0; _credits = prop ? prop->value : 0;
prop = _profile.at<IntProperty>("StoryProgress"_s); LOG_INFO("Getting the story progress.");
prop = _profile.at<IntProperty>(PROFILE_STORY_PROGRESS);
_storyProgress = prop ? prop->value : 0; _storyProgress = prop ? prop->value : 0;
prop = _profile.at<IntProperty>("LastMissionID"_s); LOG_INFO("Getting the last mission ID.");
prop = _profile.at<IntProperty>(PROFILE_LAST_MISSION_ID);
_lastMissionId = prop ? prop->value : 0; _lastMissionId = prop ? prop->value : 0;
_verseSteel = getResource("ResourceMaterial"_s, VerseSteel); LOG_INFO("Getting the materials.");
_undinium = getResource("ResourceMaterial"_s, Undinium); _verseSteel = getResource(PROFILE_MATERIAL, VerseSteel);
_necriumAlloy = getResource("ResourceMaterial"_s, NecriumAlloy); _undinium = getResource(PROFILE_MATERIAL, Undinium);
_lunarite = getResource("ResourceMaterial"_s, Lunarite); _necriumAlloy = getResource(PROFILE_MATERIAL, NecriumAlloy);
_asterite = getResource("ResourceMaterial"_s, Asterite); _lunarite = getResource(PROFILE_MATERIAL, Lunarite);
_asterite = getResource(PROFILE_MATERIAL, Asterite);
_ednil = getResource("ResourceMaterial"_s, Ednil); _ednil = getResource(PROFILE_MATERIAL, Ednil);
_nuflalt = getResource("ResourceMaterial"_s, Nuflalt); _nuflalt = getResource(PROFILE_MATERIAL, Nuflalt);
_aurelene = getResource("ResourceMaterial"_s, Aurelene); _aurelene = getResource(PROFILE_MATERIAL, Aurelene);
_soldus = getResource("ResourceMaterial"_s, Soldus); _soldus = getResource(PROFILE_MATERIAL, Soldus);
_synthesisedN = getResource("ResourceMaterial"_s, SynthesisedN); _synthesisedN = getResource(PROFILE_MATERIAL, SynthesisedN);
_alcarbonite = getResource("ResourceMaterial"_s, Alcarbonite); _alcarbonite = getResource(PROFILE_MATERIAL, Alcarbonite);
_keriphene = getResource("ResourceMaterial"_s, Keriphene); _keriphene = getResource(PROFILE_MATERIAL, Keriphene);
_nitinolCM = getResource("ResourceMaterial"_s, NitinolCM); _nitinolCM = getResource(PROFILE_MATERIAL, NitinolCM);
_quarkium = getResource("ResourceMaterial"_s, Quarkium); _quarkium = getResource(PROFILE_MATERIAL, Quarkium);
_alterene = getResource("ResourceMaterial"_s, Alterene); _alterene = getResource(PROFILE_MATERIAL, Alterene);
_mixedComposition = getResource("ResourceQuarkData"_s, MixedComposition); _mixedComposition = getResource(PROFILE_QUARK_DATA, MixedComposition);
_voidResidue = getResource("ResourceQuarkData"_s, VoidResidue); _voidResidue = getResource(PROFILE_QUARK_DATA, VoidResidue);
_muscularConstruction = getResource("ResourceQuarkData"_s, MuscularConstruction); _muscularConstruction = getResource(PROFILE_QUARK_DATA, MuscularConstruction);
_mineralExoskeletology = getResource("ResourceQuarkData"_s, MineralExoskeletology); _mineralExoskeletology = getResource(PROFILE_QUARK_DATA, MineralExoskeletology);
_carbonisedSkin = getResource("ResourceQuarkData"_s, CarbonisedSkin); _carbonisedSkin = getResource(PROFILE_QUARK_DATA, CarbonisedSkin);
_valid = true; _valid = true;
} }
@ -162,9 +158,10 @@ 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>("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 false; return false;
} }
@ -188,11 +185,12 @@ auto Profile::credits() const -> Int {
} }
auto Profile::setCredits(Int amount) -> bool { auto Profile::setCredits(Int amount) -> bool {
auto credits_prop = _profile.at<IntProperty>("Credit"_s); auto credits_prop = _profile.at<IntProperty>(PROFILE_CREDITS);
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});
} }
@ -216,6 +214,7 @@ 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});
} }
@ -238,7 +237,7 @@ auto Profile::verseSteel() const -> Int {
} }
auto Profile::setVerseSteel(Int amount) -> bool { auto Profile::setVerseSteel(Int amount) -> bool {
return setResource("ResourceMaterial"_s, VerseSteel, amount); return setResource(PROFILE_MATERIAL, VerseSteel, amount);
} }
auto Profile::undinium() const -> Int { auto Profile::undinium() const -> Int {
@ -246,7 +245,7 @@ auto Profile::undinium() const -> Int {
} }
auto Profile::setUndinium(Int amount) -> bool { auto Profile::setUndinium(Int amount) -> bool {
return setResource("ResourceMaterial"_s, Undinium, amount); return setResource(PROFILE_MATERIAL, Undinium, amount);
} }
auto Profile::necriumAlloy() const -> Int { auto Profile::necriumAlloy() const -> Int {
@ -254,7 +253,7 @@ auto Profile::necriumAlloy() const -> Int {
} }
auto Profile::setNecriumAlloy(Int amount) -> bool { auto Profile::setNecriumAlloy(Int amount) -> bool {
return setResource("ResourceMaterial"_s, NecriumAlloy, amount); return setResource(PROFILE_MATERIAL, NecriumAlloy, amount);
} }
auto Profile::lunarite() const -> Int { auto Profile::lunarite() const -> Int {
@ -262,7 +261,7 @@ auto Profile::lunarite() const -> Int {
} }
auto Profile::setLunarite(Int amount) -> bool { auto Profile::setLunarite(Int amount) -> bool {
return setResource("ResourceMaterial"_s, Lunarite, amount); return setResource(PROFILE_MATERIAL, Lunarite, amount);
} }
auto Profile::asterite() const -> Int { auto Profile::asterite() const -> Int {
@ -270,7 +269,7 @@ auto Profile::asterite() const -> Int {
} }
auto Profile::setAsterite(Int amount) -> bool { auto Profile::setAsterite(Int amount) -> bool {
return setResource("ResourceMaterial"_s, Asterite, amount); return setResource(PROFILE_MATERIAL, Asterite, amount);
} }
auto Profile::ednil() const -> Int { auto Profile::ednil() const -> Int {
@ -278,7 +277,7 @@ auto Profile::ednil() const -> Int {
} }
auto Profile::setEdnil(Int amount) -> bool { auto Profile::setEdnil(Int amount) -> bool {
return setResource("ResourceMaterial"_s, Ednil, amount); return setResource(PROFILE_MATERIAL, Ednil, amount);
} }
auto Profile::nuflalt() const -> Int { auto Profile::nuflalt() const -> Int {
@ -286,7 +285,7 @@ auto Profile::nuflalt() const -> Int {
} }
auto Profile::setNuflalt(Int amount) -> bool { auto Profile::setNuflalt(Int amount) -> bool {
return setResource("ResourceMaterial"_s, Nuflalt, amount); return setResource(PROFILE_MATERIAL, Nuflalt, amount);
} }
auto Profile::aurelene() const -> Int { auto Profile::aurelene() const -> Int {
@ -294,7 +293,7 @@ auto Profile::aurelene() const -> Int {
} }
auto Profile::setAurelene(Int amount) -> bool { auto Profile::setAurelene(Int amount) -> bool {
return setResource("ResourceMaterial"_s, Aurelene, amount); return setResource(PROFILE_MATERIAL, Aurelene, amount);
} }
auto Profile::soldus() const -> Int { auto Profile::soldus() const -> Int {
@ -302,7 +301,7 @@ auto Profile::soldus() const -> Int {
} }
auto Profile::setSoldus(Int amount) -> bool { auto Profile::setSoldus(Int amount) -> bool {
return setResource("ResourceMaterial"_s, Soldus, amount); return setResource(PROFILE_MATERIAL, Soldus, amount);
} }
auto Profile::synthesisedN() const -> Int { auto Profile::synthesisedN() const -> Int {
@ -310,7 +309,7 @@ auto Profile::synthesisedN() const -> Int {
} }
auto Profile::setSynthesisedN(Int amount) -> bool { auto Profile::setSynthesisedN(Int amount) -> bool {
return setResource("ResourceMaterial"_s, SynthesisedN, amount); return setResource(PROFILE_MATERIAL, SynthesisedN, amount);
} }
auto Profile::alcarbonite() const -> Int { auto Profile::alcarbonite() const -> Int {
@ -318,7 +317,7 @@ auto Profile::alcarbonite() const -> Int {
} }
auto Profile::setAlcarbonite(Int amount) -> bool { auto Profile::setAlcarbonite(Int amount) -> bool {
return setResource("ResourceMaterial"_s, Alcarbonite, amount); return setResource(PROFILE_MATERIAL, Alcarbonite, amount);
} }
auto Profile::keriphene() const -> Int { auto Profile::keriphene() const -> Int {
@ -326,7 +325,7 @@ auto Profile::keriphene() const -> Int {
} }
auto Profile::setKeriphene(Int amount) -> bool { auto Profile::setKeriphene(Int amount) -> bool {
return setResource("ResourceMaterial"_s, Keriphene, amount); return setResource(PROFILE_MATERIAL, Keriphene, amount);
} }
auto Profile::nitinolCM() const -> Int { auto Profile::nitinolCM() const -> Int {
@ -334,7 +333,7 @@ auto Profile::nitinolCM() const -> Int {
} }
auto Profile::setNitinolCM(Int amount) -> bool { auto Profile::setNitinolCM(Int amount) -> bool {
return setResource("ResourceMaterial"_s, NitinolCM, amount); return setResource(PROFILE_MATERIAL, NitinolCM, amount);
} }
auto Profile::quarkium() const -> Int { auto Profile::quarkium() const -> Int {
@ -342,7 +341,7 @@ auto Profile::quarkium() const -> Int {
} }
auto Profile::setQuarkium(Int amount) -> bool { auto Profile::setQuarkium(Int amount) -> bool {
return setResource("ResourceMaterial"_s, Quarkium, amount); return setResource(PROFILE_MATERIAL, Quarkium, amount);
} }
auto Profile::alterene() const -> Int { auto Profile::alterene() const -> Int {
@ -350,7 +349,7 @@ auto Profile::alterene() const -> Int {
} }
auto Profile::setAlterene(Int amount) -> bool { auto Profile::setAlterene(Int amount) -> bool {
return setResource("ResourceMaterial"_s, Alterene, amount); return setResource(PROFILE_MATERIAL, Alterene, amount);
} }
auto Profile::mixedComposition() const -> Int { auto Profile::mixedComposition() const -> Int {
@ -358,7 +357,7 @@ auto Profile::mixedComposition() const -> Int {
} }
auto Profile::setMixedComposition(Int amount) -> bool { auto Profile::setMixedComposition(Int amount) -> bool {
return setResource("ResourceQuarkData"_s, MixedComposition, amount); return setResource(PROFILE_QUARK_DATA, MixedComposition, amount);
} }
auto Profile::voidResidue() const -> Int { auto Profile::voidResidue() const -> Int {
@ -366,7 +365,7 @@ auto Profile::voidResidue() const -> Int {
} }
auto Profile::setVoidResidue(Int amount) -> bool { auto Profile::setVoidResidue(Int amount) -> bool {
return setResource("ResourceQuarkData"_s, VoidResidue, amount); return setResource(PROFILE_QUARK_DATA, VoidResidue, amount);
} }
auto Profile::muscularConstruction() const -> Int { auto Profile::muscularConstruction() const -> Int {
@ -374,7 +373,7 @@ auto Profile::muscularConstruction() const -> Int {
} }
auto Profile::setMuscularConstruction(Int amount) -> bool { auto Profile::setMuscularConstruction(Int amount) -> bool {
return setResource("ResourceQuarkData"_s, MuscularConstruction, amount); return setResource(PROFILE_QUARK_DATA, MuscularConstruction, amount);
} }
auto Profile::mineralExoskeletology() const -> Int { auto Profile::mineralExoskeletology() const -> Int {
@ -382,7 +381,7 @@ auto Profile::mineralExoskeletology() const -> Int {
} }
auto Profile::setMineralExoskeletology(Int amount) -> bool { auto Profile::setMineralExoskeletology(Int amount) -> bool {
return setResource("ResourceQuarkData"_s, MineralExoskeletology, amount); return setResource(PROFILE_QUARK_DATA, MineralExoskeletology, amount);
} }
auto Profile::carbonisedSkin() const -> Int { auto Profile::carbonisedSkin() const -> Int {
@ -390,7 +389,7 @@ auto Profile::carbonisedSkin() const -> Int {
} }
auto Profile::setCarbonisedSkin(Int amount) -> bool { auto Profile::setCarbonisedSkin(Int amount) -> bool {
return setResource("ResourceQuarkData"_s, CarbonisedSkin, amount); return setResource(PROFILE_QUARK_DATA, CarbonisedSkin, amount);
} }
auto Profile::getResource(Containers::StringView container, MaterialID id) -> Int { auto Profile::getResource(Containers::StringView container, MaterialID id) -> Int {
@ -413,9 +412,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) {
_lastError = "Couldn't find "_s + container + " in "_s + _filename; mats_prop = new ArrayProperty;
_valid = false; mats_prop->name.emplace(container);
return false; _profile.appendProperty(ArrayProperty::ptr{mats_prop});
} }
auto predicate = [&id](UnrealPropertyBase::ptr& prop){ auto predicate = [&id](UnrealPropertyBase::ptr& prop){

View file

@ -33,11 +33,6 @@ 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);
@ -51,9 +46,6 @@ 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();
@ -138,7 +130,6 @@ class Profile {
Containers::String _filename; Containers::String _filename;
ProfileType _type; ProfileType _type;
ProfileVersion _version;
UESaveFile _profile; UESaveFile _profile;

View file

@ -0,0 +1,10 @@
#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,8 +18,6 @@
#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>
@ -29,6 +27,8 @@
#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,6 +53,8 @@ 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;
@ -61,14 +63,12 @@ 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{
std::regex legacy_regex("(Demo)?Profile[0-9]{17}\\.sav", std::regex::nosubs); return !((file.hasPrefix("DemoProfile") || file.hasPrefix("Profile")) && file.hasSuffix(".sav"));
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()) {
Utility::Warning{} << "Profile"_s << file << "is invalid:"_s << profile.lastError(); LOG_WARNING_FORMAT("Profile {} is invalid: {}", file, profile.lastError());
continue; continue;
} }
@ -86,6 +86,7 @@ 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;
} }
@ -101,6 +102,7 @@ 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;
} }
@ -129,7 +131,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}.mbprofbackup", auto filename = Utility::format("{}_{}{:.2d}{:.2d}_{:.2d}{:.2d}{:.2d}.backup.mbst",
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);
@ -140,26 +142,28 @@ 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());
@ -189,6 +193,7 @@ 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;
} }
@ -210,11 +215,12 @@ 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); return !(file.hasSuffix(".mbprofbackup"_s) || file.hasSuffix(".backup.mbst"));
}; };
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));
@ -252,21 +258,11 @@ void ProfileManager::refreshBackups() {
backup.company = info[0]; backup.company = info[0];
if(info[1] == "full") { if(info[1].hasPrefix("full")) {
backup.type = ProfileType::FullGame; backup.type = ProfileType::FullGame;
backup.version = ProfileVersion::Legacy;
} }
else if(info[1] == "demo") { else if(info[1].hasPrefix("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;
@ -297,6 +293,7 @@ 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;
} }
@ -323,6 +320,7 @@ 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;
} }
@ -332,6 +330,7 @@ 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;
} }
@ -340,6 +339,7 @@ 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,12 +351,14 @@ 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,7 +29,6 @@ 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,10 +14,12 @@
// 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>
@ -98,7 +100,11 @@ void SaveTool::fileUpdateEvent(SDL_Event& event) {
if(is_current_profile) { if(is_current_profile) {
_currentProfile = nullptr; _currentProfile = nullptr;
_uiState = UiState::ProfileManager; _uiState = UiState::ProfileManager;
_profileManager->refreshProfiles(); if(!_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,7 +52,8 @@ 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 in ProfileManager", _profileManager->lastError().data(), window()); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_profileManager->lastError().data(), window());
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
@ -95,9 +96,7 @@ void SaveTool::drawProfileManager() {
} }
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
ImGui::Text("%s%s", ImGui::TextUnformatted(profile.isDemo() ? "Demo" : "Full");
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)) {
@ -161,7 +160,11 @@ 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());
} }
_profileManager->refreshProfiles(); if(!_profileManager->refreshProfiles()) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_profileManager->lastError().data(), window());
exit(EXIT_FAILURE);
}
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
ImGui::SameLine(); ImGui::SameLine();
@ -278,9 +281,7 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
backup.timestamp.second); backup.timestamp.second);
ImGui::TableSetColumnIndex(2); ImGui::TableSetColumnIndex(2);
ImGui::Text("%s%s", ImGui::TextUnformatted(backup.type == ProfileType::Demo ? "Demo" : "Full");
backup.type == ProfileType::Demo ? "Demo" : "Full",
backup.version == ProfileVersion::Legacy ? " (legacy)" : "");
ImGui::TableSetColumnIndex(3); ImGui::TableSetColumnIndex(3);
ImGui::PushID(i); ImGui::PushID(i);
@ -340,12 +341,18 @@ auto SaveTool::drawBackupProfilePopup(std::size_t profile_index) -> ImGuiID {
ImGui::TableSetColumnIndex(1); ImGui::TableSetColumnIndex(1);
if(ImGui::Button("Yes")) { if(ImGui::Button("Yes")) {
_profileManager->backupProfile(profile_index, true); if(!_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())) {
_profileManager->backupProfile(profile_index, false); if(!_profileManager->backupProfile(profile_index, false)) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error",
_profileManager->lastError().data(), window());
}
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
ImGui::SameLine(); ImGui::SameLine();