// 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 "../BinaryReader.h" #include "../BinaryWriter.h" #include "../PropertySerialiser.h" #include "../Types/GenericStructProperty.h" #include "../Types/NoneProperty.h" #include "../../Logger/Logger.h" #include "StructSerialiser.h" auto StructSerialiser::types() -> Containers::ArrayView { using namespace Containers::Literals; static const Containers::Array types{InPlaceInit, {"StructProperty"_s}}; return types; } auto StructSerialiser::deserialise(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, UnsignedInt count, BinaryReader& reader, PropertySerialiser& serialiser) -> Containers::Array { Containers::String item_type; if(!reader.readUEString(item_type)) { LOG_ERROR_FORMAT("Couldn't read struct property {}'s item type.", name); return nullptr; } Containers::StaticArray<16, char> guid{ValueInit}; if(!reader.readStaticArray(guid)) { LOG_ERROR_FORMAT("Couldn't read struct property {}'s GUID.", name); return nullptr; } char terminator; if(!reader.readChar(terminator) || terminator != '\0') { LOG_ERROR_FORMAT("Couldn't read a null byte in struct property {}.", name); return nullptr; } Containers::Array array; if(count == 0) { auto prop = Containers::pointer(); prop->structType = std::move(item_type); prop->structGuid = std::move(guid); } else { for(UnsignedInt i = 0; i < count; i++) { auto prop = Containers::pointer(); prop = serialiser.readItem(reader, item_type, UnsignedLong(-1), name); if(!prop) { prop = readStructValue(name, item_type, value_length, reader, serialiser); } if(!prop) { LOG_ERROR("Invalid property"); return nullptr; } dynamic_cast(prop.get())->structGuid = guid; arrayAppend(array, std::move(prop)); } } return array; } auto StructSerialiser::deserialise(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> UnrealPropertyBase::ptr { Containers::String item_type; if(!reader.readUEString(item_type)) { LOG_ERROR_FORMAT("Couldn't read struct property {}'s item type.", name); return nullptr; } if(item_type == "None") { return NoneProperty::ptr{}; } Containers::StaticArray<16, char> guid{ValueInit}; if(!reader.readStaticArray(guid)) { LOG_ERROR_FORMAT("Couldn't read struct property {}'s GUID.", name); return nullptr; } char terminator; if(!reader.readChar(terminator) || terminator != '\0') { LOG_ERROR_FORMAT("Couldn't read a null byte in byte property {}.", name); return nullptr; } UnrealPropertyBase::ptr prop = serialiser.readItem(reader, item_type, value_length, name); if(!prop) { prop = readStructValue(name, item_type, value_length, reader, serialiser); if(prop) { dynamic_cast(prop.get())->structGuid = std::move(guid); } } return prop; } auto StructSerialiser::serialise(Containers::ArrayView props, Containers::StringView item_type, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool { bytes_written += writer.writeUEStringToArray(*(props.front()->name)); bytes_written += writer.writeUEStringToArray(item_type); UnsignedLong vl_pos = writer.arrayPosition(); bytes_written += writer.writeValueToArray(0ull); auto struct_prop = dynamic_cast(props.front().get()); if(!struct_prop) { LOG_ERROR("The property is not a valid struct property."); return false; } bytes_written += writer.writeUEStringToArray(struct_prop->structType); bytes_written += writer.writeDataToArray(arrayView(struct_prop->structGuid)); bytes_written += writer.writeValueToArray('\0'); UnsignedLong vl_start = writer.arrayPosition(); UnsignedLong bytes_written_here = 0; for(auto& prop : props) { struct_prop = dynamic_cast(prop.get()); if(!struct_prop) { LOG_ERROR("The property is not a valid struct property."); return false; } if(!serialiser.writeItem(prop, struct_prop->structType, bytes_written_here, writer)) { if(!writeStructValue(struct_prop, bytes_written_here, writer, serialiser)) { LOG_ERROR("Couldn't write the struct value."); return false; } } } UnsignedLong vl_stop = writer.arrayPosition() - vl_start; writer.writeValueToArrayAt(vl_stop, vl_pos); bytes_written += vl_stop; return true; } auto StructSerialiser::serialise(UnrealPropertyBase::ptr& prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool { auto struct_prop = dynamic_cast(prop.get()); if(!struct_prop) { LOG_ERROR("The property is not a valid struct property."); return false; } writer.writeUEStringToArray(struct_prop->structType); writer.writeDataToArray(arrayView(struct_prop->structGuid)); writer.writeValueToArray('\0'); if(!serialiser.writeItem(prop, struct_prop->structType, bytes_written, writer)) { UnsignedLong dummy_bytes_written = 0; UnsignedLong vl_start = writer.arrayPosition(); if(!writeStructValue(struct_prop, dummy_bytes_written, writer, serialiser)) { LOG_ERROR("Couldn't write the struct value."); return false; } bytes_written += writer.arrayPosition() - vl_start; } return true; } auto StructSerialiser::readStructValue(Containers::StringView name, Containers::StringView type, UnsignedLong value_length, BinaryReader& reader, PropertySerialiser& serialiser) -> StructProperty::ptr { auto st_prop = Containers::pointer(); st_prop->structType = type; UnrealPropertyBase::ptr prop; while((prop = serialiser.read(reader)) != nullptr) { arrayAppend(st_prop->properties, std::move(prop)); if(st_prop->properties.back()->name == "None" && st_prop->properties.back()->propertyType == "NoneProperty" && dynamic_cast(st_prop->properties.back().get()) != nullptr) { break; } } st_prop->name.emplace(name); return st_prop; } auto StructSerialiser::writeStructValue(StructProperty* prop, UnsignedLong& bytes_written, BinaryWriter& writer, PropertySerialiser& serialiser) -> bool { auto struct_prop = dynamic_cast(prop); if(!struct_prop) { LOG_ERROR("The property is not a valid struct property."); return false; } for(auto& item : struct_prop->properties) { if(!serialiser.write(item, bytes_written, writer)) { LOG_ERROR("Couldn't write the struct's data."); return false; } } return true; }