# -*- encoding: utf-8 -*- from __future__ import annotations import io import struct from typing import Optional, Tuple from .XRMath import IVec3 class XRReader: def __init__(self, buffer: bytes): self._buffer = buffer self._pos = 0 def __len__(self) -> int: return len(self._buffer) def _read(self, size: int) -> Tuple[bytes, int]: pos = min(len(self._buffer), self._pos + size) buffer = self._buffer[self._pos : pos] return (buffer, pos) def read(self, size: int = -1) -> bytes: if size < 0: size = len(self._buffer) if len(self._buffer) <= self._pos: return b"" (buffer, pos) = self._read(size) self._pos = pos return buffer def peek(self, size: int = -1) -> bytes: if size < 0: size = len(self._buffer) if len(self._buffer) <= self._pos: return b"" (buffer, _pos) = self._read(size) return buffer def seek(self, pos: int, whence: int = io.SEEK_SET) -> int: if whence == 0: if pos < 0: raise ValueError(f"negative seek position {pos}") self._pos = pos elif whence == 1: self._pos = max(0, self._pos + pos) elif whence == 2: self._pos = max(0, len(self._buffer) + pos) else: raise ValueError("unsupported whence value") return self._pos def elapsed(self) -> int: return len(self._buffer) - self._pos def eof(self) -> bool: return self.elapsed() <= 0 def u8(self) -> int: return int(struct.unpack(" int: return int(struct.unpack(" int: return int(struct.unpack(" int: return int(struct.unpack(" int: return int(struct.unpack(" int: return int(struct.unpack(" int: return int(struct.unpack(" int: return int(struct.unpack(" bool: return bool(struct.unpack(" float: return float(struct.unpack(" str: chars = bytearray() while not self.eof(): c = self.read(1) if c == b"\x00": return str(chars, "utf-8") else: chars += c return "" def fvec3(self) -> IVec3: (f1, f2, f3) = struct.unpack(" Optional[int]: dw_type = 0 dw_size = 0 success = False if self.last_pos != 0: self.seek(self.last_pos) dw_type = self.u32() dw_size = self.u32() if (dw_type & (~(1 << 31))) == id: success = True if not success: self.seek(0) while not self.eof(): dw_type = self.u32() dw_size = self.u32() if (dw_type & (~(1 << 31))) == id: success = True break else: self.seek(dw_size, io.SEEK_CUR) if not success: self.last_pos = 0 return None if (self._pos + dw_size) < len(self._buffer): self.last_pos = self._pos + dw_size else: self.last_pos = 0 return dw_size def open_chunk(self, id: int) -> Optional[XRStream]: size = self.find_chunk(id) if size and size != 0: data = self.read(size) return XRStream(data) return None