// 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 "../BinaryReader.h" #include "../BinaryWriter.h" #include "../PropertySerialiser.h" #include "../Types/NoneProperty.h" #include "../../Logger/Logger.h" #include "MapPropertySerialiser.h" using namespace Containers::Literals; auto MapPropertySerialiser::deserialiseProperty(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr { auto prop = Containers::pointer(); if(!reader.readUEString(prop->keyType)) { LOG_ERROR_FORMAT("Couldn't read map property {}'s key type.", name); return nullptr; } if(!reader.readUEString(prop->valueType)) { LOG_ERROR_FORMAT("Couldn't read map property {}'s value type.", name); return nullptr; } char terminator; if(!reader.readChar(terminator) || terminator != '\0') { LOG_ERROR_FORMAT("Couldn't read a null byte in map property {}.", name); return nullptr; } UnsignedInt null; if(!reader.readUnsignedInt(null) || null != 0u) { LOG_ERROR_FORMAT("Couldn't read a null int in map property {}.", name); return nullptr; } UnsignedInt count; if(!reader.readUnsignedInt(count)) { LOG_ERROR_FORMAT("Couldn't read map property {}'s item count.", name); return nullptr; } // Begin dirty code because the MapProperty format doesn't seem to match any of the GVAS reading stuff I've found, // so I'm just gonna write stuff that matches the only MapProperty I can find in MB's save files. arrayReserve(prop->map, count); for(UnsignedInt i = 0; i < count; i++) { MapProperty::KeyValuePair pair; if(prop->keyType == "IntProperty"_s || prop->keyType == "StrProperty"_s) { pair.key = serialiser.readItem(reader, prop->keyType, -1, name); if(pair.key == nullptr) { LOG_ERROR_FORMAT("Couldn't read a valid key in map property {}.", name); return nullptr; } } else { // Add other branches depending on key type, should more maps appear in the future. LOG_ERROR_FORMAT("Key type {} not implemented.", prop->keyType); return nullptr; } UnrealPropertyBase::ptr value_item; if(prop->valueType == "StructProperty"_s) { while((value_item = serialiser.read(reader)) != nullptr) { arrayAppend(pair.values, std::move(value_item)); if(pair.values.back()->name == "None"_s && pair.values.back()->propertyType == "NoneProperty"_s && dynamic_cast(pair.values.back().get()) != nullptr) { break; } } } else if(prop->valueType == "ByteProperty"_s) { if((value_item = serialiser.readItem(reader, prop->valueType, -1, name)) == nullptr) { return nullptr; } arrayAppend(pair.values, std::move(value_item)); } arrayAppend(prop->map, std::move(pair)); } // End dirty code return prop; } auto MapPropertySerialiser::serialiseProperty(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool { auto map_prop = dynamic_cast(prop.get()); if(!map_prop) { LOG_ERROR("The property is not a valid map property."); return false; } writer.writeUEStringToArray(map_prop->keyType); writer.writeUEStringToArray(map_prop->valueType); writer.writeValueToArray('\0'); UnsignedLong value_start = writer.arrayPosition(); writer.writeValueToArray(0u); writer.writeValueToArray(UnsignedInt(map_prop->map.size())); UnsignedLong dummy_bytes_written = 0; for(auto& pair : map_prop->map) { if(!serialiser.writeItem(pair.key, map_prop->keyType, dummy_bytes_written, writer)) { LOG_ERROR("Couldn't write a key."); return false; } for(auto& value : pair.values) { if(map_prop->valueType == "StructProperty"_s) { if(!serialiser.write(value, dummy_bytes_written, writer)) { LOG_ERROR("Couldn't write a value."); return false; } } else { if(!serialiser.writeItem(value, map_prop->valueType, dummy_bytes_written, writer)) { LOG_ERROR("Couldn't write a value."); return false; } } } } bytes_written += (writer.arrayPosition() - value_start); return true; }