summaryrefslogtreecommitdiff
path: root/src/packets
diff options
context:
space:
mode:
Diffstat (limited to 'src/packets')
-rw-r--r--src/packets/general.ts398
-rw-r--r--src/packets/index.ts58
-rw-r--r--src/packets/input.ts24
-rw-r--r--src/packets/utils.ts37
4 files changed, 517 insertions, 0 deletions
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