// 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 . #include #include #include #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(PROFILE_ACCOUNT); if(!account_prop) { _lastError = "Couldn't find an account ID in "_s + _filename; _valid = false; return; } _account = account_prop->value; refreshValues(); } auto Profile::valid() const -> bool { return _valid; } auto Profile::lastError() const -> Containers::StringView { return _lastError; } auto Profile::filename() const -> Containers::StringView { return _filename; } auto Profile::type() const -> ProfileType { return _type; } auto Profile::isDemo() const -> bool { return _type == ProfileType::Demo; } auto Profile::account() const -> Containers::StringView { 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(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(PROFILE_ACTIVE_FRAME_SLOT); _activeFrameSlot = prop ? prop->value : 0; LOG_INFO("Getting the credits."); prop = _profile.at(PROFILE_CREDITS); _credits = prop ? prop->value : 0; LOG_INFO("Getting the story progress."); prop = _profile.at(PROFILE_STORY_PROGRESS); _storyProgress = prop ? prop->value : 0; LOG_INFO("Getting the last mission ID."); prop = _profile.at(PROFILE_LAST_MISSION_ID); _lastMissionId = prop ? prop->value : 0; 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); _halliteFragma = getResource(PROFILE_MATERIAL, HalliteFragma); _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); _nanoc = getResource(PROFILE_MATERIAL, Nanoc); _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); _cosmium = getResource(PROFILE_MATERIAL, Cosmium); _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); _isolatedVoidParticle = getResource(PROFILE_QUARK_DATA, IsolatedVoidParticle); _valid = true; } auto Profile::companyName() const -> Containers::StringView { return _name; } auto Profile::renameCompany(Containers::StringView new_name) -> bool { auto name_prop = _profile.at(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; } auto Profile::activeFrameSlot() const -> Int { return _activeFrameSlot; } auto Profile::credits() const -> Int { return _credits; } auto Profile::setCredits(Int amount) -> bool { auto credits_prop = _profile.at(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}); } credits_prop->value = amount; if(!_profile.saveToFile()) { _lastError = _profile.lastError(); return false; } return true; } auto Profile::storyProgress() const -> Int { return _storyProgress; } auto Profile::setStoryProgress(Int progress) -> bool { auto story_progress_prop = _profile.at("StoryProgress"_s); 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}); } story_progress_prop->value = progress; if(!_profile.saveToFile()) { _lastError = _profile.lastError(); return false; } return true; } auto Profile::lastMissionId() const -> Int { return _lastMissionId; } auto Profile::verseSteel() const -> Int { return _verseSteel; } auto Profile::setVerseSteel(Int amount) -> bool { return setResource(PROFILE_MATERIAL, VerseSteel, amount); } auto Profile::undinium() const -> Int { return _undinium; } auto Profile::setUndinium(Int amount) -> bool { return setResource(PROFILE_MATERIAL, Undinium, amount); } auto Profile::necriumAlloy() const -> Int { return _necriumAlloy; } auto Profile::setNecriumAlloy(Int amount) -> bool { return setResource(PROFILE_MATERIAL, NecriumAlloy, amount); } auto Profile::lunarite() const -> Int { return _lunarite; } auto Profile::setLunarite(Int amount) -> bool { return setResource(PROFILE_MATERIAL, Lunarite, amount); } auto Profile::asterite() const -> Int { return _asterite; } auto Profile::setAsterite(Int amount) -> bool { return setResource(PROFILE_MATERIAL, Asterite, amount); } Int Profile::halliteFragma() const { return _halliteFragma; } bool Profile::setHalliteFragma(Int amount) { return setResource(PROFILE_MATERIAL, HalliteFragma, amount); } auto Profile::ednil() const -> Int { return _ednil; } auto Profile::setEdnil(Int amount) -> bool { return setResource(PROFILE_MATERIAL, Ednil, amount); } auto Profile::nuflalt() const -> Int { return _nuflalt; } auto Profile::setNuflalt(Int amount) -> bool { return setResource(PROFILE_MATERIAL, Nuflalt, amount); } auto Profile::aurelene() const -> Int { return _aurelene; } auto Profile::setAurelene(Int amount) -> bool { return setResource(PROFILE_MATERIAL, Aurelene, amount); } auto Profile::soldus() const -> Int { return _soldus; } auto Profile::setSoldus(Int amount) -> bool { return setResource(PROFILE_MATERIAL, Soldus, amount); } auto Profile::synthesisedN() const -> Int { return _synthesisedN; } auto Profile::setSynthesisedN(Int amount) -> bool { return setResource(PROFILE_MATERIAL, SynthesisedN, amount); } Int Profile::nanoc() const { return _nanoc; } bool Profile::setNanoc(Int amount) { return setResource(PROFILE_MATERIAL, Nanoc, amount); } auto Profile::alcarbonite() const -> Int { return _alcarbonite; } auto Profile::setAlcarbonite(Int amount) -> bool { return setResource(PROFILE_MATERIAL, Alcarbonite, amount); } auto Profile::keriphene() const -> Int { return _keriphene; } auto Profile::setKeriphene(Int amount) -> bool { return setResource(PROFILE_MATERIAL, Keriphene, amount); } auto Profile::nitinolCM() const -> Int { return _nitinolCM; } auto Profile::setNitinolCM(Int amount) -> bool { return setResource(PROFILE_MATERIAL, NitinolCM, amount); } auto Profile::quarkium() const -> Int { return _quarkium; } auto Profile::setQuarkium(Int amount) -> bool { return setResource(PROFILE_MATERIAL, Quarkium, amount); } auto Profile::alterene() const -> Int { return _alterene; } auto Profile::setAlterene(Int amount) -> bool { return setResource(PROFILE_MATERIAL, Alterene, amount); } Int Profile::cosmium() const { return _cosmium; } bool Profile::setCosmium(Int amount) { return setResource(PROFILE_MATERIAL, Cosmium, amount); } auto Profile::mixedComposition() const -> Int { return _mixedComposition; } auto Profile::setMixedComposition(Int amount) -> bool { return setResource(PROFILE_QUARK_DATA, MixedComposition, amount); } auto Profile::voidResidue() const -> Int { return _voidResidue; } auto Profile::setVoidResidue(Int amount) -> bool { return setResource(PROFILE_QUARK_DATA, VoidResidue, amount); } auto Profile::muscularConstruction() const -> Int { return _muscularConstruction; } auto Profile::setMuscularConstruction(Int amount) -> bool { return setResource(PROFILE_QUARK_DATA, MuscularConstruction, amount); } auto Profile::mineralExoskeletology() const -> Int { return _mineralExoskeletology; } auto Profile::setMineralExoskeletology(Int amount) -> bool { return setResource(PROFILE_QUARK_DATA, MineralExoskeletology, amount); } auto Profile::carbonisedSkin() const -> Int { return _carbonisedSkin; } auto Profile::setCarbonisedSkin(Int amount) -> bool { return setResource(PROFILE_QUARK_DATA, CarbonisedSkin, amount); } Int Profile::isolatedVoidParticle() const { return _isolatedVoidParticle; } bool Profile::setIsolatedVoidParticle(Int amount) { return setResource(PROFILE_QUARK_DATA, IsolatedVoidParticle, amount); } auto Profile::getResource(Containers::StringView container, MaterialID id) -> Int { auto mats_prop = _profile.at(container); if(!mats_prop) { return 0; } auto predicate = [&id](UnrealPropertyBase::ptr& prop){ auto res_prop = static_cast(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(it->get())->quantity : 0; } auto Profile::setResource(Containers::StringView container, MaterialID id, Int amount) -> bool { auto mats_prop = _profile.at(container); if(!mats_prop) { mats_prop = new ArrayProperty; mats_prop->name.emplace(container); mats_prop->itemType = "StructProperty"; _profile.appendProperty(ArrayProperty::ptr{mats_prop}); } auto predicate = [&id](UnrealPropertyBase::ptr& prop){ auto res_prop = static_cast(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; if(mats_prop->items.isEmpty()) { res_prop->name.emplace(container); } res_prop->id = id; ResourceItemValue::ptr prop{res_prop}; arrayAppend(mats_prop->items, std::move(prop)); } else { res_prop = static_cast(it->get()); } res_prop->quantity = amount; if(!_profile.saveToFile()) { _lastError = _profile.lastError(); return false; } return true; }