-
-
Save Dominus-Sicarum/8ed5354e65c36d0910819b1d32087652 to your computer and use it in GitHub Desktop.
REXPaint .xp read/parse class
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Outputs a GlyphImage instance with following structure: | |
GlyphImage { | |
'layer' : [ | |
Map( | |
integer: { | |
'char' : alt-code or glyph, | |
'bg' : background css hex-string, | |
'fg' : foreground css hex-string | |
} | |
) | |
], | |
'width' : integer, | |
'height' : integer, | |
} | |
Individual cell position can be calculated as follows: | |
x = Math.floor(key / GlyphImage.height) | |
y = (key % GlyphImage.height) | |
*/ | |
const zlib = require('zlib') | |
// char-code array, slightly altered to mimic REXPaint's default charset | |
const cp437 = [ | |
0x0000, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C, | |
0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8, 0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC, | |
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, | |
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, | |
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, | |
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, | |
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, | |
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x00A6, 0x007D, 0x007E, 0x2302, | |
0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, | |
0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, | |
0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, | |
0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, | |
0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, | |
0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, | |
0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, | |
0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x25A1 | |
] | |
const whitespace = [0, 32] // NUL and SP symbol alt-codes | |
const transparentBG = ['#000000', '#ff00ff'] // black and hot-pink | |
function rgb2hex (r, g, b) { | |
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1) | |
} | |
class GlyphImage { | |
constructor (deflated, toGlyph = true, ignoreTransparent = true) { | |
this.layer = [] //because I like layer[n] more than layers[n] | |
let x = 0 | |
let y = 0 | |
let inflated = zlib.unzipSync(deflated) | |
let offset = 8 | |
let layerCount = inflated.readInt32LE(4) | |
this.width = inflated.readInt32LE(8) //we only need these once | |
this.height = inflated.readInt32LE(12) | |
for (let i = 0; i < layerCount; i++) { | |
let layerMap = new Map() //this is gonna be a map for easier access and ability to skip over keys | |
offset += 8 //skip over width/height, since they're pointless now | |
for (let c = 0, length = this.width * this.height; c < length; c++) { | |
let alt = inflated.readInt32LE(offset) | |
offset += 4 | |
// get both color hex values | |
// we don't want to calculate at render | |
let fg = rgb2hex( | |
inflated[offset + 0], | |
inflated[offset + 1], | |
inflated[offset + 2] | |
) | |
let bg = rgb2hex( | |
inflated[offset + 3], | |
inflated[offset + 4], | |
inflated[offset + 5] | |
) | |
offset += 6 | |
// skip transparent cells (no glyph and black or hot-pink bg) | |
if (ignoreTransparent) | |
if (whitespace.includes(alt) && transparentBG.includes(bg)) | |
continue | |
let glyphData = {} | |
// write alt-code or char-string | |
if (toGlyph) | |
glyphData.char = String.fromCharCode(cp437[alt]) | |
else | |
glyphData.char = alt | |
glyphData.bg = bg | |
glyphData.fg = fg | |
layerMap.set(c, glyphData) | |
} | |
this.layer.push(layerMap) | |
} | |
} | |
//returns a combination of layer maps, ready to be iterated over with for...of | |
getComposite () { | |
let composite = this.layer[0] | |
for (var i = 1; i < this.layer.length; i++) | |
for (let [key, value] of this.layer[i]) | |
composite.set(key, value) | |
return composite | |
} | |
} | |
module.exports = {GlyphImage} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment