You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

gbp-decode

Package Overview
Dependencies
Maintainers
1
Versions
31
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

gbp-decode - npm Package Compare versions

Comparing version
1.3.12
to
1.3.13
+2
-2
package.json
{
"name": "gbp-decode",
"version": "1.3.12",
"version": "1.3.13",
"description": "A set of functions to decode gameboy printer code",

@@ -24,3 +24,3 @@ "scripts": {

"type": "git",
"url": "https://github.com/HerrZatacke/gbp-decode.git"
"url": "git+https://github.com/HerrZatacke/gbp-decode.git"
},

@@ -27,0 +27,0 @@ "keywords": [

'use strict';
const completeFrame = require('./shared/gbp-decode.ccdfabeb.cjs');
const commandName = (command) => {
switch (command) {
case completeFrame.COMMAND.INIT:
return "INIT";
case completeFrame.COMMAND.PRINT:
return "PRINT";
case completeFrame.COMMAND.DATA:
return "DATA";
case completeFrame.COMMAND.STATUS:
return "STATUS";
default:
return "-";
}
};
const logPackets = (packets) => {
console.log(
packets.map(({ command, data, hasCompression, dataLength }) => ({
command: commandName(command),
hasCompression: hasCompression ? "yes" : "no",
dataLength,
data: data.margins ? `marginUpper: ${data.marginUpper} - marginLower: ${data.marginLower}` : data.slice(0, 20).join(",")
}))
);
return packets;
};
const main = {
parsePackets: completeFrame.parsePackets,
parseReducedPackets: completeFrame.parseReducedPackets,
inflateTransferPackages: completeFrame.inflateTransferPackages,
getImageDataStream: completeFrame.getImageDataStream,
decompressDataStream: completeFrame.decompressDataStream,
decodePrintCommands: completeFrame.decodePrintCommands,
harmonizePalettes: completeFrame.harmonizePalettes,
transformToClassic: completeFrame.transformToClassic,
unpack: completeFrame.unpack,
parsePaletteByte: completeFrame.parsePaletteByte,
harmonizePalette: completeFrame.harmonizePalette,
completeFrame: completeFrame.completeFrame,
logPackets
};
module.exports = main;
declare enum COMMAND {
INIT = 1,
PRINT = 2,
DATA = 4,
STATUS = 15,
TRANSFER = 16
}
type PaletteData = [number, number, number, number];
type HarmonisedPalette = [number, number];
interface PrintData {
margins: number;
marginUpper: number;
marginLower: number;
palette: number;
paletteData: PaletteData;
}
interface Packet {
command: null | COMMAND;
buffer: number[];
data: number[];
hasCompression: number;
dataLength: number;
checksum?: number;
}
type ParsedPacket = Omit<Packet, 'buffer'>;
interface PrintPacket extends Omit<Packet, 'data'> {
command: COMMAND.PRINT;
data: PrintData;
}
interface TransformedImage {
transformed: string[];
palette?: PaletteData;
}
declare const _default: {
parsePackets: (bytes: number[]) => ParsedPacket[];
parseReducedPackets: (bytes: number[]) => ParsedPacket[];
inflateTransferPackages: (packets: ParsedPacket[]) => ParsedPacket[];
getImageDataStream: (packets: ParsedPacket[]) => ParsedPacket[];
decompressDataStream: (packets: ParsedPacket[]) => ParsedPacket[];
decodePrintCommands: (packets: ParsedPacket[]) => (ParsedPacket | PrintPacket)[];
harmonizePalettes: (packets: (ParsedPacket | PrintPacket)[]) => (ParsedPacket | PrintPacket)[];
transformToClassic: (packets: (ParsedPacket | PrintPacket)[]) => string[][];
unpack: (data: number[]) => number[];
parsePaletteByte: (paletteRaw: number) => PaletteData;
harmonizePalette: (charA: number, charB: number, paletteDefinition?: PaletteData) => HarmonisedPalette;
completeFrame: (images: string[][]) => string[][];
logPackets: (packets: ParsedPacket[]) => ParsedPacket[];
};
export { type HarmonisedPalette, type Packet, type PaletteData, type ParsedPacket, type PrintData, type PrintPacket, type TransformedImage, _default as default };
declare enum COMMAND {
INIT = 1,
PRINT = 2,
DATA = 4,
STATUS = 15,
TRANSFER = 16
}
type PaletteData = [number, number, number, number];
type HarmonisedPalette = [number, number];
interface PrintData {
margins: number;
marginUpper: number;
marginLower: number;
palette: number;
paletteData: PaletteData;
}
interface Packet {
command: null | COMMAND;
buffer: number[];
data: number[];
hasCompression: number;
dataLength: number;
checksum?: number;
}
type ParsedPacket = Omit<Packet, 'buffer'>;
interface PrintPacket extends Omit<Packet, 'data'> {
command: COMMAND.PRINT;
data: PrintData;
}
interface TransformedImage {
transformed: string[];
palette?: PaletteData;
}
declare const _default: {
parsePackets: (bytes: number[]) => ParsedPacket[];
parseReducedPackets: (bytes: number[]) => ParsedPacket[];
inflateTransferPackages: (packets: ParsedPacket[]) => ParsedPacket[];
getImageDataStream: (packets: ParsedPacket[]) => ParsedPacket[];
decompressDataStream: (packets: ParsedPacket[]) => ParsedPacket[];
decodePrintCommands: (packets: ParsedPacket[]) => (ParsedPacket | PrintPacket)[];
harmonizePalettes: (packets: (ParsedPacket | PrintPacket)[]) => (ParsedPacket | PrintPacket)[];
transformToClassic: (packets: (ParsedPacket | PrintPacket)[]) => string[][];
unpack: (data: number[]) => number[];
parsePaletteByte: (paletteRaw: number) => PaletteData;
harmonizePalette: (charA: number, charB: number, paletteDefinition?: PaletteData) => HarmonisedPalette;
completeFrame: (images: string[][]) => string[][];
logPackets: (packets: ParsedPacket[]) => ParsedPacket[];
};
export { type HarmonisedPalette, type Packet, type PaletteData, type ParsedPacket, type PrintData, type PrintPacket, type TransformedImage, _default as default };
declare enum COMMAND {
INIT = 1,
PRINT = 2,
DATA = 4,
STATUS = 15,
TRANSFER = 16
}
type PaletteData = [number, number, number, number];
type HarmonisedPalette = [number, number];
interface PrintData {
margins: number;
marginUpper: number;
marginLower: number;
palette: number;
paletteData: PaletteData;
}
interface Packet {
command: null | COMMAND;
buffer: number[];
data: number[];
hasCompression: number;
dataLength: number;
checksum?: number;
}
type ParsedPacket = Omit<Packet, 'buffer'>;
interface PrintPacket extends Omit<Packet, 'data'> {
command: COMMAND.PRINT;
data: PrintData;
}
interface TransformedImage {
transformed: string[];
palette?: PaletteData;
}
declare const _default: {
parsePackets: (bytes: number[]) => ParsedPacket[];
parseReducedPackets: (bytes: number[]) => ParsedPacket[];
inflateTransferPackages: (packets: ParsedPacket[]) => ParsedPacket[];
getImageDataStream: (packets: ParsedPacket[]) => ParsedPacket[];
decompressDataStream: (packets: ParsedPacket[]) => ParsedPacket[];
decodePrintCommands: (packets: ParsedPacket[]) => (ParsedPacket | PrintPacket)[];
harmonizePalettes: (packets: (ParsedPacket | PrintPacket)[]) => (ParsedPacket | PrintPacket)[];
transformToClassic: (packets: (ParsedPacket | PrintPacket)[]) => string[][];
unpack: (data: number[]) => number[];
parsePaletteByte: (paletteRaw: number) => PaletteData;
harmonizePalette: (charA: number, charB: number, paletteDefinition?: PaletteData) => HarmonisedPalette;
completeFrame: (images: string[][]) => string[][];
logPackets: (packets: ParsedPacket[]) => ParsedPacket[];
};
export { type HarmonisedPalette, type Packet, type PaletteData, type ParsedPacket, type PrintData, type PrintPacket, type TransformedImage, _default as default };
import { C as COMMAND, p as parsePackets, b as parseReducedPackets, i as inflateTransferPackages, g as getImageDataStream, d as decompressDataStream, a as decodePrintCommands, h as harmonizePalettes, t as transformToClassic, u as unpack, e as parsePaletteByte, f as harmonizePalette, c as completeFrame } from './shared/gbp-decode.1cf0b17c.mjs';
const commandName = (command) => {
switch (command) {
case COMMAND.INIT:
return "INIT";
case COMMAND.PRINT:
return "PRINT";
case COMMAND.DATA:
return "DATA";
case COMMAND.STATUS:
return "STATUS";
default:
return "-";
}
};
const logPackets = (packets) => {
console.log(
packets.map(({ command, data, hasCompression, dataLength }) => ({
command: commandName(command),
hasCompression: hasCompression ? "yes" : "no",
dataLength,
data: data.margins ? `marginUpper: ${data.marginUpper} - marginLower: ${data.marginLower}` : data.slice(0, 20).join(",")
}))
);
return packets;
};
const main = {
parsePackets,
parseReducedPackets,
inflateTransferPackages,
getImageDataStream,
decompressDataStream,
decodePrintCommands,
harmonizePalettes,
transformToClassic,
unpack,
parsePaletteByte,
harmonizePalette,
completeFrame,
logPackets
};
export { main as default };
var COMMAND = /* @__PURE__ */ ((COMMAND2) => {
COMMAND2[COMMAND2["INIT"] = 1] = "INIT";
COMMAND2[COMMAND2["PRINT"] = 2] = "PRINT";
COMMAND2[COMMAND2["DATA"] = 4] = "DATA";
COMMAND2[COMMAND2["STATUS"] = 15] = "STATUS";
COMMAND2[COMMAND2["TRANSFER"] = 16] = "TRANSFER";
return COMMAND2;
})(COMMAND || {});
var STATE = /* @__PURE__ */ ((STATE2) => {
STATE2[STATE2["AWAIT_MAGIC_BYTES"] = 0] = "AWAIT_MAGIC_BYTES";
STATE2[STATE2["AWAIT_COMMAND"] = 1] = "AWAIT_COMMAND";
STATE2[STATE2["AWAIT_COMPRESSION_INFO"] = 2] = "AWAIT_COMPRESSION_INFO";
STATE2[STATE2["AWAIT_PACKET_DATA_LENGTH"] = 3] = "AWAIT_PACKET_DATA_LENGTH";
STATE2[STATE2["AWAIT_DATA"] = 4] = "AWAIT_DATA";
STATE2[STATE2["AWAIT_CHECKSUM"] = 5] = "AWAIT_CHECKSUM";
STATE2[STATE2["AWAIT_KEEPALIVE"] = 6] = "AWAIT_KEEPALIVE";
STATE2[STATE2["AWAIT_STATUS_QUERY"] = 7] = "AWAIT_STATUS_QUERY";
return STATE2;
})(STATE || {});
const EMPTY_PACKET = {
command: null,
buffer: [],
data: [],
hasCompression: 0,
dataLength: 0,
checksum: 0
};
var DECOMP_MODE = /* @__PURE__ */ ((DECOMP_MODE2) => {
DECOMP_MODE2[DECOMP_MODE2["DETECT_LENGTH"] = 0] = "DETECT_LENGTH";
DECOMP_MODE2[DECOMP_MODE2["COMPRESSED"] = 1] = "COMPRESSED";
DECOMP_MODE2[DECOMP_MODE2["UNCOMPRESSED"] = 2] = "UNCOMPRESSED";
return DECOMP_MODE2;
})(DECOMP_MODE || {});
const parsePackets = (bytes) => {
let state = STATE.AWAIT_MAGIC_BYTES;
let packet = { ...EMPTY_PACKET };
const packets = [];
bytes.forEach((byte) => {
switch (state) {
case STATE.AWAIT_MAGIC_BYTES:
if (packet.buffer.length === 0 && byte === 136) {
packet.buffer.push(byte);
return;
} else if (packet.buffer.length === 1 && byte === 51) {
packet.buffer = [];
state = STATE.AWAIT_COMMAND;
return;
} else {
packet = { ...EMPTY_PACKET };
return;
}
case STATE.AWAIT_COMMAND:
packet.command = byte;
state = STATE.AWAIT_COMPRESSION_INFO;
return;
case STATE.AWAIT_COMPRESSION_INFO:
packet.hasCompression = byte;
state = STATE.AWAIT_PACKET_DATA_LENGTH;
return;
case STATE.AWAIT_PACKET_DATA_LENGTH:
if (packet.buffer.length === 0) {
packet.buffer.push(byte);
return;
}
packet.dataLength = packet.buffer[0] + (byte << 8);
packet.buffer = [];
if (packet.dataLength === 0) {
state = STATE.AWAIT_CHECKSUM;
} else {
state = STATE.AWAIT_DATA;
}
return;
case STATE.AWAIT_DATA:
if (packet.buffer.length < packet.dataLength) {
packet.buffer.push(byte);
return;
}
packet.data = packet.buffer;
packet.buffer = [];
state = STATE.AWAIT_CHECKSUM;
return;
case STATE.AWAIT_CHECKSUM:
if (packet.buffer.length === 0) {
packet.buffer.push(byte);
return;
}
packet.checksum = packet.buffer[0] + (byte << 8);
packet.buffer = [];
state = STATE.AWAIT_KEEPALIVE;
return;
case STATE.AWAIT_KEEPALIVE:
state = STATE.AWAIT_STATUS_QUERY;
return;
case STATE.AWAIT_STATUS_QUERY:
state = STATE.AWAIT_MAGIC_BYTES;
packets.push({
checksum: packet.checksum,
command: packet.command,
data: packet.data,
dataLength: packet.dataLength,
hasCompression: packet.hasCompression
});
packet = { ...EMPTY_PACKET };
return;
}
});
return packets;
};
const getImageDataStream = (packets) => {
return packets.filter(({ command }) => command === COMMAND.DATA || command === COMMAND.PRINT);
};
const unpack = (data) => {
const dataOut = [];
let mode = DECOMP_MODE.DETECT_LENGTH;
let length = 0;
data.forEach((byte) => {
switch (mode) {
case DECOMP_MODE.DETECT_LENGTH:
if (byte & 128) {
mode = DECOMP_MODE.COMPRESSED;
length = (byte & 127) + 2;
} else {
mode = DECOMP_MODE.UNCOMPRESSED;
length = byte + 1;
}
return;
case DECOMP_MODE.UNCOMPRESSED:
length -= 1;
if (length === 0) {
mode = DECOMP_MODE.DETECT_LENGTH;
}
dataOut.push(byte);
return;
case DECOMP_MODE.COMPRESSED:
dataOut.push(...[...Array(length)].map(() => byte));
mode = DECOMP_MODE.DETECT_LENGTH;
length = 0;
return;
}
});
return dataOut;
};
const decompressDataStream = (packets) => {
return packets.map((packet) => {
if (packet.command === COMMAND.DATA) {
return {
...packet,
hasCompression: 0,
data: packet.hasCompression ? unpack(packet.data) : packet.data
};
}
return packet;
});
};
const parsePaletteByte = (paletteRaw) => {
return [
paletteRaw >> 6 & 3,
paletteRaw >> 4 & 3,
paletteRaw >> 2 & 3,
paletteRaw >> 0 & 3
];
};
const decodePrintCommands = (packets) => {
return packets.map((packet) => {
if (packet.command === COMMAND.PRINT) {
const printData = {
margins: packet.data[1],
marginUpper: packet.data[1] >> 4,
marginLower: packet.data[1] & 15,
palette: packet.data[2],
paletteData: parsePaletteByte(packet.data[2])
};
return {
...packet,
data: printData
};
}
return packet;
});
};
const harmonizePalette = (charA, charB, paletteDefinition = [3, 2, 1, 0]) => {
const bits = [...Array(8)].map((_, index) => ({
a: (charB >> 7 - index) % 2,
b: (charA >> 7 - index) % 2
}));
const res = bits.map(({ a, b }) => (a << 1) + b).map((val) => paletteDefinition[3 - val]).map((mapped) => ({
a: (mapped >> 1) % 2,
b: mapped % 2
})).reduce((acc, current, index) => ({
a: acc.a + (current.a << 7 - index),
b: acc.b + (current.b << 7 - index)
}), {
a: 0,
b: 0
});
return [
res.b & 255,
res.a & 255
];
};
const harmonizePalettes = (packets) => {
let unharmonizedPackets = [];
return packets.map((packet) => {
switch (packet.command) {
case COMMAND.DATA:
unharmonizedPackets.push(packet);
break;
case COMMAND.PRINT:
if (packet.data.palette === 0) {
unharmonizedPackets = [];
break;
}
while (unharmonizedPackets.length) {
let unharmonizedPacket = unharmonizedPackets.shift();
const data = [];
if (!unharmonizedPacket) {
throw Error("error harmonizing");
}
for (let i = 0; i < unharmonizedPacket.data.length; i += 2) {
data.push(
...harmonizePalette(
unharmonizedPacket.data[i],
unharmonizedPacket.data[i + 1],
packet.data.paletteData
)
);
}
Object.assign(unharmonizedPacket, { data });
}
break;
}
return packet;
});
};
const transformToClassic = (packets) => {
let image = {
transformed: []
};
let currentLine = [];
const images = [];
packets.forEach((packet) => {
switch (packet.command) {
case COMMAND.DATA:
for (let i = 0; i < packet.data.length; i += 1) {
currentLine.push(packet.data[i].toString(16).padStart(2, "0"));
if (i % 16 === 15) {
image.transformed.push(currentLine.join(" "));
currentLine = [];
}
}
break;
case COMMAND.PRINT:
image.palette = packet.data.paletteData || image.palette;
if (packet.data.marginLower !== 0) {
images.push(image.transformed);
image = {
transformed: []
};
currentLine = [];
}
break;
}
});
if (image.transformed.length) {
images.push(image.transformed);
}
return images;
};
const parseReducedPackets = (bytes) => {
let state = STATE.AWAIT_COMMAND;
let packet = {
command: null,
buffer: [],
data: [],
hasCompression: 0,
dataLength: 0
};
const packets = [];
const nextPacket = () => {
packets.push({
command: packet.command,
data: packet.data,
dataLength: packet.dataLength,
hasCompression: packet.hasCompression
});
packet = {
command: null,
buffer: [],
data: [],
hasCompression: 0,
dataLength: 0
};
state = STATE.AWAIT_COMMAND;
};
bytes.forEach((byte, index) => {
switch (state) {
case STATE.AWAIT_COMMAND:
packet.command = byte;
switch (packet.command) {
case COMMAND.INIT:
nextPacket();
return;
case COMMAND.DATA:
state = STATE.AWAIT_COMPRESSION_INFO;
return;
case COMMAND.PRINT:
state = STATE.AWAIT_PACKET_DATA_LENGTH;
return;
case COMMAND.TRANSFER:
state = STATE.AWAIT_PACKET_DATA_LENGTH;
return;
default:
throw new Error(`Unknown packet command: 0x${packet.command.toString(16)} at index ${index}`);
}
case STATE.AWAIT_COMPRESSION_INFO:
packet.hasCompression = byte;
state = STATE.AWAIT_PACKET_DATA_LENGTH;
return;
case STATE.AWAIT_PACKET_DATA_LENGTH:
if (packet.buffer.length === 0) {
packet.buffer.push(byte);
return;
}
packet.dataLength = packet.buffer[0] + (byte << 8);
packet.buffer = [];
if (packet.dataLength === 0) {
state = STATE.AWAIT_COMMAND;
nextPacket();
return;
}
state = STATE.AWAIT_DATA;
return;
case STATE.AWAIT_DATA:
packet.buffer.push(byte);
if (packet.buffer.length === packet.dataLength) {
packet.data = packet.buffer;
state = STATE.AWAIT_COMMAND;
if (packet.command === COMMAND.TRANSFER) {
nextPacket();
packet = {
buffer: [],
command: COMMAND.PRINT,
data: [1, 3, 228, 127],
hasCompression: 0,
dataLength: 4
};
}
nextPacket();
}
break;
}
});
return packets;
};
const twoTiles = new Array(2 * 16).fill(0);
const inflate = (arr) => {
const chunks = [];
let i = 0;
const n = arr.length;
while (i < n) {
chunks.push(...twoTiles, ...arr.slice(i, i += 256), ...twoTiles);
}
return chunks;
};
const inflateTransferPackages = (packets) => packets.map((packet) => {
if (packet.command !== COMMAND.TRANSFER) {
return packet;
}
return {
...packet,
command: COMMAND.DATA,
data: inflate(packet.data)
};
});
const fourtyLines = new Array(40).fill("00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00");
const completeFrame = (images) => images.map((image) => {
if (image.length !== 280) {
return image;
}
return [
...fourtyLines,
...image,
...fourtyLines
];
});
export { COMMAND as C, decodePrintCommands as a, parseReducedPackets as b, completeFrame as c, decompressDataStream as d, parsePaletteByte as e, harmonizePalette as f, getImageDataStream as g, harmonizePalettes as h, inflateTransferPackages as i, parsePackets as p, transformToClassic as t, unpack as u };
'use strict';
var COMMAND = /* @__PURE__ */ ((COMMAND2) => {
COMMAND2[COMMAND2["INIT"] = 1] = "INIT";
COMMAND2[COMMAND2["PRINT"] = 2] = "PRINT";
COMMAND2[COMMAND2["DATA"] = 4] = "DATA";
COMMAND2[COMMAND2["STATUS"] = 15] = "STATUS";
COMMAND2[COMMAND2["TRANSFER"] = 16] = "TRANSFER";
return COMMAND2;
})(COMMAND || {});
var STATE = /* @__PURE__ */ ((STATE2) => {
STATE2[STATE2["AWAIT_MAGIC_BYTES"] = 0] = "AWAIT_MAGIC_BYTES";
STATE2[STATE2["AWAIT_COMMAND"] = 1] = "AWAIT_COMMAND";
STATE2[STATE2["AWAIT_COMPRESSION_INFO"] = 2] = "AWAIT_COMPRESSION_INFO";
STATE2[STATE2["AWAIT_PACKET_DATA_LENGTH"] = 3] = "AWAIT_PACKET_DATA_LENGTH";
STATE2[STATE2["AWAIT_DATA"] = 4] = "AWAIT_DATA";
STATE2[STATE2["AWAIT_CHECKSUM"] = 5] = "AWAIT_CHECKSUM";
STATE2[STATE2["AWAIT_KEEPALIVE"] = 6] = "AWAIT_KEEPALIVE";
STATE2[STATE2["AWAIT_STATUS_QUERY"] = 7] = "AWAIT_STATUS_QUERY";
return STATE2;
})(STATE || {});
const EMPTY_PACKET = {
command: null,
buffer: [],
data: [],
hasCompression: 0,
dataLength: 0,
checksum: 0
};
var DECOMP_MODE = /* @__PURE__ */ ((DECOMP_MODE2) => {
DECOMP_MODE2[DECOMP_MODE2["DETECT_LENGTH"] = 0] = "DETECT_LENGTH";
DECOMP_MODE2[DECOMP_MODE2["COMPRESSED"] = 1] = "COMPRESSED";
DECOMP_MODE2[DECOMP_MODE2["UNCOMPRESSED"] = 2] = "UNCOMPRESSED";
return DECOMP_MODE2;
})(DECOMP_MODE || {});
const parsePackets = (bytes) => {
let state = STATE.AWAIT_MAGIC_BYTES;
let packet = { ...EMPTY_PACKET };
const packets = [];
bytes.forEach((byte) => {
switch (state) {
case STATE.AWAIT_MAGIC_BYTES:
if (packet.buffer.length === 0 && byte === 136) {
packet.buffer.push(byte);
return;
} else if (packet.buffer.length === 1 && byte === 51) {
packet.buffer = [];
state = STATE.AWAIT_COMMAND;
return;
} else {
packet = { ...EMPTY_PACKET };
return;
}
case STATE.AWAIT_COMMAND:
packet.command = byte;
state = STATE.AWAIT_COMPRESSION_INFO;
return;
case STATE.AWAIT_COMPRESSION_INFO:
packet.hasCompression = byte;
state = STATE.AWAIT_PACKET_DATA_LENGTH;
return;
case STATE.AWAIT_PACKET_DATA_LENGTH:
if (packet.buffer.length === 0) {
packet.buffer.push(byte);
return;
}
packet.dataLength = packet.buffer[0] + (byte << 8);
packet.buffer = [];
if (packet.dataLength === 0) {
state = STATE.AWAIT_CHECKSUM;
} else {
state = STATE.AWAIT_DATA;
}
return;
case STATE.AWAIT_DATA:
if (packet.buffer.length < packet.dataLength) {
packet.buffer.push(byte);
return;
}
packet.data = packet.buffer;
packet.buffer = [];
state = STATE.AWAIT_CHECKSUM;
return;
case STATE.AWAIT_CHECKSUM:
if (packet.buffer.length === 0) {
packet.buffer.push(byte);
return;
}
packet.checksum = packet.buffer[0] + (byte << 8);
packet.buffer = [];
state = STATE.AWAIT_KEEPALIVE;
return;
case STATE.AWAIT_KEEPALIVE:
state = STATE.AWAIT_STATUS_QUERY;
return;
case STATE.AWAIT_STATUS_QUERY:
state = STATE.AWAIT_MAGIC_BYTES;
packets.push({
checksum: packet.checksum,
command: packet.command,
data: packet.data,
dataLength: packet.dataLength,
hasCompression: packet.hasCompression
});
packet = { ...EMPTY_PACKET };
return;
}
});
return packets;
};
const getImageDataStream = (packets) => {
return packets.filter(({ command }) => command === COMMAND.DATA || command === COMMAND.PRINT);
};
const unpack = (data) => {
const dataOut = [];
let mode = DECOMP_MODE.DETECT_LENGTH;
let length = 0;
data.forEach((byte) => {
switch (mode) {
case DECOMP_MODE.DETECT_LENGTH:
if (byte & 128) {
mode = DECOMP_MODE.COMPRESSED;
length = (byte & 127) + 2;
} else {
mode = DECOMP_MODE.UNCOMPRESSED;
length = byte + 1;
}
return;
case DECOMP_MODE.UNCOMPRESSED:
length -= 1;
if (length === 0) {
mode = DECOMP_MODE.DETECT_LENGTH;
}
dataOut.push(byte);
return;
case DECOMP_MODE.COMPRESSED:
dataOut.push(...[...Array(length)].map(() => byte));
mode = DECOMP_MODE.DETECT_LENGTH;
length = 0;
return;
}
});
return dataOut;
};
const decompressDataStream = (packets) => {
return packets.map((packet) => {
if (packet.command === COMMAND.DATA) {
return {
...packet,
hasCompression: 0,
data: packet.hasCompression ? unpack(packet.data) : packet.data
};
}
return packet;
});
};
const parsePaletteByte = (paletteRaw) => {
return [
paletteRaw >> 6 & 3,
paletteRaw >> 4 & 3,
paletteRaw >> 2 & 3,
paletteRaw >> 0 & 3
];
};
const decodePrintCommands = (packets) => {
return packets.map((packet) => {
if (packet.command === COMMAND.PRINT) {
const printData = {
margins: packet.data[1],
marginUpper: packet.data[1] >> 4,
marginLower: packet.data[1] & 15,
palette: packet.data[2],
paletteData: parsePaletteByte(packet.data[2])
};
return {
...packet,
data: printData
};
}
return packet;
});
};
const harmonizePalette = (charA, charB, paletteDefinition = [3, 2, 1, 0]) => {
const bits = [...Array(8)].map((_, index) => ({
a: (charB >> 7 - index) % 2,
b: (charA >> 7 - index) % 2
}));
const res = bits.map(({ a, b }) => (a << 1) + b).map((val) => paletteDefinition[3 - val]).map((mapped) => ({
a: (mapped >> 1) % 2,
b: mapped % 2
})).reduce((acc, current, index) => ({
a: acc.a + (current.a << 7 - index),
b: acc.b + (current.b << 7 - index)
}), {
a: 0,
b: 0
});
return [
res.b & 255,
res.a & 255
];
};
const harmonizePalettes = (packets) => {
let unharmonizedPackets = [];
return packets.map((packet) => {
switch (packet.command) {
case COMMAND.DATA:
unharmonizedPackets.push(packet);
break;
case COMMAND.PRINT:
if (packet.data.palette === 0) {
unharmonizedPackets = [];
break;
}
while (unharmonizedPackets.length) {
let unharmonizedPacket = unharmonizedPackets.shift();
const data = [];
if (!unharmonizedPacket) {
throw Error("error harmonizing");
}
for (let i = 0; i < unharmonizedPacket.data.length; i += 2) {
data.push(
...harmonizePalette(
unharmonizedPacket.data[i],
unharmonizedPacket.data[i + 1],
packet.data.paletteData
)
);
}
Object.assign(unharmonizedPacket, { data });
}
break;
}
return packet;
});
};
const transformToClassic = (packets) => {
let image = {
transformed: []
};
let currentLine = [];
const images = [];
packets.forEach((packet) => {
switch (packet.command) {
case COMMAND.DATA:
for (let i = 0; i < packet.data.length; i += 1) {
currentLine.push(packet.data[i].toString(16).padStart(2, "0"));
if (i % 16 === 15) {
image.transformed.push(currentLine.join(" "));
currentLine = [];
}
}
break;
case COMMAND.PRINT:
image.palette = packet.data.paletteData || image.palette;
if (packet.data.marginLower !== 0) {
images.push(image.transformed);
image = {
transformed: []
};
currentLine = [];
}
break;
}
});
if (image.transformed.length) {
images.push(image.transformed);
}
return images;
};
const parseReducedPackets = (bytes) => {
let state = STATE.AWAIT_COMMAND;
let packet = {
command: null,
buffer: [],
data: [],
hasCompression: 0,
dataLength: 0
};
const packets = [];
const nextPacket = () => {
packets.push({
command: packet.command,
data: packet.data,
dataLength: packet.dataLength,
hasCompression: packet.hasCompression
});
packet = {
command: null,
buffer: [],
data: [],
hasCompression: 0,
dataLength: 0
};
state = STATE.AWAIT_COMMAND;
};
bytes.forEach((byte, index) => {
switch (state) {
case STATE.AWAIT_COMMAND:
packet.command = byte;
switch (packet.command) {
case COMMAND.INIT:
nextPacket();
return;
case COMMAND.DATA:
state = STATE.AWAIT_COMPRESSION_INFO;
return;
case COMMAND.PRINT:
state = STATE.AWAIT_PACKET_DATA_LENGTH;
return;
case COMMAND.TRANSFER:
state = STATE.AWAIT_PACKET_DATA_LENGTH;
return;
default:
throw new Error(`Unknown packet command: 0x${packet.command.toString(16)} at index ${index}`);
}
case STATE.AWAIT_COMPRESSION_INFO:
packet.hasCompression = byte;
state = STATE.AWAIT_PACKET_DATA_LENGTH;
return;
case STATE.AWAIT_PACKET_DATA_LENGTH:
if (packet.buffer.length === 0) {
packet.buffer.push(byte);
return;
}
packet.dataLength = packet.buffer[0] + (byte << 8);
packet.buffer = [];
if (packet.dataLength === 0) {
state = STATE.AWAIT_COMMAND;
nextPacket();
return;
}
state = STATE.AWAIT_DATA;
return;
case STATE.AWAIT_DATA:
packet.buffer.push(byte);
if (packet.buffer.length === packet.dataLength) {
packet.data = packet.buffer;
state = STATE.AWAIT_COMMAND;
if (packet.command === COMMAND.TRANSFER) {
nextPacket();
packet = {
buffer: [],
command: COMMAND.PRINT,
data: [1, 3, 228, 127],
hasCompression: 0,
dataLength: 4
};
}
nextPacket();
}
break;
}
});
return packets;
};
const twoTiles = new Array(2 * 16).fill(0);
const inflate = (arr) => {
const chunks = [];
let i = 0;
const n = arr.length;
while (i < n) {
chunks.push(...twoTiles, ...arr.slice(i, i += 256), ...twoTiles);
}
return chunks;
};
const inflateTransferPackages = (packets) => packets.map((packet) => {
if (packet.command !== COMMAND.TRANSFER) {
return packet;
}
return {
...packet,
command: COMMAND.DATA,
data: inflate(packet.data)
};
});
const fourtyLines = new Array(40).fill("00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00");
const completeFrame = (images) => images.map((image) => {
if (image.length !== 280) {
return image;
}
return [
...fourtyLines,
...image,
...fourtyLines
];
});
exports.COMMAND = COMMAND;
exports.completeFrame = completeFrame;
exports.decodePrintCommands = decodePrintCommands;
exports.decompressDataStream = decompressDataStream;
exports.getImageDataStream = getImageDataStream;
exports.harmonizePalette = harmonizePalette;
exports.harmonizePalettes = harmonizePalettes;
exports.inflateTransferPackages = inflateTransferPackages;
exports.parsePackets = parsePackets;
exports.parsePaletteByte = parsePaletteByte;
exports.parseReducedPackets = parseReducedPackets;
exports.transformToClassic = transformToClassic;
exports.unpack = unpack;