diff --git a/src/coders/rle.cpp b/src/coders/rle.cpp index 771d1a41..a13454a5 100644 --- a/src/coders/rle.cpp +++ b/src/coders/rle.cpp @@ -35,6 +35,45 @@ size_t rle::encode(const ubyte* src, size_t srclen, ubyte* dst) { return offset; } +size_t rle::decode16(const ubyte* src, size_t srclen, ubyte* dst) { + auto src16 = reinterpret_cast(src); + auto dst16 = reinterpret_cast(dst); + size_t offset = 0; + for (size_t i = 0; i < srclen / 2;) { + uint16_t len = src16[i++]; + uint16_t c = src16[i++]; + for (size_t j = 0; j <= len; j++) { + dst16[offset++] = c; + } + } + return offset * 2; +} + +size_t rle::encode16(const ubyte* src, size_t srclen, ubyte* dst) { + if (srclen == 0) { + return 0; + } + auto src16 = reinterpret_cast(src); + auto dst16 = reinterpret_cast(dst); + size_t offset = 0; + uint16_t counter = 0; + uint16_t c = src16[0]; + for (size_t i = 1; i < srclen / 2; i++) { + uint16_t cnext = src16[i]; + if (cnext != c || counter == 0xFFFF) { + dst16[offset++] = counter; + dst16[offset++] = c; + c = cnext; + counter = 0; + } else { + counter++; + } + } + dst16[offset++] = counter; + dst16[offset++] = c; + return offset * 2; +} + size_t extrle::decode(const ubyte* src, size_t srclen, ubyte* dst) { size_t offset = 0; for (size_t i = 0; i < srclen;) { diff --git a/src/coders/rle.hpp b/src/coders/rle.hpp index 0692cc9a..eb6c5b1d 100644 --- a/src/coders/rle.hpp +++ b/src/coders/rle.hpp @@ -5,6 +5,9 @@ namespace rle { size_t encode(const ubyte* src, size_t length, ubyte* dst); size_t decode(const ubyte* src, size_t length, ubyte* dst); + + size_t encode16(const ubyte* src, size_t length, ubyte* dst); + size_t decode16(const ubyte* src, size_t length, ubyte* dst); } namespace extrle { diff --git a/src/files/WorldConverter.cpp b/src/files/WorldConverter.cpp index a6b977bb..ee3f1fa6 100644 --- a/src/files/WorldConverter.cpp +++ b/src/files/WorldConverter.cpp @@ -61,7 +61,7 @@ void WorldConverter::createUpgradeTasks() { if (issue.regionLayer == REGION_LAYER_VOXELS) { addRegionsTasks(issue.regionLayer, ConvertTaskType::UPGRADE_VOXELS); } else { - addRegionsTasks(issue.regionLayer, ConvertTaskType::UPGRADE_SIMPLE); + addRegionsTasks(issue.regionLayer, ConvertTaskType::UPGRADE_REGION); } } } @@ -159,7 +159,7 @@ std::shared_ptr WorldConverter::startTask( return pool; } -void WorldConverter::upgradeSimple(const fs::path& file, int x, int z) const { +void WorldConverter::upgradeRegion(const fs::path& file, int x, int z) const { throw std::runtime_error("unsupported region format"); } @@ -194,10 +194,11 @@ void WorldConverter::convert(const ConvertTask& task) const { if (!fs::is_regular_file(task.file)) return; switch (task.type) { - case ConvertTaskType::UPGRADE_SIMPLE: - upgradeSimple(task.file, task.x, task.z); + case ConvertTaskType::UPGRADE_REGION: + upgradeRegion(task.file, task.x, task.z); break; case ConvertTaskType::UPGRADE_VOXELS: + upgradeRegion(task.file, task.x, task.z); upgradeVoxels(task.file, task.x, task.z); break; case ConvertTaskType::VOXELS: diff --git a/src/files/WorldConverter.hpp b/src/files/WorldConverter.hpp index d8a3ce5f..63cf5993 100644 --- a/src/files/WorldConverter.hpp +++ b/src/files/WorldConverter.hpp @@ -23,7 +23,7 @@ enum class ConvertTaskType { /// @brief rewrite player PLAYER, /// @brief refresh region file version - UPGRADE_SIMPLE, + UPGRADE_REGION, /// @brief rewrite voxels region file to new format UPGRADE_VOXELS, }; @@ -45,7 +45,7 @@ class WorldConverter : public Task { uint tasksDone = 0; bool upgradeMode; - void upgradeSimple(const fs::path& file, int x, int z) const; + void upgradeRegion(const fs::path& file, int x, int z) const; void upgradeVoxels(const fs::path& file, int x, int z) const; void convertPlayer(const fs::path& file) const; void convertVoxels(const fs::path& file, int x, int z) const; diff --git a/src/util/Buffer.hpp b/src/util/Buffer.hpp new file mode 100644 index 00000000..7ff0ca8a --- /dev/null +++ b/src/util/Buffer.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + +namespace util { + template + class Buffer { + std::unique_ptr ptr; + size_t length; + public: + Buffer(size_t length) + : ptr(std::make_unique(length)), length(length) { + } + + Buffer(std::unique_ptr ptr, size_t length) + : ptr(std::move(ptr)), length(length) {} + + Buffer(const T* src, size_t length) + : ptr(std::make_unique(length)), length(length) { + std::memcpy(ptr, src, length); + } + + T& operator[](long long index) { + return ptr[index]; + } + + const T& operator[](long long index) const { + return ptr[index]; + } + + T* data() { + return ptr.get(); + } + + const T* data() const { + return ptr.get(); + } + + size_t size() const { + return length; + } + + std::unique_ptr release() { + return std::move(ptr); + } + + Buffer clone() const { + return Buffer(ptr.get(), length); + } + }; +} diff --git a/test/coders/rle.cpp b/test/coders/rle.cpp index c93a034a..4ce06d1a 100644 --- a/test/coders/rle.cpp +++ b/test/coders/rle.cpp @@ -3,46 +3,39 @@ #include "typedefs.hpp" #include "coders/rle.hpp" -TEST(RLE, EncodeDecode) { - const int initial_size = 50'000; +static void test_encode_decode( + size_t(*encodefunc)(const ubyte*, size_t, ubyte*), + size_t(*decodefunc)(const ubyte*, size_t, ubyte*) +) { + const size_t initial_size = 50'000; uint8_t initial[initial_size]; uint8_t next = rand(); - for (int i = 0; i < initial_size; i++) { + for (size_t i = 0; i < initial_size; i++) { initial[i] = next; if (rand() % 13 == 0) { next = rand(); } } uint8_t encoded[initial_size * 2]; - auto encoded_size = rle::encode(initial, initial_size, encoded); + size_t encoded_size = encodefunc(initial, initial_size, encoded); uint8_t decoded[initial_size * 2]; - auto decoded_size = rle::decode(encoded, encoded_size, decoded); + size_t decoded_size = decodefunc(encoded, encoded_size, decoded); EXPECT_EQ(decoded_size, initial_size); - for (int i = 0; i < decoded_size; i++) { + for (size_t i = 0; i < decoded_size; i++) { EXPECT_EQ(decoded[i], initial[i]); } } +TEST(RLE, EncodeDecode) { + test_encode_decode(rle::encode, rle::decode); +} + +TEST(RLE16, EncodeDecode) { + test_encode_decode(rle::encode16, rle::decode16); +} + TEST(ExtRLE, EncodeDecode) { - const int initial_size = 50'000; - uint8_t initial[initial_size]; - uint8_t next = rand(); - for (int i = 0; i < initial_size; i++) { - initial[i] = next; - if (rand() % 13 == 0) { - next = rand(); - } - } - uint8_t encoded[initial_size * 2]; - auto encoded_size = extrle::encode(initial, initial_size, encoded); - uint8_t decoded[initial_size * 2]; - auto decoded_size = extrle::decode(encoded, encoded_size, decoded); - - EXPECT_EQ(decoded_size, initial_size); - - for (int i = 0; i < decoded_size; i++) { - EXPECT_EQ(decoded[i], initial[i]); - } + test_encode_decode(extrle::encode, extrle::decode); } diff --git a/test/voxels/Chunk.cpp b/test/voxels/Chunk.cpp new file mode 100644 index 00000000..2a82882b --- /dev/null +++ b/test/voxels/Chunk.cpp @@ -0,0 +1,25 @@ +#include + +#include "voxels/Chunk.hpp" + +TEST(Chunk, EncodeDecode) { + Chunk chunk1(0, 0); + for (uint i = 0; i < CHUNK_VOL; i++) { + chunk1.voxels[i].id = rand(); + chunk1.voxels[i].state.rotation = rand(); + chunk1.voxels[i].state.segment = rand(); + chunk1.voxels[i].state.userbits = rand(); + } + auto bytes = chunk1.encode(); + + Chunk chunk2(0, 0); + chunk2.decode(bytes.get()); + + for (uint i = 0; i < CHUNK_VOL; i++) { + EXPECT_EQ(chunk1.voxels[i].id, chunk2.voxels[i].id); + EXPECT_EQ( + blockstate2int(chunk1.voxels[i].state), + blockstate2int(chunk2.voxels[i].state) + ); + } +}