diff options
Diffstat (limited to 'tasd')
| -rw-r--r-- | tasd/__init__.py | 4 | ||||
| -rw-r--r-- | tasd/constants.py | 56 | ||||
| -rw-r--r-- | tasd/file.py | 57 | ||||
| -rw-r--r-- | tasd/packet.py | 304 | ||||
| -rw-r--r-- | tasd/packets/__init__.py | 6 | ||||
| -rw-r--r-- | tasd/packets/extra.py | 38 | ||||
| -rw-r--r-- | tasd/packets/general.py | 406 | ||||
| -rw-r--r-- | tasd/packets/genesis.py | 18 | ||||
| -rw-r--r-- | tasd/packets/input.py | 106 | ||||
| -rw-r--r-- | tasd/packets/nes.py | 38 | ||||
| -rw-r--r-- | tasd/packets/snes.py | 50 | ||||
| -rw-r--r-- | tasd/utils.py | 128 |
12 files changed, 1211 insertions, 0 deletions
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) |
