summaryrefslogtreecommitdiff
path: root/src/file.ts
blob: 139aa765fbc263b03749d99ec4df80324ce889f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import { PACKET_TYPES } from "./constants";
import { parse } from "./packets";
import { DumpLastModifiedPacket, PortControllerPacket } from "./packets/general";
import { InputChunkPacket } from "./packets/input";
import { TASDPacket } from "./packets/utils";
import { readUint16, readUint8, readUintN, writeUint16, writeUint8, concatUint8Array } from "./utils";

export class TASD {
  static MAGIC = new Uint8Array([0x54, 0x41, 0x53, 0x44]);
  public version: number;
  public g_keylen: number;
  public packets: TASDPacket[];
  private _isDirty: boolean = false;
  constructor(_version: number, _keylen: number) {
    this.version = _version;
    this.g_keylen = _keylen;
    this.packets = [];
  }
  get dirty() {
    return this._isDirty;
  }
  markDirty() {
    this._isDirty = true;
  }
  toBuffer() {
    const header = new Uint8Array(7);
    header.set(TASD.MAGIC, 0);
    writeUint16(this.version, header, 4);
    writeUint8(this.g_keylen, header, 6);
    const packets = [ header ];
    if (this._isDirty) {
      this.packets = this.packets.filter((packet) => packet.key !== PACKET_TYPES.DUMP_LAST_MODIFIED);
      this.packets.push(new DumpLastModifiedPacket(BigInt(Math.floor(Date.now() / 1000))));
    }
    for (const packet of this.packets) {
      packets.push(packet.toBuffer(this.g_keylen));
    }
    return concatUint8Array(packets);
  }
  pprint() {
    console.log("VERSION", this.version);
    console.log("G_KEYLEN", this.g_keylen);
    for (const packet of this.packets) {
      console.log(packet.toString());
    }
  }
  compactInputs() {
    if (this.packets.some(packet => packet.key === PACKET_TYPES.INPUT_MOMENT)) return;
    const otherPackets = this.packets.filter(packet => packet.key !== PACKET_TYPES.INPUT_CHUNK);
    const inputPackets = this.packets.filter(packet => packet.key === PACKET_TYPES.INPUT_CHUNK);
    const ports: Uint8Array[][] = [];
    for (const packet of otherPackets.filter(packet => packet.key === PACKET_TYPES.PORT_CONTROLLER)) {
      ports[(packet as PortControllerPacket).port] = [];
    }
    for (const packet of inputPackets as InputChunkPacket[]) {
      ports[packet.port].push(packet.inputs);
    }
    for (let port = 1; port < 256; port++) {
      if (ports[port]) {
        otherPackets.push(new InputChunkPacket(port, concatUint8Array(ports[port])));
      }
    }
    this.packets = otherPackets;
    this.markDirty();
  }
  static parseFile(buffer: Uint8Array) {
    if (buffer.length < 7) throw new Error("Input Too Short");
    for (let i = 0; i < TASD.MAGIC.length; i++) {
      if (buffer[i] !== TASD.MAGIC[i]) throw new Error("Invalid Magic Bytes");
    }
    const version = readUint16(buffer, 4);
    if (version !== 1) throw new Error("Unsupported Version");
    const g_keylen = readUint8(buffer, 6);
    if (g_keylen !== 2) throw new Error("G_KEYLEN not equal to 2, unsupported");
    const file = new TASD(version, g_keylen);
    let index = 7;
    while (index < buffer.length) {
      const p_key = readUintN(buffer, index, g_keylen);
      index += g_keylen;
      const p_pexp = readUint8(buffer, index);
      index += 1;
      const p_plen = readUintN(buffer, index, p_pexp);
      index += p_pexp;
      const p_payload = buffer.subarray(index, index+p_plen);
      index += p_plen;
      file.packets.push(parse(p_key, p_payload));
    }
    return file;
  }
}