70 lines
1.7 KiB
Python
70 lines
1.7 KiB
Python
"""Little-endian .NET BinaryReader/BinaryWriter helpers."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import struct
|
|
from typing import BinaryIO
|
|
|
|
|
|
def read_7bit_int(stream: BinaryIO) -> int:
|
|
value = 0
|
|
shift = 0
|
|
while True:
|
|
raw = stream.read(1)
|
|
if not raw:
|
|
raise EOFError("truncated .NET 7-bit int")
|
|
value |= (raw[0] & 0x7F) << shift
|
|
if not raw[0] & 0x80:
|
|
return value
|
|
shift += 7
|
|
if shift > 35:
|
|
raise ValueError("invalid .NET 7-bit int")
|
|
|
|
|
|
def write_7bit_int(stream: BinaryIO, value: int) -> None:
|
|
if value < 0:
|
|
raise ValueError("7-bit int must be non-negative")
|
|
while value >= 0x80:
|
|
stream.write(bytes([(value & 0x7F) | 0x80]))
|
|
value >>= 7
|
|
stream.write(bytes([value & 0x7F]))
|
|
|
|
|
|
def read_dotnet_string(stream: BinaryIO) -> str:
|
|
length = read_7bit_int(stream)
|
|
raw = stream.read(length)
|
|
if len(raw) != length:
|
|
raise EOFError("truncated .NET string")
|
|
return raw.decode("utf-8")
|
|
|
|
|
|
def write_dotnet_string(stream: BinaryIO, text: str) -> None:
|
|
raw = text.encode("utf-8")
|
|
write_7bit_int(stream, len(raw))
|
|
stream.write(raw)
|
|
|
|
|
|
def read_i32(stream: BinaryIO) -> int:
|
|
raw = stream.read(4)
|
|
if len(raw) != 4:
|
|
raise EOFError("truncated int32")
|
|
return struct.unpack("<i", raw)[0]
|
|
|
|
|
|
def read_i64(stream: BinaryIO) -> int:
|
|
raw = stream.read(8)
|
|
if len(raw) != 8:
|
|
raise EOFError("truncated int64")
|
|
return struct.unpack("<q", raw)[0]
|
|
|
|
|
|
def read_bool(stream: BinaryIO) -> bool:
|
|
raw = stream.read(1)
|
|
if not raw:
|
|
raise EOFError("truncated bool")
|
|
return raw[0] != 0
|
|
|
|
|
|
def write_bool(stream: BinaryIO, value: bool) -> None:
|
|
stream.write(b"\x01" if value else b"\x00")
|