173 lines
4.9 KiB
C++
173 lines
4.9 KiB
C++
|
|
#include "Globals.h"
|
|
|
|
#include "OSSupport/GZipFile.h"
|
|
#include "FastNBT.h"
|
|
|
|
#include "SchematicFileSerializer.h"
|
|
|
|
bool cSchematicFileSerializer::LoadFromSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName)
|
|
{
|
|
// Un-GZip the contents:
|
|
AString Contents;
|
|
cGZipFile File;
|
|
if (!File.Open(a_FileName, cGZipFile::fmRead))
|
|
{
|
|
LOG("Cannot open the schematic file \"%s\".", a_FileName.c_str());
|
|
return false;
|
|
}
|
|
int NumBytesRead = File.ReadRestOfFile(Contents);
|
|
if (NumBytesRead < 0)
|
|
{
|
|
LOG("Cannot read GZipped data in the schematic file \"%s\", error %d", a_FileName.c_str(), NumBytesRead);
|
|
return false;
|
|
}
|
|
File.Close();
|
|
|
|
// Parse the NBT:
|
|
cParsedNBT NBT(Contents.data(), Contents.size());
|
|
if (!NBT.IsValid())
|
|
{
|
|
LOG("Cannot parse the NBT in the schematic file \"%s\".", a_FileName.c_str());
|
|
return false;
|
|
}
|
|
|
|
return LoadFromSchematicNBT(a_BlockArea, NBT);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool cSchematicFileSerializer::SaveToSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName)
|
|
{
|
|
cFastNBTWriter Writer("Schematic");
|
|
Writer.AddShort("Width", a_BlockArea.m_SizeX);
|
|
Writer.AddShort("Height", a_BlockArea.m_SizeY);
|
|
Writer.AddShort("Length", a_BlockArea.m_SizeZ);
|
|
Writer.AddString("Materials", "Alpha");
|
|
if (a_BlockArea.HasBlockTypes())
|
|
{
|
|
Writer.AddByteArray("Blocks", (const char *)a_BlockArea.m_BlockTypes, a_BlockArea.GetBlockCount());
|
|
}
|
|
else
|
|
{
|
|
AString Dummy(a_BlockArea.GetBlockCount(), 0);
|
|
Writer.AddByteArray("Blocks", Dummy.data(), Dummy.size());
|
|
}
|
|
if (a_BlockArea.HasBlockMetas())
|
|
{
|
|
Writer.AddByteArray("Data", (const char *)a_BlockArea.m_BlockMetas, a_BlockArea.GetBlockCount());
|
|
}
|
|
else
|
|
{
|
|
AString Dummy(a_BlockArea.GetBlockCount(), 0);
|
|
Writer.AddByteArray("Data", Dummy.data(), Dummy.size());
|
|
}
|
|
// TODO: Save entities and block entities
|
|
Writer.BeginList("Entities", TAG_Compound);
|
|
Writer.EndList();
|
|
Writer.BeginList("TileEntities", TAG_Compound);
|
|
Writer.EndList();
|
|
Writer.Finish();
|
|
|
|
// Save to file
|
|
cGZipFile File;
|
|
if (!File.Open(a_FileName, cGZipFile::fmWrite))
|
|
{
|
|
LOG("Cannot open file \"%s\" for writing.", a_FileName.c_str());
|
|
return false;
|
|
}
|
|
if (!File.Write(Writer.GetResult()))
|
|
{
|
|
LOG("Cannot write data to file \"%s\".", a_FileName.c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cParsedNBT & a_NBT)
|
|
{
|
|
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)
|
|
{
|
|
LOG("Materials tag is present and \"%s\" instead of \"Alpha\". Possibly a wrong-format schematic file.", Materials.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
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)
|
|
)
|
|
{
|
|
LOG("Dimensions are missing from the schematic file (%d, %d, %d), (%d, %d, %d)",
|
|
TSizeX, TSizeY, TSizeZ,
|
|
a_NBT.GetType(TSizeX), a_NBT.GetType(TSizeY), a_NBT.GetType(TSizeZ)
|
|
);
|
|
return false;
|
|
}
|
|
|
|
int SizeX = a_NBT.GetShort(TSizeX);
|
|
int SizeY = a_NBT.GetShort(TSizeY);
|
|
int SizeZ = a_NBT.GetShort(TSizeZ);
|
|
if ((SizeX < 1) || (SizeY < 1) || (SizeZ < 1))
|
|
{
|
|
LOG("Dimensions are invalid in the schematic file: %d, %d, %d", SizeX, SizeY, SizeZ);
|
|
return false;
|
|
}
|
|
|
|
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))
|
|
{
|
|
LOG("BlockTypes are invalid in the schematic file: %d", TBlockTypes);
|
|
return false;
|
|
}
|
|
bool AreMetasPresent = (TBlockMetas > 0) && (a_NBT.GetType(TBlockMetas) == TAG_ByteArray);
|
|
|
|
a_BlockArea.Clear();
|
|
a_BlockArea.SetSize(SizeX, SizeY, SizeZ, AreMetasPresent ? (cBlockArea::baTypes | cBlockArea::baMetas) : cBlockArea::baTypes);
|
|
|
|
// Copy the block types and metas:
|
|
int NumBytes = a_BlockArea.m_SizeX * a_BlockArea.m_SizeY * a_BlockArea.m_SizeZ;
|
|
if (a_NBT.GetDataLength(TBlockTypes) < NumBytes)
|
|
{
|
|
LOG("BlockTypes truncated in the schematic file (exp %d, got %d bytes). Loading partial.",
|
|
NumBytes, a_NBT.GetDataLength(TBlockTypes)
|
|
);
|
|
NumBytes = a_NBT.GetDataLength(TBlockTypes);
|
|
}
|
|
memcpy(a_BlockArea.m_BlockTypes, a_NBT.GetData(TBlockTypes), NumBytes);
|
|
|
|
if (AreMetasPresent)
|
|
{
|
|
int NumBytes = a_BlockArea.m_SizeX * a_BlockArea.m_SizeY * a_BlockArea.m_SizeZ;
|
|
if (a_NBT.GetDataLength(TBlockMetas) < NumBytes)
|
|
{
|
|
LOG("BlockMetas truncated in the schematic file (exp %d, got %d bytes). Loading partial.",
|
|
NumBytes, a_NBT.GetDataLength(TBlockMetas)
|
|
);
|
|
NumBytes = a_NBT.GetDataLength(TBlockMetas);
|
|
}
|
|
memcpy(a_BlockArea.m_BlockMetas, a_NBT.GetData(TBlockMetas), NumBytes);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|