forked from Kolyah35/minecraft-pe-0.6.1
the whole game
This commit is contained in:
216
src/world/level/storage/RegionFile.cpp
Executable file
216
src/world/level/storage/RegionFile.cpp
Executable file
@@ -0,0 +1,216 @@
|
||||
#include "RegionFile.h"
|
||||
#include "../../../platform/log.h"
|
||||
|
||||
const int SECTOR_BYTES = 4096;
|
||||
const int SECTOR_INTS = SECTOR_BYTES / 4;
|
||||
const int SECTOR_COLS = 32;
|
||||
|
||||
static const char* const REGION_DAT_NAME = "chunks.dat";
|
||||
|
||||
static void logAssert(int actual, int expected) {
|
||||
if (actual != expected) {
|
||||
LOGI("ERROR: I/O operation failed (%d vs %d)\n", actual, expected);
|
||||
}
|
||||
}
|
||||
|
||||
RegionFile::RegionFile(const std::string& basePath)
|
||||
: file(NULL)
|
||||
{
|
||||
filename = basePath;
|
||||
filename += "/";
|
||||
filename += REGION_DAT_NAME;
|
||||
|
||||
offsets = new int[SECTOR_INTS];
|
||||
|
||||
emptyChunk = new int[SECTOR_INTS];
|
||||
memset(emptyChunk, 0, SECTOR_INTS * sizeof(int));
|
||||
}
|
||||
|
||||
RegionFile::~RegionFile()
|
||||
{
|
||||
close();
|
||||
delete [] offsets;
|
||||
delete [] emptyChunk;
|
||||
}
|
||||
|
||||
bool RegionFile::open()
|
||||
{
|
||||
close();
|
||||
|
||||
memset(offsets, 0, SECTOR_INTS * sizeof(int));
|
||||
|
||||
// attempt to open file
|
||||
file = fopen(filename.c_str(), "r+b");
|
||||
if (file)
|
||||
{
|
||||
// read offset table
|
||||
logAssert(fread(offsets, sizeof(int), SECTOR_INTS, file), SECTOR_INTS);
|
||||
|
||||
// mark initial sector as blocked
|
||||
sectorFree[0] = false;
|
||||
|
||||
// setup blocked sectors
|
||||
for (int sector = 0; sector < SECTOR_INTS; sector++)
|
||||
{
|
||||
int offset = offsets[sector];
|
||||
if (offset)
|
||||
{
|
||||
int base = offset >> 8;
|
||||
int count = offset & 0xff;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
sectorFree[base + i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// new world
|
||||
file = fopen(filename.c_str(), "w+b");
|
||||
if (!file)
|
||||
{
|
||||
LOGI("Failed to create chunk file %s\n", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// write sector header (all zeroes)
|
||||
logAssert(fwrite(offsets, sizeof(int), SECTOR_INTS, file), SECTOR_INTS);
|
||||
|
||||
// mark initial sector as blocked
|
||||
sectorFree[0] = false;
|
||||
}
|
||||
|
||||
return file != NULL;
|
||||
}
|
||||
|
||||
void RegionFile::close()
|
||||
{
|
||||
if (file)
|
||||
{
|
||||
fclose(file);
|
||||
file = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool RegionFile::readChunk(int x, int z, RakNet::BitStream** destChunkData)
|
||||
{
|
||||
int offset = offsets[x + z * SECTOR_COLS];
|
||||
|
||||
if (offset == 0)
|
||||
{
|
||||
// not written to file yet
|
||||
return false;
|
||||
}
|
||||
|
||||
int sectorNum = offset >> 8;
|
||||
|
||||
fseek(file, sectorNum * SECTOR_BYTES, SEEK_SET);
|
||||
int length = 0;
|
||||
fread(&length, sizeof(int), 1, file);
|
||||
|
||||
assert(length < ((offset & 0xff) * SECTOR_BYTES));
|
||||
length -= sizeof(int);
|
||||
if (length <= 0) {
|
||||
//*destChunkData = NULL;
|
||||
//return false;
|
||||
}
|
||||
|
||||
unsigned char* data = new unsigned char[length];
|
||||
logAssert(fread(data, 1, length, file), length);
|
||||
*destChunkData = new RakNet::BitStream(data, length, false);
|
||||
//delete [] data;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RegionFile::writeChunk(int x, int z, RakNet::BitStream& chunkData)
|
||||
{
|
||||
int size = chunkData.GetNumberOfBytesUsed() + sizeof(int);
|
||||
|
||||
int offset = offsets[x + z * SECTOR_COLS];
|
||||
int sectorNum = offset >> 8;
|
||||
int sectorCount = offset & 0xff;
|
||||
int sectorsNeeded = (size / SECTOR_BYTES) + 1;
|
||||
|
||||
if (sectorsNeeded > 256)
|
||||
{
|
||||
LOGI("ERROR: Chunk is too big to be saved to file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sectorNum != 0 && sectorCount == sectorsNeeded) {
|
||||
// the sector can be written on top of its old data
|
||||
write(sectorNum, chunkData);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we need a new location
|
||||
|
||||
// mark old sectors as free
|
||||
for (int i = 0; i < sectorCount; i++)
|
||||
{
|
||||
sectorFree[sectorNum + i] = true;
|
||||
}
|
||||
|
||||
// find an available slot with enough run length
|
||||
int slot = 0;
|
||||
int runLength = 0;
|
||||
bool extendFile = false;
|
||||
while (runLength < sectorsNeeded)
|
||||
{
|
||||
if (sectorFree.find(slot + runLength) == sectorFree.end())
|
||||
{
|
||||
extendFile = true;
|
||||
break;
|
||||
}
|
||||
if (sectorFree[slot + runLength] == true)
|
||||
{
|
||||
runLength++;
|
||||
}
|
||||
else
|
||||
{
|
||||
slot = slot + runLength + 1;
|
||||
runLength = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (extendFile)
|
||||
{
|
||||
fseek(file, 0, SEEK_END);
|
||||
for (int i = 0; i < (sectorsNeeded - runLength); i++)
|
||||
{
|
||||
fwrite(emptyChunk, sizeof(int), SECTOR_INTS, file);
|
||||
sectorFree[slot + i] = true;
|
||||
}
|
||||
}
|
||||
offsets[x + z * SECTOR_COLS] = (slot << 8) | sectorsNeeded;
|
||||
// mark slots as taken
|
||||
for (int i = 0; i < sectorsNeeded; i++)
|
||||
{
|
||||
sectorFree[slot + i] = false;
|
||||
}
|
||||
|
||||
// write!
|
||||
write(slot, chunkData);
|
||||
|
||||
// write sector data
|
||||
fseek(file, (x + z * SECTOR_COLS) * sizeof(int), SEEK_SET);
|
||||
fwrite(&offsets[x + z * SECTOR_COLS], sizeof(int), 1, file);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RegionFile::write(int sector, RakNet::BitStream& chunkData)
|
||||
{
|
||||
fseek(file, sector * SECTOR_BYTES, SEEK_SET);
|
||||
//LOGI("writing %d B to sector %d\n", chunkData.GetNumberOfBytesUsed(), sector);
|
||||
int size = chunkData.GetNumberOfBytesUsed() + sizeof(int);
|
||||
logAssert(fwrite(&size, sizeof(int), 1, file), 1);
|
||||
logAssert(fwrite(chunkData.GetData(), 1, chunkData.GetNumberOfBytesUsed(), file), chunkData.GetNumberOfBytesUsed());
|
||||
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user