summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore46
-rw-r--r--LICENSE21
-rw-r--r--README.md63
-rw-r--r--pyproject.toml23
-rw-r--r--tasd/__init__.py4
-rw-r--r--tasd/constants.py56
-rw-r--r--tasd/file.py57
-rw-r--r--tasd/packet.py304
-rw-r--r--tasd/packets/__init__.py6
-rw-r--r--tasd/packets/extra.py38
-rw-r--r--tasd/packets/general.py406
-rw-r--r--tasd/packets/genesis.py18
-rw-r--r--tasd/packets/input.py106
-rw-r--r--tasd/packets/nes.py38
-rw-r--r--tasd/packets/snes.py50
-rw-r--r--tasd/utils.py128
16 files changed, 1364 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..dc5610e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,46 @@
+### Python ###
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+## TASD files
+*.tasd
+
+## Publish Process
+publish/
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f036dab
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 TheMas3212
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ec20e21
--- /dev/null
+++ b/README.md
@@ -0,0 +1,63 @@
+
+# TASD
+
+This is a python library for the serialization and deserialization of tasd files using the [tasd file specfication](https://tasd.io/)
+
+
+## Installation
+
+Install tasd with pip
+
+```bash
+ pip install tasd
+```
+
+## Usage/Examples
+
+
+Read a file in a print all the packets in it
+```py
+from tasd import TASD
+
+with open("example.tasd", "rb") as f:
+ file = TASD.from_bytes(f.read())
+
+print(file)
+for packet in file. packets:
+ print(packet)
+```
+
+Create a tasd file from scratch and add packets to it
+```py
+from tasd import TASD, packets
+
+file = TASD()
+file.packets.append(packets.extra.Comment(comment="This is a comment"))
+file.packets.append(packets.general.Attribution(type=packets.general.Attribution.ATTRIBUTION_TYPE.TASD_FILE_CREATOR, name="Me"))
+
+for packet in file. packets:
+ print(packet)
+
+print(file.to_bytes())
+```
+## Brief API Reference
+
+Not an exhaustive list
+
+`tasd.TASD`
+
+Class holding properties of the tasd file itself: Version and KeyLength
+
+`tasd.constants.PACKET_TYPES`
+
+Enumlike class containing the different packet type indentifiers
+
+`tasd.packets`
+
+Submodule containing all the different packet classes
+
+For details about the different packet types i suggest reading the specification document to determine what packets you need to consider for your use case
+## Appendix
+
+Any additional information goes here
+
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..f8493aa
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,23 @@
+[project]
+name = "tasd"
+version = "1.0.0"
+authors = [
+ { name="TheMas3212", email="mas3212@gmail.com" },
+]
+description = "TASD file serializer and deserializer"
+readme = "README.md"
+requires-python = ">=3.10"
+classifiers = [
+ "Programming Language :: Python :: 3",
+ "Operating System :: OS Independent",
+]
+license = "MIT"
+license-files = ["LICEN[CS]E*"]
+
+[project.urls]
+Homepage = "https://github.com/themas3212/tasd-py"
+Issues = "https://github.com/themas3212/tasd-py/issues"
+
+[build-system]
+requires = ["setuptools >= 77.0.3"]
+build-backend = "setuptools.build_meta" \ No newline at end of file
diff --git a/tasd/__init__.py b/tasd/__init__.py
new file mode 100644
index 0000000..44b3a2c
--- /dev/null
+++ b/tasd/__init__.py
@@ -0,0 +1,4 @@
+from tasd.file import TASD
+import tasd.packets
+import tasd.constants
+import tasd.utils \ No newline at end of file
diff --git a/tasd/constants.py b/tasd/constants.py
new file mode 100644
index 0000000..b4d41fb
--- /dev/null
+++ b/tasd/constants.py
@@ -0,0 +1,56 @@
+class _MetaPacketType():
+ def __init_subclass__(self):
+ keys = [key for key in self.__dict__.keys() if type(self.__dict__[key]) == int]
+ self._key2name = dict()
+ for key in keys:
+ self._key2name[self.__dict__[key]] = key
+
+class PacketType(_MetaPacketType):
+ # General Keys
+ CONSOLE_TYPE = 0x0001
+ CONSOLE_REGION = 0x0002
+ GAME_TITLE = 0x0003
+ ROM_NAME = 0x0004
+ ATTRIBUTION = 0x0005
+ CATEGORY = 0x0006
+ EMULATOR_NAME = 0x0007
+ EMULATOR_VERSION = 0x0008
+ EMULATOR_CORE = 0x0009
+ TAS_LAST_MODIFIED = 0x000A
+ DUMP_CREATED = 0x000B
+ DUMP_LAST_MODIFIED = 0x000C
+ TOTAL_FRAMES = 0x000D
+ RERECORDS = 0x000E
+ SOURCE_LINK = 0x000F
+ BLANK_FRAMES = 0x0010
+ VERIFIED = 0x0011
+ MEMORY_INIT = 0x0012
+ GAME_IDENTIFIER = 0x0013
+ MOVIE_LICENSE = 0x0014
+ MOVIE_FILE = 0x0015
+ PORT_CONTROLLER = 0x00F0
+ PORT_OVERREAD = 0x00F1
+ # NES Specific Keys
+ NES_LATCH_FILTER = 0x0101
+ NES_CLOCK_FILTER = 0x0102
+ NES_GAME_GENIE_CODE = 0x0104
+ # SNES Specific Keys
+ SNES_LATCH_FILTER = 0x0201
+ SNES_CLOCK_FILTER = 0x0202
+ SNES_GAME_GENIE_CODE = 0x0204
+ SNES_LATCH_TRAIN = 0x0205
+ # Genesis Specific Keys
+ GENESIS_GAME_GENIE_CODE = 0x0804
+ # Input Frame/Timing Keys
+ INPUT_CHUNK = 0xFE01
+ INPUT_MOMENT = 0xFE02
+ TRANSITION = 0xFE03
+ LAG_FRAME_CHUNK = 0xFE04
+ MOVIE_TRANSITION = 0xFE05
+ # Extraneous Keys
+ COMMENT = 0xFF01
+ EXPERIMENTAL = 0xFFFE
+ UNSPECIFIED = 0xFFFF
+ @classmethod
+ def lookup(cls, key):
+ return cls._key2name.get(key, f'UNKNOWN_PACKET_{key:04X}')
diff --git a/tasd/file.py b/tasd/file.py
new file mode 100644
index 0000000..53d4f74
--- /dev/null
+++ b/tasd/file.py
@@ -0,0 +1,57 @@
+import tasd.packets as packets
+import tasd.constants as constants
+import tasd.utils as utils
+from tasd.packet import lookup_packet, _TASDPacket
+from typing import List
+
+class TASD():
+ MAGIC = b"TASD"
+ def __init__(self, version = 1, g_keylen = 2):
+ self.version = version
+ self.g_keylen = g_keylen
+ self.packets: List[_TASDPacket] = []
+ @classmethod
+ def from_bytes(cls, buffer: bytes, *, bypass_errors = False):
+ magic = utils.readBytes(buffer, 0, 4)
+ if magic != cls.MAGIC:
+ raise ValueError("Invalid TASD File, Missing Magic")
+ version = utils.readUint16(buffer, 4)
+ if version != 1 and not bypass_errors:
+ raise ValueError("Unsupported TASD File, Version Unsupported", version)
+ g_keylen = utils.readUint8(buffer, 6)
+ if g_keylen != 2:
+ raise ValueError("Unsupported TASD File, Unknown G_KEYLEN", g_keylen)
+ self = cls(version, g_keylen)
+ index = 7
+ while index < len(buffer):
+ key = utils.readUintN(buffer, index, self.g_keylen)
+ index += self.g_keylen
+ packet_cls = lookup_packet(key)
+ pexp = utils.readUint8(buffer, index)
+ index += 1
+ plen = utils.readUintN(buffer, index, pexp)
+ index += pexp
+ data = utils.readBytes(buffer, index, plen)
+ index += plen
+ packet = packet_cls.from_bytes(data)
+ self.packets.append(packet)
+ return self
+ def to_bytes(self):
+ header = bytearray(7)
+ header[0:4] = self.MAGIC
+ utils.writeUint16(self.version, header, 4)
+ utils.writeUint8(self.g_keylen, header, 6)
+ packets = [ header ]
+ for packet in self.packets:
+ plen = packet.size()
+ pexp = utils.minUint(plen)
+ size = self.g_keylen + 1 + pexp
+ buffer = bytearray(size)
+ utils.writeUintN(packet._key, buffer, 0, self.g_keylen)
+ utils.writeUint8(pexp, buffer, self.g_keylen)
+ utils.writeUintN(plen, buffer, self.g_keylen + 1, pexp)
+ payload = packet.to_bytes()
+ packets.append(buffer + payload)
+ return b"".join(packets)
+ def __repr__(self):
+ return f"TASD(version={self.version}, g_keylen={self.g_keylen})"
diff --git a/tasd/packet.py b/tasd/packet.py
new file mode 100644
index 0000000..5d9ed68
--- /dev/null
+++ b/tasd/packet.py
@@ -0,0 +1,304 @@
+from typing import NamedTuple, List, Any
+from enum import Enum
+
+from tasd.constants import PacketType
+from tasd.utils import *
+
+class _VariableType(Enum):
+ STRING_EOP = 0x00
+ STRING_PLEN = 0x01
+ STRING_PEXP = 0x02
+ BYTES_EOP = 0x10
+ BYTES_PLEN = 0x11
+ BYTES_PEXP = 0x12
+ INT_8 = 0x20
+ INT_16 = 0x21
+ INT_24 = 0x22
+ INT_32 = 0x23
+ INT_64 = 0x24
+ INT_N = 0x25
+ UINT_8 = 0x30
+ UINT_16 = 0x31
+ UINT_24 = 0x32
+ UINT_32 = 0x33
+ UINT_64 = 0x34
+ UINT_N = 0x35
+ BOOL = 0x40
+ LIST_UINT_64 = 0x50
+
+
+class _VariableDefinition(NamedTuple):
+ name: str
+ type: _VariableType
+ default: Any
+
+class _EnumDefinition(NamedTuple):
+ name: str
+ enum: Enum
+
+class _TASDPacket():
+ @classmethod
+ def from_bytes(cls, buffer: bytes):
+ self = cls()
+ index = 0
+ for variable in cls._variables:
+ match variable.type:
+ case _VariableType.STRING_EOP:
+ setattr(self, variable.name, readString(buffer, index, len(buffer) - index))
+ index = len(buffer)
+ case _VariableType.STRING_PLEN:
+ plen = readInt8(buffer, index)
+ index += 1
+ setattr(self, variable.name, readString(buffer, index, plen))
+ index += plen
+ case _VariableType.STRING_PEXP:
+ pexp = readInt8(buffer, index)
+ index += 1
+ plen = readIntN(buffer, index, pexp)
+ index += pexp
+ setattr(self, variable.name, readString(buffer, index, plen))
+ index += plen
+ case _VariableType.BYTES_EOP:
+ setattr(self, variable.name, readBytes(buffer, index, len(buffer)))
+ index = len(buffer)
+ case _VariableType.BYTES_PLEN:
+ plen = readInt8(buffer, index)
+ index += 1
+ setattr(self, variable.name, readBytes(buffer, index, plen))
+ index += plen
+ case _VariableType.BYTES_PEXP:
+ pexp = readInt8(buffer, index)
+ index += 1
+ plen = readIntN(buffer, index, pexp)
+ index += pexp
+ setattr(self, variable.name, readBytes(buffer, index, plen))
+ index += plen
+ case _VariableType.INT_8:
+ setattr(self, variable.name, readInt8(buffer, index))
+ index += 1
+ case _VariableType.INT_16:
+ setattr(self, variable.name, readInt16(buffer, index))
+ index += 2
+ case _VariableType.INT_24:
+ setattr(self, variable.name, readInt24(buffer, index))
+ index += 3
+ case _VariableType.INT_32:
+ setattr(self, variable.name, readInt32(buffer, index))
+ index += 4
+ case _VariableType.INT_64:
+ setattr(self, variable.name, readInt64(buffer, index))
+ index += 8
+ case _VariableType.INT_N:
+ pexp = readInt8(buffer, index)
+ index += 1
+ setattr(self, variable.name, readIntN(buffer, index, pexp))
+ index += pexp
+ case _VariableType.UINT_8:
+ setattr(self, variable.name, readUint8(buffer, index))
+ index += 1
+ case _VariableType.UINT_16:
+ setattr(self, variable.name, readUint16(buffer, index))
+ index += 2
+ case _VariableType.UINT_24:
+ setattr(self, variable.name, readUint24(buffer, index))
+ index += 3
+ case _VariableType.UINT_32:
+ setattr(self, variable.name, readUint32(buffer, index))
+ index += 4
+ case _VariableType.UINT_64:
+ setattr(self, variable.name, readUint64(buffer, index))
+ index += 8
+ case _VariableType.UINT_N:
+ pexp = readUint8(buffer, index)
+ index += 1
+ setattr(self, variable.name, readUintN(buffer, index, pexp))
+ index += pexp
+ case _VariableType.BOOL:
+ setattr(self, variable.name, readBoolean(buffer, index))
+ index += 1
+ case _VariableType.LIST_UINT_64:
+ l = list()
+ while index < len(buffer):
+ l.append(readUint64(buffer, index))
+ index += 8
+ setattr(self, variable.name, l)
+ case _:
+ raise ValueError("Unknown Variable Type", variable)
+ return self
+ def size(self):
+ size = 0
+ for variable in self._variables:
+ value = getattr(self, variable.name)
+ match variable.type:
+ case _VariableType.STRING_EOP:
+ size += sizeString(value)
+ case _VariableType.STRING_PLEN:
+ size += ( 1 + sizeString(value) )
+ case _VariableType.STRING_PEXP:
+ vsize = sizeString(value)
+ size += ( 1 + minUint(vsize) + vsize )
+ case _VariableType.BYTES_EOP:
+ size += len(value)
+ case _VariableType.BYTES_PLEN:
+ size += ( 1 + len(value) )
+ case _VariableType.BYTES_PEXP:
+ vsize = len(value)
+ size += ( 1 + minUint(vsize) + vsize )
+ case _VariableType.INT_8:
+ size += 1
+ case _VariableType.INT_16:
+ size += 2
+ case _VariableType.INT_24:
+ size += 3
+ case _VariableType.INT_32:
+ size += 4
+ case _VariableType.INT_64:
+ size += 8
+ case _VariableType.INT_N:
+ size += minInt(value)
+ case _VariableType.UINT_8:
+ size += 1
+ case _VariableType.UINT_16:
+ size += 2
+ case _VariableType.UINT_24:
+ size += 3
+ case _VariableType.UINT_32:
+ size += 4
+ case _VariableType.UINT_64:
+ size += 8
+ case _VariableType.UINT_N:
+ size += minUint(value)
+ case _VariableType.BOOL:
+ size += 1
+ case _VariableType.LIST_UINT_64:
+ size += ( 8 * len(value) )
+ case _:
+ raise ValueError("Unknown Variable Type", variable)
+ return size
+ def to_bytes(self):
+ buffer = bytearray(self.size())
+ index = 0
+ for variable in self._variables:
+ value = getattr(self, variable.name)
+ match variable.type:
+ case _VariableType.STRING_EOP:
+ writeString(value, buffer, index)
+ index += sizeString(value)
+ case _VariableType.STRING_PLEN:
+ plen = sizeString(value)
+ writeUint8(plen, buffer, index)
+ index += 1
+ writeString(value, buffer, index)
+ index += plen
+ case _VariableType.STRING_PEXP:
+ plen = sizeString(value)
+ pexp = minUint(plen)
+ writeUint8(pexp, buffer, index)
+ index += 1
+ writeUintN(plen, buffer, index, pexp)
+ index += pexp
+ writeString(value, buffer, index)
+ index += plen
+ case _VariableType.BYTES_EOP:
+ writeBytes(value, buffer, index)
+ index += len(value)
+ case _VariableType.BYTES_PLEN:
+ plen = len(value)
+ writeUint8(plen, buffer, index)
+ index += 1
+ writeBytes(value, buffer, index)
+ index += plen
+ case _VariableType.BYTES_PEXP:
+ plen = len(value)
+ pexp = minUint(plen)
+ writeUint8(pexp, buffer, index)
+ index += 1
+ writeUintN(plen, buffer, index, pexp)
+ index += pexp
+ writeBytes(value, buffer, index)
+ index += plen
+ case _VariableType.INT_8:
+ writeInt8(value, buffer, index)
+ index += 1
+ case _VariableType.INT_16:
+ writeInt16(value, buffer, index)
+ index += 2
+ case _VariableType.INT_24:
+ writeInt24(value, buffer, index)
+ index += 3
+ case _VariableType.INT_32:
+ writeInt32(value, buffer, index)
+ index += 4
+ case _VariableType.INT_64:
+ writeInt64(value, buffer, index)
+ index += 8
+ case _VariableType.INT_N:
+ pexp = minInt(value)
+ writeUint8(pexp, buffer, index)
+ index += 1
+ writeIntN(value, buffer, index, pexp)
+ index += pexp
+ case _VariableType.UINT_8:
+ writeUint8(value, buffer, index)
+ index += 1
+ case _VariableType.UINT_16:
+ writeUint16(value, buffer, index)
+ index += 2
+ case _VariableType.UINT_24:
+ writeUint24(value, buffer, index)
+ index += 3
+ case _VariableType.UINT_32:
+ writeUint32(value, buffer, index)
+ index += 4
+ case _VariableType.UINT_64:
+ writeUint64(value, buffer, index)
+ index += 8
+ case _VariableType.UINT_N:
+ pexp = minInt(value)
+ writeUint8(pexp, buffer, index)
+ index += 1
+ writeUintN(value, buffer, index, pexp)
+ index += pexp
+ case _VariableType.BOOL:
+ writeBoolean(value, buffer, index)
+ index += 1
+ case _VariableType.LIST_UINT_64:
+ for val in value:
+ writeUint64(val, buffer, index)
+ index += 8
+ case _:
+ raise ValueError("Unknown Variable Type", variable)
+ return buffer
+ def __repr__(self):
+ return f"{self._name}({', '.join(f'{var.name}={getattr(self, var.name)}' for var in self._variables)})"
+
+_lookup_table = dict()
+
+def _TASDPacketFactory(name: str, key: int, variables: List[_VariableDefinition] = [], enums: List[_EnumDefinition] = []):
+ class Packet(_TASDPacket):
+ _variables = variables
+ _key = key
+ _name = name
+ def __init__(self, **kwargs):
+ for variable in Packet._variables:
+ setattr(self, variable.name, kwargs.get(variable.name, variable.default))
+
+ for enum in enums:
+ setattr(Packet, enum.name, enum.enum)
+
+ _lookup_table[key] = Packet
+
+ Packet.__name__ = name
+ Packet.__qualname__ = name
+ return Packet
+
+def _build_unknown_packet(key):
+ _UnknownPacket = _TASDPacketFactory(f"UnknownPacket_{key:04x}", key, [ _VariableDefinition("data", _VariableType.BYTES_EOP, b"") ])
+ return _UnknownPacket
+
+def lookup_packet(key: int) -> _TASDPacket:
+ packet = _lookup_table.get(key, None)
+ if packet == None:
+ packet = _build_unknown_packet(key)
+ return packet
+ \ No newline at end of file
diff --git a/tasd/packets/__init__.py b/tasd/packets/__init__.py
new file mode 100644
index 0000000..002c4ca
--- /dev/null
+++ b/tasd/packets/__init__.py
@@ -0,0 +1,6 @@
+import tasd.packets.general
+import tasd.packets.nes
+import tasd.packets.snes
+import tasd.packets.genesis
+import tasd.packets.input
+import tasd.packets.extra
diff --git a/tasd/packets/extra.py b/tasd/packets/extra.py
new file mode 100644
index 0000000..41ad604
--- /dev/null
+++ b/tasd/packets/extra.py
@@ -0,0 +1,38 @@
+from tasd.packet import _TASDPacketFactory as _TASD_PF, _VariableType as _VType, _VariableDefinition as _VDef, _EnumDefinition as _EDef
+from tasd.constants import PacketType
+
+'''
+ 4.3.6.1. COMMENT Packet
+'''
+
+Comment = _TASD_PF(
+ "Comment",
+ PacketType.COMMENT,
+ [
+ _VDef("comment", _VType.STRING_EOP, "")
+ ]
+)
+
+'''
+ 4.3.6.2. EXPERIMENTAL Packet
+'''
+
+Experimental = _TASD_PF(
+ "Experimental",
+ PacketType.EXPERIMENTAL,
+ [
+ _VDef("experimental", _VType.BOOL, False)
+ ]
+)
+
+'''
+ 4.3.6.3. UNSPECIFIED Packet
+'''
+
+Unspecified = _TASD_PF(
+ "Unspecified",
+ PacketType.UNSPECIFIED,
+ [
+ _VDef("data", _VType.BYTES_EOP, b"")
+ ]
+) \ No newline at end of file
diff --git a/tasd/packets/general.py b/tasd/packets/general.py
new file mode 100644
index 0000000..d643324
--- /dev/null
+++ b/tasd/packets/general.py
@@ -0,0 +1,406 @@
+from tasd.packet import _TASDPacketFactory as _TASD_PF, _VariableType as _VType, _VariableDefinition as _VDef, _EnumDefinition as _EDef
+from tasd.constants import PacketType
+
+'''
+ 4.3.1.1. CONSOLE_TYPE Packet
+'''
+
+class CONSOLE_TYPE():
+ NES = 0x01
+ SNES = 0x02
+ N64 = 0x03
+ GC = 0x04
+ GB = 0x05
+ GBC = 0x06
+ GBA = 0x07
+ GENESIS = 0x08
+ A2600 = 0x09
+ CUSTOM = 0xFF
+ConsoleType = _TASD_PF(
+ "ConsoleType",
+ PacketType.CONSOLE_TYPE,
+ [
+ _VDef("console", _VType.UINT_8, CONSOLE_TYPE.NES),
+ _VDef("name", _VType.STRING_EOP, "")
+ ],
+ [
+ _EDef("CONSOLE_TYPE", CONSOLE_TYPE)
+ ]
+)
+
+'''
+ 4.3.1.2. CONSOLE_REGION Packet
+'''
+
+class CONSOLE_REGION():
+ NTSC = 0x01
+ PAL = 0x02
+ OTHER = 0xFF
+ConsoleRegion = _TASD_PF(
+ "ConsoleRegion",
+ PacketType.CONSOLE_REGION,
+ [
+ _VDef("region", _VType.UINT_8, CONSOLE_REGION.NTSC)
+ ],
+ [
+ _EDef("CONSOLE_REGION", CONSOLE_REGION)
+ ]
+)
+
+'''
+ 4.3.1.3. GAME_TITLE Packet
+'''
+
+GameTitle = _TASD_PF(
+ "GameTitle",
+ PacketType.GAME_TITLE,
+ [
+ _VDef("title", _VType.STRING_EOP, "")
+ ]
+)
+
+
+'''
+ 4.3.1.4. ROM_NAME Packet
+'''
+
+RomName = _TASD_PF(
+ "RomName",
+ PacketType.ROM_NAME,
+ [
+ _VDef("name", _VType.STRING_EOP, "")
+ ]
+)
+
+'''
+ 4.3.1.5. ATTRIBUTION Packet
+'''
+
+class ATTRIBUTION_TYPE():
+ AUTHOR = 0x01
+ VERIFIER = 0x02
+ TASD_FILE_CREATOR = 0x03
+ TASD_FILE_EDITOR = 0x04
+ OTHER = 0xFF
+Attribution = _TASD_PF(
+ "Attribution",
+ PacketType.ATTRIBUTION,
+ [
+ _VDef("type", _VType.UINT_8, ATTRIBUTION_TYPE.AUTHOR),
+ _VDef("name", _VType.STRING_EOP, "")
+ ],
+ [
+ _EDef("ATTRIBUTION_TYPE", ATTRIBUTION_TYPE)
+ ]
+)
+
+
+'''
+ 4.3.1.6. CATEGORY Packet
+'''
+
+Category = _TASD_PF(
+ "Category",
+ PacketType.CATEGORY,
+ [
+ _VDef("category", _VType.STRING_EOP, "")
+ ]
+)
+
+'''
+ 4.3.1.7. EMULATOR_NAME Packet
+'''
+
+EmulatorName = _TASD_PF(
+ "EmulatorName",
+ PacketType.EMULATOR_NAME,
+ [
+ _VDef("name", _VType.STRING_EOP, "")
+ ]
+)
+
+'''
+ 4.3.1.8. EMULATOR_VERSION Packet
+'''
+
+EmulatorVersion = _TASD_PF(
+ "EmulatorVersion",
+ PacketType.EMULATOR_VERSION,
+ [
+ _VDef("version", _VType.STRING_EOP, "")
+ ]
+)
+
+'''
+ 4.3.1.9. EMULATOR_CORE Packet
+'''
+
+EmulatorCore = _TASD_PF(
+ "EmulatorCore",
+ PacketType.EMULATOR_CORE,
+ [
+ _VDef("core", _VType.STRING_EOP, "")
+ ]
+)
+
+'''
+ 4.3.1.10. TAS_LAST_MODIFIED Packet
+'''
+
+TASLastModified = _TASD_PF(
+ "TASLastModified",
+ PacketType.TAS_LAST_MODIFIED,
+ [
+ _VDef("timestamp", _VType.INT_64, 0)
+ ]
+)
+
+'''
+ 4.3.1.11. DUMP_CREATED Packet
+'''
+
+DumpCreated = _TASD_PF(
+ "DumpCreated",
+ PacketType.DUMP_CREATED,
+ [
+ _VDef("timestamp", _VType.INT_64, 0)
+ ]
+)
+
+'''
+ 4.3.1.12. DUMP_LAST_MODIFIED Packet
+'''
+
+DumpLastModified = _TASD_PF(
+ "DumpLastModified",
+ PacketType.DUMP_LAST_MODIFIED,
+ [
+ _VDef("timestamp", _VType.INT_64, 0)
+ ]
+)
+
+'''
+ 4.3.1.13. TOTAL_FRAMES Packet
+'''
+
+TotalFrames = _TASD_PF(
+ "TotalFrames",
+ PacketType.TOTAL_FRAMES,
+ [
+ _VDef("frames", _VType.UINT_32, 0)
+ ]
+)
+
+'''
+ 4.3.1.14. RERECORDS Packet
+'''
+
+Rerecords = _TASD_PF(
+ "Rerecords",
+ PacketType.RERECORDS,
+ [
+ _VDef("rerecords", _VType.UINT_32, 0)
+ ]
+)
+
+'''
+ 4.3.1.15. SOURCE_LINK Packet
+'''
+
+SourceLink = _TASD_PF(
+ "SourceLink",
+ PacketType.SOURCE_LINK,
+ [
+ _VDef("link", _VType.STRING_EOP, "")
+ ]
+)
+
+'''
+ 4.3.1.16. BLANK_FRAMES Packet
+'''
+
+BlankFrames = _TASD_PF(
+ "BlankFrames",
+ PacketType.BLANK_FRAMES,
+ [
+ _VDef("frames", _VType.INT_16, 0)
+ ]
+)
+
+'''
+ 4.3.1.17. VERIFIED Packet
+'''
+
+Verified = _TASD_PF(
+ "Verified",
+ PacketType.VERIFIED,
+ [
+ _VDef("verified", _VType.BOOL, False)
+ ]
+)
+
+'''
+ 4.3.1.18. MEMORY_INIT Packet
+'''
+
+class MEMORY_INIT_TYPE():
+ NONE = 0x01
+ ALL_00 = 0x02
+ ALL_FF = 0x03
+ PATTERN_00000000FFFFFFFF = 0x04
+ RANDOM = 0x05
+ CUSTOM = 0xFF
+class MEMORY_INIT_DEVICE():
+ NES_CPU_RAM = 0x0101
+ NES_CART_SRAM = 0x0102
+ SNES_CPU_RAM = 0x0201
+ SNES_CART_SRAM = 0x0202
+ GB_CPU_RAM = 0x0501
+ GB_CART_SRAM = 0x0502
+ GBC_CPU_RAM = 0x0601
+ GBC_CART_SRAM = 0x0602
+ GBA_CPU_RAM = 0x0701
+ GBA_CART_SRAM = 0x0702
+ GENESIS_CPU_RAM = 0x0801
+ GENESIS_CART_SRAM = 0x0802
+ A2600_CPU_RAM = 0x0901
+ A2600_CART_SRAM = 0x0902
+ CUSTOM = 0xFFFF
+MemoryInit = _TASD_PF(
+ "MemoryInit",
+ PacketType.MEMORY_INIT,
+ [
+ _VDef("type", _VType.UINT_8, MEMORY_INIT_TYPE.NONE),
+ _VDef("device", _VType.UINT_16, MEMORY_INIT_DEVICE.NES_CPU_RAM),
+ _VDef("required", _VType.BOOL, False),
+ _VDef("name", _VType.STRING_PLEN, ""),
+ _VDef("data", _VType.BYTES_EOP, b"")
+ ],
+ [
+ _EDef("MEMORY_INIT_TYPE", MEMORY_INIT_TYPE),
+ _EDef("MEMORY_INIT_DEVICE", MEMORY_INIT_DEVICE)
+ ]
+)
+
+'''
+ 4.3.1.19. GAME_IDENTIFIER Packet
+'''
+
+class GAME_IDENTIFIER_TYPE():
+ MD5 = 0x01
+ SHA1 = 0x02
+ SHA224 = 0x03
+ SHA256 = 0x04
+ SHA384 = 0x05
+ SHA512 = 0x06
+ SHA512_224 = 0x07
+ SHA512_256 = 0x08
+ SHA3_224 = 0x09
+ SHA3_256 = 0x0A
+ SHA3_384 = 0x0B
+ SHA3_512 = 0x0C
+ SHAKE_128 = 0x0D
+ SHAKE_256 = 0x0E
+ OTHER = 0xFF
+class GAME_IDENTIFIER_BASE():
+ RAW_BINARY = 0x01
+ BASE16 = 0x02
+ BASE32 = 0x03
+ BASE64 = 0x04
+ OTHER = 0xFF
+GameIdentifier = _TASD_PF(
+ "GameIdentifier",
+ PacketType.GAME_IDENTIFIER,
+ [
+ _VDef("type", _VType.UINT_8, GAME_IDENTIFIER_TYPE.MD5),
+ _VDef("encoding", _VType.UINT_8, GAME_IDENTIFIER_BASE.RAW_BINARY),
+ _VDef("name", _VType.STRING_PLEN, ""),
+ _VDef("identifier", _VType.BYTES_EOP, b"")
+ ],
+ [
+ _EDef("GAME_IDENTIFIER_TYPE", GAME_IDENTIFIER_TYPE),
+ _EDef("GAME_IDENTIFIER_BASE", GAME_IDENTIFIER_BASE)
+ ])
+
+'''
+ 4.3.1.20. MOVIE_LICENSE Packet
+'''
+
+MovieLicense = _TASD_PF(
+ "MovieLicense",
+ PacketType.MOVIE_LICENSE,
+ [
+ _VDef("link", _VType.STRING_EOP, "")
+ ]
+)
+
+'''
+ 4.3.1.21. MOVIE_FILE Packet
+'''
+
+MovieFile = _TASD_PF(
+ "MovieFile",
+ PacketType.MOVIE_FILE,
+ [
+ _VDef("name", _VType.STRING_PLEN, ""),
+ _VDef("data", _VType.BYTES_EOP, b"")
+ ]
+)
+
+'''
+ 4.3.1.22. PORT_CONTROLLER Packet
+'''
+
+class CONTROLLER_TYPE():
+ NES_STANDARD_CONTROLLER = 0x0101
+ NES_FOUR_SCORE = 0x0102
+ NES_ZAPPER = 0x0103
+ NES_POWER_PAD = 0x0104
+ FAMICOM_FAMILY_BASIC_KEYBOARD = 0x0105
+ SNES_STANDARD_CONTROLLER = 0x0201
+ SNES_SUPER_MULTITAP = 0x0202
+ SNES_MOUSE = 0x0203
+ SNES_SUPERSCOPE = 0x0204
+ N64_STANDARD_CONTROLLER = 0x0301
+ N64_STANDARD_CONTROLLER_WITH_RUMBLE_PAK = 0x0302
+ N64_STANDARD_CONTROLLER_WITH_CONTROLLER_PAK = 0x0303
+ N64_STANDARD_CONTROLLER_WITH_TRANSFER_PAK = 0x0304
+ N64_MOUSE = 0x0305
+ N64_VOICE_RECOGNITION_UNIT = 0x0306
+ N64_RANDNET_KEYBOARD = 0x0307
+ N64_DENSHA_DE_GO = 0x0308
+ GC_STANDARD_CONTROLLER = 0x0401
+ GC_KEYBOARD = 0x0402
+ GB_GAMEPAD = 0x0501
+ GBC_GAMEPAD = 0x0601
+ GBA_GAMEPAD = 0x0701
+ GENESIS_3BUTTON = 0x0801
+ GENESIS_6BUTTON = 0x0802
+ A2600_JOYSTICK = 0x0901
+ A2600_PADDLE = 0x0902
+ A2600_KEYBOARD_CONTROLLER = 0x0903
+ OTHER_UNSPECIFIED = 0xFFFF
+PortController = _TASD_PF(
+ "PortController",
+ PacketType.PORT_CONTROLLER,
+ [
+ _VDef("port", _VType.UINT_8, 1),
+ _VDef("type", _VType.UINT_16, CONTROLLER_TYPE.NES_STANDARD_CONTROLLER)
+ ],
+ [
+ _EDef("CONTROLLER_TYPE", CONTROLLER_TYPE)
+ ]
+)
+
+'''
+ 4.3.1.23. PORT_OVERREAD Packet
+'''
+
+PortOverread = _TASD_PF(
+ "PortOverread",
+ PacketType.PORT_OVERREAD,
+ [
+ _VDef("port", _VType.UINT_8, 1),
+ _VDef("high", _VType.BOOL, False)
+ ]
+)
diff --git a/tasd/packets/genesis.py b/tasd/packets/genesis.py
new file mode 100644
index 0000000..b8b7cc4
--- /dev/null
+++ b/tasd/packets/genesis.py
@@ -0,0 +1,18 @@
+from tasd.packet import _TASDPacketFactory as _TASD_PF, _VariableType as _VType, _VariableDefinition as _VDef, _EnumDefinition as _EDef
+from tasd.constants import PacketType
+
+'''
+ 4.3.4.1. GENESIS_GAME_GENIE_CODE Packet
+'''
+
+GenesisGameGenieCode = _TASD_PF(
+ "GenesisGameGenieCode",
+ PacketType.GENESIS_GAME_GENIE_CODE,
+ [
+ _VDef("code", _VType.STRING_EOP, "")
+ ]
+)
+
+__all__ = [
+ GenesisGameGenieCode
+] \ No newline at end of file
diff --git a/tasd/packets/input.py b/tasd/packets/input.py
new file mode 100644
index 0000000..00d9b2b
--- /dev/null
+++ b/tasd/packets/input.py
@@ -0,0 +1,106 @@
+from tasd.packet import _TASDPacketFactory as _TASD_PF, _VariableType as _VType, _VariableDefinition as _VDef, _EnumDefinition as _EDef
+from tasd.constants import PacketType
+
+'''
+ 4.3.5.1. INPUT_CHUNK Packet
+'''
+
+InputChunk = _TASD_PF(
+ "InputChunk",
+ PacketType.INPUT_CHUNK,
+ [
+ _VDef("port", _VType.UINT_8, 1),
+ _VDef("data", _VType.BYTES_EOP, b"")
+ ]
+)
+
+'''
+ 4.3.5.2. INPUT_MOMENT Packet
+'''
+
+class INPUT_MOMENT_INDEX_TYPE():
+ FRAME = 0x01
+ CYCLE_COUNT = 0x02
+ MILLISECONDS = 0x03
+ MICROSECONDS_X10 = 0x04
+InputMoment = _TASD_PF(
+ "InputMoment",
+ PacketType.INPUT_MOMENT,
+ [
+ _VDef("port", _VType.UINT_8, 1),
+ _VDef("hold", _VType.BOOL, False),
+ _VDef("index_type", _VType.UINT_8, INPUT_MOMENT_INDEX_TYPE.FRAME),
+ _VDef("index", _VType.UINT_64, 0),
+ _VDef("inputs", _VType.BYTES_EOP, b"")
+ ],
+ [
+ _EDef("INPUT_MOMENT_INDEX_TYPE", INPUT_MOMENT_INDEX_TYPE)
+ ]
+)
+
+'''
+ 4.3.5.3. TRANSITION Packet
+'''
+
+class TRANSITION_INDEX_TYPE():
+ FRAME = 0x01
+ CYCLE_COUNT = 0x02
+ MILLISECONDS = 0x03
+ MICROSECONDS = 0x04
+ NANOSECONDS = 0x05
+ INPUT_CHUNK_INDEX = 0x06
+class TRANSITION_TYPE():
+ SOFT_RESET = 0x01
+ POWER_RESET = 0x02
+ RESTART_TASD_FILE = 0x03
+ PACKET_DERIVED = 0xFF
+Transition = _TASD_PF(
+ "Transition",
+ PacketType.TRANSITION,
+ [
+ _VDef("port", _VType.UINT_8, 1),
+ _VDef("index_type", _VType.UINT_8, TRANSITION_INDEX_TYPE.FRAME),
+ _VDef("index", _VType.UINT_64, 0),
+ _VDef("type", _VType.UINT_8, TRANSITION_TYPE.SOFT_RESET),
+ _VDef("packet", _VType.BYTES_EOP, b"")
+ ],
+ [
+ _EDef("TRANSITION_INDEX_TYPE", TRANSITION_INDEX_TYPE),
+ _EDef("TRANSITION_TYPE", TRANSITION_TYPE)
+ ]
+)
+
+'''
+ 4.3.5.4. LAG_FRAME_CHUNK Packet
+'''
+
+LagFrameChunk = _TASD_PF(
+ "LagFrameChunk",
+ PacketType.LAG_FRAME_CHUNK,
+ [
+ _VDef("frame", _VType.UINT_32, 0),
+ _VDef("count", _VType.UINT_32, 0)
+ ]
+)
+
+'''
+ 4.3.5.5. MOVIE_TRANSITION
+'''
+
+class MOVIE_TRANSITION_TYPE():
+ SOFT_RESET = 0x01
+ POWER_RESET = 0x02
+ RESTART_TASD_FILE = 0x03
+ PACKET_DERIVED = 0xFF
+MovieTransition = _TASD_PF(
+ "MovieTransition",
+ PacketType.MOVIE_TRANSITION,
+ [
+ _VDef("frame", _VType.UINT_32, 0),
+ _VDef("type", _VType.UINT_8, MOVIE_TRANSITION_TYPE.SOFT_RESET),
+ _VDef("packet", _VType.BYTES_EOP, b"")
+ ],
+ [
+ _EDef("MOVIE_TRANSITION_TYPE", MOVIE_TRANSITION_TYPE),
+ ]
+) \ No newline at end of file
diff --git a/tasd/packets/nes.py b/tasd/packets/nes.py
new file mode 100644
index 0000000..7981ca8
--- /dev/null
+++ b/tasd/packets/nes.py
@@ -0,0 +1,38 @@
+from tasd.packet import _TASDPacketFactory as _TASD_PF, _VariableType as _VType, _VariableDefinition as _VDef, _EnumDefinition as _EDef
+from tasd.constants import PacketType
+
+'''
+ 4.3.2.1. NES_LATCH_FILTER Packet
+'''
+
+NESLatchFilter = _TASD_PF(
+ "NESLatchFilter",
+ PacketType.NES_LATCH_FILTER,
+ [
+ _VDef("time", _VType.UINT_16, 0)
+ ]
+)
+
+'''
+ 4.3.2.2. NES_CLOCK_FILTER Packet
+'''
+
+NESClockFilter = _TASD_PF(
+ "NESClockFilter",
+ PacketType.NES_CLOCK_FILTER,
+ [
+ _VDef("time", _VType.UINT_8, 0)
+ ]
+)
+
+'''
+ 4.3.2.3. NES_GAME_GENIE_CODE Packet
+'''
+
+NESGameGenieCode = _TASD_PF(
+ "NESGameGenieCode",
+ PacketType.NES_GAME_GENIE_CODE,
+ [
+ _VDef("code", _VType.STRING_EOP, "")
+ ]
+) \ No newline at end of file
diff --git a/tasd/packets/snes.py b/tasd/packets/snes.py
new file mode 100644
index 0000000..52b2e62
--- /dev/null
+++ b/tasd/packets/snes.py
@@ -0,0 +1,50 @@
+from tasd.packet import _TASDPacketFactory as _TASD_PF, _VariableType as _VType, _VariableDefinition as _VDef, _EnumDefinition as _EDef
+from tasd.constants import PacketType
+
+'''
+ 4.3.3.1. SNES_LATCH_FILTER Packet
+'''
+
+SNESLatchFilter = _TASD_PF(
+ "SNESLatchFilter",
+ PacketType.SNES_LATCH_FILTER,
+ [
+ _VDef("time", _VType.UINT_16, 0)
+ ]
+)
+
+'''
+ 4.3.3.2. SNES_CLOCK_FILTER Packet
+'''
+
+SNESClockFilter = _TASD_PF(
+ "SNESClockFilter",
+ PacketType.SNES_CLOCK_FILTER,
+ [
+ _VDef("time", _VType.UINT_8, 0)
+ ]
+)
+
+'''
+ 4.3.3.3. SNES_GAME_GENIE_CODE Packet
+'''
+
+SNESGameGenieCode = _TASD_PF(
+ "SNESGameGenieCode",
+ PacketType.SNES_GAME_GENIE_CODE,
+ [
+ _VDef("code", _VType.STRING_EOP, "")
+ ]
+)
+
+'''
+ 4.3.3.4. SNES_LATCH_TRAIN Packet
+'''
+
+SNESLatchTrain = _TASD_PF(
+ "SNESLatchTrain",
+ PacketType.SNES_LATCH_TRAIN,
+ [
+ _VDef("trains", _VType.LIST_UINT_64, None)
+ ]
+) \ No newline at end of file
diff --git a/tasd/utils.py b/tasd/utils.py
new file mode 100644
index 0000000..d1393f2
--- /dev/null
+++ b/tasd/utils.py
@@ -0,0 +1,128 @@
+def readString(buffer: bytearray, index: int, length: int) -> str:
+ return buffer[index:index+length].decode("utf8")
+def writeString(string: str, buffer: bytearray, index: int) -> None:
+ rawstring = string.encode("utf8")
+ buffer[index:index+len(rawstring)] = rawstring
+def sizeString(string: str) -> int:
+ return len(string.encode("utf8"))
+
+def readBytes(buffer: bytearray, index: int, length: int) -> bytes:
+ return buffer[index:index+length]
+def writeBytes(value: bytes, buffer: bytearray, index: int) -> None:
+ buffer[index:index+len(value)] = value
+
+def readUint8(buffer: bytearray, index: int) -> int:
+ return int.from_bytes(buffer[index:index+1], byteorder="big", signed=False)
+def readUint16(buffer: bytearray, index: int) -> int:
+ return int.from_bytes(buffer[index:index+2], byteorder="big", signed=False)
+def readUint24(buffer: bytearray, index: int) -> int:
+ return int.from_bytes(buffer[index:index+3], byteorder="big", signed=False)
+def readUint32(buffer: bytearray, index: int) -> int:
+ return int.from_bytes(buffer[index:index+4], byteorder="big", signed=False)
+def readUint64(buffer: bytearray, index: int) -> int:
+ return int.from_bytes(buffer[index:index+8], byteorder="big", signed=False)
+def readUintN(buffer: bytearray, index: int, size: int) -> int:
+ if size == 1:
+ return readUint8(buffer, index)
+ if size == 2:
+ return readUint16(buffer, index)
+ if size == 3:
+ return readUint24(buffer, index)
+ if size == 4:
+ return readUint32(buffer, index)
+ raise ValueError("Unsuported UintN size", size)
+
+def writeUint8(value: int, buffer: bytearray, index: int) -> None:
+ buffer[index:index+1] = (value & (2 ** 8 - 1)).to_bytes(1, byteorder="big", signed=False)
+def writeUint16(value: int, buffer: bytearray, index: int) -> None:
+ buffer[index:index+2] = (value & (2 ** 16 - 1)).to_bytes(2, byteorder="big", signed=False)
+def writeUint24(value: int, buffer: bytearray, index: int) -> None:
+ buffer[index:index+3] = (value & (2 ** 24 - 1)).to_bytes(3, byteorder="big", signed=False)
+def writeUint32(value: int, buffer: bytearray, index: int) -> None:
+ buffer[index:index+4] = (value & (2 ** 32 - 1)).to_bytes(4, byteorder="big", signed=False)
+def writeUint64(value: int, buffer: bytearray, index: int) -> None:
+ buffer[index:index+8] = (value & (2 ** 64 - 1)).to_bytes(8, byteorder="big", signed=False)
+def writeUintN(value: int, buffer: bytearray, index: int, size: int) -> None:
+ if size == 1:
+ return writeUint8(value, buffer, index)
+ if size == 2:
+ return writeUint16(value, buffer, index)
+ if size == 3:
+ return writeUint24(value, buffer, index)
+ if size == 4:
+ return writeUint32(value, buffer, index)
+ raise ValueError("Unsuported UintN size", size)
+
+def minUint(value: int) -> int:
+ if value < 256:
+ return 1
+ if value < 65536:
+ return 2
+ if value < 16777216:
+ return 3
+ if value < 4294967296:
+ return 4
+ raise ValueError("Unsupported UInt size", value);
+
+
+def readInt8(buffer: bytearray, index: int) -> int:
+ return int.from_bytes(buffer[index:index+1], byteorder="big", signed=True)
+def readInt16(buffer: bytearray, index: int) -> int:
+ return int.from_bytes(buffer[index:index+2], byteorder="big", signed=True)
+def readInt24(buffer: bytearray, index: int) -> int:
+ return int.from_bytes(buffer[index:index+3], byteorder="big", signed=True)
+def readInt32(buffer: bytearray, index: int) -> int:
+ return int.from_bytes(buffer[index:index+4], byteorder="big", signed=True)
+def readInt64(buffer: bytearray, index: int) -> int:
+ return int.from_bytes(buffer[index:index+8], byteorder="big", signed=True)
+def readIntN(buffer: bytearray, index: int, n: int) -> int:
+ if n == 1:
+ return readInt8(buffer, index)
+ if n == 2:
+ return readInt16(buffer, index)
+ if n == 3:
+ return readInt24(buffer, index)
+ if n == 4:
+ return readInt32(buffer, index)
+ raise ValueError("Unsuported IntN size", n)
+
+def writeInt8(value: int, buffer: bytearray, index: int) -> None:
+ buffer[index:index+1] = (value & (2 ** 8 - 1)).to_bytes(1, byteorder="big", signed=True)
+def writeInt16(value: int, buffer: bytearray, index: int) -> None:
+ buffer[index:index+2] = (value & (2 ** 16 - 1)).to_bytes(2, byteorder="big", signed=True)
+def writeInt24(value: int, buffer: bytearray, index: int) -> None:
+ buffer[index:index+3] = (value & (2 ** 24 - 1)).to_bytes(3, byteorder="big", signed=True)
+def writeInt32(value: int, buffer: bytearray, index: int) -> None:
+ buffer[index:index+4] = (value & (2 ** 32 - 1)).to_bytes(4, byteorder="big", signed=True)
+def writeInt64(value: int, buffer: bytearray, index: int) -> None:
+ buffer[index:index+8] = (value & (2 ** 64 - 1)).to_bytes(8, byteorder="big", signed=True)
+def writeIntN(value: int, buffer: bytearray, index: int, size: int) -> None:
+ if size == 1:
+ return writeInt8(value, buffer, index)
+ if size == 2:
+ return writeInt16(value, buffer, index)
+ if size == 3:
+ return writeInt24(value, buffer, index)
+ if size == 4:
+ return writeInt32(value, buffer, index)
+ raise ValueError("Unsuported IntN size", size)
+
+def minInt(value: int) -> int:
+ if -0x80 <= value <= 0x7f:
+ return 1
+ if -0x8000 <= value <= 0x7fff:
+ return 2
+ if -0x800000 <= value <= 0x7fffff:
+ return 3
+ if -0x80000000 <= value <= 0x7fffffff:
+ return 4
+ raise ValueError("Unsupported Int size", value);
+
+def readBoolean(buffer: bytearray, index: int) -> bool:
+ if buffer[index] == 1:
+ return True
+ if buffer[index] == 0:
+ return False
+ raise ValueError("Invalid Boolean value")
+def writeBoolean(bool: bool, buffer: bytearray, index: int) -> None:
+ buffer[index] = (1 if bool else 0)