Files
psn-js/src/decoder.ts
T
Joel Wetzell 68db557090 linting
2026-07-13 14:15:47 -05:00

133 lines
4.1 KiB
TypeScript

import { Decoders } from './index.js';
import {
type DataPacketChunk,
type InfoPacketChunk,
type Tracker,
TrackerFromData,
TrackerFromInfo,
} from './models/index.js';
export class Decoder {
infoPacketFrames: { [key: number]: InfoPacketChunk[] } = {};
dataPacketFrames: { [key: number]: DataPacketChunk[] } = {};
trackers: { [key: string]: Tracker } = {};
systemName: string = '';
updateInfo(framePackets: InfoPacketChunk[]) {
framePackets.forEach((packet) => {
if (packet.data.systemName?.data) {
this.systemName = packet.data.systemName?.data.systemName;
}
if (packet.data.trackerList?.data) {
packet.data.trackerList.data.trackers.forEach((infoTrackerChunk) => {
if (!this.trackers[infoTrackerChunk.chunk.header.id]) {
const tracker = TrackerFromInfo(infoTrackerChunk);
if (tracker) {
this.trackers[infoTrackerChunk.chunk.header.id] = tracker;
}
}
const tracker = this.trackers[infoTrackerChunk.chunk.header.id];
tracker.updateInfo(infoTrackerChunk);
});
}
});
}
updateData(framePackets: DataPacketChunk[]) {
framePackets.forEach((packet) => {
if (packet.data.trackerList) {
packet.data.trackerList?.data.trackers.forEach((dataTrackerChunk) => {
if (!this.trackers[dataTrackerChunk.chunk.header.id]) {
const tracker = TrackerFromData(dataTrackerChunk);
if (tracker) {
this.trackers[dataTrackerChunk.chunk.header.id] = tracker;
}
}
const tracker = this.trackers[dataTrackerChunk.chunk.header.id];
tracker.updateData(dataTrackerChunk);
});
}
});
}
// TODO(jwetzell): add invalid frame id decoding. Scenario where a frame id is reused before one is complete
decode(packetBuf: Uint8Array) {
const packet = Decoders.Chunk(packetBuf);
if (packet === undefined) {
return packet;
}
if (packet.header.id === 0x6756) {
const infoPacket = Decoders.InfoPacketChunk(packetBuf);
if (infoPacket?.chunk.header.hasSubchunks) {
const currentInfoPacketHeader = infoPacket.data.packetHeader;
if (!currentInfoPacketHeader) {
// NOTE(jwetzell): not sure that info packets without a header subchunk are valid?
throw new Error('malformed packet');
}
const systemSubChunk = infoPacket.data.systemName;
if (!systemSubChunk) {
// NOTE(jwetzell): not sure that info packets without a system subchunk are valid?
throw new Error('malformed packet');
}
if (currentInfoPacketHeader.data.frameId) {
if (
this.infoPacketFrames[currentInfoPacketHeader.data.frameId] ===
undefined
) {
this.infoPacketFrames[currentInfoPacketHeader.data.frameId] = [];
}
this.infoPacketFrames[currentInfoPacketHeader.data.frameId].push(
infoPacket
);
if (
this.infoPacketFrames[currentInfoPacketHeader.data.frameId]
.length === currentInfoPacketHeader.data.framePacketCount
) {
this.updateInfo(
this.infoPacketFrames[currentInfoPacketHeader.data.frameId]
);
delete this.infoPacketFrames[currentInfoPacketHeader.data.frameId];
}
}
}
} else if (packet.header.id === 0x6755) {
const dataPacket = Decoders.DataPacketChunk(packetBuf);
if (dataPacket.chunk.header.hasSubchunks) {
const currentDataPacketHeader = dataPacket.data.packetHeader;
if (!currentDataPacketHeader) {
// NOTE(jwetzell): not sure that info packets without a header subchunk are valid?
throw new Error('malformed packet');
}
if (currentDataPacketHeader.data.frameId) {
if (
this.dataPacketFrames[currentDataPacketHeader.data.frameId] ===
undefined
) {
this.dataPacketFrames[currentDataPacketHeader.data.frameId] = [];
}
this.dataPacketFrames[currentDataPacketHeader.data.frameId].push(
dataPacket
);
if (
this.dataPacketFrames[currentDataPacketHeader.data.frameId]
.length === currentDataPacketHeader.data.framePacketCount
) {
this.updateData(
this.dataPacketFrames[currentDataPacketHeader.data.frameId]
);
delete this.dataPacketFrames[currentDataPacketHeader.data.frameId];
}
}
}
}
}
}