// 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);
}
}