// 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 . #include #include #include #include #include #include "PropertyNames.h" #include "../Logger/Logger.h" #include "../Gvas/Types/ArrayProperty.h" #include "../Gvas/Types/BoolProperty.h" #include "../Gvas/Types/ColourStructProperty.h" #include "../Gvas/Types/GenericStructProperty.h" #include "../Gvas/Types/IntProperty.h" #include "../Gvas/Types/StringProperty.h" #include "Mass.h" using namespace Containers::Literals; namespace mbst::GameObjects { Mass::Mass(Containers::StringView path) { auto split = Utility::Path::split(path); _folder = split.first(); _filename = split.second(); refreshValues(); } Containers::StringView Mass::lastError() { return _lastError; } Containers::Optional Mass::getNameFromFile(Containers::StringView path) { if(!Utility::Path::exists(path)) { LOG_ERROR_FORMAT("{} couldn't be found.", path); return Containers::NullOpt; } Gvas::File 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; } Containers::StringView Mass::filename() { return _filename; } Containers::StringView Mass::name() { CORRADE_INTERNAL_ASSERT(_name); return *_name; } bool Mass::setName(Containers::StringView new_name) { _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; } Mass::State Mass::state() { return _state; } bool Mass::dirty() const { 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; } } std::int32_t& Mass::engine() { return _tuning.engineId; } Containers::ArrayView Mass::gears() { return _tuning.gearIds; } std::int32_t& Mass::os() { return _tuning.osId; } Containers::ArrayView Mass::modules() { return _tuning.moduleIds; } std::int32_t& Mass::architecture() { return _tuning.archId; } Containers::ArrayView Mass::techs() { return _tuning.techIds; } Containers::StringView Mass::account() { return _account; } bool Mass::updateAccount(Containers::StringView new_account) { _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, std::int32_t& 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(std::uint32_t 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; } } }