From 9cb88728511c314695731d92cb4ab16c8e3b051e Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Mon, 23 Apr 2012 21:20:32 +0000 Subject: Anvil storage writing (Basic storage is working, NO entities except for chests are working! Don't use for real servers) git-svn-id: http://mc-server.googlecode.com/svn/trunk@475 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/ChunkDef.h | 49 +++++++- source/NBT.cpp | 9 +- source/NBT.h | 4 +- source/WSSAnvil.cpp | 314 ++++++++++++++++++++++++++++++++++++++++++++++-- source/WSSAnvil.h | 35 ++++-- source/cBlockEntity.h | 11 +- source/cChestEntity.cpp | 2 +- source/cFile.cpp | 20 ++- source/cFile.h | 3 + source/cMakeDir.cpp | 6 +- source/cMakeDir.h | 13 +- 11 files changed, 431 insertions(+), 35 deletions(-) (limited to 'source') diff --git a/source/ChunkDef.h b/source/ChunkDef.h index 647f9b51e..837a515b5 100644 --- a/source/ChunkDef.h +++ b/source/ChunkDef.h @@ -235,9 +235,12 @@ public: class cChunkDataCollector : public cChunkDataCallback { -protected: +public: + BLOCKTYPE m_BlockData[cChunkDef::BlockDataSize]; +protected: + virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override { memcpy(m_BlockData, a_BlockTypes, cChunkDef::NumBlocks); @@ -266,6 +269,50 @@ protected: +/** A simple implementation of the cChunkDataCallback interface that collects all block data into a separate buffers +*/ +class cChunkDataSeparateCollector : + public cChunkDataCallback +{ +public: + + BLOCKTYPE m_BlockTypes[cChunkDef::NumBlocks]; + + // TODO: These should be NIBBLETYPE: + BLOCKTYPE m_BlockMetas[cChunkDef::NumBlocks / 2]; + BLOCKTYPE m_BlockLight[cChunkDef::NumBlocks / 2]; + BLOCKTYPE m_BlockSkyLight[cChunkDef::NumBlocks / 2]; + +protected: + + virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override + { + memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes)); + } + + + virtual void BlockMeta(const BLOCKTYPE * a_BlockMeta) override + { + memcpy(m_BlockMetas, a_BlockMeta, sizeof(m_BlockMetas)); + } + + + virtual void BlockLight(const BLOCKTYPE * a_BlockLight) override + { + memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight)); + } + + + virtual void BlockSkyLight(const BLOCKTYPE * a_BlockSkyLight) override + { + memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight)); + } +} ; + + + + + /** Interface class used for comparing clients of two chunks. Used primarily for entity moving while both chunks are locked. */ diff --git a/source/NBT.cpp b/source/NBT.cpp index af420cd27..ba2f3d0f6 100644 --- a/source/NBT.cpp +++ b/source/NBT.cpp @@ -74,7 +74,7 @@ cNBTTag * cNBTTag::CreateTag(cNBTTag * a_Parent, eTagType a_Type, const AString case TAG_Double: return new cNBTDouble (a_Parent, a_Name); case TAG_ByteArray: return new cNBTByteArray(a_Parent, a_Name); case TAG_String: return new cNBTString (a_Parent, a_Name); - case TAG_List: return new cNBTList (a_Parent, a_Name); + case TAG_List: return new cNBTList (a_Parent, a_Name, TAG_End); case TAG_Compound: return new cNBTCompound (a_Parent, a_Name); default: { @@ -128,8 +128,8 @@ void cNBTList::Clear(void) int cNBTList::Add(cNBTTag * iTag) { // Catch usage errors while debugging: - ASSERT(m_ChildrenType != TAG_End); - ASSERT(iTag->GetType() == m_ChildrenType); + ASSERT(m_ChildrenType != TAG_End); // Didn't call SetChildrenType() + ASSERT(iTag->GetType() == m_ChildrenType); // Child of different type // Catch errors while running: if (m_ChildrenType == TAG_End) @@ -535,12 +535,11 @@ int cNBTParser::ReadTag(const char ** a_Data, int * a_Length, cNBTTag::eTagType { char ItemType; RETURN_INT_IF_FAILED(ReadByte (a_Data, a_Length, ItemType)); - cNBTList * List = new cNBTList(a_Parent, a_Name); + cNBTList * List = new cNBTList(a_Parent, a_Name, (cNBTTag::eTagType)ItemType); if (List == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } - RETURN_INT_IF_FAILED(List->SetChildrenType((cNBTTag::eTagType)ItemType)); RETURN_INT_IF_FAILED(ReadList(a_Data, a_Length, List)); *a_Tag = List; return ERROR_SUCCESS; diff --git a/source/NBT.h b/source/NBT.h index 6d3ecebd9..f63e1fea1 100644 --- a/source/NBT.h +++ b/source/NBT.h @@ -109,8 +109,8 @@ class cNBTList : eTagType m_ChildrenType; public: - cNBTList(cNBTTag * a_Parent) : cNBTTag(a_Parent, TAG_List), m_ChildrenType(TAG_End) {} - cNBTList(cNBTTag * a_Parent, const AString & a_Name) : cNBTTag(a_Parent, TAG_List, a_Name), m_ChildrenType(TAG_End) {} + cNBTList(cNBTTag * a_Parent, eTagType a_ChildrenType) : cNBTTag(a_Parent, TAG_List), m_ChildrenType(a_ChildrenType) {} + cNBTList(cNBTTag * a_Parent, const AString & a_Name, eTagType a_ChildrenType) : cNBTTag(a_Parent, TAG_List, a_Name), m_ChildrenType(a_ChildrenType) {} virtual ~cNBTList() {Clear(); } void Clear (void); diff --git a/source/WSSAnvil.cpp b/source/WSSAnvil.cpp index 6520a7c95..1b916a63e 100644 --- a/source/WSSAnvil.cpp +++ b/source/WSSAnvil.cpp @@ -11,6 +11,10 @@ #include "BlockID.h" #include "cChestEntity.h" #include "cItem.h" +#include "StringCompression.h" +#include "cEntity.h" +#include "cBlockEntity.h" +#include "cMakeDir.h" @@ -22,16 +26,136 @@ Since only the header is actually in the memory, this number can be high, but st #define MAX_MCA_FILES 32 /// The maximum size of an inflated chunk -#define CHUNK_INFLATE_MAX 128 KiB +#define CHUNK_INFLATE_MAX 256 KiB +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cNBTChunkSerializer + +class cNBTChunkSerializer : + public cChunkDataSeparateCollector +{ +public: + cNBTChunkSerializer(cNBTList * a_Entities, cNBTList * a_TileEntities) : + m_Entities(a_Entities), + m_TileEntities(a_TileEntities) + { + } + +protected: + + /* From cChunkDataSeparateCollector we inherit: + - m_BlockTypes[] + - m_BlockMetas[] + - m_BlockLight[] + - m_BlockSkyLight[] + */ + + // TODO: Biomes + + // We need to save entities and blockentities into NBT + cNBTList * m_Entities; // Tag where entities will be saved + cNBTList * m_TileEntities; // Tag where block-entities will be saved + + + cNBTCompound * AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID) + { + cNBTCompound * res = new cNBTCompound(m_TileEntities); + res->Add(new cNBTInt (res, "x", a_Entity->GetPosX())); + res->Add(new cNBTInt (res, "y", a_Entity->GetPosY())); + res->Add(new cNBTInt (res, "z", a_Entity->GetPosZ())); + res->Add(new cNBTString(res, "id", a_EntityTypeID)); + return res; + } + + + void AddItem(cNBTList * a_Items, cItem * a_Item, int a_Slot) + { + cNBTCompound * Tag = new cNBTCompound(a_Items); + Tag->Add(new cNBTShort(Tag, "id", a_Item->m_ItemID)); + Tag->Add(new cNBTShort(Tag, "Damage", a_Item->m_ItemHealth)); + Tag->Add(new cNBTByte (Tag, "Count", a_Item->m_ItemCount)); + Tag->Add(new cNBTByte (Tag, "Slot", a_Slot)); + } + + + void AddChestEntity(cChestEntity * a_Entity) + { + cNBTCompound * Base = AddBasicTileEntity(a_Entity, "chest"); + cNBTList * Items = new cNBTList(Base, "Items", cNBTTag::TAG_Compound); + Base->Add(Items); + for (int i = 0; i < cChestEntity::c_ChestHeight * cChestEntity::c_ChestWidth; i++) + { + cItem * Item = a_Entity->GetSlot(i); + if ((Item == NULL) || Item->IsEmpty()) + { + continue; + } + AddItem(Items, Item, i); + } + } + + + virtual void Entity(cEntity * a_Entity) override + { + // TODO: Add entity into NBT: + } + + + virtual void BlockEntity(cBlockEntity * a_Entity) + { + // Add tile-entity into NBT: + switch (a_Entity->GetBlockType()) + { + case E_BLOCK_CHEST: AddChestEntity((cChestEntity *)a_Entity); break; + default: + { + ASSERT(!"Unhandled block entity saved into Anvil"); + } + } + } +} ; // class cNBTChunkSerializer + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cWSSAnvil: +cWSSAnvil::cWSSAnvil(cWorld * a_World) : + super(a_World) +{ + // Create a level.dat file for mapping tools, if it doesn't already exist: + AString fnam; + Printf(fnam, "%s/level.dat", a_World->GetName().c_str()); + if (!cFile::Exists(fnam)) + { + std::auto_ptr Root(new cNBTCompound(NULL)); + cNBTCompound * Data = new cNBTCompound(Root.get()); + Root->Add(Data); + Data->Add(new cNBTInt(Data, "SpawnX", (int)(a_World->GetSpawnX()))); + Data->Add(new cNBTInt(Data, "SpawnY", (int)(a_World->GetSpawnY()))); + Data->Add(new cNBTInt(Data, "SpawnZ", (int)(a_World->GetSpawnZ()))); + AString Uncompressed; + cNBTSerializer::Serialize(Root.get(), Uncompressed); + gzFile gz = gzopen(fnam.c_str(), "wb"); + if (gz != NULL) + { + gzwrite(gz, Uncompressed.data(), Uncompressed.size()); + } + gzclose(gz); + } +} + + + + + cWSSAnvil::~cWSSAnvil() { cCSLock Lock(m_CS); @@ -63,8 +187,18 @@ bool cWSSAnvil::LoadChunk(const cChunkCoords & a_Chunk) bool cWSSAnvil::SaveChunk(const cChunkCoords & a_Chunk) { - // TODO: We're read-only for now - return false; + AString ChunkData; + if (!SaveChunkToData(a_Chunk, ChunkData)) + { + return false; + } + if (!SetChunkData(a_Chunk, ChunkData)) + { + return false; + } + + // Everything successful + return true; } @@ -86,6 +220,21 @@ bool cWSSAnvil::GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data) +bool cWSSAnvil::SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data) +{ + cCSLock Lock(m_CS); + cMCAFile * File = LoadMCAFile(a_Chunk); + if (File == NULL) + { + return false; + } + return File->SetChunkData(a_Chunk, a_Data); +} + + + + + cWSSAnvil::cMCAFile * cWSSAnvil::LoadMCAFile(const cChunkCoords & a_Chunk) { // ASSUME m_CS is locked @@ -111,7 +260,9 @@ cWSSAnvil::cMCAFile * cWSSAnvil::LoadMCAFile(const cChunkCoords & a_Chunk) // Load it anew: AString FileName; - Printf(FileName, "%s/r.%d.%d.mca", m_World->GetName().c_str(), RegionX, RegionZ); + Printf(FileName, "%s/region", m_World->GetName().c_str()); + cMakeDir::MakeDir(FileName); + AppendPrintf(FileName, "/r.%d.%d.mca", RegionX, RegionZ); cMCAFile * f = new cMCAFile(FileName, RegionX, RegionZ); if (f == NULL) { @@ -167,6 +318,23 @@ bool cWSSAnvil::LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & +bool cWSSAnvil::SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data) +{ + std::auto_ptr Tree(SaveChunkToNBT(a_Chunk)); + if (Tree.get() == NULL) + { + return false; + } + AString Uncompressed; + cNBTSerializer::Serialize(Tree.get(), Uncompressed); + CompressString(Uncompressed.data(), Uncompressed.size(), a_Data); + return true; +} + + + + + bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, cNBTTag & a_NBT) { // The data arrays, in MCA-native y/z/x ordering (will be reordered for the final chunk data) @@ -299,6 +467,50 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, cNBTTag & a_NBT) +cNBTTag * cWSSAnvil::SaveChunkToNBT(const cChunkCoords & a_Chunk) +{ + std::auto_ptr res(new cNBTCompound(NULL)); + cNBTCompound * Level = new cNBTCompound(res.get(), "Level"); + res->Add(Level); + cNBTList * Entities = new cNBTList(Level, "Entities", cNBTTag::TAG_Compound); + Level->Add(Entities); + cNBTList * TileEntities = new cNBTList(Level, "TileEntities", cNBTTag::TAG_Compound); + Level->Add(TileEntities); + cNBTChunkSerializer Serializer(Entities, TileEntities); + if (!m_World->GetChunkData(a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ, Serializer)) + { + return NULL; + } + + Level->Add(new cNBTInt(Level, "xPos", a_Chunk.m_ChunkX)); + Level->Add(new cNBTInt(Level, "zPos", a_Chunk.m_ChunkZ)); + + // TODO: Save biomes: + // Level->Add(new cNBTByteArray(Level, "Biomes", AString(Serializer.m_Biomes, sizeof(Serializer.m_Biomes)); + + // Save blockdata: + cNBTList * Sections = new cNBTList(Level, "Sections", cNBTTag::TAG_Compound); + Level->Add(Sections); + int SliceSizeBlock = cChunkDef::Width * cChunkDef::Width * 16; + int SliceSizeNibble = SliceSizeBlock / 2; + for (int Y = 0; Y < 16; Y++) + { + cNBTCompound * Slice = new cNBTCompound(Sections); + Sections->Add(Slice); + Slice->Add(new cNBTByteArray(Slice, "Blocks", AString(Serializer.m_BlockTypes + Y * SliceSizeBlock, SliceSizeBlock))); + Slice->Add(new cNBTByteArray(Slice, "Data", AString(Serializer.m_BlockMetas + Y * SliceSizeNibble, SliceSizeNibble))); + Slice->Add(new cNBTByteArray(Slice, "SkyLight", AString(Serializer.m_BlockSkyLight + Y * SliceSizeNibble, SliceSizeNibble))); + Slice->Add(new cNBTByteArray(Slice, "BlockLight", AString(Serializer.m_BlockLight + Y * SliceSizeNibble, SliceSizeNibble))); + Slice->Add(new cNBTByte(Slice, "Y", Y)); + } + + return res.release(); +} + + + + + void cWSSAnvil::LoadEntitiesFromNBT(cEntityList & a_Entitites, const cNBTList * a_NBT) { // TODO: Load the entities @@ -423,7 +635,7 @@ bool cWSSAnvil::GetBlockEntityNBTPos(const cNBTCompound * a_NBT, int & a_X, int cWSSAnvil::cMCAFile::cMCAFile(const AString & a_FileName, int a_RegionX, int a_RegionZ) : m_RegionX(a_RegionX), m_RegionZ(a_RegionZ), - m_File(a_FileName, cFile::fmRead), + m_File(a_FileName, cFile::fmReadWrite), m_FileName(a_FileName) { if (!m_File.IsOpen()) @@ -434,9 +646,18 @@ cWSSAnvil::cMCAFile::cMCAFile(const AString & a_FileName, int a_RegionX, int a_R // Load the header: if (m_File.Read(m_Header, sizeof(m_Header)) != sizeof(m_Header)) { - LOGWARNING("Cannot read MCA header from file \"%s\", chunks in that file will be lost", m_FileName.c_str()); - m_File.Close(); - return; + // Cannot read the header - perhaps the file has just been created? + // Try writing a NULL header (both chunk offsets and timestamps): + memset(m_Header, 0, sizeof(m_Header)); + if ( + (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) || + (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) + ) + { + LOGWARNING("Cannot process MCA header in file \"%s\", chunks in that file will be lost", m_FileName.c_str()); + m_File.Close(); + return; + } } } @@ -492,3 +713,80 @@ bool cWSSAnvil::cMCAFile::GetChunkData(const cChunkCoords & a_Chunk, AString & a + +bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data) +{ + if (!m_File.IsOpen()) + { + return false; + } + int LocalX = a_Chunk.m_ChunkX % 32; + if (LocalX < 0) + { + LocalX = 32 + LocalX; + } + int LocalZ = a_Chunk.m_ChunkZ % 32; + if (LocalZ < 0) + { + LocalZ = 32 + LocalZ; + } + + unsigned ChunkSector = FindFreeLocation(LocalX, LocalZ, a_Data); + + // Store the chunk data: + m_File.Seek(ChunkSector * 4096); + unsigned ChunkSize = htonl(a_Data.size() + 1); + if (m_File.Write(&ChunkSize, 4) != 4) + { + return false; + } + char CompressionType = 2; + if (m_File.Write(&CompressionType, 1) != 1) + { + return false; + } + if (m_File.Write(a_Data.data(), a_Data.size()) != a_Data.size()) + { + return false; + } + + // Store the header: + ChunkSize = (a_Data.size() + MCA_CHUNK_HEADER_LENGTH + 4095) / 4096; // Round data size *up* to nearest 4KB sector, make it a sector number + ASSERT(ChunkSize < 256); + m_Header[LocalX + 32 * LocalZ] = htonl((ChunkSector << 8) | ChunkSize); + m_File.Seek(0); + if (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) + { + return false; + } + + return true; +} + + + + + +unsigned cWSSAnvil::cMCAFile::FindFreeLocation(int a_LocalX, int a_LocalZ, const AString & a_Data) +{ + // See if it fits the current location: + unsigned ChunkLocation = ntohl(m_Header[a_LocalX + 32 * a_LocalZ]); + unsigned ChunkLen = ChunkLocation & 0xff; + if (a_Data.size() + MCA_CHUNK_HEADER_LENGTH <= (ChunkLen * 4096)) + { + return ChunkLocation >> 8; + } + + // Doesn't fit, append to the end of file (we're wasting a lot of space, TODO: fix this later) + unsigned MaxLocation = 2 << 8; // Minimum sector is #2 - after the headers + for (int i = 0; i < ARRAYCOUNT(m_Header); i++) + { + ChunkLocation = ntohl(m_Header[i]); + ChunkLocation = ChunkLocation + ((ChunkLocation & 0xff) << 8); // Add the number of sectors used; don't care about the 4th byte + if (MaxLocation < ChunkLocation) + { + MaxLocation = ChunkLocation; + } + } // for i - m_Header[] + return MaxLocation >> 8; +} diff --git a/source/WSSAnvil.h b/source/WSSAnvil.h index 1052d0e5a..37d813f28 100644 --- a/source/WSSAnvil.h +++ b/source/WSSAnvil.h @@ -16,8 +16,14 @@ enum { - // The MCA header is 8 KiB - MCA_HEADER_SIZE = 8192, + /// Maximum number of chunks in an MCA file - also the count of the header items + MCA_MAX_CHUNKS = 32 * 32, + + /// The MCA header is 8 KiB + MCA_HEADER_SIZE = MCA_MAX_CHUNKS * 8, + + /// There are 5 bytes of header in front of each chunk + MCA_CHUNK_HEADER_LENGTH = 5, } ; @@ -40,7 +46,7 @@ class cWSSAnvil : public: - cWSSAnvil(cWorld * a_World) : super(a_World) {} + cWSSAnvil(cWorld * a_World); virtual ~cWSSAnvil(); protected: @@ -51,7 +57,9 @@ protected: cMCAFile(const AString & a_FileName, int a_RegionX, int a_RegionZ); - bool GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data); + bool GetChunkData (const cChunkCoords & a_Chunk, AString & a_Data); + bool SetChunkData (const cChunkCoords & a_Chunk, const AString & a_Data); + bool EraseChunkData(const cChunkCoords & a_Chunk); int GetRegionX (void) const {return m_RegionX; } int GetRegionZ (void) const {return m_RegionZ; } @@ -66,23 +74,36 @@ protected: // The header, copied from the file so we don't have to seek to it all the time // First 1024 entries are chunk locations - the 3 + 1 byte sector-offset and sector-count - // The next 1024 entries are chunk timestamps - unused in MCS - unsigned m_Header[MCA_HEADER_SIZE / sizeof(unsigned)]; + unsigned m_Header[MCA_MAX_CHUNKS]; + + // Chunk timestamps, following the chunk headers, are unused by MCS + + /// Finds a free location large enough to hold a_Data. Gets a hint of the chunk coords, places the data there if it fits. Returns the sector number. + unsigned FindFreeLocation(int a_LocalX, int a_LocalZ, const AString & a_Data); } ; typedef std::list cMCAFiles; cCriticalSection m_CS; cMCAFiles m_Files; // a MRU cache of MCA files - /// Gets chunk data from the correct file; locks CS as needed + /// Gets chunk data from the correct file; locks file CS as needed bool GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data); + /// Sets chunk data into the correct file; locks file CS as needed + bool SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data); + /// Loads the chunk from the data (no locking needed) bool LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & a_Data); + /// Saves the chunk into datastream (no locking needed) + bool SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data); + /// Loads the chunk from NBT data (no locking needed) bool LoadChunkFromNBT(const cChunkCoords & a_Chunk, cNBTTag & a_NBT); + /// Saves the chunk into NBT data; returns NULL for failure + cNBTTag * SaveChunkToNBT(const cChunkCoords & a_Chunk); + /// 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); diff --git a/source/cBlockEntity.h b/source/cBlockEntity.h index 3f0190c77..8a9e51f56 100644 --- a/source/cBlockEntity.h +++ b/source/cBlockEntity.h @@ -34,10 +34,10 @@ class cPacket; class cBlockEntity { protected: - cBlockEntity(ENUM_BLOCK_ID a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World) - : m_PosX( a_X ) - , m_PosY( a_Y ) - , m_PosZ( a_Z ) + cBlockEntity(ENUM_BLOCK_ID a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) + : m_PosX( a_BlockX ) + , m_PosY( a_BlockY ) + , m_PosZ( a_BlockZ ) , m_BlockType( a_BlockType ) , m_World( a_World ) {} @@ -45,6 +45,7 @@ public: virtual ~cBlockEntity() {}; virtual void Destroy() {}; + // Position, in absolute block coordinates: int GetPosX() { return m_PosX; } int GetPosY() { return m_PosY; } int GetPosZ() { return m_PosZ; } @@ -78,7 +79,7 @@ public: virtual cPacket * GetPacket(void) {return NULL; } protected: - int m_PosX; // Position in block coordinates + int m_PosX; // Position in absolute block coordinates int m_PosY; int m_PosZ; diff --git a/source/cChestEntity.cpp b/source/cChestEntity.cpp index f1a99b8c3..e1976836b 100644 --- a/source/cChestEntity.cpp +++ b/source/cChestEntity.cpp @@ -27,7 +27,7 @@ cChestEntity::cChestEntity(int a_X, int a_Y, int a_Z, cWorld * a_World) , m_TopChest( false ) , m_JoinedChest( NULL ) { - m_Content = new cItem[ c_ChestHeight*c_ChestWidth ]; + m_Content = new cItem[ c_ChestHeight * c_ChestWidth ]; } diff --git a/source/cFile.cpp b/source/cFile.cpp index 5ea71107a..cd61800e2 100644 --- a/source/cFile.cpp +++ b/source/cFile.cpp @@ -69,7 +69,7 @@ bool cFile::Open(const AString & iFileName, EMode iMode) { case fmRead: Mode = "rb"; break; case fmWrite: Mode = "wb"; break; - case fmReadWrite: Mode = "ab+"; break; + case fmReadWrite: Mode = "rb+"; break; default: { ASSERT(!"Unhandled file mode"); @@ -77,6 +77,14 @@ bool cFile::Open(const AString & iFileName, EMode iMode) } } m_File = fopen(iFileName.c_str(), Mode); + if ((m_File == NULL) && (iMode == fmReadWrite)) + { + // Fix for MS not following C spec, opening "a" mode files for writing at the end only + // The file open operation has been tried with "read update", fails if file not found + // So now we know either the file doesn't exist or we don't have rights, no need to worry about file contents. + // Simply re-open for read-writing, erasing existing contents: + m_File = fopen(iFileName.c_str(), "wb+"); + } return (m_File != NULL); } @@ -251,3 +259,13 @@ int cFile::ReadRestOfFile(AString & a_Contents) + +bool cFile::Exists(const AString & a_FileName) +{ + cFile test(a_FileName, fmRead); + return test.IsOpen(); +} + + + + diff --git a/source/cFile.h b/source/cFile.h index 390847eaf..567c98219 100644 --- a/source/cFile.h +++ b/source/cFile.h @@ -86,6 +86,9 @@ public: /// Reads the file from current position till EOF into an AString; returns the number of bytes read or -1 for error int ReadRestOfFile(AString & a_Contents); + /// Returns true if the file specified exists + static bool Exists(const AString & a_FileName); + private: #ifdef USE_STDIO_FILE FILE * m_File; diff --git a/source/cMakeDir.cpp b/source/cMakeDir.cpp index 8c43a7d92..a28b3a088 100644 --- a/source/cMakeDir.cpp +++ b/source/cMakeDir.cpp @@ -7,16 +7,16 @@ -void cMakeDir::MakeDir( const char* a_Directory ) +void cMakeDir::MakeDir(const AString & a_Directory) { #ifdef _WIN32 SECURITY_ATTRIBUTES Attrib; Attrib.nLength = sizeof(SECURITY_ATTRIBUTES); Attrib.lpSecurityDescriptor = NULL; Attrib.bInheritHandle = false; - ::CreateDirectory(a_Directory, &Attrib); + ::CreateDirectory(a_Directory.c_str(), &Attrib); #else - mkdir(a_Directory, S_IRWXU | S_IRWXG | S_IRWXO); + mkdir(a_Directory.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); #endif } diff --git a/source/cMakeDir.h b/source/cMakeDir.h index 378df2a36..f109e17dd 100644 --- a/source/cMakeDir.h +++ b/source/cMakeDir.h @@ -1,7 +1,16 @@ + #pragma once + + + + class cMakeDir { public: - static void MakeDir( const char* a_Directory ); -}; \ No newline at end of file + static void MakeDir(const AString & a_Directory); +}; + + + + -- cgit v1.2.3