upgrade regions format version to 3

This commit is contained in:
MihailRis 2024-09-06 12:25:52 +03:00
parent 69b90f53c3
commit e30c1b3c03
15 changed files with 138 additions and 149 deletions

View File

@ -1,6 +1,7 @@
# Menu # Menu
menu.missing-content=Missing Content! menu.missing-content=Missing Content!
world.convert-request=Content indices have changed! Convert world files? world.convert-request=Content indices have changed! Convert world files?
world.upgrade-request=World format is outdated! Convert world files?
pack.remove-confirm=Do you want to erase all pack(s) content from the world forever? pack.remove-confirm=Do you want to erase all pack(s) content from the world forever?
error.pack-not-found=Could not to find pack error.pack-not-found=Could not to find pack
error.dependency-not-found=Dependency pack is not found error.dependency-not-found=Dependency pack is not found

View File

@ -46,6 +46,7 @@ world.generators.default=Обычный
world.generators.flat=Плоский world.generators.flat=Плоский
world.Create World=Создать Мир world.Create World=Создать Мир
world.convert-request=Есть изменения в индексах! Конвертировать мир? world.convert-request=Есть изменения в индексах! Конвертировать мир?
world.upgrade-request=Формат мира устарел! Конвертировать мир?
world.delete-confirm=Удалить мир безвозвратно? world.delete-confirm=Удалить мир безвозвратно?
# Настройки # Настройки

View File

@ -82,7 +82,12 @@ std::unique_ptr<ubyte[]> compression::decompress(
} }
case Method::EXTRLE16: { case Method::EXTRLE16: {
auto decompressed = std::make_unique<ubyte[]>(dstlen); auto decompressed = std::make_unique<ubyte[]>(dstlen);
extrle::decode16(src, srclen, decompressed.get()); size_t decoded = extrle::decode16(src, srclen, decompressed.get());
if (decoded != dstlen) {
throw std::runtime_error(
"expected decompressed size " + std::to_string(dstlen) +
" got " + std::to_string(decoded));
}
return decompressed; return decompressed;
} }
case Method::GZIP: { case Method::GZIP: {

View File

@ -17,7 +17,7 @@ inline constexpr bool ENGINE_DEBUG_BUILD = true;
inline const std::string ENGINE_VERSION_STRING = "0.23"; inline const std::string ENGINE_VERSION_STRING = "0.23";
/// @brief world regions format version /// @brief world regions format version
inline constexpr uint REGION_FORMAT_VERSION = 2; inline constexpr uint REGION_FORMAT_VERSION = 3;
/// @brief max simultaneously open world region files /// @brief max simultaneously open world region files
inline constexpr uint MAX_OPEN_REGION_FILES = 32; inline constexpr uint MAX_OPEN_REGION_FILES = 32;

View File

@ -55,7 +55,9 @@ std::shared_ptr<ContentReport> ContentReport::create(
report->items.setup(itemlist.get(), content->items); report->items.setup(itemlist.get(), content->items);
report->buildIssues(); report->buildIssues();
if (report->hasContentReorder() || report->hasMissingContent()) { if (report->isUpgradeRequired() ||
report->hasContentReorder() ||
report->hasMissingContent()) {
return report; return report;
} else { } else {
return nullptr; return nullptr;
@ -79,6 +81,16 @@ static void build_issues(
void ContentReport::buildIssues() { void ContentReport::buildIssues() {
build_issues(issues, blocks); build_issues(issues, blocks);
build_issues(issues, items); build_issues(issues, items);
if (regionsVersion < REGION_FORMAT_VERSION) {
for (int layer = REGION_LAYER_VOXELS;
layer < REGION_LAYERS_COUNT;
layer++) {
ContentIssue issue {ContentIssueType::REGION_FORMAT_UPDATE};
issue.regionLayer = static_cast<RegionLayerIndex>(layer);
issues.push_back(issue);
}
}
} }
const std::vector<ContentIssue>& ContentReport::getIssues() const { const std::vector<ContentIssue>& ContentReport::getIssues() const {

View File

@ -13,14 +13,14 @@ static fs::path get_region_filename(int x, int z) {
/// @brief Read missing chunks data (null pointers) from region file /// @brief Read missing chunks data (null pointers) from region file
static void fetch_chunks(WorldRegion* region, int x, int z, regfile* file) { static void fetch_chunks(WorldRegion* region, int x, int z, regfile* file) {
auto* chunks = region->getChunks(); auto* chunks = region->getChunks();
uint32_t* sizes = region->getSizes(); auto sizes = region->getSizes();
for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) { for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) {
int chunk_x = (i % REGION_SIZE) + x * REGION_SIZE; int chunk_x = (i % REGION_SIZE) + x * REGION_SIZE;
int chunk_z = (i / REGION_SIZE) + z * REGION_SIZE; int chunk_z = (i / REGION_SIZE) + z * REGION_SIZE;
if (chunks[i] == nullptr) { if (chunks[i] == nullptr) {
chunks[i] = chunks[i] = RegionsLayer::readChunkData(
RegionsLayer::readChunkData(chunk_x, chunk_z, sizes[i], file); chunk_x, chunk_z, sizes[i][0], sizes[i][1], file);
} }
} }
} }
@ -44,23 +44,26 @@ regfile::regfile(fs::path filename) : file(std::move(filename)) {
} }
} }
std::unique_ptr<ubyte[]> regfile::read(int index, uint32_t& length) { std::unique_ptr<ubyte[]> regfile::read(int index, uint32_t& size, uint32_t& srcSize) {
size_t file_size = file.length(); size_t file_size = file.length();
size_t table_offset = file_size - REGION_CHUNKS_COUNT * 4; size_t table_offset = file_size - REGION_CHUNKS_COUNT * 4;
uint32_t offset; uint32_t buff32;
file.seekg(table_offset + index * 4); file.seekg(table_offset + index * 4);
file.read(reinterpret_cast<char*>(&offset), 4); file.read(reinterpret_cast<char*>(&buff32), 4);
offset = dataio::read_int32_big(reinterpret_cast<const ubyte*>(&offset), 0); uint32_t offset = dataio::le2h(buff32);
if (offset == 0) { if (offset == 0) {
return nullptr; return nullptr;
} }
file.seekg(offset); file.seekg(offset);
file.read(reinterpret_cast<char*>(&offset), 4); file.read(reinterpret_cast<char*>(&buff32), 4);
length = dataio::read_int32_big(reinterpret_cast<const ubyte*>(&offset), 0); size = dataio::le2h(buff32);
auto data = std::make_unique<ubyte[]>(length); file.read(reinterpret_cast<char*>(&buff32), 4);
file.read(reinterpret_cast<char*>(data.get()), length); srcSize = dataio::le2h(buff32);
auto data = std::make_unique<ubyte[]>(size);
file.read(reinterpret_cast<char*>(data.get()), size);
return data; return data;
} }
@ -150,7 +153,7 @@ WorldRegion* RegionsLayer::getOrCreateRegion(int x, int z) {
return region; return region;
} }
ubyte* RegionsLayer::getData(int x, int z, uint32_t& size) { ubyte* RegionsLayer::getData(int x, int z, uint32_t& size, uint32_t& srcSize) {
int regionX, regionZ, localX, localZ; int regionX, regionZ, localX, localZ;
calc_reg_coords(x, z, regionX, regionZ, localX, localZ); calc_reg_coords(x, z, regionX, regionZ, localX, localZ);
@ -159,15 +162,17 @@ ubyte* RegionsLayer::getData(int x, int z, uint32_t& size) {
if (data == nullptr) { if (data == nullptr) {
auto regfile = getRegFile({regionX, regionZ}); auto regfile = getRegFile({regionX, regionZ});
if (regfile != nullptr) { if (regfile != nullptr) {
auto dataptr = readChunkData(x, z, size, regfile.get()); auto dataptr = readChunkData(x, z, size, srcSize, regfile.get());
if (dataptr) { if (dataptr) {
data = dataptr.get(); data = dataptr.get();
region->put(localX, localZ, std::move(dataptr), size); region->put(localX, localZ, std::move(dataptr), size, srcSize);
} }
} }
} }
if (data != nullptr) { if (data != nullptr) {
size = region->getChunkDataSize(localX, localZ); auto sizevec = region->getChunkDataSize(localX, localZ);
size = sizevec[0];
srcSize = sizevec[1];
return data; return data;
} }
return nullptr; return nullptr;
@ -187,47 +192,50 @@ void RegionsLayer::writeRegion(int x, int z, WorldRegion* entry) {
char header[REGION_HEADER_SIZE] = REGION_FORMAT_MAGIC; char header[REGION_HEADER_SIZE] = REGION_FORMAT_MAGIC;
header[8] = REGION_FORMAT_VERSION; header[8] = REGION_FORMAT_VERSION;
header[9] = 0; // flags header[9] = static_cast<ubyte>(compression); // FIXME
std::ofstream file(filename, std::ios::out | std::ios::binary); std::ofstream file(filename, std::ios::out | std::ios::binary);
file.write(header, REGION_HEADER_SIZE); file.write(header, REGION_HEADER_SIZE);
size_t offset = REGION_HEADER_SIZE; size_t offset = REGION_HEADER_SIZE;
char intbuf[4] {}; uint32_t intbuf;
uint offsets[REGION_CHUNKS_COUNT] {}; uint offsets[REGION_CHUNKS_COUNT] {};
auto* region = entry->getChunks(); auto region = entry->getChunks();
uint32_t* sizes = entry->getSizes(); auto sizes = entry->getSizes();
for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) { for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) {
ubyte* chunk = region[i].get(); ubyte* chunk = region[i].get();
if (chunk == nullptr) { if (chunk == nullptr) {
offsets[i] = 0; continue;
} else { }
offsets[i] = offset; offsets[i] = offset;
size_t compressedSize = sizes[i]; auto sizevec = sizes[i];
dataio::write_int32_big( uint32_t compressedSize = sizevec[0];
compressedSize, reinterpret_cast<ubyte*>(intbuf), 0 uint32_t srcSize = sizevec[1];
);
offset += 4 + compressedSize; intbuf = dataio::h2le(compressedSize);
file.write(reinterpret_cast<const char*>(&intbuf), 4);
offset += 4;
intbuf = dataio::h2le(srcSize);
file.write(reinterpret_cast<const char*>(&intbuf), 4);
offset += 4;
file.write(intbuf, 4);
file.write(reinterpret_cast<const char*>(chunk), compressedSize); file.write(reinterpret_cast<const char*>(chunk), compressedSize);
} offset += compressedSize;
} }
for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) { for (size_t i = 0; i < REGION_CHUNKS_COUNT; i++) {
dataio::write_int32_big( intbuf = dataio::h2le(offsets[i]);
offsets[i], reinterpret_cast<ubyte*>(intbuf), 0 file.write(reinterpret_cast<const char*>(&intbuf), 4);
);
file.write(intbuf, 4);
} }
} }
std::unique_ptr<ubyte[]> RegionsLayer::readChunkData( std::unique_ptr<ubyte[]> RegionsLayer::readChunkData(
int x, int z, uint32_t& length, regfile* rfile int x, int z, uint32_t& size, uint32_t& srcSize, regfile* rfile
) { ) {
int regionX, regionZ, localX, localZ; int regionX, regionZ, localX, localZ;
calc_reg_coords(x, z, regionX, regionZ, localX, localZ); calc_reg_coords(x, z, regionX, regionZ, localX, localZ);
int chunkIndex = localZ * REGION_SIZE + localX; int chunkIndex = localZ * REGION_SIZE + localX;
return rfile->read(chunkIndex, length); return rfile->read(chunkIndex, size, srcSize);
} }

View File

@ -167,7 +167,7 @@ void WorldConverter::upgradeRegion(
void WorldConverter::convertVoxels(const fs::path& file, int x, int z) const { void WorldConverter::convertVoxels(const fs::path& file, int x, int z) const {
logger.info() << "converting voxels region " << x << "_" << z; logger.info() << "converting voxels region " << x << "_" << z;
wfile->getRegions().processRegion(x, z, REGION_LAYER_VOXELS, CHUNK_DATA_LEN, wfile->getRegions().processRegion(x, z, REGION_LAYER_VOXELS,
[=](std::unique_ptr<ubyte[]> data, uint32_t*) { [=](std::unique_ptr<ubyte[]> data, uint32_t*) {
Chunk::convert(data.get(), report.get()); Chunk::convert(data.get(), report.get());
return data; return data;
@ -238,8 +238,13 @@ bool WorldConverter::isActive() const {
} }
void WorldConverter::write() { void WorldConverter::write() {
if (upgradeMode) {
logger.info() << "refreshing version";
wfile->patchIndicesVersion("region-version", REGION_FORMAT_VERSION);
} else {
logger.info() << "writing world"; logger.info() << "writing world";
wfile->write(nullptr, upgradeMode ? nullptr : content); wfile->write(nullptr, content);
}
} }
void WorldConverter::waitForEnd() { void WorldConverter::waitForEnd() {

View File

@ -172,6 +172,17 @@ bool WorldFiles::readResourcesData(const Content* content) {
return true; return true;
} }
void WorldFiles::patchIndicesVersion(const std::string& field, uint version) {
fs::path file = getIndicesFile();
if (!fs::is_regular_file(file)) {
logger.error() << file.filename().u8string() << " does not exists";
return;
}
auto root = files::read_json(file);
root->put(field, version);
files::write_json(file, root.get(), true);
}
static void erase_pack_indices(dynamic::Map* root, const std::string& id) { static void erase_pack_indices(dynamic::Map* root, const std::string& id) {
auto prefix = id + ":"; auto prefix = id + ":";
auto blocks = root->list("blocks"); auto blocks = root->list("blocks");

View File

@ -51,6 +51,8 @@ public:
std::optional<WorldInfo> readWorldInfo(); std::optional<WorldInfo> readWorldInfo();
bool readResourcesData(const Content* content); bool readResourcesData(const Content* content);
void patchIndicesVersion(const std::string& field, uint version);
/// @brief Write all unsaved data to world files /// @brief Write all unsaved data to world files
/// @param world target world /// @param world target world
/// @param content world content /// @param content world content

View File

@ -14,7 +14,7 @@ WorldRegion::WorldRegion()
: chunksData( : chunksData(
std::make_unique<std::unique_ptr<ubyte[]>[]>(REGION_CHUNKS_COUNT) std::make_unique<std::unique_ptr<ubyte[]>[]>(REGION_CHUNKS_COUNT)
), ),
sizes(std::make_unique<uint32_t[]>(REGION_CHUNKS_COUNT)) { sizes(std::make_unique<glm::u32vec2[]>(REGION_CHUNKS_COUNT)) {
} }
WorldRegion::~WorldRegion() = default; WorldRegion::~WorldRegion() = default;
@ -30,23 +30,23 @@ std::unique_ptr<ubyte[]>* WorldRegion::getChunks() const {
return chunksData.get(); return chunksData.get();
} }
uint32_t* WorldRegion::getSizes() const { glm::u32vec2* WorldRegion::getSizes() const {
return sizes.get(); return sizes.get();
} }
void WorldRegion::put( void WorldRegion::put(
uint x, uint z, std::unique_ptr<ubyte[]> data, uint32_t size uint x, uint z, std::unique_ptr<ubyte[]> data, uint32_t size, uint32_t srcSize
) { ) {
size_t chunk_index = z * REGION_SIZE + x; size_t chunk_index = z * REGION_SIZE + x;
chunksData[chunk_index] = std::move(data); chunksData[chunk_index] = std::move(data);
sizes[chunk_index] = size; sizes[chunk_index] = glm::u32vec2(size, srcSize);
} }
ubyte* WorldRegion::getChunkData(uint x, uint z) { ubyte* WorldRegion::getChunkData(uint x, uint z) {
return chunksData[z * REGION_SIZE + x].get(); return chunksData[z * REGION_SIZE + x].get();
} }
uint WorldRegion::getChunkDataSize(uint x, uint z) { glm::u32vec2 WorldRegion::getChunkDataSize(uint x, uint z) {
return sizes[z * REGION_SIZE + x]; return sizes[z * REGION_SIZE + x];
} }
@ -56,7 +56,7 @@ WorldRegions::WorldRegions(const fs::path& directory) : directory(directory) {
} }
auto& voxels = layers[REGION_LAYER_VOXELS]; auto& voxels = layers[REGION_LAYER_VOXELS];
voxels.folder = directory / fs::path("regions"); voxels.folder = directory / fs::path("regions");
voxels.compression = compression::Method::EXTRLE8; voxels.compression = compression::Method::EXTRLE16;
auto& lights = layers[REGION_LAYER_LIGHTS]; auto& lights = layers[REGION_LAYER_LIGHTS];
lights.folder = directory / fs::path("lights"); lights.folder = directory / fs::path("lights");
@ -85,8 +85,9 @@ void WorldRegions::put(
int z, int z,
RegionLayerIndex layerid, RegionLayerIndex layerid,
std::unique_ptr<ubyte[]> data, std::unique_ptr<ubyte[]> data,
size_t size size_t srcSize
) { ) {
size_t size = srcSize;
auto& layer = layers[layerid]; auto& layer = layers[layerid];
if (layer.compression != compression::Method::NONE) { if (layer.compression != compression::Method::NONE) {
data = compression::compress( data = compression::compress(
@ -97,7 +98,7 @@ void WorldRegions::put(
WorldRegion* region = layer.getOrCreateRegion(regionX, regionZ); WorldRegion* region = layer.getOrCreateRegion(regionX, regionZ);
region->setUnsaved(true); region->setUnsaved(true);
region->put(localX, localZ, std::move(data), size); region->put(localX, localZ, std::move(data), size, srcSize);
} }
static std::unique_ptr<ubyte[]> write_inventories( static std::unique_ptr<ubyte[]> write_inventories(
@ -188,30 +189,35 @@ void WorldRegions::put(Chunk* chunk, std::vector<ubyte> entitiesData) {
std::unique_ptr<ubyte[]> WorldRegions::getVoxels(int x, int z) { std::unique_ptr<ubyte[]> WorldRegions::getVoxels(int x, int z) {
uint32_t size; uint32_t size;
uint32_t srcSize;
auto& layer = layers[REGION_LAYER_VOXELS]; auto& layer = layers[REGION_LAYER_VOXELS];
auto* data = layer.getData(x, z, size); auto* data = layer.getData(x, z, size, srcSize);
if (data == nullptr) { if (data == nullptr) {
return nullptr; return nullptr;
} }
return compression::decompress(data, size, CHUNK_DATA_LEN, layer.compression); assert(srcSize == CHUNK_DATA_LEN);
return compression::decompress(data, size, srcSize, layer.compression);
} }
std::unique_ptr<light_t[]> WorldRegions::getLights(int x, int z) { std::unique_ptr<light_t[]> WorldRegions::getLights(int x, int z) {
uint32_t size; uint32_t size;
uint32_t srcSize;
auto& layer = layers[REGION_LAYER_LIGHTS]; auto& layer = layers[REGION_LAYER_LIGHTS];
auto* bytes = layer.getData(x, z, size); auto* bytes = layer.getData(x, z, size, srcSize);
if (bytes == nullptr) { if (bytes == nullptr) {
return nullptr; return nullptr;
} }
auto data = compression::decompress( auto data = compression::decompress(
bytes, size, LIGHTMAP_DATA_LEN, layer.compression bytes, size, srcSize, layer.compression
); );
assert(srcSize == LIGHTMAP_DATA_LEN);
return Lightmap::decode(data.get()); return Lightmap::decode(data.get());
} }
chunk_inventories_map WorldRegions::fetchInventories(int x, int z) { chunk_inventories_map WorldRegions::fetchInventories(int x, int z) {
uint32_t bytesSize; uint32_t bytesSize;
auto bytes = layers[REGION_LAYER_INVENTORIES].getData(x, z, bytesSize); uint32_t srcSize;
auto bytes = layers[REGION_LAYER_INVENTORIES].getData(x, z, bytesSize, srcSize);
if (bytes == nullptr) { if (bytes == nullptr) {
return {}; return {};
} }
@ -221,7 +227,7 @@ chunk_inventories_map WorldRegions::fetchInventories(int x, int z) {
void WorldRegions::processInventories( void WorldRegions::processInventories(
int x, int z, const inventoryproc& func int x, int z, const inventoryproc& func
) { ) {
processRegion(x, z, REGION_LAYER_INVENTORIES, 0, processRegion(x, z, REGION_LAYER_INVENTORIES,
[=](std::unique_ptr<ubyte[]> data, uint32_t* size) { [=](std::unique_ptr<ubyte[]> data, uint32_t* size) {
auto inventories = load_inventories(data.get(), *size); auto inventories = load_inventories(data.get(), *size);
for (const auto& [_, inventory] : inventories) { for (const auto& [_, inventory] : inventories) {
@ -236,7 +242,8 @@ dynamic::Map_sptr WorldRegions::fetchEntities(int x, int z) {
return nullptr; return nullptr;
} }
uint32_t bytesSize; uint32_t bytesSize;
const ubyte* data = layers[REGION_LAYER_ENTITIES].getData(x, z, bytesSize); uint32_t srcSize;
const ubyte* data = layers[REGION_LAYER_ENTITIES].getData(x, z, bytesSize, srcSize);
if (data == nullptr) { if (data == nullptr) {
return nullptr; return nullptr;
} }
@ -248,7 +255,7 @@ dynamic::Map_sptr WorldRegions::fetchEntities(int x, int z) {
} }
void WorldRegions::processRegion( void WorldRegions::processRegion(
int x, int z, RegionLayerIndex layerid, uint32_t dataLen, const regionproc& func int x, int z, RegionLayerIndex layerid, const regionproc& func
) { ) {
auto& layer = layers[layerid]; auto& layer = layers[layerid];
if (layer.getRegion(x, z)) { if (layer.getRegion(x, z)) {
@ -263,24 +270,21 @@ void WorldRegions::processRegion(
int gx = cx + x * REGION_SIZE; int gx = cx + x * REGION_SIZE;
int gz = cz + z * REGION_SIZE; int gz = cz + z * REGION_SIZE;
uint32_t length; uint32_t length;
uint32_t srcSize;
auto data = auto data =
RegionsLayer::readChunkData(gx, gz, length, regfile.get()); RegionsLayer::readChunkData(gx, gz, length, srcSize, regfile.get());
if (data == nullptr) { if (data == nullptr) {
continue; continue;
} }
uint32_t totalLength = dataLen;
if (layer.compression != compression::Method::NONE) { if (layer.compression != compression::Method::NONE) {
if (dataLen == 0) {
throw std::invalid_argument("invalid data length");
}
data = compression::decompress( data = compression::decompress(
data.get(), length, dataLen, layer.compression data.get(), length, srcSize, layer.compression
); );
} else { } else {
totalLength = length; srcSize = length;
} }
if (auto writeData = func(std::move(data), &totalLength)) { if (auto writeData = func(std::move(data), &srcSize)) {
put(gx, gz, layerid, std::move(writeData), totalLength); put(gx, gz, layerid, std::move(writeData), srcSize);
} }
} }
} }

View File

@ -38,21 +38,21 @@ public:
class WorldRegion { class WorldRegion {
std::unique_ptr<std::unique_ptr<ubyte[]>[]> chunksData; std::unique_ptr<std::unique_ptr<ubyte[]>[]> chunksData;
std::unique_ptr<uint32_t[]> sizes; std::unique_ptr<glm::u32vec2[]> sizes;
bool unsaved = false; bool unsaved = false;
public: public:
WorldRegion(); WorldRegion();
~WorldRegion(); ~WorldRegion();
void put(uint x, uint z, std::unique_ptr<ubyte[]> data, uint32_t size); void put(uint x, uint z, std::unique_ptr<ubyte[]> data, uint32_t size, uint32_t srcSize);
ubyte* getChunkData(uint x, uint z); ubyte* getChunkData(uint x, uint z);
uint getChunkDataSize(uint x, uint z); glm::u32vec2 getChunkDataSize(uint x, uint z);
void setUnsaved(bool unsaved); void setUnsaved(bool unsaved);
bool isUnsaved() const; bool isUnsaved() const;
std::unique_ptr<ubyte[]>* getChunks() const; std::unique_ptr<ubyte[]>* getChunks() const;
uint32_t* getSizes() const; glm::u32vec2* getSizes() const;
}; };
struct regfile { struct regfile {
@ -63,7 +63,7 @@ struct regfile {
regfile(fs::path filename); regfile(fs::path filename);
regfile(const regfile&) = delete; regfile(const regfile&) = delete;
std::unique_ptr<ubyte[]> read(int index, uint32_t& length); std::unique_ptr<ubyte[]> read(int index, uint32_t& size, uint32_t& srcSize);
}; };
using regionsmap = std::unordered_map<glm::ivec2, std::unique_ptr<WorldRegion>>; using regionsmap = std::unordered_map<glm::ivec2, std::unique_ptr<WorldRegion>>;
@ -152,9 +152,10 @@ struct RegionsLayer {
/// @brief Get chunk data. Read from file if not loaded yet. /// @brief Get chunk data. Read from file if not loaded yet.
/// @param x chunk x coord /// @param x chunk x coord
/// @param z chunk z coord /// @param z chunk z coord
/// @param size [out] chunk data length /// @param size [out] compressed chunk data length
/// @param size [out] source chunk data length
/// @return nullptr if no saved chunk data found /// @return nullptr if no saved chunk data found
[[nodiscard]] ubyte* getData(int x, int z, uint32_t& size); [[nodiscard]] ubyte* getData(int x, int z, uint32_t& size, uint32_t& srcSize);
/// @brief Write or rewrite region file /// @brief Write or rewrite region file
/// @param x region X /// @param x region X
@ -167,11 +168,12 @@ struct RegionsLayer {
/// @brief Read chunk data from region file /// @brief Read chunk data from region file
/// @param x chunk x coord /// @param x chunk x coord
/// @param z chunk z coord /// @param z chunk z coord
/// @param length [out] chunk data length /// @param size [out] compressed chunk data length
/// @param srcSize [out] source chunk data length
/// @param rfile region file /// @param rfile region file
/// @return nullptr if chunk is not present in region file /// @return nullptr if chunk is not present in region file
[[nodiscard]] static std::unique_ptr<ubyte[]> readChunkData( [[nodiscard]] static std::unique_ptr<ubyte[]> readChunkData(
int x, int z, uint32_t& length, regfile* rfile int x, int z, uint32_t& size, uint32_t& srcSize, regfile* rfile
); );
}; };
@ -229,7 +231,7 @@ public:
/// @param layerid regions layer index /// @param layerid regions layer index
/// @param func processing callback /// @param func processing callback
void processRegion( void processRegion(
int x, int z, RegionLayerIndex layerid, uint32_t dataLen, const regionproc& func); int x, int z, RegionLayerIndex layerid, const regionproc& func);
void processInventories( void processInventories(
int x, int z, const inventoryproc& func); int x, int z, const inventoryproc& func);

View File

@ -12,6 +12,7 @@
static inline size_t VOXELS_DATA_SIZE_V1 = CHUNK_VOL * 4; static inline size_t VOXELS_DATA_SIZE_V1 = CHUNK_VOL * 4;
static inline size_t VOXELS_DATA_SIZE_V2 = CHUNK_VOL * 4; static inline size_t VOXELS_DATA_SIZE_V2 = CHUNK_VOL * 4;
#include <iostream>
static util::Buffer<ubyte> convert_voxels_1to2(const ubyte* buffer, uint32_t size) { static util::Buffer<ubyte> convert_voxels_1to2(const ubyte* buffer, uint32_t size) {
auto data = compression::decompress( auto data = compression::decompress(
buffer, size, VOXELS_DATA_SIZE_V1, compression::Method::EXTRLE8); buffer, size, VOXELS_DATA_SIZE_V1, compression::Method::EXTRLE8);
@ -35,13 +36,15 @@ static util::Buffer<ubyte> convert_voxels_1to2(const ubyte* buffer, uint32_t siz
} }
size_t outLen; size_t outLen;
auto compressed = compression::compress( auto compressed = compression::compress(
data.get(), VOXELS_DATA_SIZE_V2, outLen, compression::Method::EXTRLE16); dstBuffer.data(), VOXELS_DATA_SIZE_V2, outLen, compression::Method::EXTRLE16);
return util::Buffer<ubyte>(std::move(compressed), outLen); return util::Buffer<ubyte>(std::move(compressed), outLen);
} }
#include "util/timeutil.hpp"
util::Buffer<ubyte> compatibility::convert_region_2to3( util::Buffer<ubyte> compatibility::convert_region_2to3(
const util::Buffer<ubyte>& src, RegionLayerIndex layer const util::Buffer<ubyte>& src, RegionLayerIndex layer
) { ) {
timeutil::ScopeLogTimer log(555);
const size_t REGION_CHUNKS = 1024; const size_t REGION_CHUNKS = 1024;
const size_t HEADER_SIZE = 10; const size_t HEADER_SIZE = 10;
const size_t OFFSET_TABLE_SIZE = REGION_CHUNKS * sizeof(uint32_t); const size_t OFFSET_TABLE_SIZE = REGION_CHUNKS * sizeof(uint32_t);

View File

@ -73,7 +73,8 @@ void show_convert_request(
) { ) {
guiutil::confirm( guiutil::confirm(
engine->getGUI(), engine->getGUI(),
langs::get(L"world.convert-request"), langs::get(report->isUpgradeRequired() ?
L"world.upgrade-request" : L"world.convert-request"),
[=]() { [=]() {
auto converter = auto converter =
create_converter(engine, worldFiles, content, report, postRunnable); create_converter(engine, worldFiles, content, report, postRunnable);

View File

@ -77,33 +77,6 @@ std::unique_ptr<Chunk> Chunk::clone() const {
return other; return other;
} }
/**
Current chunk format:
- byte-order: big-endian
- [don't panic!] first and second bytes are separated for RLE efficiency
```cpp
uint8_t voxel_id_first_byte[CHUNK_VOL];
uint8_t voxel_id_second_byte[CHUNK_VOL];
uint8_t voxel_states_first_byte[CHUNK_VOL];
uint8_t voxel_states_second_byte[CHUNK_VOL];
```
Total size: (CHUNK_VOL * 4) bytes
*/
std::unique_ptr<ubyte[]> Chunk::encode() const {
auto buffer = std::make_unique<ubyte[]>(CHUNK_DATA_LEN);
for (uint i = 0; i < CHUNK_VOL; i++) {
buffer[i] = voxels[i].id >> 8;
buffer[CHUNK_VOL + i] = voxels[i].id & 0xFF;
blockstate_t state = blockstate2int(voxels[i].state);
buffer[CHUNK_VOL * 2 + i] = state >> 8;
buffer[CHUNK_VOL * 3 + i] = state & 0xFF;
}
return buffer;
}
/** /**
Current chunk format: Current chunk format:
- byte-order: little-endian - byte-order: little-endian
@ -115,7 +88,7 @@ std::unique_ptr<ubyte[]> Chunk::encode() const {
Total size: (CHUNK_VOL * 4) bytes Total size: (CHUNK_VOL * 4) bytes
*/ */
std::unique_ptr<ubyte[]> Chunk::encodeV2() const { std::unique_ptr<ubyte[]> Chunk::encode() const {
auto buffer = std::make_unique<ubyte[]>(CHUNK_DATA_LEN); auto buffer = std::make_unique<ubyte[]>(CHUNK_DATA_LEN);
auto dst = reinterpret_cast<uint16_t*>(buffer.get()); auto dst = reinterpret_cast<uint16_t*>(buffer.get());
for (uint i = 0; i < CHUNK_VOL; i++) { for (uint i = 0; i < CHUNK_VOL; i++) {
@ -126,26 +99,6 @@ std::unique_ptr<ubyte[]> Chunk::encodeV2() const {
} }
bool Chunk::decode(const ubyte* data) { bool Chunk::decode(const ubyte* data) {
for (uint i = 0; i < CHUNK_VOL; i++) {
voxel& vox = voxels[i];
ubyte bid1 = data[i];
ubyte bid2 = data[CHUNK_VOL + i];
ubyte bst1 = data[CHUNK_VOL * 2 + i];
ubyte bst2 = data[CHUNK_VOL * 3 + i];
vox.id =
(static_cast<blockid_t>(bid1) << 8) | static_cast<blockid_t>(bid2);
vox.state = int2blockstate(
(static_cast<blockstate_t>(bst1) << 8) |
static_cast<blockstate_t>(bst2)
);
}
return true;
}
bool Chunk::decodeV2(const ubyte* data) {
auto src = reinterpret_cast<const uint16_t*>(data); auto src = reinterpret_cast<const uint16_t*>(data);
for (uint i = 0; i < CHUNK_VOL; i++) { for (uint i = 0; i < CHUNK_VOL; i++) {
voxel& vox = voxels[i]; voxel& vox = voxels[i];
@ -157,17 +110,6 @@ bool Chunk::decodeV2(const ubyte* data) {
} }
void Chunk::convert(ubyte* data, const ContentReport* report) { void Chunk::convert(ubyte* data, const ContentReport* report) {
for (uint i = 0; i < CHUNK_VOL; i++) {
blockid_t id =
((static_cast<blockid_t>(data[i]) << 8) |
static_cast<blockid_t>(data[CHUNK_VOL + i]));
blockid_t replacement = report->blocks.getId(id);
data[i] = replacement >> 8;
data[CHUNK_VOL + i] = replacement & 0xFF;
}
}
void Chunk::convertV2(ubyte* data, const ContentReport* report) {
auto buffer = reinterpret_cast<uint16_t*>(data); auto buffer = reinterpret_cast<uint16_t*>(data);
for (uint i = 0; i < CHUNK_VOL; i++) { for (uint i = 0; i < CHUNK_VOL; i++) {
blockid_t id = dataio::le2h(buffer[i]); blockid_t id = dataio::le2h(buffer[i]);

View File

@ -66,20 +66,12 @@ public:
flags.unsaved = true; flags.unsaved = true;
} }
/// @brief Encode chunk to bytes array of size CHUNK_DATA_LEN
/// @see /doc/specs/outdated/region_voxels_chunk_spec_v1.md
std::unique_ptr<ubyte[]> encode() const;
/// @brief Encode chunk to bytes array of size CHUNK_DATA_LEN /// @brief Encode chunk to bytes array of size CHUNK_DATA_LEN
/// @see /doc/specs/region_voxels_chunk_spec.md /// @see /doc/specs/region_voxels_chunk_spec.md
std::unique_ptr<ubyte[]> encodeV2() const; std::unique_ptr<ubyte[]> encode() const;
/// @return true if all is fine /// @return true if all is fine
bool decode(const ubyte* data); bool decode(const ubyte* data);
/// @return true if all is fine
bool decodeV2(const ubyte* data);
static void convert(ubyte* data, const ContentReport* report); static void convert(ubyte* data, const ContentReport* report);
static void convertV2(ubyte* data, const ContentReport* report);
}; };