MassBuilderSaveTool/src/Profile/Profile.cpp

314 lines
9.0 KiB
C++

// MassBuilderSaveTool
// Copyright (C) 2021-2022 Guillaume Jacquemin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <algorithm>
#include <Corrade/Containers/Pair.h>
#include <Corrade/Utility/Path.h>
#include "PropertyNames.h"
#include "../Logger/Logger.h"
#include "../UESaveFile/Types/ArrayProperty.h"
#include "../UESaveFile/Types/ResourceItemValue.h"
#include "../UESaveFile/Types/IntProperty.h"
#include "../UESaveFile/Types/StringProperty.h"
#include "Profile.h"
using namespace Corrade;
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;
}
_filename = Utility::Path::split(path).second();
if(_filename.hasPrefix("Demo"_s)) {
_type = ProfileType::Demo;
}
else {
_type = ProfileType::FullGame;
}
auto account_prop = _profile.at<StringProperty>(PROFILE_ACCOUNT);
if(!account_prop) {
_lastError = "Couldn't find an account ID in "_s + _filename;
_valid = false;
return;
}
_account = account_prop->value;
refreshValues();
}
bool
Profile::valid() const {
return _valid;
}
Containers::StringView
Profile::lastError() const {
return _lastError;
}
Containers::StringView
Profile::filename() const {
return _filename;
}
ProfileType
Profile::type() const {
return _type;
}
bool
Profile::isDemo() const {
return _type == ProfileType::Demo;
}
Containers::StringView
Profile::account() const {
return _account;
}
void
Profile::refreshValues() {
if(!_profile.reloadData()) {
LOG_ERROR(_profile.lastError());
_valid = false;
return;
}
if(_profile.saveType() != "/Game/Core/Save/bpSaveGameProfile.bpSaveGameProfile_C"_s) {
LOG_ERROR_FORMAT("{} is not a valid profile save.", _filename);
_valid = false;
return;
}
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;
LOG_INFO("Getting the active frame slot.");
auto prop = _profile.at<IntProperty>(PROFILE_ACTIVE_FRAME_SLOT);
_activeFrameSlot = prop ? prop->value : 0;
LOG_INFO("Getting the credits.");
prop = _profile.at<IntProperty>(PROFILE_CREDITS);
_credits = prop ? prop->value : 0;
LOG_INFO("Getting the story progress.");
prop = _profile.at<IntProperty>(PROFILE_STORY_PROGRESS);
_storyProgress = prop ? prop->value : 0;
LOG_INFO("Getting the last mission ID.");
prop = _profile.at<IntProperty>(PROFILE_LAST_MISSION_ID);
_lastMissionId = prop ? prop->value : 0;
LOG_INFO("Getting the materials.");
_materials[VerseSteel] = getResource(PROFILE_MATERIAL, VerseSteel);
_materials[Undinium] = getResource(PROFILE_MATERIAL, Undinium);
_materials[NecriumAlloy] = getResource(PROFILE_MATERIAL, NecriumAlloy);
_materials[Lunarite] = getResource(PROFILE_MATERIAL, Lunarite);
_materials[Asterite] = getResource(PROFILE_MATERIAL, Asterite);
_materials[HalliteFragma] = getResource(PROFILE_MATERIAL, HalliteFragma);
_materials[Ednil] = getResource(PROFILE_MATERIAL, Ednil);
_materials[Nuflalt] = getResource(PROFILE_MATERIAL, Nuflalt);
_materials[Aurelene] = getResource(PROFILE_MATERIAL, Aurelene);
_materials[Soldus] = getResource(PROFILE_MATERIAL, Soldus);
_materials[SynthesisedN] = getResource(PROFILE_MATERIAL, SynthesisedN);
_materials[Nanoc] = getResource(PROFILE_MATERIAL, Nanoc);
_materials[Alcarbonite] = getResource(PROFILE_MATERIAL, Alcarbonite);
_materials[Keriphene] = getResource(PROFILE_MATERIAL, Keriphene);
_materials[NitinolCM] = getResource(PROFILE_MATERIAL, NitinolCM);
_materials[Quarkium] = getResource(PROFILE_MATERIAL, Quarkium);
_materials[Alterene] = getResource(PROFILE_MATERIAL, Alterene);
_materials[Cosmium] = getResource(PROFILE_MATERIAL, Cosmium);
_materials[MixedComposition] = getResource(PROFILE_QUARK_DATA, MixedComposition);
_materials[VoidResidue] = getResource(PROFILE_QUARK_DATA, VoidResidue);
_materials[MuscularConstruction] = getResource(PROFILE_QUARK_DATA, MuscularConstruction);
_materials[MineralExoskeletology] = getResource(PROFILE_QUARK_DATA, MineralExoskeletology);
_materials[CarbonisedSkin] = getResource(PROFILE_QUARK_DATA, CarbonisedSkin);
_materials[IsolatedVoidParticle] = getResource(PROFILE_QUARK_DATA, IsolatedVoidParticle);
_valid = true;
}
Containers::StringView
Profile::companyName() const {
return _name;
}
bool
Profile::renameCompany(Containers::StringView new_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 false;
}
name_prop->value = new_name;
if(!_profile.saveToFile()) {
_lastError = _profile.lastError();
return false;
}
return true;
}
std::int32_t
Profile::activeFrameSlot() const {
return _activeFrameSlot;
}
std::int32_t
Profile::credits() const {
return _credits;
}
bool
Profile::setCredits(std::int32_t amount) {
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(std::int32_t);
_profile.appendProperty(IntProperty::ptr{credits_prop});
}
credits_prop->value = amount;
if(!_profile.saveToFile()) {
_lastError = _profile.lastError();
return false;
}
return true;
}
std::int32_t
Profile::storyProgress() const {
return _storyProgress;
}
bool
Profile::setStoryProgress(std::int32_t progress) {
auto story_progress_prop = _profile.at<IntProperty>("StoryProgress"_s);
if(!story_progress_prop) {
story_progress_prop = new IntProperty;
story_progress_prop->name.emplace("StoryProgress"_s);
story_progress_prop->valueLength = sizeof(std::int32_t);
_profile.appendProperty(IntProperty::ptr{story_progress_prop});
}
story_progress_prop->value = progress;
if(!_profile.saveToFile()) {
_lastError = _profile.lastError();
return false;
}
return true;
}
std::int32_t
Profile::lastMissionId() const {
return _lastMissionId;
}
std::int32_t
Profile::material(MaterialID id) const {
return _materials.at(id);
}
bool
Profile::setMaterial(MaterialID id, std::int32_t amount) {
Containers::StringView container = id > MixedComposition ? PROFILE_QUARK_DATA : PROFILE_MATERIAL;
auto mats_prop = _profile.at<ArrayProperty>(container);
if(!mats_prop) {
mats_prop = new ArrayProperty;
mats_prop->name.emplace(container);
_profile.appendProperty(ArrayProperty::ptr{mats_prop});
}
auto predicate = [&id](UnrealPropertyBase::ptr& prop){
auto res_prop = static_cast<ResourceItemValue*>(prop.get());
return res_prop->id == id;
};
auto it = std::find_if(mats_prop->items.begin(), mats_prop->items.end(), predicate);
ResourceItemValue* res_prop;
if(it == mats_prop->items.end()) {
res_prop = new ResourceItemValue;
res_prop->id = id;
ResourceItemValue::ptr prop{res_prop};
arrayAppend(mats_prop->items, std::move(prop));
}
else {
res_prop = static_cast<ResourceItemValue*>(it->get());
}
res_prop->quantity = amount;
if(!_profile.saveToFile()) {
_lastError = _profile.lastError();
return false;
}
return true;
}
std::int32_t
Profile::getResource(Containers::StringView container, MaterialID id) {
auto mats_prop = _profile.at<ArrayProperty>(container);
if(!mats_prop) {
return 0;
}
auto predicate = [&id](UnrealPropertyBase::ptr& prop){
auto res_prop = static_cast<ResourceItemValue*>(prop.get());
return res_prop->id == id;
};
auto it = std::find_if(mats_prop->items.begin(), mats_prop->items.end(), predicate);
return it != mats_prop->items.end() ? static_cast<ResourceItemValue*>(it->get())->quantity : 0;
}