From f2aab487c22f4919d48896d33cd0f09ba0928395 Mon Sep 17 00:00:00 2001 From: TheMas3212 Date: Sat, 23 Sep 2023 12:21:43 +1000 Subject: Add additional Packet Types --- src/packets/general.ts | 398 +++++++++++++++++++++++++++++++++++++++++++++++++ src/packets/index.ts | 58 +++++++ src/packets/input.ts | 24 +++ src/packets/utils.ts | 37 +++++ 4 files changed, 517 insertions(+) create mode 100644 src/packets/general.ts create mode 100644 src/packets/index.ts create mode 100644 src/packets/input.ts create mode 100644 src/packets/utils.ts (limited to 'src/packets') diff --git a/src/packets/general.ts b/src/packets/general.ts new file mode 100644 index 0000000..9b80818 --- /dev/null +++ b/src/packets/general.ts @@ -0,0 +1,398 @@ +import { PACKET_TYPES } from "../constants"; +import { TASD } from "../file"; +import { encodeString, readString, readUint16, readUint32, readUint64, readUint8, writeString, writeUint16, writeUint32, writeUint64, writeUint8 } from "../utils"; +import { TASDPacket, buildBuffer } from "./utils"; + +export class ConsoleTypePacket implements TASDPacket { + constructor(public type: number, public name: string) {} + get key() { + return PACKET_TYPES.CONSOLE_TYPE; + } + get size() { + return this.name.length + 1; + } + static fromBuffer(buffer: Uint8Array) { + return new ConsoleTypePacket(buffer[0], readString(buffer, 1, buffer.length)); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = new Uint8Array(this.name.length + 1); + writeUint8(this.type, payload, 0); + writeString(this.name, payload, 1); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `ConsoleType ${this.type}, ${this.name}`; + } +} + +export class ConsoleRegionPacket implements TASDPacket { + constructor(public region: number) {} + get key() { + return PACKET_TYPES.CONSOLE_REGION; + } + get size() { + return 1; + } + static fromBuffer(buffer: Uint8Array) { + return new ConsoleRegionPacket(buffer[0]); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = new Uint8Array(1); + writeUint8(this.region, payload, 0); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `ConsoleRegion ${this.region}`; + } +} + +export class GameTitlePacket implements TASDPacket { + constructor(public title: string) {} + get key() { + return PACKET_TYPES.GAME_TITLE; + } + get size() { + return this.title.length; + } + static fromBuffer(buffer: Uint8Array) { + return new GameTitlePacket(readString(buffer, 0, buffer.length)); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = encodeString(this.title); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `GameTitle ${this.title}`; + } +} + +export class ROMNamePacket implements TASDPacket { + constructor(public name: string) {} + get key() { + return PACKET_TYPES.ROM_NAME; + } + get size() { + return this.name.length; + } + static fromBuffer(buffer: Uint8Array) { + return new ROMNamePacket(readString(buffer, 0, buffer.length)); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = encodeString(this.name); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `RomName ${this.name}`; + } +} + +export class AttributionPacket implements TASDPacket { + constructor(public type: number, public name: string) {} + get key() { + return PACKET_TYPES.ATTRIBUTION; + } + get size() { + return this.name.length + 1; + } + static fromBuffer(buffer: Uint8Array) { + return new AttributionPacket(buffer[0], readString(buffer, 1, buffer.length)); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = new Uint8Array(this.name.length + 1); + writeUint8(this.type, payload, 0); + writeString(this.name, payload, 1); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `Attribution ${this.type}, ${this.name}`; + } +} + +export class CategoryPacket implements TASDPacket { + constructor(public name: string) {} + get key() { + return PACKET_TYPES.CATEGORY; + } + get size() { + return this.name.length; + } + static fromBuffer(buffer: Uint8Array) { + return new CategoryPacket(readString(buffer, 0, buffer.length)); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = encodeString(this.name); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `Category ${this.name}`; + } +} + +export class EmulatorNamePacket implements TASDPacket { + constructor(public name: string) {} + get key() { + return PACKET_TYPES.EMULATOR_NAME; + } + get size() { + return this.name.length; + } + static fromBuffer(buffer: Uint8Array) { + return new EmulatorNamePacket(readString(buffer, 0, buffer.length)); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = encodeString(this.name); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `EmulatorName ${this.name}`; + } +} + +export class EmulatorVersionPacket implements TASDPacket { + constructor(public version: string) {} + get key() { + return PACKET_TYPES.EMULATOR_VERSION; + } + get size() { + return this.version.length; + } + static fromBuffer(buffer: Uint8Array) { + return new EmulatorVersionPacket(readString(buffer, 0, buffer.length)); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = encodeString(this.version); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `EmulatorVersion ${this.version}`; + } +} + +export class EmulatorCorePacket implements TASDPacket { + constructor(public core: string) {} + get key() { + return PACKET_TYPES.EMULATOR_CORE; + } + get size() { + return this.core.length; + } + static fromBuffer(buffer: Uint8Array) { + return new EmulatorCorePacket(readString(buffer, 0, buffer.length)); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = encodeString(this.core); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `EmulatorCore ${this.core}`; + } +} + +export class TASLastModifedPacket implements TASDPacket { + constructor(public timestamp: bigint) {} + get key() { + return PACKET_TYPES.TAS_LAST_MODIFIED; + } + get size() { + return 8; + } + static fromBuffer(buffer: Uint8Array) { + return new TASLastModifedPacket(readUint64(buffer, 0)); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = new Uint8Array(8); + writeUint64(this.timestamp, payload, 0); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `TASLastModified ${new Date(Number(this.timestamp * 1000n)).toUTCString()}`; + } +} + +export class DumpCreatedPacket implements TASDPacket { + constructor(public timestamp: bigint) {} + get key() { + return PACKET_TYPES.DUMP_CREATED; + } + get size() { + return 8; + } + static fromBuffer(buffer: Uint8Array) { + return new DumpCreatedPacket(readUint64(buffer, 0)); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = new Uint8Array(8); + writeUint64(this.timestamp, payload, 0); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `DumpCreated ${new Date(Number(this.timestamp * 1000n)).toUTCString()}`; + } +} + +export class DumpLastModifiedPacket implements TASDPacket { + constructor(public timestamp: bigint) {} + get key() { + return PACKET_TYPES.DUMP_LAST_MODIFIED; + } + get size() { + return 8; + } + static fromBuffer(buffer: Uint8Array) { + return new DumpLastModifiedPacket(readUint64(buffer, 0)); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = new Uint8Array(8); + writeUint64(this.timestamp, payload, 0); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `DumpLastModified ${new Date(Number(this.timestamp * 1000n)).toUTCString()}`; + } +} + +export class TotalFramesPacket implements TASDPacket { + constructor(public frames: number) {} + get key() { + return PACKET_TYPES.TOTAL_FRAMES; + } + get size() { + return 4; + } + static fromBuffer(buffer: Uint8Array) { + return new TotalFramesPacket(readUint32(buffer, 0)); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = new Uint8Array(4); + writeUint32(this.frames, payload, 0); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `TotalFrames ${this.frames}`; + } +} + +export class RerecordsPacket implements TASDPacket { + constructor(public rerecords: number) {} + get key() { + return PACKET_TYPES.RERECORDS; + } + get size() { + return 4; + } + static fromBuffer(buffer: Uint8Array) { + return new RerecordsPacket(readUint32(buffer, 0)); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = new Uint8Array(4); + writeUint32(this.rerecords, payload, 0); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `Rerecords ${this.rerecords}`; + } +} + +export class SourceLinkPacket implements TASDPacket { + constructor(public link: string) {} + get key() { + return PACKET_TYPES.SOURCE_LINK; + } + get size() { + return this.link.length; + } + static fromBuffer(buffer: Uint8Array) { + return new SourceLinkPacket(readString(buffer, 0, buffer.length)); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = encodeString(this.link); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `SourceLink ${this.link}`; + } +} + +export class BlankFramesPacket implements TASDPacket { + constructor(public frames: number) {} + get key() { + return PACKET_TYPES.BLANK_FRAMES; + } + get size() { + return 2; + } + static fromBuffer(buffer: Uint8Array) { + return new BlankFramesPacket(readUint16(buffer, 0)); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = new Uint8Array(2); + writeUint16(this.frames, payload, 0); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `BlankFrames ${this.frames}`; + } +} + +export class VerifiedPacket implements TASDPacket { + constructor(public verified: boolean) {} + get key() { + return PACKET_TYPES.VERIFIED; + } + get size() { + return 1; + } + static fromBuffer(buffer: Uint8Array) { + return new VerifiedPacket(readUint8(buffer, 0) !== 0); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = new Uint8Array(2); + writeUint8(this.verified ? 1 : 0, payload, 0); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `Verified ${this.verified}`; + } +} + +export class MovieLicensePacket implements TASDPacket { + constructor(public link: string) {} + get key() { + return PACKET_TYPES.MOVIE_LICENSE; + } + get size() { + return this.link.length; + } + static fromBuffer(buffer: Uint8Array) { + return new MovieLicensePacket(readString(buffer, 0, buffer.length)); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = encodeString(this.link); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `SourceLink ${this.link}`; + } +} + +export class PortControllerPacket implements TASDPacket { + constructor(public port: number, public type: number) {} + get key() { + return PACKET_TYPES.PORT_CONTROLLER; + } + get size() { + return 3; + } + static fromBuffer(buffer: Uint8Array) { + return new PortControllerPacket(buffer[0], readUint16(buffer, 1)); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = new Uint8Array(3); + writeUint8(this.port, payload, 0); + writeUint16(this.type, payload, 1); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `PortController ${this.port}, 0x${this.type.toString(16).padStart(4, '0')}`; + } +} \ No newline at end of file diff --git a/src/packets/index.ts b/src/packets/index.ts new file mode 100644 index 0000000..0bf2195 --- /dev/null +++ b/src/packets/index.ts @@ -0,0 +1,58 @@ +import { PACKET_TYPES } from "../constants"; +import { AttributionPacket, BlankFramesPacket, CategoryPacket, ConsoleRegionPacket, ConsoleTypePacket, DumpCreatedPacket, DumpLastModifiedPacket, EmulatorCorePacket, EmulatorNamePacket, EmulatorVersionPacket, GameTitlePacket, MovieLicensePacket, PortControllerPacket, ROMNamePacket, RerecordsPacket, SourceLinkPacket, TASLastModifedPacket, TotalFramesPacket, VerifiedPacket } from "./general"; +import { InputChunkPacket } from "./input"; +import { TASDPacket, UnknownPacket } from "./utils"; +export * as General from './general'; +export * as Input from './input'; + +export function parse(type: number, buffer: Uint8Array): TASDPacket { + switch (type) { + // General + case PACKET_TYPES.CONSOLE_TYPE: return ConsoleTypePacket.fromBuffer(buffer); + case PACKET_TYPES.CONSOLE_REGION: return ConsoleRegionPacket.fromBuffer(buffer); + case PACKET_TYPES.GAME_TITLE: return GameTitlePacket.fromBuffer(buffer); + case PACKET_TYPES.ROM_NAME: return ROMNamePacket.fromBuffer(buffer); + case PACKET_TYPES.ATTRIBUTION: return AttributionPacket.fromBuffer(buffer); + case PACKET_TYPES.CATEGORY: return CategoryPacket.fromBuffer(buffer); + case PACKET_TYPES.EMULATOR_NAME: return EmulatorNamePacket.fromBuffer(buffer); + case PACKET_TYPES.EMULATOR_VERSION: return EmulatorVersionPacket.fromBuffer(buffer); + case PACKET_TYPES.EMULATOR_CORE: return EmulatorCorePacket.fromBuffer(buffer); + case PACKET_TYPES.TAS_LAST_MODIFIED: return TASLastModifedPacket.fromBuffer(buffer); + case PACKET_TYPES.DUMP_CREATED: return DumpCreatedPacket.fromBuffer(buffer); + case PACKET_TYPES.DUMP_LAST_MODIFIED: return DumpLastModifiedPacket.fromBuffer(buffer); + case PACKET_TYPES.TOTAL_FRAMES: return TotalFramesPacket.fromBuffer(buffer); + case PACKET_TYPES.RERECORDS: return RerecordsPacket.fromBuffer(buffer); + case PACKET_TYPES.SOURCE_LINK: return SourceLinkPacket.fromBuffer(buffer); + case PACKET_TYPES.BLANK_FRAMES: return BlankFramesPacket.fromBuffer(buffer); + case PACKET_TYPES.VERIFIED: return VerifiedPacket.fromBuffer(buffer); + // case PACKET_TYPES.MEMORY_INIT: return XXXXX.fromBuffer(buffer); + // case PACKET_TYPES.GAME_IDENTIFIER: return XXXXX.fromBuffer(buffer); + case PACKET_TYPES.MOVIE_LICENSE: return MovieLicensePacket.fromBuffer(buffer); + // case PACKET_TYPES.MOVIE_FILE: return XXXXX.fromBuffer(buffer); + case PACKET_TYPES.PORT_CONTROLLER: return PortControllerPacket.fromBuffer(buffer); + // NES + // case PACKET_TYPES.NES_LATCH_FILTER: return XXXXX.fromBuffer(buffer); + // case PACKET_TYPES.NES_CLOCK_FILTER: return XXXXX.fromBuffer(buffer); + // case PACKET_TYPES.NES_OVERREAD: return XXXXX.fromBuffer(buffer); + // case PACKET_TYPES.NES_GAME_GENIE_CODE: return XXXXX.fromBuffer(buffer); + // SNES + // case PACKET_TYPES.SNES_CLOCK_FILTER: return XXXXX.fromBuffer(buffer); + // case PACKET_TYPES.SNES_OVERREAD: return XXXXX.fromBuffer(buffer); + // case PACKET_TYPES.SNES_GAME_GENIE_CODE: return XXXXX.fromBuffer(buffer); + // case PACKET_TYPES.SNES_LATCH_TRAIN: return XXXXX.fromBuffer(buffer); + // Genesis + // case PACKET_TYPES.GENESIS_GAME_GENIE_CODE: return XXXXX.fromBuffer(buffer); + // Input + case PACKET_TYPES.INPUT_CHUNK: return InputChunkPacket.fromBuffer(buffer); + // case PACKET_TYPES.INPUT_MOMENT: return XXXXX.fromBuffer(buffer); + // case PACKET_TYPES.TRANSITION: return XXXXX.fromBuffer(buffer); + // case PACKET_TYPES.LAG_FRAME_CHUNK: return XXXXX.fromBuffer(buffer); + // case PACKET_TYPES.MOVIE_TRANSITION: return XXXXX.fromBuffer(buffer); + // Extra + // case PACKET_TYPES.COMMENT: return XXXXX.fromBuffer(buffer); + // case PACKET_TYPES.EXPERIMENTAL: return XXXXX.fromBuffer(buffer); + // case PACKET_TYPES.UNSPECIFIED: return XXXXX.fromBuffer(buffer); + // Unknown + default: return new UnknownPacket(type, buffer); + } +} \ No newline at end of file diff --git a/src/packets/input.ts b/src/packets/input.ts new file mode 100644 index 0000000..b084ebe --- /dev/null +++ b/src/packets/input.ts @@ -0,0 +1,24 @@ +import { PACKET_TYPES } from "../constants"; +import { TASDPacket, buildBuffer } from "./utils"; + +export class InputChunkPacket implements TASDPacket { + constructor(public port: number, public inputs: Uint8Array) {} + get key() { + return PACKET_TYPES.INPUT_CHUNK; + } + get size() { + return this.inputs.length + 1; + } + static fromBuffer(buffer: Uint8Array) { + return new InputChunkPacket(buffer[0], buffer.subarray(1)); + } + toBuffer(g_keylen: number): Uint8Array { + const payload = new Uint8Array(this.inputs.length + 1); + payload[0] = this.port; + payload.set(this.inputs, 1); + return buildBuffer(g_keylen, this.key, payload); + } + toString() { + return `InputChunk ${this.port}, ${this.size}`; + } +} \ No newline at end of file diff --git a/src/packets/utils.ts b/src/packets/utils.ts new file mode 100644 index 0000000..3a5befc --- /dev/null +++ b/src/packets/utils.ts @@ -0,0 +1,37 @@ +import { packetName } from "../constants"; +import { minUInt, writeUintN, writeUint8 } from "../utils"; + +export interface TASDPacket { + key: number; + size: number; + toBuffer(g_keylen: number): Uint8Array; + toString(): string; +} + +export function buildBuffer(g_keylen: number, key: number, payload: Uint8Array) { + const plen = payload.length; + const pexp = minUInt(plen); + const buffer = new Uint8Array(g_keylen + 1 + pexp + plen); + let index = 0; + writeUintN(key, buffer, index, g_keylen); + index += g_keylen; + writeUint8(pexp, buffer, index); + index += 1; + writeUintN(plen, buffer, index, pexp); + index += pexp; + buffer.set(payload, index); + return buffer; +} + +export class UnknownPacket implements TASDPacket { + constructor(public key: number, public payload: Uint8Array) {} + get size() { + return this.payload.length; + } + toBuffer(g_keylen: number) { + return buildBuffer(g_keylen, this.key, this.payload); + } + toString() { + return `UnknownPacket ${packetName(this.key)}, ${this.size}`; + } +} \ No newline at end of file -- cgit v1.2.3