// 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 "../BinaryIo/Reader.h" #include "../Configuration/Configuration.h" #include "../Logger/Logger.h" #include "../Utilities/Crc32.h" #include "Keys.h" #include "Import.h" namespace mbst::ImportExport { static Containers::String last_import_error; Containers::StringView lastImportError() { return last_import_error; } Containers::Optional importStyle(Containers::StringView filename) { auto path = Utility::Path::join(conf().directories().styles, filename); if(!Utility::Path::exists(path)) { LOG_ERROR(last_import_error = path + " doesn't exist."); return Containers::NullOpt; } BinaryIo::Reader reader{path}; if(!reader.open()) { last_import_error = path + " couldn't be opened."; return Containers::NullOpt; } Containers::Array magic_bytes; if(!reader.readArray(magic_bytes, 9)) { LOG_ERROR(last_import_error = "Couldn't read the magic bytes."); return Containers::NullOpt; } Containers::StringView magic_bytes_view = magic_bytes; static const auto expected_magic_bytes = "MBSTSTYLE"_s; if(magic_bytes_view != expected_magic_bytes) { LOG_ERROR(last_import_error = "Magic bytes are mismatched."); return Containers::NullOpt; } std::size_t data_size; if(!reader.readUint64(data_size)) { LOG_ERROR(last_import_error = "Couldn't read data size."); return Containers::NullOpt; } std::uint32_t crc; if(!reader.readUint32(crc)) { LOG_ERROR(last_import_error = "Couldn't read CRC-32 checksum."); return Containers::NullOpt; } auto position = reader.position(); { Containers::Array data; if(!reader.readArray(data, data_size)) { LOG_ERROR(last_import_error = "Couldn't read data."); return Containers::NullOpt; } auto computed_crc = Utilities::crc32(0, data); if(computed_crc != crc) { LOG_ERROR(last_import_error = Utility::format("CRC-32 checksum doesn't match. " "Expected {}, got {} instead.", crc, computed_crc)); return Containers::NullOpt; } } if(!reader.seek(position)) { LOG_ERROR(last_import_error = Utility::format("Couldn't seek to position {}.", position)); return Containers::NullOpt; } GameObjects::CustomStyle style{}; while(!reader.eof()) { Keys::CustomStyle key; if(!reader.readValue(key)) { if(reader.eof()) { break; } LOG_ERROR(last_import_error = "Couldn't read key."); return Containers::NullOpt; } switch(key) { case Keys::Name: if(!reader.readUEString(style.name)) { LOG_ERROR(last_import_error = "Couldn't read style name."); return Containers::NullOpt; } break; case Keys::Colour: if(!reader.readValue(style.colour)) { LOG_ERROR(last_import_error = "Couldn't read style colour."); return Containers::NullOpt; } break; case Keys::Metallic: if(!reader.readFloat(style.metallic)) { LOG_ERROR(last_import_error = "Couldn't read style metallic."); return Containers::NullOpt; } break; case Keys::Gloss: if(!reader.readFloat(style.gloss)) { LOG_ERROR(last_import_error = "Couldn't read style gloss."); return Containers::NullOpt; } break; case Keys::Glow: if(!reader.readValue(style.glow)) { LOG_ERROR(last_import_error = "Couldn't read style glow."); return Containers::NullOpt; } break; case Keys::PatternId: if(!reader.readInt32(style.patternId)) { LOG_ERROR(last_import_error = "Couldn't read style pattern ID."); return Containers::NullOpt; } break; case Keys::PatternOpacity: if(!reader.readFloat(style.opacity)) { LOG_ERROR(last_import_error = "Couldn't read style pattern opacity."); return Containers::NullOpt; } break; case Keys::PatternOffset: if(!reader.readValue(style.offset)) { LOG_ERROR(last_import_error = "Couldn't read style pattern offset."); return Containers::NullOpt; } break; case Keys::PatternRotation: if(!reader.readFloat(style.rotation)) { LOG_ERROR(last_import_error = "Couldn't read style pattern rotation."); return Containers::NullOpt; } break; case Keys::PatternScale: if(!reader.readFloat(style.scale)) { LOG_ERROR(last_import_error = "Couldn't read style pattern scale."); return Containers::NullOpt; } break; default: LOG_ERROR(last_import_error = Utility::format("Unknown key {}.", key)); return Containers::NullOpt; } } return Utility::move(style); } }