MassBuilderSaveTool/src/Gvas/Serialisers/Struct.cpp

246 lines
8.0 KiB
C++

// 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 <https://www.gnu.org/licenses/>.
#include <Corrade/Containers/String.h>
#include "../../BinaryIo/Reader.h"
#include "../../BinaryIo/Writer.h"
#include "../PropertySerialiser.h"
#include "../Types/GenericStructProperty.h"
#include "../Types/NoneProperty.h"
#include "../../Logger/Logger.h"
#include "Struct.h"
namespace Gvas { namespace Serialisers {
StringArrayView
Struct::types() {
using namespace Containers::Literals;
static const Containers::Array<Containers::String> types{InPlaceInit, {"StructProperty"_s}};
return types;
}
PropertyArray
Struct::deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length,
std::uint32_t count, BinaryIo::Reader& reader, PropertySerialiser& serialiser)
{
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<Types::UnrealPropertyBase::ptr> array;
if(count == 0) {
auto prop = Containers::pointer<Types::GenericStructProperty>();
prop->structType = Utility::move(item_type);
prop->structGuid = guid;
}
else {
for(std::uint32_t i = 0; i < count; i++) {
auto prop = Containers::pointer<Types::UnrealPropertyBase>();
prop = serialiser.readItem(reader, item_type, std::size_t(-1), name);
if(!prop) {
prop = readStructValue(name, item_type, value_length, reader, serialiser);
}
if(!prop) {
LOG_ERROR("Invalid property");
return nullptr;
}
dynamic_cast<Types::StructProperty*>(prop.get())->structGuid = guid;
arrayAppend(array, Utility::move(prop));
}
}
return array;
}
Types::UnrealPropertyBase::ptr
Struct::deserialise(Containers::StringView name, Containers::StringView type, std::size_t value_length,
BinaryIo::Reader& reader, PropertySerialiser& serialiser)
{
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 Containers::pointer<Types::NoneProperty>();
}
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;
}
auto prop = serialiser.readItem(reader, item_type, value_length, name);
if(!prop) {
prop = readStructValue(name, item_type, value_length, reader, serialiser);
if(prop) {
dynamic_cast<Types::GenericStructProperty*>(prop.get())->structGuid = guid;
}
}
return prop;
}
bool
Struct::serialise(PropertyArrayView props, Containers::StringView item_type, std::size_t& bytes_written,
BinaryIo::Writer& writer, PropertySerialiser& serialiser)
{
bytes_written += writer.writeUEStringToArray(*(props.front()->name));
bytes_written += writer.writeUEStringToArray(item_type);
std::size_t vl_pos = writer.arrayPosition();
bytes_written += writer.writeValueToArray<std::size_t>(0ull);
auto struct_prop = dynamic_cast<Types::StructProperty*>(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<char>('\0');
std::size_t vl_start = writer.arrayPosition();
std::size_t bytes_written_here = 0;
for(auto& prop : props) {
struct_prop = dynamic_cast<Types::StructProperty*>(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;
}
}
}
std::size_t vl_stop = writer.arrayPosition() - vl_start;
writer.writeValueToArrayAt(vl_stop, vl_pos);
bytes_written += vl_stop;
return true;
}
bool
Struct::serialise(Types::UnrealPropertyBase::ptr& prop, std::size_t& bytes_written, BinaryIo::Writer& writer,
PropertySerialiser& serialiser)
{
auto struct_prop = dynamic_cast<Types::StructProperty*>(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<char>('\0');
if(!serialiser.writeItem(prop, struct_prop->structType, bytes_written, writer)) {
std::size_t dummy_bytes_written = 0;
std::size_t 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;
}
Types::StructProperty::ptr
Struct::readStructValue(Containers::StringView name, Containers::StringView type, std::size_t value_length,
BinaryIo::Reader& reader, PropertySerialiser& serialiser)
{
static_cast<void>(value_length);
auto st_prop = Containers::pointer<Types::GenericStructProperty>();
st_prop->structType = type;
Types::UnrealPropertyBase::ptr prop;
while((prop = serialiser.read(reader)) != nullptr) {
arrayAppend(st_prop->properties, Utility::move(prop));
if(st_prop->properties.back()->name == "None" &&
st_prop->properties.back()->propertyType == "NoneProperty" &&
dynamic_cast<Types::NoneProperty*>(st_prop->properties.back().get()) != nullptr)
{
break;
}
}
st_prop->name.emplace(name);
return st_prop;
}
bool
Struct::writeStructValue(Types::StructProperty* prop, std::size_t& bytes_written, BinaryIo::Writer& writer,
PropertySerialiser& serialiser)
{
auto struct_prop = dynamic_cast<Types::GenericStructProperty*>(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;
}
}}