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.14
to
1.3.15
+1
dist/completeFrame.d.ts
export declare const completeFrame: (images: string[][]) => string[][];
const fourtyLines = (new Array(40)).fill('00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00');
export const completeFrame = (images) => (images.map((image) => {
if (image.length !== 280) {
return image;
}
return [
...fourtyLines,
...image,
...fourtyLines,
];
}));
import { Packet } from "./Types";
export declare enum COMMAND {
INIT = 1,
PRINT = 2,
DATA = 4,
STATUS = 15,
TRANSFER = 16
}
export declare enum STATE {
AWAIT_MAGIC_BYTES = 0,
AWAIT_COMMAND = 1,
AWAIT_COMPRESSION_INFO = 2,
AWAIT_PACKET_DATA_LENGTH = 3,
AWAIT_DATA = 4,
AWAIT_CHECKSUM = 5,
AWAIT_KEEPALIVE = 6,
AWAIT_STATUS_QUERY = 7
}
export declare const EMPTY_PACKET: Packet;
export declare enum DECOMP_MODE {
DETECT_LENGTH = 0,
COMPRESSED = 1,
UNCOMPRESSED = 2
}
export var COMMAND;
(function (COMMAND) {
COMMAND[COMMAND["INIT"] = 1] = "INIT";
COMMAND[COMMAND["PRINT"] = 2] = "PRINT";
COMMAND[COMMAND["DATA"] = 4] = "DATA";
COMMAND[COMMAND["STATUS"] = 15] = "STATUS";
COMMAND[COMMAND["TRANSFER"] = 16] = "TRANSFER";
})(COMMAND || (COMMAND = {}));
export var STATE;
(function (STATE) {
STATE[STATE["AWAIT_MAGIC_BYTES"] = 0] = "AWAIT_MAGIC_BYTES";
STATE[STATE["AWAIT_COMMAND"] = 1] = "AWAIT_COMMAND";
STATE[STATE["AWAIT_COMPRESSION_INFO"] = 2] = "AWAIT_COMPRESSION_INFO";
STATE[STATE["AWAIT_PACKET_DATA_LENGTH"] = 3] = "AWAIT_PACKET_DATA_LENGTH";
STATE[STATE["AWAIT_DATA"] = 4] = "AWAIT_DATA";
STATE[STATE["AWAIT_CHECKSUM"] = 5] = "AWAIT_CHECKSUM";
STATE[STATE["AWAIT_KEEPALIVE"] = 6] = "AWAIT_KEEPALIVE";
STATE[STATE["AWAIT_STATUS_QUERY"] = 7] = "AWAIT_STATUS_QUERY";
})(STATE || (STATE = {}));
export const EMPTY_PACKET = {
command: null,
buffer: [],
data: [],
hasCompression: 0,
dataLength: 0,
checksum: 0,
};
export var DECOMP_MODE;
(function (DECOMP_MODE) {
DECOMP_MODE[DECOMP_MODE["DETECT_LENGTH"] = 0] = "DETECT_LENGTH";
DECOMP_MODE[DECOMP_MODE["COMPRESSED"] = 1] = "COMPRESSED";
DECOMP_MODE[DECOMP_MODE["UNCOMPRESSED"] = 2] = "UNCOMPRESSED";
})(DECOMP_MODE || (DECOMP_MODE = {}));
import { ParsedPacket, PrintPacket } from "./Types";
export declare const decodePrintCommands: (packets: ParsedPacket[]) => (PrintPacket | ParsedPacket)[];
import { COMMAND } from "./constants";
import { parsePaletteByte } from "./parsePaletteByte";
export 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] & 0xf,
palette: packet.data[2],
paletteData: parsePaletteByte(packet.data[2]),
};
return {
...packet,
data: printData,
};
}
return packet;
});
};
import { ParsedPacket } from "./Types";
export declare const decompressDataStream: (packets: ParsedPacket[]) => ParsedPacket[];
import { COMMAND } from "./constants";
import { unpack } from "./unpack";
export 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;
});
};
import { ParsedPacket } from "./Types";
export declare const getImageDataStream: (packets: ParsedPacket[]) => ParsedPacket[];
import { COMMAND } from "./constants";
export const getImageDataStream = (packets) => {
return packets.filter(({ command }) => (command === COMMAND.DATA ||
command === COMMAND.PRINT));
};
import { HarmonisedPalette, PaletteData } from "./Types";
export declare const harmonizePalette: (charA: number, charB: number, paletteDefinition?: PaletteData) => HarmonisedPalette;
export 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 & 0xff,
res.a & 0xff,
];
};
import { ParsedPacket, PrintPacket } from "./Types";
export declare const harmonizePalettes: (packets: (PrintPacket | ParsedPacket)[]) => (PrintPacket | ParsedPacket)[];
import { COMMAND } from "./constants";
import { harmonizePalette } from "./harmonizePalette";
export 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;
});
};
export {};
import fs from "node:fs/promises";
import path from "node:path";
import { toByteArray } from "./toByteArray";
import { parsePackets } from "./parsePackets";
import { getImageDataStream } from "./getImageDataStream";
import { decompressDataStream } from "./decompressDataStream";
import { decodePrintCommands } from "./decodePrintCommands";
import { harmonizePalettes } from "./harmonizePalettes";
import { transformToClassic } from "./transformToClassic";
import { parseReducedPackets } from "./parseReducedPackets";
import { inflateTransferPackages } from "./inflateTransferPackages";
import { completeFrame } from "./completeFrame";
const outDir = path.join(process.cwd(), 'out');
const runStandard = async (fileName, outKey) => {
const filePath = path.join(process.cwd(), 'in', fileName);
let fileContents;
let rawPackets;
let parsedPackets;
let dataPackets;
let decompPackets;
let printInfoPackets;
let harmonizedPackets;
let classicPrintStreamImages;
try {
fileContents = await fs.readFile(filePath, { encoding: 'utf8' });
}
catch (error) {
console.error(error.message);
process.exit(-1);
}
rawPackets = toByteArray(fileContents);
parsedPackets = parsePackets(rawPackets);
dataPackets = getImageDataStream(parsedPackets);
decompPackets = decompressDataStream(dataPackets);
printInfoPackets = decodePrintCommands(decompPackets);
harmonizedPackets = harmonizePalettes(printInfoPackets);
classicPrintStreamImages = transformToClassic(harmonizedPackets);
try {
await Promise.all(classicPrintStreamImages.map(async (image, index) => {
image.unshift('!{"command":"INIT"}');
await fs.writeFile(path.join(outDir, `out_${outKey}_${index}.txt`), image.join('\n'), { encoding: 'utf8' });
}));
}
catch (error) {
console.error(error);
}
};
const runReduced = async (fileName, outKey) => {
const filePath = path.join(process.cwd(), 'in', fileName);
let fileContents;
let reducedPackages;
let rawPackets;
let parsedPackets;
let dataPackets;
let decompPackets;
let printInfoPackets;
let harmonizedPackets;
let reducedClassicPrintStreamImages;
let classicPrintStreamImages;
try {
fileContents = await fs.readFile(filePath, { encoding: 'utf8' });
}
catch (error) {
console.error(error.message);
process.exit(-1);
}
reducedPackages = toByteArray(fileContents);
rawPackets = parseReducedPackets(reducedPackages);
parsedPackets = inflateTransferPackages(rawPackets);
dataPackets = getImageDataStream(parsedPackets);
decompPackets = decompressDataStream(dataPackets);
printInfoPackets = decodePrintCommands(decompPackets);
harmonizedPackets = harmonizePalettes(printInfoPackets);
reducedClassicPrintStreamImages = transformToClassic(harmonizedPackets);
classicPrintStreamImages = completeFrame(reducedClassicPrintStreamImages);
try {
await Promise.all(classicPrintStreamImages.map(async (image, index) => {
image.unshift('!{"command":"INIT"}');
await fs.writeFile(path.join(outDir, `out_${outKey}_${index}.txt`), image.join('\n'), { encoding: 'utf8' });
}));
}
catch (error) {
console.error(error);
}
};
fs.mkdir(outDir, {
recursive: true,
}).then(() => {
runStandard('alice.txt', 'alice');
runStandard('all.txt', 'all');
runStandard('comp.txt', 'comp');
runStandard('gradient.txt', 'gradient');
runStandard('uncomp.txt', 'uncomp');
runStandard('white.txt', 'white');
runReduced('pico.txt', 'pico');
});
import { ParsedPacket } from "./Types";
export declare const inflateTransferPackages: (packets: ParsedPacket[]) => ParsedPacket[];
import { COMMAND } from "./constants";
const twoTiles = (new Array(2 * 16)).fill(0x00);
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;
};
export const inflateTransferPackages = (packets) => (packets.map((packet) => {
if (packet.command !== COMMAND.TRANSFER) {
return packet;
}
return {
...packet,
command: COMMAND.DATA,
data: inflate(packet.data),
};
}));
import { ParsedPacket } from "./Types";
export declare const logPackets: (packets: ParsedPacket[]) => ParsedPacket[];
import { COMMAND } from "./constants";
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 '-';
}
};
export 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;
};
import { ParsedPacket } from "./Types";
export declare const parsePackets: (bytes: number[]) => ParsedPacket[];
import { STATE, EMPTY_PACKET } from "./constants";
export 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 === 0x88) {
packet.buffer.push(byte);
return;
}
else if (packet.buffer.length === 1 && byte === 0x33) {
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;
};
import { PaletteData } from "./Types";
export declare const parsePaletteByte: (paletteRaw: number) => PaletteData;
export const parsePaletteByte = (paletteRaw) => {
return [
(paletteRaw >> 6) & 0x3,
(paletteRaw >> 4) & 0x3,
(paletteRaw >> 2) & 0x3,
(paletteRaw >> 0) & 0x3,
];
};
import { ParsedPacket } from "./Types";
export declare const parseReducedPackets: (bytes: number[]) => ParsedPacket[];
import { COMMAND, STATE } from "./constants";
export 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;
default:
}
});
return packets;
};
export declare const toByteArray: (fileContents: string) => number[];
export const toByteArray = (fileContents) => (fileContents
.split('\n')
.filter((line) => !line.startsWith('//'))
.map(line => line.trim())
.map(line => line
.split(' ')
.filter(Boolean)
.map((cc) => parseInt(cc, 16)))
.flat());
import { ParsedPacket, PrintPacket } from "./Types";
export declare const transformToClassic: (packets: (PrintPacket | ParsedPacket)[]) => string[][];
import { COMMAND } from "./constants";
export 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;
default:
break;
}
});
if (image.transformed.length) {
images.push(image.transformed);
}
return images;
};
import { COMMAND } from "./constants";
export type PaletteData = [number, number, number, number];
export type HarmonisedPalette = [number, number];
export interface PrintData {
margins: number;
marginUpper: number;
marginLower: number;
palette: number;
paletteData: PaletteData;
}
export interface Packet {
command: null | COMMAND;
buffer: number[];
data: number[];
hasCompression: number;
dataLength: number;
checksum?: number;
}
export type ParsedPacket = Omit<Packet, 'buffer'>;
export interface PrintPacket extends Omit<Packet, 'data'> {
command: COMMAND.PRINT;
data: PrintData;
}
export interface TransformedImage {
transformed: string[];
palette?: PaletteData;
}
export {};
export declare const unpack: (data: number[]) => number[];
import { DECOMP_MODE } from "./constants";
export 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 & 0x80) {
mode = DECOMP_MODE.COMPRESSED;
length = (byte & 0x7f) + 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;
};
+2
-3
{
"name": "gbp-decode",
"version": "1.3.14",
"version": "1.3.15",
"description": "A set of functions to decode gameboy printer code",

@@ -19,4 +19,3 @@ "scripts": {

"files": [
"dist/shared/*",
"dist/main.*"
"dist"
],

@@ -23,0 +22,0 @@ "repository": {