Source code for scfile.formats.mdat.decoder

import zstandard as zstd

from scfile import formats
from scfile.core import FileDecoder, RegionContent
from scfile.enums import ByteOrder, F, FileFormat
from scfile.structures import regions as S


CHUNKS_COUNT = 32 * 32  # 1024
SECTION_SIZE = 16 * 16 * 16  # 4096
NIBBLE_SIZE = 16 * 16 * 8  # 2048


[docs] class MdatDecoder(FileDecoder[RegionContent]): format = FileFormat.MDAT order = ByteOrder.BIG _content = RegionContent
[docs] def as_mca(self): return self.convert_to(formats.mca.McaEncoder)
[docs] def parse(self): table = [(self._readb(F.I32), self._readb(F.I32), self.read(16)) for _ in range(CHUNKS_COUNT)] offsets, counts, uuids = map(list, zip(*table)) dctx = zstd.ZstdDecompressor() chunks: list[S.RegionChunk] = [] for index in range(CHUNKS_COUNT): offset = offsets[index] if offset == 0: continue self.seek(offset * SECTION_SIZE) # header full_size, blocks_mask, add_mask, fixed_size, compressed_size = self._readarray(F.U32, 5).tolist() # read raw data compressed = self.read(compressed_size) decompressed = dctx.decompress(compressed) # split data pos = 0 sections_count = bin(blocks_mask).count("1") blocks = decompressed[pos : (pos := pos + sections_count * SECTION_SIZE)] chunk = S.RegionChunk( index=index, header=S.ChunkHeader(full_size, blocks_mask, add_mask, fixed_size, compressed_size), blocks=blocks, ) if self.options.full_chunk: add_count = bin(add_mask).count("1") chunk.meta = decompressed[pos : (pos := pos + sections_count * NIBBLE_SIZE)] chunk.light = decompressed[pos : (pos := pos + sections_count * NIBBLE_SIZE * 3)] chunk.add = decompressed[pos : (pos := pos + add_count * NIBBLE_SIZE)] chunk.extra = decompressed[pos:] chunks.append(chunk) self.data.offsets = offsets self.data.counts = counts self.data.uuid = uuids self.data.chunks = chunks