From 7e743398a92f6878659fc429bda26501ca2fa357 Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Mon, 30 Jan 2012 13:54:39 +0000 Subject: Initial cFile implementation (using stdio FILE) and test in cChunkMap git-svn-id: http://mc-server.googlecode.com/svn/trunk@192 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/cChunkMap.cpp | 170 +++++++++++++++++++++---------------------- source/cChunkMap.h | 5 +- source/cFile.cpp | 200 +++++++++++++++++++++++++++++++++++++++++++++++++++ source/cFile.h | 99 +++++++++++++++++++++++++ 4 files changed, 388 insertions(+), 86 deletions(-) create mode 100644 source/cFile.cpp create mode 100644 source/cFile.h (limited to 'source') diff --git a/source/cChunkMap.cpp b/source/cChunkMap.cpp index 417b4a98e..b7f1e08a2 100644 --- a/source/cChunkMap.cpp +++ b/source/cChunkMap.cpp @@ -16,6 +16,11 @@ #include "zlib.h" #include + +// TODO: Move this into Globals.h after cFile has been finalized +#include "cFile.h" + + #define USE_MEMCPY #define LAYER_SIZE (32) @@ -24,18 +29,32 @@ -cChunkMap::cChunkMap(cWorld* a_World ) - : m_Layers( 0 ) - , m_NumLayers( 0 ) - , m_World( a_World ) +//////////////////////////////////////////////////////////////////////////////// +// cChunkMap::cChunkLayer: + +cChunkMap::cChunkData* cChunkMap::cChunkLayer::GetChunk( int a_X, int a_Z ) { + const int LocalX = a_X - m_X * LAYER_SIZE; + const int LocalZ = a_Z - m_Z * LAYER_SIZE; + //LOG("LocalX:%i LocalZ:%i", LocalX, LocalZ ); + if ((LocalX < LAYER_SIZE) && (LocalZ < LAYER_SIZE) && (LocalX > -1) && (LocalZ > -1)) + { + return &m_Chunks[ LocalX + LocalZ * LAYER_SIZE ]; + } + return 0; } -cChunkMap::~cChunkMap() +//////////////////////////////////////////////////////////////////////////////// +// cChunkMap: + +cChunkMap::cChunkMap(cWorld* a_World ) + : m_Layers( 0 ) + , m_NumLayers( 0 ) + , m_World( a_World ) { } @@ -43,14 +62,8 @@ cChunkMap::~cChunkMap() -cChunkMap::cChunkData* cChunkMap::cChunkLayer::GetChunk( int a_X, int a_Z ) +cChunkMap::~cChunkMap() { - const int LocalX = a_X - m_X * LAYER_SIZE; - const int LocalZ = a_Z - m_Z * LAYER_SIZE; - //LOG("LocalX:%i LocalZ:%i", LocalX, LocalZ ); - if( LocalX < LAYER_SIZE && LocalZ < LAYER_SIZE && LocalX > -1 && LocalZ > -1 ) - return &m_Chunks[ LocalX + LocalZ * LAYER_SIZE ]; - return 0; } @@ -449,14 +462,10 @@ void cChunkMap::SaveLayer( cChunkLayer* a_Layer ) char SourceFile[128]; - sprintf_s(SourceFile, 128, ( WorldName + "/X%i_Z%i.pak").c_str(), a_Layer->m_X, a_Layer->m_Z ); + sprintf_s(SourceFile, ARRAYCOUNT(SourceFile), ( WorldName + "/X%i_Z%i.pak").c_str(), a_Layer->m_X, a_Layer->m_Z ); - FILE* f = 0; - #ifdef _WIN32 - if (fopen_s(&f, SourceFile, "wb" ) != 0 ) // no error - #else - if( (f = fopen(SourceFile, "wb" )) == 0 ) // no error - #endif + cFile f; + if (!f.Open(SourceFile, cFile::fmWrite)) { LOGERROR("ERROR: Could not write to file %s", SourceFile ); return; @@ -466,8 +475,8 @@ void cChunkMap::SaveLayer( cChunkLayer* a_Layer ) // Header char PakVersion = 1; char ChunkVersion = 1; - fwrite( &PakVersion, sizeof(PakVersion), 1, f ); // pak version - fwrite( &ChunkVersion, sizeof(ChunkVersion), 1, f ); // chunk version + f.Write(&PakVersion, sizeof(PakVersion)); // pak version + f.Write(&ChunkVersion, sizeof(ChunkVersion)); // chunk version // Count number of chunks in layer short NumChunks = 0; @@ -479,120 +488,118 @@ void cChunkMap::SaveLayer( cChunkLayer* a_Layer ) } } - fwrite( &NumChunks, sizeof( NumChunks ), 1, f ); - LOG("Num Chunks in layer: %i", NumChunks ); + f.Write(&NumChunks, sizeof(NumChunks)); + LOG("Num Chunks in layer [%d, %d]: %i", a_Layer->m_X, a_Layer->m_Z, NumChunks); - //--------------- // Chunk headers - for( int z = 0; z < LAYER_SIZE; ++z ) + for (int z = 0; z < LAYER_SIZE; ++z) { - for( int x = 0; x < LAYER_SIZE; ++x ) + for (int x = 0; x < LAYER_SIZE; ++x) { - cChunkData & Data = a_Layer->m_Chunks[x + z*LAYER_SIZE]; - CompressChunk( &Data ); - if( Data.m_Compressed || Data.m_LiveChunk ) + cChunkData & Data = a_Layer->m_Chunks[x + z * LAYER_SIZE]; + CompressChunk(&Data); + if (Data.m_Compressed != NULL) { - int ChunkX = a_Layer->m_X*LAYER_SIZE + x; - int ChunkZ = a_Layer->m_Z*LAYER_SIZE + z; + int ChunkX = a_Layer->m_X * LAYER_SIZE + x; + int ChunkZ = a_Layer->m_Z * LAYER_SIZE + z; unsigned int Size = Data.m_CompressedSize; // Needs to be size of compressed data unsigned int USize = Data.m_UncompressedSize; // Uncompressed size - fwrite( &ChunkX, sizeof( ChunkX ), 1, f ); - fwrite( &ChunkZ, sizeof( ChunkZ ), 1, f ); - fwrite( &Size, sizeof( Size ), 1, f ); - fwrite( &USize, sizeof( USize ), 1, f ); + f.Write(&ChunkX, sizeof(ChunkX)); + f.Write(&ChunkZ, sizeof(ChunkZ)); + f.Write(&Size, sizeof(Size)); + f.Write(&USize, sizeof(USize)); } - } - } + } // for x - a_Layer->mChunks[x] + } // for z - a_Layer->m_Chunks[z] - //---------------- // Chunk data - for( int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i ) + for (int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i) { - char* Compressed = a_Layer->m_Chunks[i].m_Compressed; - if( Compressed ) + char * Compressed = a_Layer->m_Chunks[i].m_Compressed; + if (Compressed != NULL) { - fwrite( Compressed, a_Layer->m_Chunks[i].m_CompressedSize, 1, f ); - if(a_Layer->m_Chunks[i].m_LiveChunk) // If there's a live chunk we have no need for compressed data + f.Write(Compressed, a_Layer->m_Chunks[i].m_CompressedSize); + if (a_Layer->m_Chunks[i].m_LiveChunk != NULL) // If there's a live chunk we have no need for compressed data { delete [] a_Layer->m_Chunks[i].m_Compressed; a_Layer->m_Chunks[i].m_Compressed = 0; a_Layer->m_Chunks[i].m_CompressedSize = 0; } } - } - - fclose(f); + } // for i - a_Layer->m_Chunks[] } +#define READ(File, Var) \ + if (File.Read(&Var, sizeof(Var)) != sizeof(Var)) \ + { \ + LOGERROR("ERROR READING %s FROM FILE %s (line %d)", #Var, SourceFile, __LINE__); \ + return NULL; \ + } + cChunkMap::cChunkLayer* cChunkMap::LoadLayer(int a_LayerX, int a_LayerZ ) { std::string WorldName = m_World->GetName(); char SourceFile[128]; - sprintf_s(SourceFile, 128, (WorldName + "/X%i_Z%i.pak").c_str(), a_LayerX, a_LayerZ ); + sprintf_s(SourceFile, ARRAYCOUNT(SourceFile), (WorldName + "/X%i_Z%i.pak").c_str(), a_LayerX, a_LayerZ ); - FILE* f = 0; - #ifdef _WIN32 - if (fopen_s(&f, SourceFile, "rb" ) != 0 ) // no error - #else - if ((f = fopen(SourceFile, "rb" )) == 0 ) // no error - #endif + cFile f(SourceFile, cFile::fmRead); + if (!f.IsOpen()) { return NULL; } char PakVersion = 0; char ChunkVersion = 0; - if( fread( &PakVersion, sizeof(PakVersion), 1, f) != 1 ) { LOGERROR("ERROR 1 READING FROM FILE %s", SourceFile); fclose(f); return false; } - if( PakVersion != 1 ) { LOGERROR("WRONG PAK VERSION!"); fclose(f); return 0; } - if( fread( &ChunkVersion, sizeof(ChunkVersion), 1, f) != 1 ) { LOGERROR("ERROR 2 READING FROM FILE %s", SourceFile); fclose(f); return false; } - if( ChunkVersion != 1 ) + + READ(f, PakVersion); + if (PakVersion != 1) { - LOGERROR("WRONG CHUNK VERSION!"); - fclose(f); + LOGERROR("WRONG PAK VERSION in file \"%s\"!", SourceFile); return NULL; } - - short NumChunks = 0; - if( fread( &NumChunks, sizeof(NumChunks), 1, f) != 1 ) + + READ(f, ChunkVersion); + if (ChunkVersion != 1 ) { - LOGERROR("ERROR 3 READING FROM FILE %s", SourceFile); - fclose(f); + LOGERROR("WRONG CHUNK VERSION in file \"%s\"!", SourceFile); return NULL; } + + short NumChunks = 0; + READ(f, NumChunks); - LOG("Num chunks: %i", NumChunks ); - LOG("Source File: %s", SourceFile ); + LOG("Num chunks in file \"%s\": %i", SourceFile, NumChunks); - cChunkLayer* Layer = new cChunkLayer( LAYER_SIZE*LAYER_SIZE ); + std::auto_ptr Layer(new cChunkLayer(LAYER_SIZE * LAYER_SIZE)); // The auto_ptr deletes the Layer if we exit with an error Layer->m_X = a_LayerX; Layer->m_Z = a_LayerZ; - cChunkData** OrderedData = new cChunkData*[ NumChunks ]; // So we can loop over the chunks in the order they were loaded + + cChunkData * OrderedData[LAYER_SIZE * LAYER_SIZE]; // So we can loop over the chunks in the order they were loaded // Loop over all chunk headers for( short i = 0; i < NumChunks; ++i ) { int ChunkX = 0; int ChunkZ = 0; - if( fread( &ChunkX, sizeof(ChunkX), 1, f) != 1 ) { LOGERROR("ERROR 4 READING FROM FILE %s", SourceFile); fclose(f); return false; } - if( fread( &ChunkZ, sizeof(ChunkZ), 1, f) != 1 ) { LOGERROR("ERROR 5 READING FROM FILE %s", SourceFile); fclose(f); return false; } + READ(f, ChunkX); + READ(f, ChunkZ); cChunkData* Data = Layer->GetChunk( ChunkX, ChunkZ ); - if( Data ) + + if (Data == NULL) { - if( fread( &Data->m_CompressedSize, sizeof(Data->m_CompressedSize), 1, f) != 1 ) { LOGERROR("ERROR 6 READING FROM FILE %s", SourceFile); fclose(f); return false; } - if( fread( &Data->m_UncompressedSize, sizeof(Data->m_UncompressedSize), 1, f) != 1 ) { LOGERROR("ERROR 7 READING FROM FILE %s", SourceFile); fclose(f); return false; } + LOGERROR("Chunk with wrong coordinates in pak file! %i %i", ChunkX, ChunkZ ); + return NULL; } else { - LOGERROR("Chunk with wrong coordinates in pak file! %i %i", ChunkX, ChunkZ ); - fclose(f); - return NULL; + READ(f, Data->m_CompressedSize); + READ(f, Data->m_UncompressedSize); } - OrderedData[i] = Data; } @@ -601,18 +608,13 @@ cChunkMap::cChunkLayer* cChunkMap::LoadLayer(int a_LayerX, int a_LayerZ ) { cChunkData* Data = OrderedData[i]; Data->m_Compressed = new char[ Data->m_CompressedSize ]; - //printf("Compressed chunk size: %i\n",Data->m_CompressedSize); - if( fread( Data->m_Compressed, Data->m_CompressedSize, 1, f) != 1 ) + if (f.Read(Data->m_Compressed, Data->m_CompressedSize) != Data->m_CompressedSize) { LOGERROR("ERROR 8 READING FROM FILE %s", SourceFile); - fclose(f); return NULL; } } - delete [] OrderedData; - - fclose(f); - return Layer; + return Layer.release(); } diff --git a/source/cChunkMap.h b/source/cChunkMap.h index e375a0de0..f2d1f4468 100644 --- a/source/cChunkMap.h +++ b/source/cChunkMap.h @@ -55,8 +55,9 @@ private: , m_Z( 0 ) , m_NumChunksLoaded( 0 ) {} - cChunkData* GetChunk( int a_X, int a_Z ); - cChunkData* m_Chunks; + cChunkData * GetChunk( int a_X, int a_Z ); + + cChunkData * m_Chunks; int m_X, m_Z; int m_NumChunksLoaded; }; diff --git a/source/cFile.cpp b/source/cFile.cpp new file mode 100644 index 000000000..84308984a --- /dev/null +++ b/source/cFile.cpp @@ -0,0 +1,200 @@ + +// cFile.cpp + +// Implements the cFile class providing an OS-independent abstraction of a file. + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "cFile.h" + + + + + + +/// Simple constructor - creates an unopened file object, use Open() to open / create a real file +cFile::cFile(void) : + #ifdef USE_STDIO_FILE + m_File(NULL) + #else + m_File(INVALID_HANDLE_VALUE) + #endif // USE_STDIO_FILE +{ + // Nothing needed yet +} + + + + + +/// Constructs and opens / creates the file specified, use IsOpen() to check for success +cFile::cFile(const char * iFileName, EMode iMode) : + #ifdef USE_STDIO_FILE + m_File(NULL) + #else + m_File(INVALID_HANDLE_VALUE) + #endif // USE_STDIO_FILE +{ + Open(iFileName, iMode); +} + + + + + +/// Auto-closes the file, if open +cFile::~cFile() +{ + if (IsOpen()) + { + Close(); + } +} + + + + + +bool cFile::Open(const char * iFileName, EMode iMode) +{ + assert(!IsOpen()); // You should close the file before opening another one + + if (IsOpen()) + { + Close(); + } + + const char * Mode = NULL; + switch (iMode) + { + case fmRead: Mode = "rb"; break; + case fmWrite: Mode = "wb"; break; + case fmReadWrite: Mode = "ab+"; break; + default: + { + assert(!"Unhandled file mode"); + return false; + } + } + m_File = fopen(iFileName, Mode); + return (m_File != NULL); +} + + + + + +void cFile::Close(void) +{ + assert(IsOpen()); // You should not close file objects that don't have an open file. + + if (!IsOpen()) + { + return; + } + + fclose(m_File); +} + + + + + +bool cFile::IsOpen(void) const +{ + return (m_File != NULL); +} + + + + + +bool cFile::IsEOF(void) const +{ + assert(IsOpen()); + + if (!IsOpen()) + { + // Unopened files behave as at EOF + return true; + } + + return (feof(m_File) != 0); +} + + + + + +/// Reads up to iNumBytes bytes into iBuffer, returns the number of bytes actually read, or -1 on failure; asserts if not open +int cFile::Read (void * iBuffer, int iNumBytes) +{ + assert(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + return fread(iBuffer, 1, iNumBytes, m_File); // fread() returns the portion of Count parameter actually read, so we need to send iNumBytes as Count +} + + + + + +/// Writes up to iNumBytes bytes from iBuffer, returns the number of bytes actually written, or -1 on failure; asserts if not open +int cFile::Write(const void * iBuffer, int iNumBytes) +{ + assert(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + return fwrite(iBuffer, 1, iNumBytes, m_File); // fwrite() returns the portion of Count parameter actually written, so we need to send iNumBytes as Count +} + + + + + +/// Seeks to iPosition bytes from file start, returns old position or -1 for failure +int cFile::Seek (int iPosition) +{ + assert(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + if (fseek(m_File, iPosition, SEEK_SET) != 0) + { + return -1; + } + return ftell(m_File); +} + + + + + + +/// Returns the current position (bytes from file start) +int cFile::Tell (void) const +{ + assert(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + return ftell(m_File); +} + + + + diff --git a/source/cFile.h b/source/cFile.h new file mode 100644 index 000000000..0c3abb1d9 --- /dev/null +++ b/source/cFile.h @@ -0,0 +1,99 @@ + +// cFile.h + +// Interfaces to the cFile class providing an OS-independent abstraction of a file. + +/* +The object is optimized towards binary reads. +The object has no multithreading locks, don't use from multiple threads! +Usage: +1, Construct a cFile instance (no-param constructor) +2, Open a file using Open(), check return value for success +3, Read / write +4, Destroy the instance + +-- OR -- + +1, Construct a cFile instance opening the file (filename-param constructor) +2, Check if the file was opened using IsOpen() +3, Read / write +4, Destroy the instance +*/ + + + + + +#pragma once +#ifndef CFILE_H_INCLUDED +#define CFILE_H_INCLUDED + + + + + +#ifndef _WIN32 + #define USE_STDIO_FILE +#endif // _WIN32 + +// DEBUG: +#define USE_STDIO_FILE + + + + + +class cFile +{ +public: + /// The mode in which to open the file + enum EMode + { + fmRead, // Read-only. If the file doesn't exist, object will not be valid + fmWrite, // Write-only. If the file already exists, it will be overwritten + fmReadWrite, // Read/write. If the file already exists, it will be left intact + } ; + + /// Simple constructor - creates an unopened file object, use Open() to open / create a real file + cFile(void); + + /// Constructs and opens / creates the file specified, use IsOpen() to check for success + cFile(const char * iFileName, EMode iMode); + + /// Auto-closes the file, if open + ~cFile(); + + bool Open(const char * iFileName, EMode iMode); + void Close(void); + bool IsOpen(void) const; + bool IsEOF(void) const; + + /// Reads up to iNumBytes bytes into iBuffer, returns the number of bytes actually read, or -1 on failure; asserts if not open + int Read (void * iBuffer, int iNumBytes); + + /// Writes up to iNumBytes bytes from iBuffer, returns the number of bytes actually written, or -1 on failure; asserts if not open + int Write(const void * iBuffer, int iNumBytes); + + /// Seeks to iPosition bytes from file start, returns old position or -1 for failure; asserts if not open + int Seek (int iPosition); + + /// Returns the current position (bytes from file start) or -1 for failure; asserts if not open + int Tell (void) const; + +private: + #ifdef USE_STDIO_FILE + FILE * m_File; + #else + HANDLE m_File; + #endif +} ; + + + + + +#endif // CFILE_H_INCLUDED + + + + -- cgit v1.2.3