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
Profile/Profile.h
Profile/Profile.cpp
Profile/PropertyNames.h
Profile/ResourceIDs.h
MassManager/MassManager.h
MassManager/MassManager.cpp

View File

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

View File

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

View File

@ -33,11 +33,6 @@ enum class ProfileType : UnsignedByte {
FullGame
};
enum class ProfileVersion : UnsignedByte {
Legacy, // pre-0.8
Normal // 0.8 and later
};
class Profile {
public:
explicit Profile(Containers::StringView path);
@ -51,9 +46,6 @@ class Profile {
auto type() const -> ProfileType;
auto isDemo() const -> bool;
auto version() const -> ProfileVersion;
auto isLegacy() const -> bool;
auto account() const -> Containers::StringView;
void refreshValues();
@ -138,7 +130,6 @@ class Profile {
Containers::String _filename;
ProfileType _type;
ProfileVersion _version;
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 <chrono>
#include <iomanip>
#include <regex>
#include <Corrade/Containers/ScopeGuard.h>
#include <Corrade/Containers/StaticArray.h>
@ -29,6 +27,8 @@
#include <zip.h>
#include "../Logger/Logger.h"
#include "ProfileManager.h"
using namespace Containers::Literals;
@ -53,6 +53,8 @@ auto ProfileManager::profiles() -> Containers::ArrayView<Profile> {
}
auto ProfileManager::refreshProfiles() -> bool {
LOG_INFO("Refreshing profiles.");
_profiles = Containers::Array<Profile>{};
using Utility::Path::ListFlag;
@ -61,14 +63,12 @@ auto ProfileManager::refreshProfiles() -> bool {
if(!files) {
_lastError = _saveDirectory + " can't be opened.";
LOG_ERROR(_lastError);
return false;
}
auto predicate = [](Containers::StringView file)->bool{
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);
return !((file.hasPrefix("DemoProfile") || file.hasPrefix("Profile")) && file.hasSuffix(".sav"));
};
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)};
if(!profile.valid()) {
Utility::Warning{} << "Profile"_s << file << "is invalid:"_s << profile.lastError();
LOG_WARNING_FORMAT("Profile {} is invalid: {}", file, profile.lastError());
continue;
}
@ -86,6 +86,7 @@ auto ProfileManager::refreshProfiles() -> bool {
if(_profiles.isEmpty()) {
_lastError = "No valid profiles were found."_s;
LOG_ERROR(_lastError);
return false;
}
@ -101,6 +102,7 @@ auto ProfileManager::deleteProfile(std::size_t index, bool delete_builds) -> boo
_lastError = Utility::format("Couldn't delete {} (filename: {}).",
_profiles[index].companyName(),
_profiles[index].filename());
LOG_ERROR(_lastError);
refreshProfiles();
return false;
}
@ -129,7 +131,7 @@ auto ProfileManager::backupProfile(std::size_t index, bool backup_builds) -> boo
std::tm* time = std::localtime(&timestamp);
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(),
time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
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) {
zip_error_init_with_code(&error, error_code);
_lastError = zip_error_strerror(&error);
LOG_ERROR(_lastError);
return false;
}
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) {
_lastError = zip_strerror(zip);
LOG_ERROR(_lastError);
zip_source_free(profile_source);
return false;
}
if(zip_file_add(zip, profile.filename().data(), profile_source, ZIP_FL_ENC_UTF_8) == -1) {
_lastError = zip_strerror(zip);
LOG_ERROR(_lastError);
zip_source_free(profile_source);
return false;
}
auto comment = Utility::format("{}|{}{}|{}-{:.2d}-{:.2d}-{:.2d}-{:.2d}-{:.2d}",
auto comment = Utility::format("{}|{}|{}-{:.2d}-{:.2d}-{:.2d}-{:.2d}-{:.2d}",
profile.companyName(),
profile.isDemo() ? "demo"_s : "full"_s,
profile.isLegacy() ? ""_s : "_new"_s,
time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
time->tm_hour, time->tm_min, time->tm_sec);
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) {
_lastError = zip_strerror(zip);
LOG_ERROR(_lastError);
return false;
}
@ -210,11 +215,12 @@ void ProfileManager::refreshBackups() {
if(!files) {
_lastError = _backupsDirectory + " can't be opened.";
LOG_ERROR(_lastError);
return;
}
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));
@ -252,21 +258,11 @@ void ProfileManager::refreshBackups() {
backup.company = info[0];
if(info[1] == "full") {
if(info[1].hasPrefix("full")) {
backup.type = ProfileType::FullGame;
backup.version = ProfileVersion::Legacy;
}
else if(info[1] == "demo") {
else if(info[1].hasPrefix("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 {
continue;
@ -297,6 +293,7 @@ void ProfileManager::refreshBackups() {
auto ProfileManager::deleteBackup(std::size_t index) -> bool {
if(!Utility::Path::remove(Utility::Path::join(_backupsDirectory, _backups[index].filename))) {
_lastError = "Couldn't delete " + _backups[index].filename;
LOG_ERROR(_lastError);
return false;
}
@ -323,6 +320,7 @@ auto ProfileManager::restoreBackup(std::size_t index) -> bool {
zip_error_t error;
zip_error_init_with_code(&error, error_code);
_lastError = zip_error_strerror(&error);
LOG_ERROR(_lastError);
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");
if(out == nullptr) {
_lastError = Utility::format(error_format.data(), file, std::strerror(errno));
LOG_ERROR(_lastError);
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);
if(zf == nullptr) {
_lastError = Utility::format(error_format.data(), file, zip_strerror(zip));
LOG_ERROR(_lastError);
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) {
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.");
LOG_ERROR(_lastError);
return false;
}
}
if(bytes_read == -1) {
_lastError = Utility::format(error_format.data(), file, "couldn't read bytes from archive.");
LOG_ERROR(_lastError);
return false;
}
}

View File

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

View File

@ -14,10 +14,12 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <Corrade/Utility/Format.h>
#include <Corrade/Utility/String.h>
#include <Corrade/Utility/Unicode.h>
#include <SDL_events.h>
#include <SDL_messagebox.h>
#include <fileapi.h>
#include <handleapi.h>
@ -98,7 +100,11 @@ void SaveTool::fileUpdateEvent(SDL_Event& event) {
if(is_current_profile) {
_currentProfile = nullptr;
_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) {
if(!_currentMass || _currentMass != &(_massManager->hangar(index))) {

View File

@ -52,7 +52,8 @@ void SaveTool::drawProfileManager() {
ImGui::TableSetColumnIndex(1);
if(ImGui::SmallButton("Refresh")) {
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);
}
}
@ -95,9 +96,7 @@ void SaveTool::drawProfileManager() {
}
ImGui::TableSetColumnIndex(1);
ImGui::Text("%s%s",
profile.isDemo() ? "Demo" : "Full",
profile.isLegacy() ? " (legacy)" : "");
ImGui::TextUnformatted(profile.isDemo() ? "Demo" : "Full");
ImGui::TableSetColumnIndex(2);
if(ImGui::SmallButton(ICON_FA_FILE_ARCHIVE)) {
@ -161,7 +160,11 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
if(!_profileManager->restoreBackup(backup_index)) {
_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::SameLine();
@ -278,9 +281,7 @@ auto SaveTool::drawBackupListPopup() -> ImGuiID {
backup.timestamp.second);
ImGui::TableSetColumnIndex(2);
ImGui::Text("%s%s",
backup.type == ProfileType::Demo ? "Demo" : "Full",
backup.version == ProfileVersion::Legacy ? " (legacy)" : "");
ImGui::TextUnformatted(backup.type == ProfileType::Demo ? "Demo" : "Full");
ImGui::TableSetColumnIndex(3);
ImGui::PushID(i);
@ -340,12 +341,18 @@ auto SaveTool::drawBackupProfilePopup(std::size_t profile_index) -> ImGuiID {
ImGui::TableSetColumnIndex(1);
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::SameLine();
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::SameLine();