2014-01-20 12:59:12 -05:00
|
|
|
|
2014-03-07 03:17:13 -05:00
|
|
|
// SchematicFileSerializer.cpp
|
|
|
|
|
|
|
|
// Implements the cSchematicFileSerializer class representing the interface to load and save cBlockArea to a .schematic file
|
|
|
|
|
2014-01-20 13:15:19 -05:00
|
|
|
#include "Globals.h"
|
|
|
|
|
|
|
|
#include "FastNBT.h"
|
2014-01-22 13:39:09 -05:00
|
|
|
#include "SchematicFileSerializer.h"
|
2018-08-28 20:51:25 -04:00
|
|
|
#include "../OSSupport/GZipFile.h"
|
2014-04-02 05:56:27 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2014-07-17 16:15:34 -04:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
2014-04-02 05:56:27 -04:00
|
|
|
// cSchematicFileSerializer:
|
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
void cSchematicFileSerializer::LoadFromSchematicFile(cBlockArea & a_BlockArea, const std::string & a_FileName)
|
2014-01-20 12:59:12 -05:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
const auto Data = GZipFile::ReadRestOfFile(a_FileName);
|
|
|
|
const cParsedNBT NBT(Data.GetView());
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2014-01-20 12:59:12 -05:00
|
|
|
if (!NBT.IsValid())
|
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
throw std::runtime_error(fmt::format("Cannot parse the NBT in the schematic file \"{}\".", a_FileName));
|
2014-01-20 12:59:12 -05:00
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
LoadFromSchematicNBT(a_BlockArea, NBT);
|
2014-01-20 12:59:12 -05:00
|
|
|
}
|
|
|
|
|
2014-01-22 13:13:41 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
void cSchematicFileSerializer::LoadFromSchematicString(cBlockArea & a_BlockArea, const ContiguousByteBufferView a_SchematicData)
|
2014-01-20 12:59:12 -05:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
const auto Extracted = Compression::Extractor().ExtractGZip(a_SchematicData);
|
|
|
|
const cParsedNBT NBT(Extracted.GetView());
|
2014-03-07 03:17:13 -05:00
|
|
|
|
|
|
|
if (!NBT.IsValid())
|
2014-01-20 12:59:12 -05:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
throw std::runtime_error("Cannot parse the NBT in the schematic data.");
|
2014-01-20 12:59:12 -05:00
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
LoadFromSchematicNBT(a_BlockArea, NBT);
|
2014-03-07 03:17:13 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
void cSchematicFileSerializer::SaveToSchematicFile(const cBlockArea & a_BlockArea, const std::string & a_FileName)
|
2014-03-07 03:17:13 -05:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
GZipFile::Write(a_FileName, SaveToSchematicNBT(a_BlockArea));
|
2014-01-20 12:59:12 -05:00
|
|
|
}
|
|
|
|
|
2014-01-22 13:13:41 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
Compression::Result cSchematicFileSerializer::SaveToSchematicString(const cBlockArea & a_BlockArea)
|
2014-03-07 03:17:13 -05:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
return Compression::Compressor().CompressGZip(SaveToSchematicNBT(a_BlockArea));
|
2014-03-07 03:17:13 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
void cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, const cParsedNBT & a_NBT)
|
2014-01-20 12:59:12 -05:00
|
|
|
{
|
|
|
|
int TMaterials = a_NBT.FindChildByName(a_NBT.GetRoot(), "Materials");
|
|
|
|
if ((TMaterials > 0) && (a_NBT.GetType(TMaterials) == TAG_String))
|
|
|
|
{
|
|
|
|
AString Materials = a_NBT.GetString(TMaterials);
|
|
|
|
if (Materials.compare("Alpha") != 0)
|
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
throw std::runtime_error(fmt::format("Materials tag is present and \"{}\" instead of \"Alpha\". Possibly a wrong-format schematic file.", Materials));
|
2014-01-20 12:59:12 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
int TSizeX = a_NBT.FindChildByName(a_NBT.GetRoot(), "Width");
|
|
|
|
int TSizeY = a_NBT.FindChildByName(a_NBT.GetRoot(), "Height");
|
|
|
|
int TSizeZ = a_NBT.FindChildByName(a_NBT.GetRoot(), "Length");
|
|
|
|
if (
|
|
|
|
(TSizeX < 0) || (TSizeY < 0) || (TSizeZ < 0) ||
|
|
|
|
(a_NBT.GetType(TSizeX) != TAG_Short) ||
|
|
|
|
(a_NBT.GetType(TSizeY) != TAG_Short) ||
|
|
|
|
(a_NBT.GetType(TSizeZ) != TAG_Short)
|
|
|
|
)
|
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
throw std::runtime_error(fmt::format(
|
|
|
|
"Dimensions are missing from the schematic file ({}, {}, {}), ({}, {}, {})",
|
2014-01-20 12:59:12 -05:00
|
|
|
TSizeX, TSizeY, TSizeZ,
|
2015-01-23 04:03:39 -05:00
|
|
|
(TSizeX >= 0) ? a_NBT.GetType(TSizeX) : -1,
|
|
|
|
(TSizeY >= 0) ? a_NBT.GetType(TSizeY) : -1,
|
|
|
|
(TSizeZ >= 0) ? a_NBT.GetType(TSizeZ) : -1
|
2021-01-11 11:39:43 -05:00
|
|
|
));
|
2014-01-20 12:59:12 -05:00
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2014-01-20 12:59:12 -05:00
|
|
|
int SizeX = a_NBT.GetShort(TSizeX);
|
|
|
|
int SizeY = a_NBT.GetShort(TSizeY);
|
|
|
|
int SizeZ = a_NBT.GetShort(TSizeZ);
|
2020-06-01 05:21:10 -04:00
|
|
|
if ((SizeX < 1) || (SizeX > 65535) || (SizeY < 1) || (SizeY > 65535) || (SizeZ < 1) || (SizeZ > 65535))
|
2014-01-20 12:59:12 -05:00
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
throw std::runtime_error(fmt::format("Dimensions are invalid in the schematic file: {}, {}, {}", SizeX, SizeY, SizeZ));
|
2014-01-20 12:59:12 -05:00
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2014-01-20 12:59:12 -05:00
|
|
|
int TBlockTypes = a_NBT.FindChildByName(a_NBT.GetRoot(), "Blocks");
|
|
|
|
int TBlockMetas = a_NBT.FindChildByName(a_NBT.GetRoot(), "Data");
|
|
|
|
if ((TBlockTypes < 0) || (a_NBT.GetType(TBlockTypes) != TAG_ByteArray))
|
|
|
|
{
|
2021-01-11 11:39:43 -05:00
|
|
|
throw std::runtime_error(fmt::format("BlockTypes are invalid in the schematic file: {}", TBlockTypes));
|
2014-01-20 12:59:12 -05:00
|
|
|
}
|
|
|
|
bool AreMetasPresent = (TBlockMetas > 0) && (a_NBT.GetType(TBlockMetas) == TAG_ByteArray);
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2014-01-20 13:15:19 -05:00
|
|
|
a_BlockArea.Clear();
|
|
|
|
a_BlockArea.SetSize(SizeX, SizeY, SizeZ, AreMetasPresent ? (cBlockArea::baTypes | cBlockArea::baMetas) : cBlockArea::baTypes);
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2014-03-10 12:07:46 -04:00
|
|
|
int TOffsetX = a_NBT.FindChildByName(a_NBT.GetRoot(), "WEOffsetX");
|
|
|
|
int TOffsetY = a_NBT.FindChildByName(a_NBT.GetRoot(), "WEOffsetY");
|
|
|
|
int TOffsetZ = a_NBT.FindChildByName(a_NBT.GetRoot(), "WEOffsetZ");
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2014-03-10 12:07:46 -04:00
|
|
|
if (
|
|
|
|
(TOffsetX < 0) || (TOffsetY < 0) || (TOffsetZ < 0) ||
|
|
|
|
(a_NBT.GetType(TOffsetX) != TAG_Int) ||
|
|
|
|
(a_NBT.GetType(TOffsetY) != TAG_Int) ||
|
|
|
|
(a_NBT.GetType(TOffsetZ) != TAG_Int)
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// Not every schematic file has an offset, so we shoudn't give a warn message.
|
2014-03-12 02:46:14 -04:00
|
|
|
a_BlockArea.SetWEOffset(0, 0, 0);
|
2014-03-10 12:07:46 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-03-12 02:46:14 -04:00
|
|
|
a_BlockArea.SetWEOffset(a_NBT.GetInt(TOffsetX), a_NBT.GetInt(TOffsetY), a_NBT.GetInt(TOffsetZ));
|
2014-03-10 12:07:46 -04:00
|
|
|
}
|
|
|
|
|
2014-01-20 12:59:12 -05:00
|
|
|
// Copy the block types and metas:
|
2015-06-18 17:30:41 -04:00
|
|
|
size_t NumTypeBytes = a_BlockArea.GetBlockCount();
|
|
|
|
if (a_NBT.GetDataLength(TBlockTypes) < NumTypeBytes)
|
2014-01-20 12:59:12 -05:00
|
|
|
{
|
2015-06-18 17:30:41 -04:00
|
|
|
LOG("BlockTypes truncated in the schematic file (exp %u, got %u bytes). Loading partial.",
|
|
|
|
static_cast<unsigned>(NumTypeBytes), static_cast<unsigned>(a_NBT.GetDataLength(TBlockTypes))
|
2014-01-20 12:59:12 -05:00
|
|
|
);
|
2015-06-18 17:30:41 -04:00
|
|
|
NumTypeBytes = a_NBT.GetDataLength(TBlockTypes);
|
2014-01-20 12:59:12 -05:00
|
|
|
}
|
2017-09-06 13:40:04 -04:00
|
|
|
memcpy(a_BlockArea.GetBlockTypes(), a_NBT.GetData(TBlockTypes), NumTypeBytes);
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2014-01-20 12:59:12 -05:00
|
|
|
if (AreMetasPresent)
|
|
|
|
{
|
2015-06-18 17:30:41 -04:00
|
|
|
size_t NumMetaBytes = a_BlockArea.GetBlockCount();
|
|
|
|
if (a_NBT.GetDataLength(TBlockMetas) < NumMetaBytes)
|
2014-01-20 12:59:12 -05:00
|
|
|
{
|
2015-06-18 17:30:41 -04:00
|
|
|
LOG("BlockMetas truncated in the schematic file (exp %u, got %u bytes). Loading partial.",
|
|
|
|
static_cast<unsigned>(NumMetaBytes), static_cast<unsigned>(a_NBT.GetDataLength(TBlockMetas))
|
2014-01-20 12:59:12 -05:00
|
|
|
);
|
2015-06-18 17:30:41 -04:00
|
|
|
NumMetaBytes = a_NBT.GetDataLength(TBlockMetas);
|
2014-01-20 12:59:12 -05:00
|
|
|
}
|
2017-09-06 13:40:04 -04:00
|
|
|
memcpy(a_BlockArea.GetBlockMetas(), a_NBT.GetData(TBlockMetas), NumMetaBytes);
|
2014-01-20 12:59:12 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-07 03:17:13 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
ContiguousByteBuffer cSchematicFileSerializer::SaveToSchematicNBT(const cBlockArea & a_BlockArea)
|
2014-03-07 03:17:13 -05:00
|
|
|
{
|
|
|
|
cFastNBTWriter Writer("Schematic");
|
2015-07-29 11:04:03 -04:00
|
|
|
Writer.AddShort("Width", static_cast<Int16>(a_BlockArea.m_Size.x));
|
|
|
|
Writer.AddShort("Height", static_cast<Int16>(a_BlockArea.m_Size.y));
|
|
|
|
Writer.AddShort("Length", static_cast<Int16>(a_BlockArea.m_Size.z));
|
2014-03-07 03:17:13 -05:00
|
|
|
Writer.AddString("Materials", "Alpha");
|
|
|
|
if (a_BlockArea.HasBlockTypes())
|
|
|
|
{
|
2017-09-06 13:40:04 -04:00
|
|
|
Writer.AddByteArray("Blocks", reinterpret_cast<const char *>(a_BlockArea.GetBlockTypes()), a_BlockArea.GetBlockCount());
|
2014-03-07 03:17:13 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
AString Dummy(a_BlockArea.GetBlockCount(), 0);
|
|
|
|
Writer.AddByteArray("Blocks", Dummy.data(), Dummy.size());
|
|
|
|
}
|
|
|
|
if (a_BlockArea.HasBlockMetas())
|
|
|
|
{
|
2017-09-06 13:40:04 -04:00
|
|
|
Writer.AddByteArray("Data", reinterpret_cast<const char *>(a_BlockArea.GetBlockMetas()), a_BlockArea.GetBlockCount());
|
2014-03-07 03:17:13 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
AString Dummy(a_BlockArea.GetBlockCount(), 0);
|
|
|
|
Writer.AddByteArray("Data", Dummy.data(), Dummy.size());
|
|
|
|
}
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2014-03-11 13:23:21 -04:00
|
|
|
Writer.AddInt("WEOffsetX", a_BlockArea.m_WEOffset.x);
|
|
|
|
Writer.AddInt("WEOffsetY", a_BlockArea.m_WEOffset.y);
|
|
|
|
Writer.AddInt("WEOffsetZ", a_BlockArea.m_WEOffset.z);
|
2014-03-10 12:07:46 -04:00
|
|
|
|
2014-03-07 03:17:13 -05:00
|
|
|
// TODO: Save entities and block entities
|
|
|
|
Writer.BeginList("Entities", TAG_Compound);
|
|
|
|
Writer.EndList();
|
|
|
|
Writer.BeginList("TileEntities", TAG_Compound);
|
|
|
|
Writer.EndList();
|
|
|
|
Writer.Finish();
|
2016-02-05 16:45:45 -05:00
|
|
|
|
2021-01-11 11:39:43 -05:00
|
|
|
return ContiguousByteBuffer(Writer.GetResult());
|
2014-03-07 03:17:13 -05:00
|
|
|
}
|