filename:
src/world/format/io/Region.cpp
branch:
feature/world
back to repo
/*
*
* _____ _ _
* / ___|| | | |
* \ `--. | |_ _ __ __ _ | |_ ___ ___
* `--. \| __|| '__| / _` || __| / _ \ / __|
* /\__/ /| |_ | | | (_| || |_ | (_) |\__ \
* \____/ \__||_| \__,_| \__| \___/ |___/
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Copyright (C) 2025 Armen Deroian
*
*/
#include "Region.h"
#include "nbt/io/NBTBuffer.h"
#include "nbt/CompoundTag.h"
#include "utils/io/CompressionUtils.h"
#include "world/format/chunk/Chunk.h"
#include <format>
namespace stratos {
constexpr int HEADER_TABLE_BYTES = 1024 * 4;
constexpr int SECTOR_BYTES = 4096;
Region::Region(int x, int y, Path regionRoot) : x(x), z(y), file(open(regionRoot.resolve(std::format("r.{}.{}.mca", x, z)), std::ios::in | std::ios::out | std::ios::binary)) {
loadHeader();
}
bool Region::isMemberChunk(const int chunkX, const int chunkZ) const {
return chunkX >> 5 == x && chunkZ >> 5 == z;
}
bool Region::isChunkGenerated(const int chunkX, const int chunkZ) const {
const ChunkEntry entry = entries[index(chunkX, chunkZ)];
return entry.offset != 0 && entry.size != 0;
}
bool Region::isChunkLoaded(const int chunkX, const int chunkZ) const {
return chunks[index(chunkX, chunkZ)] != nullptr;
}
int Region::index(const int chunkX, const int chunkZ) {
return (chunkX & 31) + (chunkZ & 31) * 32;
}
void Region::loadHeader() {
entries = std::vector<ChunkEntry>(1024);
file.seekg(0, std::ios::beg);
{
ByteBuffer locations(readBytes(file, HEADER_TABLE_BYTES));
for (int i = 0; i < 1024; i++) {
const int offset = locations.readInt24() * SECTOR_BYTES;
const int size = locations.readByte();
entries[i] = {offset, size, 0};
}
}
{
ByteBuffer timestamps(readBytes(file, HEADER_TABLE_BYTES, HEADER_TABLE_BYTES));
for (int i = 0; i < 1024; i++) entries[i].timestamp = timestamps.readInt();
}
chunks = std::vector<Chunk*>(1024, nullptr);
}
Chunk* Region::loadChunk(const int chunkX, const int chunkZ) {
if (!isMemberChunk(chunkX, chunkZ)) throw std::invalid_argument("Chunk is not part of this region");
if (isChunkLoaded(chunkX, chunkZ)) return getChunk(chunkX, chunkZ);
if (!isChunkGenerated(chunkX, chunkZ)) return nullptr;
const ChunkEntry entry = entries[index(chunkX, chunkZ)];
ByteVec buffer = readBytes(file, entry.size * SECTOR_BYTES, entry.offset * SECTOR_BYTES);
ByteBuffer byteBuffer(std::move(buffer));
const int length = byteBuffer.readInt();
const uint8_t compressionType = byteBuffer.readByte();
nbt::NBTBuffer chunkNBT(compressionType != 3 ? decompress(byteBuffer.readByteArray(length - 5)) : byteBuffer.readByteArray(length - 5));
Chunk* chunk = Chunk::fromNBT(chunkNBT.readTag().second->as<nbt::CompoundTag>());
chunks[index(chunkX, chunkZ)] = chunk;
return chunk;
}
Chunk* Region::getChunk(const int chunkX, const int chunkZ) const {
return chunks[index(chunkX, chunkZ)];
}
} // namespace stratos