diff --git a/resources/region_files/chunk_uncompressed_nbt b/resources/region_files/chunk_uncompressed_nbt new file mode 100644 index 0000000..8239bea Binary files /dev/null and b/resources/region_files/chunk_uncompressed_nbt differ diff --git a/resources/region_files/first_chunk.sh b/resources/region_files/first_chunk.sh new file mode 100755 index 0000000..7b04a9f --- /dev/null +++ b/resources/region_files/first_chunk.sh @@ -0,0 +1,74 @@ +if [ "$1" = "-v" ]; then + VERBOSE=true + shift +else + VERBOSE=false +fi + +if [ ! -f "$1" ]; then + echo "Usage: $0 [-v] FILE" + echo " -v display raw content and NBT dump" + exit 1 +fi + +# pointer data +OFFSET_HEX="0x$(dd bs=1 if="$1" count=3 2>/dev/null | hexdump -e '3/1 "%02x" "\n"')" +OFFSET="$(baseconvert -d $OFFSET_HEX)" +echo " offset: $OFFSET ($OFFSET_HEX)" +LENGTH_HEX="0x$(dd bs=1 if="$1" count=1 skip=3 2>/dev/null | hexdump -e '1/1 "%02x" "\n"')" +LENGTH="$(baseconvert -d $LENGTH_HEX)" +echo " length: $LENGTH ($LENGTH_HEX)" + +#TODO: last modified + +# chunk +DATA="$(dd if="$1" bs=4096 count=$LENGTH skip=$OFFSET 2>/dev/null | hexdump -v -e '16/1 "%02x" "\n"' | tr -d ' +')" + +# blob header +# bs=2 because each byte is two digits hex +COMPRESSED_LENGTH_HEX="0x$(dd bs=2 count=4 2> /dev/null <<< $DATA)" +COMPRESSED_LENGTH="$(baseconvert -d $COMPRESSED_LENGTH_HEX)" +echo " compressed length in bytes: $COMPRESSED_LENGTH ($COMPRESSED_LENGTH_HEX)" +FORMAT_HEX="0x$(dd bs=2 skip=4 count=1 2> /dev/null <<< $DATA)" +case $FORMAT_HEX in +"0x01") + FORMAT="gzip" + function UNCOMPRESS { + gzip -dc + } + ;; +"0x02") + FORMAT="zlib" + function UNCOMPRESS { + cat > /tmp/chunk_zlib_decompress + python3 <<< " +import zlib,sys +a = open('/tmp/chunk_zlib_decompress', 'rb') +data = a.read() +a.close() +sys.stdout.buffer.write(zlib.decompress(data)) +sys.stdout.buffer.flush() +" + rm /tmp/chunk_zlib_decompress + } + ;; +*) + FORMAT="unknown" + function UNCOMPRESS { + false + } + ;; +esac +echo " format: $FORMAT ($FORMAT_HEX)" + +if $VERBOSE; then + echo "Raw chunk data:" + echo "$DATA" + echo "NBT dump:" + + #TODO: use pipes instead of a file + dd if="$1" bs=4096 count=$LENGTH skip=$OFFSET 2>/dev/null | dd bs=1 skip=5 count=$(($COMPRESSED_LENGTH-1)) 2>/dev/null | UNCOMPRESS > /tmp/chunk_uncompressed_nbt + dumpnbt /tmp/chunk_uncompressed_nbt + rm /tmp/chunk_uncompressed_nbt +fi diff --git a/resources/region_files/r.0.0.mca b/resources/region_files/r.0.0.mca new file mode 100644 index 0000000..52cfc8d Binary files /dev/null and b/resources/region_files/r.0.0.mca differ diff --git a/src/lib/region.cpp b/src/lib/region.cpp new file mode 100644 index 0000000..5d77128 --- /dev/null +++ b/src/lib/region.cpp @@ -0,0 +1,17 @@ +// Copyright 2023, FOSS-VG Developers and Contributers +// +// Author(s): +// BodgeMaster +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied +// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// version 3 along with this program. +// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html diff --git a/src/lib/region.hpp b/src/lib/region.hpp new file mode 100644 index 0000000..185b9b9 --- /dev/null +++ b/src/lib/region.hpp @@ -0,0 +1,57 @@ +// Copyright 2023, FOSS-VG Developers and Contributers +// +// Author(s): +// BodgeMaster +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied +// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// version 3 along with this program. +// If not, see https://www.gnu.org/licenses/agpl-3.0.en.html + + +// File structure: +// 4K table of uint32_t: pointing to chunks +// -> 24bit offset, 8 bit length +// -> both offset and length are multiplied by 4096 to get the real value +// -> lowest valid offset = 2 +// -> chunk is not present if pointer=0 +// -> chunk calculation: ((x % 32) + (z % 32) * 32) * 4 +// -> what about negative chunks? +// 4k table of uint32_t: last modified timestamps +// individual chunks +// -> 5 byte header +// -> 4 byte length +// -> 1 byte compression type: 0?? 1->gzip 2->zlib +// -> extension idea: support for other compression algorithms +// - lzma +// - xz +// - lz4 +// - zstd +// -> compressed NBT data + +#pragma once + +#include + +class Region { + uint32_t storagePointers[1024]; + uint32_t lastModifiedTimestamps[1024]; + + // Chunk coordinates are uint8_t here bc they are 0<=x<32. + uint32_t* coordsToStoragePointer(uint8_t x, uint8_t z) { + return &storagePointers[z*32 + x]; + } + + // Chunk coordinates are uint8_t here bc they are 0<=x<32. + uint32_t* coordsToLastModified(uint8_t x, uint8_t z) { + return &lastModifiedTimestamps[z*32 + x]; + } +}