Refactor, world.bin file, BinaryWriter, BinaryReader

This commit is contained in:
MihailRis
2023-11-16 19:32:44 +03:00
parent 9bc236b71a
commit 6cd775c27d
14 changed files with 378 additions and 126 deletions
+71 -38
View File
@@ -2,6 +2,7 @@
#include "files.h"
#include "rle.h"
#include "binary_io.h"
#include "../window/Camera.h"
#include "../objects/Player.h"
#include "../physics/Hitbox.h"
@@ -23,6 +24,8 @@
#define PLAYER_FLAG_FLIGHT 0x1
#define PLAYER_FLAG_NOCLIP 0x2
#define WORLD_SECTION_MAIN 1
using glm::ivec2;
using glm::vec3;
using std::ios;
@@ -40,14 +43,6 @@ void int2Bytes(int value, ubyte* dest, size_t offset){
dest[offset+3] = (char) (value >> 0 & 255);
}
void floatToBytes(float fvalue, ubyte* dest, size_t offset){
uint32_t value = *((uint32_t*)&fvalue);
dest[offset] = (char) (value >> 24 & 255);
dest[offset+1] = (char) (value >> 16 & 255);
dest[offset+2] = (char) (value >> 8 & 255);
dest[offset+3] = (char) (value >> 0 & 255);
}
float bytes2Float(ubyte* src, uint offset){
uint32_t value = ((src[offset] << 24) |
(src[offset+1] << 16) |
@@ -56,7 +51,7 @@ float bytes2Float(ubyte* src, uint offset){
return *(float*)(&value);
}
WorldFiles::WorldFiles(path directory, size_t mainBufferCapacity, bool generatorTestMode)
WorldFiles::WorldFiles(path directory, bool generatorTestMode)
: directory(directory), generatorTestMode(generatorTestMode) {
compressionBuffer = new ubyte[CHUNK_DATA_LEN * 2];
}
@@ -115,14 +110,18 @@ void WorldFiles::put(Chunk* chunk){
region.compressedSizes[chunk_index] = compressedSize;
}
path WorldFiles::getRegionFile(int x, int y) {
path WorldFiles::getRegionFile(int x, int y) const {
return directory/path(std::to_string(x) + "_" + std::to_string(y) + ".bin");
}
path WorldFiles::getPlayerFile() {
path WorldFiles::getPlayerFile() const {
return directory/path("player.bin");
}
path WorldFiles::getWorldFile() const {
return directory/path("world.bin");
}
ubyte* WorldFiles::getChunk(int x, int y){
int regionX = floordiv(x, REGION_SIZE);
int regionY = floordiv(y, REGION_SIZE);
@@ -199,10 +198,11 @@ ubyte* WorldFiles::readChunkData(int x, int y, uint32_t& length){
return data;
}
void WorldFiles::write(){
void WorldFiles::write(const WorldInfo info){
if (!std::filesystem::is_directory(directory)) {
std::filesystem::create_directories(directory);
}
writeWorldInfo(info);
if (generatorTestMode)
return;
for (auto it = regions.begin(); it != regions.end(); it++){
@@ -213,26 +213,58 @@ void WorldFiles::write(){
}
}
void WorldFiles::writePlayer(Player* player){
ubyte dst[1+3*4 + 1+2*4 + 1+1];
void WorldFiles::writeWorldInfo(const WorldInfo& info) {
BinaryWriter out;
out.putCStr(WORLD_FORMAT_MAGIC);
out.put(WORLD_FORMAT_VERSION);
out.put(WORLD_SECTION_MAIN);
out.putInt64(info.seed);
out.put(info.name);
files::write_bytes(getWorldFile(), (const char*)out.data(), out.size());
}
bool WorldFiles::readWorldInfo(WorldInfo& info) {
size_t length = 0;
ubyte* data = (ubyte*)files::read_bytes(getPlayerFile(), length);
if (data == nullptr){
std::cerr << "could not to read world.bin (ignored)" << std::endl;
return false;
}
BinaryReader inp(data, length);
inp.checkMagic(WORLD_FORMAT_MAGIC, length);
/*ubyte version = */inp.get();
while (inp.hasNext()) {
ubyte section = inp.get();
switch (section) {
case WORLD_SECTION_MAIN:
info.seed = inp.getInt64();
info.name = inp.getString();
break;
}
}
return false;
}
void WorldFiles::writePlayer(Player* player){
vec3 position = player->hitbox->position;
size_t offset = 0;
dst[offset++] = SECTION_POSITION;
floatToBytes(position.x, dst, offset); offset += 4;
floatToBytes(position.y, dst, offset); offset += 4;
floatToBytes(position.z, dst, offset); offset += 4;
BinaryWriter out;
out.put(SECTION_POSITION);
out.putFloat32(position.x);
out.putFloat32(position.y);
out.putFloat32(position.z);
dst[offset++] = SECTION_ROTATION;
floatToBytes(player->camX, dst, offset); offset += 4;
floatToBytes(player->camY, dst, offset); offset += 4;
out.put(SECTION_ROTATION);
out.putFloat32(player->camX);
out.putFloat32(player->camY);
dst[offset++] = SECTION_FLAGS;
dst[offset++] = player->flight * PLAYER_FLAG_FLIGHT |
player->noclip * PLAYER_FLAG_NOCLIP;
out.put(SECTION_FLAGS);
out.put(player->flight * PLAYER_FLAG_FLIGHT |
player->noclip * PLAYER_FLAG_NOCLIP);
files::write_bytes(getPlayerFile(), (const char*)dst, sizeof(dst));
files::write_bytes(getPlayerFile(), (const char*)out.data(), out.size());
}
bool WorldFiles::readPlayer(Player* player) {
@@ -243,28 +275,29 @@ bool WorldFiles::readPlayer(Player* player) {
return false;
}
vec3 position = player->hitbox->position;
size_t offset = 0;
while (offset < length){
char section = data[offset++];
switch (section){
BinaryReader inp(data, length);
while (inp.hasNext()) {
ubyte section = inp.get();
switch (section) {
case SECTION_POSITION:
position.x = bytes2Float(data, offset); offset += 4;
position.y = bytes2Float(data, offset); offset += 4;
position.z = bytes2Float(data, offset); offset += 4;
position.x = inp.getFloat32();
position.y = inp.getFloat32();
position.z = inp.getFloat32();
break;
case SECTION_ROTATION:
player->camX = bytes2Float(data, offset); offset += 4;
player->camY = bytes2Float(data, offset); offset += 4;
player->camX = inp.getFloat32();
player->camY = inp.getFloat32();
break;
case SECTION_FLAGS:
case SECTION_FLAGS:
{
ubyte flags = data[offset++];
ubyte flags = inp.get();
player->flight = flags & PLAYER_FLAG_FLIGHT;
player->noclip = flags & PLAYER_FLAG_NOCLIP;
}
break;
}
}
player->hitbox->position = position;
player->camera->position = position + vec3(0, 1, 0);
return true;
@@ -281,7 +314,7 @@ void WorldFiles::writeRegion(int x, int y, WorldRegion& entry){
}
}
char header[10] = ".VOXREG";
char header[10] = REGION_FORMAT_MAGIC;
header[8] = REGION_FORMAT_VERSION;
header[9] = 0; // flags
std::ofstream file(getRegionFile(x, y), ios::out | ios::binary);
+16 -5
View File
@@ -17,6 +17,9 @@
#define REGION_SIZE (1 << (REGION_SIZE_BIT))
#define REGION_VOL ((REGION_SIZE) * (REGION_SIZE))
#define REGION_FORMAT_VERSION 1
#define REGION_FORMAT_MAGIC ".VOXREG"
#define WORLD_FORMAT_MAGIC ".VOXWLD"
#define WORLD_FORMAT_VERSION 1
class Player;
class Chunk;
@@ -27,27 +30,35 @@ struct WorldRegion {
bool unsaved;
};
struct WorldInfo {
std::string name;
std::filesystem::path directory;
uint64_t seed;
};
class WorldFiles {
void writeWorldInfo(const WorldInfo& info);
std::filesystem::path getRegionFile(int x, int y) const;
std::filesystem::path getPlayerFile() const;
std::filesystem::path getWorldFile() const;
public:
std::unordered_map<glm::ivec2, WorldRegion> regions;
std::filesystem::path directory;
ubyte* compressionBuffer;
bool generatorTestMode;
WorldFiles(std::filesystem::path directory, size_t mainBufferCapacity, bool generatorTestMode);
WorldFiles(std::filesystem::path directory, bool generatorTestMode);
~WorldFiles();
void put(Chunk* chunk);
bool readWorldInfo(WorldInfo& info);
bool readPlayer(Player* player);
ubyte* readChunkData(int x, int y, uint32_t& length);
ubyte* getChunk(int x, int y);
void writeRegion(int x, int y, WorldRegion& entry);
void writePlayer(Player* player);
void write();
std::filesystem::path getRegionFile(int x, int y);
std::filesystem::path getPlayerFile();
void write(const WorldInfo info);
};
#endif /* FILES_WORLDFILES_H_ */
+140
View File
@@ -0,0 +1,140 @@
#include "binary_io.h"
#include <string.h>
#include <limits>
#include <stdexcept>
using std::string;
void BinaryWriter::put(ubyte b) {
buffer.push_back(b);
}
void BinaryWriter::putCStr(const char* str) {
size_t size = strlen(str)+1;
buffer.reserve(buffer.size() + size);
for (size_t i = 0; i < size; i++) {
buffer.push_back(str[i]);
}
}
void BinaryWriter::put(const string& s) {
size_t len = s.length();
if (len > INT16_MAX) {
throw std::domain_error("length > INT16_MAX");
}
putInt16(len);
put((const ubyte*)s.data(), len);
}
void BinaryWriter::put(const ubyte* arr, size_t size) {
buffer.reserve(buffer.size() + size);
for (size_t i = 0; i < size; i++) {
buffer.push_back(arr[i]);
}
}
void BinaryWriter::putInt16(int16_t val) {
buffer.push_back((char) (val >> 8 & 255));
buffer.push_back((char) (val >> 0 & 255));
}
void BinaryWriter::putInt32(int32_t val) {
buffer.reserve(buffer.size() + 4);
buffer.push_back((char) (val >> 24 & 255));
buffer.push_back((char) (val >> 16 & 255));
buffer.push_back((char) (val >> 8 & 255));
buffer.push_back((char) (val >> 0 & 255));
}
void BinaryWriter::putInt64(int64_t val) {
buffer.reserve(buffer.size() + 8);
buffer.push_back((char) (val >> 56 & 255));
buffer.push_back((char) (val >> 48 & 255));
buffer.push_back((char) (val >> 40 & 255));
buffer.push_back((char) (val >> 32 & 255));
buffer.push_back((char) (val >> 24 & 255));
buffer.push_back((char) (val >> 16 & 255));
buffer.push_back((char) (val >> 8 & 255));
buffer.push_back((char) (val >> 0 & 255));
}
void BinaryWriter::putFloat32(float val) {
putInt32(*((uint32_t*)&val));
}
BinaryReader::BinaryReader(const ubyte* data, size_t size)
: data(data), size(size), pos(0) {
}
void BinaryReader::checkMagic(const char* data, size_t size) {
if (pos + size >= this->size) {
throw std::runtime_error("invalid magic number");
}
for (size_t i = 0; i < size; i++) {
if (this->data[pos + i] != (ubyte)data[i]){
throw std::runtime_error("invalid magic number");
}
}
pos += size;
}
ubyte BinaryReader::get() {
if (pos == size) {
throw std::underflow_error("buffer underflow");
}
return data[pos++];
}
int16_t BinaryReader::getInt16() {
if (pos+2 >= size) {
throw std::underflow_error("unexpected end");
}
pos += 2;
return (data[pos - 2] << 8) |
(data[pos - 1]);
}
int32_t BinaryReader::getInt32() {
if (pos+4 >= size) {
throw std::underflow_error("unexpected end");
}
pos += 4;
return (data[pos - 4] << 24) |
(data[pos - 3] << 16) |
(data[pos - 2] << 8) |
(data[pos - 1]);
}
int64_t BinaryReader::getInt64() {
if (pos+8 >= size) {
throw std::underflow_error("unexpected end");
}
pos += 8;
return ((int64_t)data[pos - 8] << 56) |
((int64_t)data[pos - 7] << 48) |
((int64_t)data[pos - 6] << 40) |
((int64_t)data[pos - 5] << 32) |
((int64_t)data[pos - 4] << 24) |
((int64_t)data[pos - 3] << 16) |
((int64_t)data[pos - 2] << 8) |
((int64_t)data[pos - 1]);
}
float BinaryReader::getFloat32() {
int32_t value = getInt32();
return *(float*)(&value);
}
string BinaryReader::getString() {
uint16_t length = (uint16_t)getInt16();
if (pos+length >= size) {
throw std::underflow_error("unexpected end");
}
pos += length;
return string((const char*)(data+pos-length), length);
}
bool BinaryReader::hasNext() const {
return pos < size;
}
+45
View File
@@ -0,0 +1,45 @@
#ifndef FILES_BINARY_WRITER_H_
#define FILES_BINARY_WRITER_H_
#include <string>
#include <vector>
#include "../typedefs.h"
class BinaryWriter {
std::vector<ubyte> buffer;
public:
void put(ubyte b);
void putCStr(const char* str);
void putInt16(int16_t val);
void putInt32(int32_t val);
void putInt64(int64_t val);
void putFloat32(float val);
void put(const std::string& s);
void put(const ubyte* arr, size_t size);
inline size_t size() const {
return buffer.size();
}
inline const ubyte* data() const {
return buffer.data();
}
};
class BinaryReader {
const ubyte* data;
size_t size;
size_t pos;
public:
BinaryReader(const ubyte* data, size_t size);
void checkMagic(const char* data, size_t size);
ubyte get();
int16_t getInt16();
int32_t getInt32();
int64_t getInt64();
float getFloat32();
std::string getString();
bool hasNext() const;
};
#endif // FILES_BINARY_WRITER_H_