diff --git a/source/NBT.cpp b/source/NBT.cpp index c48124cb3..c600a1868 100644 --- a/source/NBT.cpp +++ b/source/NBT.cpp @@ -88,11 +88,11 @@ cNBTTag * cNBTTag::CreateTag(cNBTTag * a_Parent, eTagType a_Type, const AString -cNBTTag * cNBTTag::FindChildByPath(const AString & iPath) +const cNBTTag * cNBTTag::FindChildByPath(const AString & iPath) const { size_t PrevIdx = 0; size_t idx = iPath.find('\\'); - cNBTTag * res = this; + const cNBTTag * res = this; while ((res != NULL) && (idx != AString::npos)) { res = res->FindChildByName(AString(iPath, PrevIdx, idx - PrevIdx)); @@ -191,9 +191,9 @@ cNBTTag * cNBTList::GetChildByIdx(size_t iIndex) -cNBTTag * cNBTList::FindChildByName(const AString & a_Name) +cNBTTag * cNBTList::FindChildByName(const AString & a_Name) const { - for (cNBTTags::iterator itr = m_Children.begin(); itr != m_Children.end(); ++itr) + for (cNBTTags::const_iterator itr = m_Children.begin(); itr != m_Children.end(); ++itr) { if ((*itr)->GetName() == a_Name) { @@ -260,9 +260,9 @@ cNBTTag * cNBTCompound::GetChildByIdx(size_t iIndex) -cNBTTag * cNBTCompound::FindChildByName(const AString & a_Name) +cNBTTag * cNBTCompound::FindChildByName(const AString & a_Name) const { - for (cNBTTags::iterator itr = m_Children.begin(); itr != m_Children.end(); ++itr) + for (cNBTTags::const_iterator itr = m_Children.begin(); itr != m_Children.end(); ++itr) { if ((*itr)->GetName() == a_Name) { @@ -580,6 +580,7 @@ int cNBTParser::ReadTag(const char ** a_Data, int * a_Length, cNBTTag::eTagType return ERROR_PRIVATE_NBTPARSER_BADTYPE; } +#undef CASE_SIMPLE_TAG @@ -618,3 +619,85 @@ cNBTTree * cNBTParser::Parse(const char * a_Data, int a_Length) +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Dumping the NBT tree (debug-only) + +#ifdef _DEBUG + +#define CASE_SIMPLE_TAG(TYPE,FMT) \ + case cNBTTag::TAG_##TYPE: \ + { \ + AString out; \ + Printf(out, "%sTAG_" TEXT(#TYPE) TEXT("(\"%hs\"): %") TEXT(FMT) TEXT("\n"), Indent.c_str(), a_Tree->GetName().c_str(), ((cNBT##TYPE *)a_Tree)->m_Value); \ + OutputDebugString(out.c_str()); \ + break; \ + } + +void DumpTree(const cNBTTree * a_Tree, int a_Level) +{ + AString Indent(a_Level, TEXT(' ')); + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + switch (a_Tree->GetType()) + { + CASE_SIMPLE_TAG(Byte, "d") + CASE_SIMPLE_TAG(Short, "d") + CASE_SIMPLE_TAG(Int, "d") + CASE_SIMPLE_TAG(Long, "I64d") + CASE_SIMPLE_TAG(Float, "f") + CASE_SIMPLE_TAG(Double, "f") + + case cNBTTag::TAG_ByteArray: + { + AString out; + Printf(out, "%sTAG_ByteArray(\"%hs\"): %d bytes\n", Indent.c_str(), a_Tree->GetName().c_str(), ((cNBTByteArray *)a_Tree)->m_Value.size()); + OutputDebugString(out.c_str()); + break; + } + + case cNBTTag::TAG_String: + { + AString out; + Printf(out, "%sTAG_String(\"%hs\"): %d bytes: \"%hs\"\n", Indent.c_str(), a_Tree->GetName().c_str(), ((cNBTString *)a_Tree)->m_Value.size(), ((cNBTString *)a_Tree)->m_Value.c_str()); + OutputDebugString(out.c_str()); + break; + } + + case cNBTTag::TAG_List: + { + const cNBTTags & Children = ((cNBTList *)a_Tree)->GetChildren(); + AString out; + Printf(out, "%sTAG_List(\"%hs\"): %d items of type %d\n%s{\n", Indent.c_str(), a_Tree->GetName().c_str(), Children.size(), ((cNBTList *)a_Tree)->GetChildrenType(), Indent.c_str()); + OutputDebugString(out.c_str()); + for (cNBTTags::const_iterator itr = Children.begin(); itr != Children.end(); ++itr) + { + DumpTree(*itr, a_Level + 1); + } // for itr - Children[] + Printf(out, "%s}\n", Indent.c_str()); + OutputDebugString(out.c_str()); + break; + } + + case cNBTTag::TAG_Compound: + { + const cNBTTags & Children = ((cNBTCompound *)a_Tree)->GetChildren(); + AString out; + Printf(out, "%sTAG_Compound(\"%hs\"): %d items\n%s{\n", Indent.c_str(), a_Tree->GetName().c_str(), Children.size(), Indent.c_str()); + OutputDebugString(out.c_str()); + for (cNBTTags::const_iterator itr = Children.begin(); itr != Children.end(); ++itr) + { + DumpTree(*itr, a_Level + 1); + } // for itr - Children[] + Printf(out, "%s}\n", Indent.c_str()); + OutputDebugString(out.c_str()); + break; + } + } +} + +#undef CASE_SIMPLE_TAG + +#endif // _DEBUG + + + + diff --git a/source/NBT.h b/source/NBT.h index 5bad0492c..a05e574de 100644 --- a/source/NBT.h +++ b/source/NBT.h @@ -62,8 +62,8 @@ public: static cNBTTag * CreateTag(cNBTTag * a_Parent, eTagType a_Type, const AString & a_Name); // Creates a new instance of a tag specified by iType, uses the correct class - virtual cNBTTag * FindChildByName(const AString & a_Name) {return NULL; } - cNBTTag * FindChildByPath(const AString & a_Path); + virtual cNBTTag * FindChildByName(const AString & a_Name) const {return NULL; } + const cNBTTag * FindChildByPath(const AString & a_Path) const; } ; typedef cNBTTag cNBTTree; @@ -118,7 +118,7 @@ public: cNBTTag * GetChildByIdx (size_t a_Index); const cNBTTags & GetChildren (void) const {return m_Children; } size_t GetChildrenCount(void) const {return m_Children.size(); } - virtual cNBTTag * FindChildByName (const AString & a_Name) override; + virtual cNBTTag * FindChildByName (const AString & a_Name) const override; int SetChildrenType(eTagType a_Type); // Only valid when list empty eTagType GetChildrenType(void) const {return m_ChildrenType; } @@ -142,7 +142,7 @@ public: cNBTTag * GetChildByIdx (size_t a_Index); const cNBTTags & GetChildren (void) const {return m_Children; } size_t GetChildrenCount(void) const {return m_Children.size(); } - virtual cNBTTag * FindChildByName (const AString & a_Name) override; + virtual cNBTTag * FindChildByName (const AString & a_Name) const override; } ; @@ -199,3 +199,14 @@ public: + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Dumping the tree (DEBUG-only) + +#ifdef _DEBUG +void DumpTree(const cNBTTree * a_Tree, int a_Level = 0); +#endif // _DEBUG + + + + diff --git a/source/WSSAnvil.cpp b/source/WSSAnvil.cpp index 8b0bbbae8..ce9afc652 100644 --- a/source/WSSAnvil.cpp +++ b/source/WSSAnvil.cpp @@ -9,6 +9,8 @@ #include "zlib.h" #include "NBT.h" #include "BlockID.h" +#include "cChestEntity.h" +#include "cItem.h" @@ -219,7 +221,9 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, cNBTTag & a_NBT) cEntityList Entities; cBlockEntityList BlockEntities; - // TODO: Load the entities from NBT + // Load the entities from NBT: + LoadEntitiesFromNBT (Entities, (cNBTList *)(a_NBT.FindChildByPath("Level\\Entities"))); + LoadBlockEntitiesFromNBT(BlockEntities, (cNBTList *)(a_NBT.FindChildByPath("Level\\TileEntities"))); // Reorder the chunk data - walk the MCA-formatted data sequentially and copy it into the right place in the ChunkData: char ChunkData[cChunk::c_BlockDataSize]; @@ -260,6 +264,124 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, cNBTTag & a_NBT) +void cWSSAnvil::LoadEntitiesFromNBT(cEntityList & a_Entitites, const cNBTList * a_NBT) +{ + // TODO: Load the entities +} + + + + + +void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, const cNBTList * a_NBT) +{ + if ((a_NBT == NULL) || (a_NBT->GetType() != cNBTTag::TAG_List)) + { + return; + } + const cNBTTags & Tags = a_NBT->GetChildren(); + + for (cNBTTags::const_iterator itr = Tags.begin(); itr != Tags.end(); ++itr) + { + if ((*itr)->GetType() != cNBTTag::TAG_Compound) + { + continue; + } + cNBTString * sID = (cNBTString *)((*itr)->FindChildByName("id")); + if (sID == NULL) + { + continue; + } + if (sID->m_Value == "Chest") + { + LoadChestFromNBT(a_BlockEntities, (cNBTCompound *)(*itr)); + } + // TODO: Other block entities + } // for itr - Tags[] +} + + + + + +void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cNBTCompound * a_NBT) +{ + ASSERT(a_NBT != NULL); + ASSERT(a_NBT->GetType() == cNBTTag::TAG_Compound); + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, x, y, z)) + { + return; + } + cNBTList * Items = (cNBTList *)(a_NBT->FindChildByName("Items")); + if ((Items == NULL) || (Items->GetType() != cNBTTag::TAG_List)) + { + return; // Make it an empty chest + } + std::auto_ptr Chest(new cChestEntity(x, y, z, m_World)); + const cNBTTags & ItemDefs = Items->GetChildren(); + for (cNBTTags::const_iterator itr = ItemDefs.begin(); itr != ItemDefs.end(); ++itr) + { + cNBTByte * Slot = (cNBTByte *)((*itr)->FindChildByName("Slot")); + if ((Slot == NULL) || (Slot->GetType() != cNBTTag::TAG_Byte)) + { + continue; + } + cItem Item; + cNBTShort * ID = (cNBTShort *)((*itr)->FindChildByName("id")); + if ((ID == NULL) || (ID->GetType() != cNBTTag::TAG_Short)) + { + continue; + } + Item.m_ItemID = (ENUM_ITEM_ID)(ID->m_Value); + cNBTShort * Damage = (cNBTShort *)((*itr)->FindChildByName("Damage")); + if ((Damage == NULL) || (Damage->GetType() != cNBTTag::TAG_Short)) + { + continue; + } + Item.m_ItemHealth = Damage->m_Value; + cNBTByte * Count = (cNBTByte *)((*itr)->FindChildByName("Count")); + if ((Count == NULL) || (Count->GetType() != cNBTTag::TAG_Byte)) + { + continue; + } + Item.m_ItemCount = Count->m_Value; + Chest->SetSlot(Slot->m_Value, Item); + } // for itr - ItemDefs[] + a_BlockEntities.push_back(Chest.release()); +} + + + + + +bool cWSSAnvil::GetBlockEntityNBTPos(const cNBTCompound * a_NBT, int & a_X, int & a_Y, int & a_Z) +{ + cNBTInt * x = (cNBTInt *)(a_NBT->FindChildByName("x")); + if ((x == NULL) || (x->GetType() != cNBTTag::TAG_Int)) + { + return false; + } + cNBTInt * y = (cNBTInt *)(a_NBT->FindChildByName("y")); + if ((y == NULL) || (y->GetType() != cNBTTag::TAG_Int)) + { + return false; + } + cNBTInt * z = (cNBTInt *)(a_NBT->FindChildByName("z")); + if ((z == NULL) || (z->GetType() != cNBTTag::TAG_Int)) + { + return false; + } + a_X = x->m_Value; + a_Y = y->m_Value; + a_Z = z->m_Value; + return true; +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cWSSAnvil::cMCAFile: diff --git a/source/WSSAnvil.h b/source/WSSAnvil.h index f02a30119..1052d0e5a 100644 --- a/source/WSSAnvil.h +++ b/source/WSSAnvil.h @@ -26,6 +26,8 @@ enum // fwd: "NBT.h" class cNBTTag; +class cNBTList; +class cNBTCompound; @@ -81,6 +83,17 @@ protected: /// Loads the chunk from NBT data (no locking needed) bool LoadChunkFromNBT(const cChunkCoords & a_Chunk, cNBTTag & a_NBT); + /// Loads the chunk's entities from NBT data (a_NBT is the Level\\Entities list tag; may be NULL) + void LoadEntitiesFromNBT(cEntityList & a_Entitites, const cNBTList * a_NBT); + + /// Loads the chunk's BlockEntities from NBT data (a_NBT is the Level\\TileEntities list tag; may be NULL) + void LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntitites, const cNBTList * a_NBT); + + void LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cNBTCompound * a_NBT); + + /// Helper function for extracting the X, Y, and Z int subtags of a NBT compound; returns true if successful + bool GetBlockEntityNBTPos(const cNBTCompound * a_NBT, int & a_X, int & a_Y, int & a_Z); + /// Gets the correct MCA file either from cache or from disk, manages the m_MCAFiles cache; assumes m_CS is locked cMCAFile * LoadMCAFile(const cChunkCoords & a_Chunk);