322 lines
10 KiB
C++
322 lines
10 KiB
C++
// MassBuilderSaveTool
|
|
// Copyright (C) 2021-2024 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 "../Gvas/Types/ArrayProperty.h"
|
|
#include "../Gvas/Types/ResourceItemValue.h"
|
|
#include "../Gvas/Types/IntProperty.h"
|
|
#include "../Gvas/Types/StringProperty.h"
|
|
|
|
#include "Profile.h"
|
|
|
|
using namespace Corrade;
|
|
using namespace Containers::Literals;
|
|
|
|
namespace mbst { namespace GameObjects {
|
|
|
|
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 = Type::Demo;
|
|
}
|
|
else {
|
|
_type = Type::FullGame;
|
|
}
|
|
|
|
auto account_prop = _profile.at<Gvas::Types::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;
|
|
}
|
|
|
|
Profile::Type
|
|
Profile::type() const {
|
|
return _type;
|
|
}
|
|
|
|
bool
|
|
Profile::isDemo() const {
|
|
return _type == Type::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<Gvas::Types::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<Gvas::Types::IntProperty>(PROFILE_ACTIVE_FRAME_SLOT);
|
|
_activeFrameSlot = prop ? prop->value : 0;
|
|
|
|
LOG_INFO("Getting the credits.");
|
|
prop = _profile.at<Gvas::Types::IntProperty>(PROFILE_CREDITS);
|
|
_credits = prop ? prop->value : 0;
|
|
|
|
LOG_INFO("Getting the story progress.");
|
|
prop = _profile.at<Gvas::Types::IntProperty>(PROFILE_STORY_PROGRESS);
|
|
_storyProgress = prop ? prop->value : 0;
|
|
|
|
LOG_INFO("Getting the last mission ID.");
|
|
prop = _profile.at<Gvas::Types::IntProperty>(PROFILE_LAST_MISSION_ID);
|
|
_lastMissionId = prop ? prop->value : 0;
|
|
|
|
LOG_INFO("Getting the materials.");
|
|
_materials[GameData::MaterialID::VerseSteel] = getResource(PROFILE_MATERIAL, GameData::MaterialID::VerseSteel);
|
|
_materials[GameData::MaterialID::Undinium] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Undinium);
|
|
_materials[GameData::MaterialID::NecriumAlloy] = getResource(PROFILE_MATERIAL, GameData::MaterialID::NecriumAlloy);
|
|
_materials[GameData::MaterialID::Lunarite] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Lunarite);
|
|
_materials[GameData::MaterialID::Asterite] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Asterite);
|
|
_materials[GameData::MaterialID::HalliteFragma] = getResource(PROFILE_MATERIAL, GameData::MaterialID::HalliteFragma);
|
|
|
|
_materials[GameData::MaterialID::Ednil] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Ednil);
|
|
_materials[GameData::MaterialID::Nuflalt] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Nuflalt);
|
|
_materials[GameData::MaterialID::Aurelene] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Aurelene);
|
|
_materials[GameData::MaterialID::Soldus] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Soldus);
|
|
_materials[GameData::MaterialID::SynthesisedN] = getResource(PROFILE_MATERIAL, GameData::MaterialID::SynthesisedN);
|
|
_materials[GameData::MaterialID::Nanoc] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Nanoc);
|
|
|
|
_materials[GameData::MaterialID::Alcarbonite] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Alcarbonite);
|
|
_materials[GameData::MaterialID::Keriphene] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Keriphene);
|
|
_materials[GameData::MaterialID::NitinolCM] = getResource(PROFILE_MATERIAL, GameData::MaterialID::NitinolCM);
|
|
_materials[GameData::MaterialID::Quarkium] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Quarkium);
|
|
_materials[GameData::MaterialID::Alterene] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Alterene);
|
|
_materials[GameData::MaterialID::Cosmium] = getResource(PROFILE_MATERIAL, GameData::MaterialID::Cosmium);
|
|
|
|
_materials[GameData::MaterialID::MixedComposition] = getResource(PROFILE_QUARK_DATA, GameData::MaterialID::MixedComposition);
|
|
_materials[GameData::MaterialID::VoidResidue] = getResource(PROFILE_QUARK_DATA, GameData::MaterialID::VoidResidue);
|
|
_materials[GameData::MaterialID::MuscularConstruction] = getResource(PROFILE_QUARK_DATA, GameData::MaterialID::MuscularConstruction);
|
|
_materials[GameData::MaterialID::MineralExoskeletology] = getResource(PROFILE_QUARK_DATA, GameData::MaterialID::MineralExoskeletology);
|
|
_materials[GameData::MaterialID::CarbonisedSkin] = getResource(PROFILE_QUARK_DATA, GameData::MaterialID::CarbonisedSkin);
|
|
_materials[GameData::MaterialID::IsolatedVoidParticle] = getResource(PROFILE_QUARK_DATA, GameData::MaterialID::IsolatedVoidParticle);
|
|
|
|
_valid = true;
|
|
}
|
|
|
|
Containers::StringView
|
|
Profile::companyName() const {
|
|
return _name;
|
|
}
|
|
|
|
bool
|
|
Profile::renameCompany(Containers::StringView new_name) {
|
|
auto name_prop = _profile.at<Gvas::Types::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<Gvas::Types::IntProperty>(PROFILE_CREDITS);
|
|
|
|
if(!credits_prop) {
|
|
credits_prop = new Gvas::Types::IntProperty;
|
|
credits_prop->name.emplace("Credit"_s);
|
|
credits_prop->valueLength = sizeof(std::int32_t);
|
|
_profile.appendProperty(Gvas::Types::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<Gvas::Types::IntProperty>("StoryProgress"_s);
|
|
|
|
if(!story_progress_prop) {
|
|
story_progress_prop = new Gvas::Types::IntProperty;
|
|
story_progress_prop->name.emplace("StoryProgress"_s);
|
|
story_progress_prop->valueLength = sizeof(std::int32_t);
|
|
_profile.appendProperty(Gvas::Types::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(GameData::MaterialID id) const {
|
|
return _materials.at(id);
|
|
}
|
|
|
|
bool
|
|
Profile::setMaterial(GameData::MaterialID id, std::int32_t amount) {
|
|
Containers::StringView container = id > GameData::MaterialID::MixedComposition ? PROFILE_QUARK_DATA : PROFILE_MATERIAL;
|
|
auto mats_prop = _profile.at<Gvas::Types::ArrayProperty>(container);
|
|
|
|
if(!mats_prop) {
|
|
mats_prop = new Gvas::Types::ArrayProperty;
|
|
mats_prop->name.emplace(container);
|
|
mats_prop->itemType = "StructProperty";
|
|
_profile.appendProperty(Gvas::Types::ArrayProperty::ptr{mats_prop});
|
|
}
|
|
|
|
auto predicate = [&id](Gvas::Types::UnrealPropertyBase::ptr& prop){
|
|
auto res_prop = dynamic_cast<Gvas::Types::ResourceItemValue*>(prop.get());
|
|
return res_prop->id == id;
|
|
};
|
|
|
|
auto it = std::find_if(mats_prop->items.begin(), mats_prop->items.end(), predicate);
|
|
|
|
Gvas::Types::ResourceItemValue* res_prop;
|
|
if(it == mats_prop->items.end()) {
|
|
res_prop = new Gvas::Types::ResourceItemValue;
|
|
if(mats_prop->items.isEmpty()) {
|
|
res_prop->name.emplace(container);
|
|
}
|
|
res_prop->id = id;
|
|
Gvas::Types::ResourceItemValue::ptr prop{res_prop};
|
|
arrayAppend(mats_prop->items, Utility::move(prop));
|
|
}
|
|
else {
|
|
res_prop = dynamic_cast<Gvas::Types::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, GameData::MaterialID id) {
|
|
auto mats_prop = _profile.at<Gvas::Types::ArrayProperty>(container);
|
|
|
|
if(!mats_prop) {
|
|
return 0;
|
|
}
|
|
|
|
auto predicate = [&id](Gvas::Types::UnrealPropertyBase::ptr& prop){
|
|
auto res_prop = dynamic_cast<Gvas::Types::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() ? dynamic_cast<Gvas::Types::ResourceItemValue*>(it->get())->quantity : 0;
|
|
}
|
|
|
|
}}
|