diff --git a/src/lib/serial/chord.spec.ts b/src/lib/serial/chord.spec.ts index a951b7c3..952d5303 100644 --- a/src/lib/serial/chord.spec.ts +++ b/src/lib/serial/chord.spec.ts @@ -1,8 +1,56 @@ import {describe, it, expect} from "vitest" -import {serializeActions} from "$lib/serial/chord" +import { + chordAsCommandCompatible, + chordFromCommandCompatible, + chordsFromFile, + chordsToFile, + deserializeActions, + serializeActions, +} from "./chord" +import type {Chord} from "./chord" describe("chords", function () { - it("should serialize actions", function () { - expect(serializeActions([67, 2])).toBe(0xcc200000000000000000000000000n) + describe("actions", function () { + it("should serialize actions", function () { + expect(serializeActions([32, 51]).toString(16)).toEqual(0xcc200000000000000000000000000n.toString(16)) + }) + + it("should deserialize actions", function () { + expect(deserializeActions(0xcc200000000000000000000000000n)).toEqual([32, 51]) + }) + + for (let i = 0; i < 12; i++) { + it(`should serialize back-forth ${i} actions`, function () { + const actions = Array.from({length: i}).map((_, i) => i + 1) + expect(deserializeActions(serializeActions(actions))).toEqual(actions) + }) + } + }) + + describe("commands", function () { + it("should convert to a command", function () { + expect(chordAsCommandCompatible({actions: [32, 51], phrase: [0x01, 0x68, 0x72, 0xd4, 0x65]})).toEqual( + "000CC200000000000000000000000000 016872D465", + ) + }) + + it("should parse a command", function () { + expect(chordFromCommandCompatible("000CC200000000000000000000000000 016872D465")).toEqual({ + actions: [32, 51], + phrase: [0x01, 0x68, 0x72, 0xd4, 0x65], + }) + }) + }) + + describe("chl file format", function () { + const fileData: Chord[] = [ + {phrase: [1, 2, 3, 4], actions: [5, 6, 7, 8, 9]}, + {phrase: [10, 11], actions: [12, 13, 14, 15]}, + {phrase: [16], actions: [17]}, + ] + + it("should should convert back-forth a file", function () { + expect(chordsFromFile(chordsToFile(fileData))).toEqual(fileData) + }) }) }) diff --git a/src/lib/serial/chord.ts b/src/lib/serial/chord.ts index 1cce27df..81dce2c0 100644 --- a/src/lib/serial/chord.ts +++ b/src/lib/serial/chord.ts @@ -9,9 +9,9 @@ export interface Chord { * @example "000CC200000000000000000000000000 7468726565" */ export function chordAsCommandCompatible(chord: Chord): string { - return `${serializeActions(chord.actions).toString(16).padStart(32)} ${chord.phrase.map(it => - it.toString(16), - )}` + return `${serializeActions(chord.actions).toString(16).padStart(32, "0")} ${chord.phrase + .map(it => it.toString(16).padStart(2, "0")) + .join("")}`.toUpperCase() } /** @@ -36,8 +36,8 @@ export function chordFromCommandCompatible(command: string): Chord { */ export function serializeActions(actions: number[]): bigint { let native = 0n - for (let i = 0; i < actions.length; i++) { - native |= BigInt(actions[i] & 0x3ff) << BigInt((11 - i) * 10) + for (let i = 1; i <= actions.length; i++) { + native |= BigInt(actions[actions.length - i] & 0x3ff) << BigInt((12 - i) * 10) } return native } @@ -91,7 +91,7 @@ export function chordsToFile(chords: Chord[]): ArrayBuffer { const view = new DataView(buffer) let byteOffset = 0 - for (const byte of CHL_MAGIC) { + for (const byte of CHL_MAGIC.split("")) { view.setUint8(byteOffset++, byte.codePointAt(0)!) } view.setUint8(byteOffset++, CHL_VERSION) @@ -99,7 +99,7 @@ export function chordsToFile(chords: Chord[]): ArrayBuffer { byteOffset += 4 for (const chord of chords) { const actions = serializeActions(chord.actions) - view.setBigUint64(byteOffset, actions << 64n, true) + view.setBigUint64(byteOffset, actions >> 64n, true) byteOffset += 8 view.setBigUint64(byteOffset, actions & 0xffff_ffff_ffff_ffffn, true) byteOffset += 8 @@ -121,17 +121,18 @@ export function chordsFromFile(buffer: ArrayBuffer): Chord[] { const view = new DataView(buffer) let byteOffset = 0 - let magic = "" + const magic = [] for (let i = 0; i < CHL_MAGIC.length; i++) { - magic += view.getUint8(byteOffset++) + magic.push(view.getUint8(byteOffset++)) } - if (magic !== CHL_MAGIC) throw new Error("Not a .chl file") + const magicString = String.fromCodePoint(...magic) + if (magicString !== CHL_MAGIC) throw new Error(`Not a .chl file [magic ${magicString}]`) if (view.getUint8(byteOffset++) !== CHL_VERSION) throw Error("Invalid .chl [version]") const chords: Chord[] = Array.from({length: view.getUint32(byteOffset, true)}) byteOffset += 4 for (let i = 0; i < chords.length; i++) { - let actions = view.getBigUint64(byteOffset, true) >> 64n + let actions = view.getBigUint64(byteOffset, true) << 64n byteOffset += 8 actions |= view.getBigUint64(byteOffset, true) byteOffset += 8