blitflash
Version:
A JavaScript implementation of the 32blit flash tools
189 lines (188 loc) • 7.43 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BlitMetaStandalone = exports.BlitImage = void 0;
const RandomAccessReader_1 = require("./RandomAccessReader");
class BlitImage {
constructor(type, dataLength, width, height, format, palette, pixels) {
this.type = type;
this.dataLength = dataLength;
this.width = width;
this.height = height;
this.format = format;
this.palette = palette;
this.pixels = pixels;
}
asDataUrl() {
const pixels = this.unpack();
if (!pixels) {
return null;
}
const canvas = document.createElement('canvas');
canvas.width = this.width;
canvas.height = this.height;
const ctx = canvas.getContext('2d');
if (!ctx) {
console.error('Failed to create a 2d canvas context.');
return null;
}
const image = ctx.createImageData(this.width, this.height);
image.data.set(pixels);
ctx.putImageData(image, 0, 0);
return canvas.toDataURL();
}
/*
* Copied from https://gist.github.com/Daft-Freak/f01aef0e6e3060f5b12e018d29401698
*/
unpack() {
if (this.format !== 2) {
return null;
}
const palette = new Uint8Array(this.palette);
const bytes = new Uint8Array(this.pixels);
const pixels = new Uint8Array(new ArrayBuffer(this.width * this.height * 4));
if (this.type === 'PK') {
const bitDepth = Math.ceil(Math.log2(palette.length / 4));
let outOff = 0;
let col = 0;
let bit = 0;
for (const byte of bytes) {
for (let b = 0; b < 8; b++) {
col <<= 1;
col |= (byte >> (7 - b)) & 1;
if (++bit == bitDepth) {
pixels[outOff++] = palette[col * 4 + 0];
pixels[outOff++] = palette[col * 4 + 1];
pixels[outOff++] = palette[col * 4 + 2];
pixels[outOff++] = palette[col * 4 + 3];
bit = 0;
col = 0;
}
}
}
return pixels;
}
if (this.type === 'RL') {
const bitDepth = Math.ceil(Math.log2(palette.length / 4));
let outOff = 0;
let col = 0, bit = 0, count = 0;
let parseState = 0;
for (const byte of bytes) {
for (let b = 0; b < 8; b++) {
switch (parseState) {
// flag
case 0: {
if (byte & (0x80 >> b))
parseState = 1;
else
parseState = 2;
break;
}
// repeat count
case 1: {
count <<= 1;
count |= (byte >> (7 - b)) & 1;
if (++bit == 8) {
parseState = 2;
bit = 0;
}
break;
}
// value
case 2: {
col <<= 1;
col |= (byte >> (7 - b)) & 1;
if (++bit == bitDepth) {
for (let c = 0; c <= count; c++) {
pixels[outOff++] = palette[col * 4 + 0];
pixels[outOff++] = palette[col * 4 + 1];
pixels[outOff++] = palette[col * 4 + 2];
pixels[outOff++] = palette[col * 4 + 3];
}
bit = 0;
col = 0;
count = 0;
parseState = 0;
}
break;
}
}
}
}
return pixels;
}
return null;
}
numPixels() {
return this.width * this.height;
}
static parse(reader) {
const header = reader.readString(6);
if (header !== 'SPRITE') {
throw new Error(`Invalid header for BlitImage: ${header}`);
}
const type = reader.readString(2);
const dataLength = reader.readUint32(true);
const width = reader.readUint16(true);
const height = reader.readUint16(true);
const format = reader.readUint8();
let paletteLength = reader.readUint8();
// See https://github.com/32blit/32blit-tools/blob/a520a742450c8da97f88f6c0ce74ac0038093e02/src/ttblit/core/struct.py#L24-L33
if (paletteLength === 0) {
paletteLength = 256;
}
const palette = reader.read(paletteLength * 4);
const pixels = reader.read(dataLength - 18 - paletteLength * 4);
return new BlitImage(type, dataLength, width, height, format, palette, pixels);
}
}
exports.BlitImage = BlitImage;
/**
* As defined at
* https://github.com/32blit/32blit-tools/blob/a520a742450c8da97f88f6c0ce74ac0038093e02/src/ttblit/core/struct.py#L79
*/
class BlitMetaStandalone {
constructor(checksum, date, title, description, version, author, blittype, category, url, filetypes, icon, splash) {
this.checksum = checksum;
this.date = date;
this.title = title;
this.description = description;
this.version = version;
this.author = author;
this.blittype = blittype;
this.category = category;
this.url = url;
this.filetypes = filetypes;
this.icon = icon;
this.splash = splash;
}
static parse(buffer) {
const reader = new RandomAccessReader_1.RandomAccessReader(buffer);
const checksum = reader.readUint32(true);
const date = reader.readString(16);
const title = reader.readString(25);
const description = reader.readString(129);
const version = reader.readString(17);
const author = reader.readString(17);
let category;
let url;
let filetypes;
const pos = reader.getPos();
const blitType = reader.readString(8);
if (blitType === 'BLITTYPE') {
category = reader.readString(17);
url = reader.readString(129);
const filetypesLength = reader.readUint8();
filetypes = [];
for (let i = 0; i < filetypesLength; i++) {
filetypes.push(reader.readString(5));
}
}
else {
reader.setPos(pos);
}
const icon = BlitImage.parse(reader);
const splash = BlitImage.parse(reader);
return new BlitMetaStandalone(checksum, date, title, description, version, author, blitType, category, url, filetypes, icon, splash);
}
}
exports.BlitMetaStandalone = BlitMetaStandalone;