// 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 #include #include "PropertyNames.h" #include "../Logger/Logger.h" #include "../UESaveFile/Types/ArrayProperty.h" #include "../UESaveFile/Types/BoolProperty.h" #include "../UESaveFile/Types/ColourStructProperty.h" #include "../UESaveFile/Types/GenericStructProperty.h" #include "../UESaveFile/Types/IntProperty.h" #include "../UESaveFile/Types/StringProperty.h" #include "Mass.h" using namespace Containers::Literals; Mass::Mass(Containers::StringView path) { auto split = Utility::Path::split(path); _folder = split.first(); _filename = split.second(); refreshValues(); } auto Mass::lastError() -> Containers::StringView { return _lastError; } auto Mass::getNameFromFile(Containers::StringView path) -> Containers::Optional { if(!Utility::Path::exists(path)) { LOG_ERROR_FORMAT("{} couldn't be found.", path); return Containers::NullOpt; } UESaveFile mass{path}; if(!mass.valid()) { LOG_ERROR_FORMAT("{} is invalid: {}", path, mass.lastError()); return Containers::NullOpt; } auto unit_data = mass.at(MASS_UNIT_DATA); if(!unit_data) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, path); return Containers::NullOpt; } auto name_prop = unit_data->at(MASS_NAME); if(!name_prop) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_NAME, path); return Containers::NullOpt; } return {name_prop->value}; } void Mass::refreshValues() { LOG_INFO_FORMAT("Refreshing values for {}.", _filename); logger().lockMutex(); logger().indent(); logger().unlockMutex(); Containers::ScopeGuard indent_guard{[]{ logger().lockMutex(); logger().unindent(); logger().unlockMutex(); }}; LOG_INFO("Checking if file exists."); if(!Utility::Path::exists(Utility::Path::join(_folder, _filename))) { LOG_WARNING_FORMAT("{} doesn't exist in {}.", _filename, _folder); _state = State::Empty; return; } if(!_mass) { LOG_INFO("Reading the GVAS save."); _mass.emplace(Utility::Path::join(_folder, _filename)); if(!_mass->valid()) { LOG_ERROR(_mass->lastError()); _state = State::Invalid; return; } } else { LOG_INFO("Reloading the GVAS data."); if(!_mass->reloadData()) { LOG_ERROR(_mass->lastError()); _state = State::Invalid; return; } } LOG_INFO("Checking the save file type."); if(_mass->saveType() != "/Game/Core/Save/bpSaveGameUnit.bpSaveGameUnit_C"_s) { LOG_ERROR_FORMAT("{} is not a valid unit save.", _filename); _state = State::Invalid; return; } LOG_INFO("Getting the unit data."); auto unit_data = _mass->at(MASS_UNIT_DATA); if(!unit_data) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename); _state = State::Invalid; return; } LOG_INFO("Reading the M.A.S.S. name."); auto name_prop = unit_data->at(MASS_NAME); if(!name_prop) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_NAME, _filename); _name = Containers::NullOpt; _state = State::Invalid; return; } _name = {name_prop->value}; getJointSliders(); if(_state == State::Invalid) { return; } getFrameStyles(); if(_state == State::Invalid) { return; } getEyeFlareColour(); if(_state == State::Invalid) { return; } getFrameCustomStyles(); if(_state == State::Invalid) { return; } getArmourParts(); if(_state == State::Invalid) { return; } getBulletLauncherAttachments(); if(_state == State::Invalid) { return; } getArmourCustomStyles(); if(_state == State::Invalid) { return; } getMeleeWeapons(); if(_state == State::Invalid) { return; } getShields(); if(_state == State::Invalid) { return; } getBulletShooters(); if(_state == State::Invalid) { return; } getEnergyShooters(); if(_state == State::Invalid) { return; } getBulletLaunchers(); if(_state == State::Invalid) { return; } getEnergyLaunchers(); if(_state == State::Invalid) { return; } getGlobalStyles(); if(_state == State::Invalid) { return; } getTuning(); if(_state == State::Invalid) { return; } LOG_INFO("Getting the associated account."); auto account_prop = _mass->at("Account"_s); if(!account_prop) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_ACCOUNT, _filename); _state = State::Invalid; return; } _account = account_prop->value; _state = State::Valid; } auto Mass::filename() -> Containers::StringView { return _filename; } auto Mass::name() -> Containers::StringView { CORRADE_INTERNAL_ASSERT(_name); return *_name; } auto Mass::setName(Containers::StringView new_name) -> bool { _name = {new_name}; auto unit_data = _mass->at("UnitData"_s); if(!unit_data) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_UNIT_DATA, _filename); _state = State::Invalid; return false; } auto name_prop = unit_data->at("Name_45_A037C5D54E53456407BDF091344529BB"_s); if(!name_prop) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", MASS_NAME, _filename); _state = State::Invalid; return false; } name_prop->value = new_name; if(!_mass->saveToFile()) { _lastError = _mass->lastError(); return false; } return true; } auto Mass::state() -> State { return _state; } auto Mass::dirty() const -> bool { return _dirty; } void Mass::setDirty(bool dirty) { _dirty = dirty; } void Mass::getTuning() { getTuningCategory(MASS_ENGINE, _tuning.engineId, MASS_GEARS, _tuning.gearIds); if(_state == State::Invalid) { return; } getTuningCategory(MASS_OS, _tuning.osId, MASS_MODULES, _tuning.moduleIds); if(_state == State::Invalid) { return; } getTuningCategory(MASS_ARCHITECT, _tuning.archId, MASS_TECHS, _tuning.techIds); if(_state == State::Invalid) { return; } } auto Mass::engine() -> Int& { return _tuning.engineId; } auto Mass::gears() -> Containers::ArrayView { return _tuning.gearIds; } auto Mass::os() -> Int& { return _tuning.osId; } auto Mass::modules() -> Containers::ArrayView { return _tuning.moduleIds; } auto Mass::architecture() -> Int& { return _tuning.archId; } auto Mass::techs() -> Containers::ArrayView { return _tuning.techIds; } auto Mass::account() -> Containers::StringView { return _account; } auto Mass::updateAccount(Containers::StringView new_account) -> bool { _account = new_account; auto account = _mass->at(MASS_ACCOUNT); if(!account) { _lastError = "Couldn't find the " MASS_ACCOUNT " property."_s; _state = State::Invalid; return false; } account->value = new_account; if(!_mass->saveToFile()) { _lastError = _mass->lastError(); return false; } return true; } void Mass::getTuningCategory(Containers::StringView big_node_prop_name, Int& big_node_id, Containers::StringView small_nodes_prop_name, Containers::ArrayView small_nodes_ids) { LOG_INFO_FORMAT("Getting tuning data ({}, {}).", big_node_prop_name, small_nodes_prop_name); auto node_id = _mass->at(big_node_prop_name); if(!node_id) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", big_node_prop_name, _filename); _state = State::Invalid; return; } big_node_id = node_id->value; auto node_ids = _mass->at(small_nodes_prop_name); if(!node_ids) { LOG_ERROR_FORMAT("Couldn't find {} in {}.", small_nodes_prop_name, _filename); _state = State::Invalid; return; } if(node_ids->items.size() != small_nodes_ids.size()) { LOG_ERROR_FORMAT("Node ID arrays are not of the same size. Expected {}, got {} instead.", small_nodes_ids.size(), node_ids->items.size()); _state = State::Invalid; return; } for(UnsignedInt i = 0; i < small_nodes_ids.size(); i++) { auto small_node_id = node_ids->at(i); CORRADE_INTERNAL_ASSERT(small_node_id); small_nodes_ids[i] = small_node_id->value; } }