UNPKG

p8-data-cart

Version:

Simple tools for generating Pico-8 data carts.

175 lines (174 loc) 5.99 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.writeCart = writeCart; const music_js_1 = require("./music.js"); const sfx_js_1 = require("./sfx.js"); function writeHeader() { return 'pico-8 cartridge // http://www.pico-8.com'; } function writeVersion(versionNumber) { return 'version ' + versionNumber; } function writeNybble(value) { switch (value) { case 0x0: return '0'; case 0x1: return '1'; case 0x2: return '2'; case 0x3: return '3'; case 0x4: return '4'; case 0x5: return '5'; case 0x6: return '6'; case 0x7: return '7'; case 0x8: return '8'; case 0x9: return '9'; case 0xa: return 'a'; case 0xb: return 'b'; case 0xc: return 'c'; case 0xd: return 'd'; case 0xe: return 'e'; case 0xf: return 'f'; default: throw new Error('Cannot write value as a nybble: ' + value); } } function writeByteExtended(value) { switch (value) { case 0x0: return '0'; case 0x1: return '1'; case 0x2: return '2'; case 0x3: return '3'; case 0x4: return '4'; case 0x5: return '5'; case 0x6: return '6'; case 0x7: return '7'; case 0x8: return '8'; case 0x9: return '9'; case 0xa: return 'a'; case 0xb: return 'b'; case 0xc: return 'c'; case 0xd: return 'd'; case 0xe: return 'e'; case 0xf: return 'f'; case 0x80: return 'g'; case 0x81: return 'h'; case 0x82: return 'i'; case 0x83: return 'j'; case 0x84: return 'k'; case 0x85: return 'l'; case 0x86: return 'm'; case 0x87: return 'n'; case 0x88: return 'o'; case 0x89: return 'p'; case 0x8a: return 'q'; case 0x8b: return 'r'; case 0x8c: return 's'; case 0x8d: return 't'; case 0x8e: return 'u'; case 0x8f: return 'v'; default: throw new Error('Cannot write value as extended hexadecimal number: ' + value); } } function writeByte(value) { const integerValue = Math.floor(value); return writeNybble(integerValue >> 4) + writeNybble(integerValue & 0xf); } function transposeByte(value) { return (value & 0xf0) >> 4 | (value & 0x0f) << 4; } function trimEmptyLines(bytes, bytesPerLine) { let zeroTrailIndex = -1; for (let i = bytes.length - 1; i >= 0; i--) { if (bytes[i] !== 0) { zeroTrailIndex = i + 1; break; } } if (zeroTrailIndex === -1) return new Uint8Array(0); const stopIndex = Math.ceil(zeroTrailIndex / bytesPerLine) * bytesPerLine; return bytes.slice(0, stopIndex); } function writeDataSection(bytes, bytesPerLine, transposeBytes = false) { bytes = trimEmptyLines(bytes, bytesPerLine); let sectionString = ''; for (let i = 0; i < bytes.length; i++) { const byteValue = transposeBytes ? transposeByte(bytes[i]) : bytes[i]; if (i > 0 && i % bytesPerLine === 0) sectionString += '\n'; sectionString += writeByte(byteValue); } return sectionString; } function writeDataSectionExtended(bytes, bytesPerLine) { bytes = trimEmptyLines(bytes, bytesPerLine); let sectionString = ''; for (let i = 0; i < bytes.length; i++) { const byteValue = bytes[i]; if (i > 0 && i % bytesPerLine === 0) sectionString += '\n'; sectionString += writeByteExtended(byteValue); } return sectionString; } function writeSection(label, content) { if (!content) return ''; return `__${label}__\n${content}\n`; } function writeLuaSection(luaCode) { return writeSection('lua', luaCode); } function writeGfxSection(bytes) { return writeSection('gfx', writeDataSection(bytes, 64, true)); } function writeLabelSection(bytes) { return writeSection('label', writeDataSectionExtended(bytes, 128)); } function writeGffSection(bytes) { return writeSection('gff', writeDataSection(bytes, 128)); } function writeMapSection(bytes) { return writeSection('map', writeDataSection(bytes, 128)); } function writeSfxSection(bytes) { const sfxData = (0, sfx_js_1.runtimeBytesToSfxData)(new Uint8Array(bytes)); const cartBytes = (0, sfx_js_1.sfxDataToCartBytes)(sfxData); return writeSection('sfx', writeDataSection(cartBytes, 84)); } function writeMusicSection(bytes) { bytes = trimEmptyLines(bytes, 4); const musicData = (0, music_js_1.runtimeBytesToMusicData)(bytes); const cartBytes = (0, music_js_1.musicDataToCartBytes)(musicData); let musicDataString = ''; for (let i = 0; i < cartBytes.length; i += 5) { musicDataString += writeByte(cartBytes[i + 0]); musicDataString += ' '; musicDataString += writeByte(cartBytes[i + 1]); musicDataString += writeByte(cartBytes[i + 2]); musicDataString += writeByte(cartBytes[i + 3]); musicDataString += writeByte(cartBytes[i + 4]); musicDataString += '\n'; } return writeSection('music', musicDataString); } function writeCart(cartData) { let cartContents = ''; cartContents += writeHeader() + '\n'; cartContents += writeVersion(cartData.version) + '\n'; if (cartData.lua !== undefined) cartContents += writeLuaSection(cartData.lua); if (cartData.gfx !== undefined) cartContents += writeGfxSection(cartData.gfx); if (cartData.label !== undefined) cartContents += writeLabelSection(cartData.label); if (cartData.gff !== undefined) cartContents += writeGffSection(cartData.gff); if (cartData.map !== undefined) cartContents += writeMapSection(cartData.map); if (cartData.sfx !== undefined) cartContents += writeSfxSection(cartData.sfx); if (cartData.music !== undefined) cartContents += writeMusicSection(cartData.music); return cartContents; }