// 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 "../Logger/Logger.h" #include "MassManager.h" using namespace Containers::Literals; namespace mbst { namespace Managers { MassManager::MassManager(Containers::StringView save_path, Containers::StringView account, bool demo, Containers::StringView staging_dir): _saveDirectory{save_path}, _account{account}, _demo{demo}, _stagingAreaDirectory{staging_dir} { Containers::String mass_filename = ""; for(std::uint32_t i = 0; i < _hangars.size(); i++) { mass_filename = Utility::Path::join(_saveDirectory, Utility::format("{}Unit{:.2d}{}.sav", demo ? "Demo"_s : ""_s, i, _account)); new(&_hangars[i]) GameObjects::Mass{mass_filename}; } refreshStagedMasses(); } Containers::StringView MassManager::lastError() { return _lastError; } GameObjects::Mass& MassManager::hangar(std::int32_t hangar) { return _hangars[hangar]; } void MassManager::refreshHangar(std::int32_t hangar) { if(hangar < 0 || hangar >= 32) { _lastError = "Hangar index out of range."; LOG_ERROR(_lastError); return; } Containers::String mass_filename = Utility::Path::join(_saveDirectory, Utility::format("{}Unit{:.2d}{}.sav", _demo ? "Demo" : "", hangar, _account)); _hangars[hangar] = GameObjects::Mass{mass_filename}; } bool MassManager::importMass(Containers::StringView staged_fn, std::int32_t hangar) { if(hangar < 0 || hangar >= 32) { _lastError = "Hangar index out of range."; LOG_ERROR(_lastError); return false; } auto it = _stagedMasses.find(Containers::String::nullTerminatedView(staged_fn)); if(it == _stagedMasses.end()) { _lastError = "Couldn't find "_s + staged_fn + " in the staged M.A.S.S.es."_s; LOG_ERROR(_lastError); return false; } Containers::String source = Utility::Path::join(_stagingAreaDirectory, staged_fn); Utility::Path::copy(source, source + ".tmp"_s); { GameObjects::Mass mass{source + ".tmp"_s}; if(!mass.updateAccount(_account)) { _lastError = mass.lastError(); Utility::Path::remove(source + ".tmp"_s); return false; } } Containers::String dest = Utility::Path::join(_saveDirectory, _hangars[hangar].filename()); if(Utility::Path::exists(dest)) { Utility::Path::remove(dest); } if(!Utility::Path::move(source + ".tmp"_s, dest)) { _lastError = Utility::format("Couldn't move {} to hangar {:.2d}", staged_fn, hangar + 1); LOG_ERROR(_lastError); return false; } return true; } bool MassManager::exportMass(std::int32_t hangar) { if(hangar < 0 || hangar >= 32) { _lastError = "Hangar index out of range."_s; LOG_ERROR(_lastError); return false; } if(_hangars[hangar].state() != GameObjects::Mass::State::Valid) { _lastError = Utility::format("There is no valid data to export in hangar {:.2d}", hangar + 1); LOG_ERROR(_lastError); return false; } Containers::String source = Utility::Path::join(_saveDirectory, _hangars[hangar].filename()); Containers::String dest = Utility::Path::join(_stagingAreaDirectory, Utility::format("{}_{}.sav", _hangars[hangar].name(), _account)); if(!Utility::Path::copy(source, dest)) { _lastError = Utility::format("Couldn't export data from hangar {:.2d} to {}", hangar, dest); LOG_ERROR(_lastError); return false; } return true; } bool MassManager::moveMass(std::int32_t source, std::int32_t destination) { if(source < 0 || source >= 32) { _lastError = "Source hangar index out of range."_s; LOG_ERROR(_lastError); return false; } if(destination < 0 || destination >= 32) { _lastError = "Destination hangar index out of range."_s; LOG_ERROR(_lastError); return false; } Containers::String source_file = Utility::Path::join(_saveDirectory, _hangars[source].filename()); Containers::String dest_file = Utility::Path::join(_saveDirectory, _hangars[destination].filename()); GameObjects::Mass::State dest_state = _hangars[destination].state(); switch(dest_state) { case GameObjects::Mass::State::Empty: break; case GameObjects::Mass::State::Invalid: Utility::Path::remove(dest_file); break; case GameObjects::Mass::State::Valid: Utility::Path::move(dest_file, dest_file + ".tmp"_s); break; } Utility::Path::move(source_file, dest_file); if(dest_state == GameObjects::Mass::State::Valid) { Utility::Path::move(dest_file + ".tmp"_s, source_file); } return true; } bool MassManager::deleteMass(std::int32_t hangar) { if(hangar < 0 || hangar >= 32) { _lastError = "Hangar index out of range."_s; LOG_ERROR(_lastError); return false; } if(!Utility::Path::remove(Utility::Path::join(_saveDirectory, _hangars[hangar].filename()))) { _lastError = Utility::format("Deletion failed: {}", std::strerror(errno)); LOG_ERROR(_lastError); return false; } return true; } const std::map& MassManager::stagedMasses() { return _stagedMasses; } void MassManager::refreshStagedMasses() { _stagedMasses.clear(); using Utility::Path::ListFlag; auto file_list = Utility::Path::list(_stagingAreaDirectory, ListFlag::SkipSpecial|ListFlag::SkipDirectories|ListFlag::SkipDotAndDotDot); if(!file_list) { LOG_ERROR_FORMAT("{} couldn't be opened.", _stagingAreaDirectory); return; } auto iter = std::remove_if(file_list->begin(), file_list->end(), [](Containers::StringView file){ return !file.hasSuffix(".sav"_s); }); auto list_view = file_list->exceptSuffix(file_list->end() - iter); LOG_INFO("Scanning for staged M.A.S.S.es..."); for(Containers::StringView file : list_view) { auto name = GameObjects::Mass::getNameFromFile(Utility::Path::join(_stagingAreaDirectory, file)); if(name) { LOG_INFO_FORMAT("Found staged M.A.S.S.: {}", *name); _stagedMasses[file] = *name; } else { LOG_WARNING_FORMAT("Skipped {}.", file); } } } void MassManager::refreshStagedMass(Containers::StringView filename) { LOG_INFO_FORMAT("Refreshing staged unit with filename {}.", filename); bool file_exists = Utility::Path::exists(Utility::Path::join(_stagingAreaDirectory, filename)); auto it = _stagedMasses.find(filename); if(file_exists) { auto name = GameObjects::Mass::getNameFromFile(Utility::Path::join(_stagingAreaDirectory, filename)); if(name) { _stagedMasses[filename] = *name; } else if(it != _stagedMasses.cend()) { _stagedMasses.erase(it); } } else if(it != _stagedMasses.cend()) { _stagedMasses.erase(it); } } bool MassManager::deleteStagedMass(Containers::StringView filename) { if(_stagedMasses.find(filename) == _stagedMasses.cend()) { _lastError = "The file "_s + filename + " couldn't be found in the list of staged M.A.S.S.es."_s; LOG_ERROR(_lastError); return false; } if(!Utility::Path::remove(Utility::Path::join(_stagingAreaDirectory, filename))) { _lastError = filename + " couldn't be deleted: " + std::strerror(errno); LOG_ERROR(_lastError); return false; } return true; } }}