From 07977624ae1e59ba77987a6011cce1909a473acb Mon Sep 17 00:00:00 2001 From: type-two Date: Fri, 21 Aug 2026 13:06:25 +1000 Subject: [PATCH] DestoGod: replacement for the TP5 bus destination sign software MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TP5 is a 2012 Windows application supplied with the Guangzhou-Tongda LED destination signs fitted to Yutong buses. It has no preview, so building a destination list is guess-and-check, and it only runs on Windows. The bus never talks to TP5 — the sign controller reads a .td5 file off an SD card, and that is the whole interface. So this replaces the software without touching any hardware or protocol: it just has to write byte-correct .td5. Formats reverse-engineered from the sample files and TP5(En).exe, then verified byte-for-byte: .td5 the file the bus reads. Fixed-layout binary; each destination block carries a CRC-16/ARC over block[3..len] and a rand() block id, which together looked like one 4-byte field because RAND_MAX is 0x7fff. .tp5 the editable project. Line-based text, UTF-16BE hex strings. .font the sign's own bitmap fonts, each glyph row XORed with its char code. The app is one self-contained HTML file: live LED preview at the real sign size with real scrolling, spreadsheet/CSV import, multi-page destinations, undoable delete, and export to both .td5 and .tp5. Verified: - rebuilds a real 46,080-byte TP5 export byte-for-byte with a recomputed CRC - all 36 stored CRCs verify against the implementation - driven through its own UI, re-exporting the real file differs in 7 bytes, all of them the export timestamp - running on a real bus: signs and driver's controller both correct Co-Authored-By: Claude Opus 5 --- .gitignore | 6 + README.md | 239 +++++ app/index.html | 198 ++++ app/main.mjs | 953 ++++++++++++++++++ dist/DestoGod.html | 2072 +++++++++++++++++++++++++++++++++++++++ re/affine.py | 38 + re/chk16.py | 28 + re/disx.py | 31 + re/find_const.py | 14 + re/findtab.py | 46 + re/parse.py | 33 + re/pe.py | 12 + re/tryranges.py | 48 + re/verify.py | 56 ++ re/xref.py | 10 + samples/EXPRESS 1.td5 | Bin 0 -> 46080 bytes samples/EXPRESS.tp5 | 1091 +++++++++++++++++++++ samples/README.md | 14 + samples/desto-test.xlsx | Bin 0 -> 5127 bytes src/codec/bitfont.mjs | 133 +++ src/codec/sheet.mjs | 196 ++++ src/codec/td5.mjs | 351 +++++++ src/codec/tp5.mjs | 215 ++++ src/fonts.js | 3 + test/authoring.mjs | 83 ++ test/fontpreview.mjs | 53 + test/png.mjs | 44 + test/roundtrip.mjs | 62 ++ test/sheet.mjs | 51 + test/structdiff.mjs | 27 + test/tp5roundtrip.mjs | 20 + tools/build-fonts.mjs | 28 + tools/bundle.mjs | 59 ++ 33 files changed, 6214 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 app/index.html create mode 100644 app/main.mjs create mode 100644 dist/DestoGod.html create mode 100644 re/affine.py create mode 100644 re/chk16.py create mode 100644 re/disx.py create mode 100644 re/find_const.py create mode 100644 re/findtab.py create mode 100644 re/parse.py create mode 100644 re/pe.py create mode 100644 re/tryranges.py create mode 100644 re/verify.py create mode 100644 re/xref.py create mode 100644 samples/EXPRESS 1.td5 create mode 100644 samples/EXPRESS.tp5 create mode 100644 samples/README.md create mode 100644 samples/desto-test.xlsx create mode 100644 src/codec/bitfont.mjs create mode 100644 src/codec/sheet.mjs create mode 100644 src/codec/td5.mjs create mode 100644 src/codec/tp5.mjs create mode 100644 src/fonts.js create mode 100644 test/authoring.mjs create mode 100644 test/fontpreview.mjs create mode 100644 test/png.mjs create mode 100644 test/roundtrip.mjs create mode 100644 test/sheet.mjs create mode 100644 test/structdiff.mjs create mode 100644 test/tp5roundtrip.mjs create mode 100644 tools/build-fonts.mjs create mode 100644 tools/bundle.mjs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8211f44 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +node_modules/ + +# generated by the test scripts +test/authored.td5 +test/fontpreview.png diff --git a/README.md b/README.md new file mode 100644 index 0000000..920c6e6 --- /dev/null +++ b/README.md @@ -0,0 +1,239 @@ +# DestoGod + +A replacement for **TP5**, the 2012 Windows program used to program the LED +destination signs on Yutong buses. + +`dist/DestoGod.html` is the whole application — one file, no installer, no +internet. Double-click it and it opens in any browser. + +--- + +## Why this exists + +TP5 is a 417 KB Chinese MFC application from November 2012, supplied by +Guangzhou Tongda with the signs Yutong fits to its buses. It has no live +preview, so building a destination list is guess-and-check, and the workflow is +Windows-only. + +The bus never talks to TP5. The sign controller reads a `.td5` file off an SD +card, and that is the entire interface. So a replacement does not need to touch +any hardware or protocol — it only has to write byte-correct `.td5` files. + +That is what this does. + +## What it does + +- **Opens the files the company already has.** Drop in a `.td5` (what goes on + the SD card) or a `.tp5` (the TP5 project). Drop in both and it uses the text + from the project with the exact artwork from the bus file. +- **Live sign preview.** The message is drawn as an LED matrix at the real size + of the real sign, and scrolls exactly as it will on the bus. TP5 shows you + nothing until you export. +- **Tells you when a message will not fit** and by how many pixels. +- **Imports a spreadsheet.** Drop in an `.xlsx` (or CSV) and it reads the + `Line | Line Name | Description | Content (Display)` layout of the template + these buses ship with — `Line Name` becomes what the driver sees on the + controller, `Content (Display)` becomes what the sign shows. Names that + already exist are updated rather than duplicated. No library involved: the + `.xlsx` is unzipped and parsed by the browser itself. +- **Paste a whole list at once** instead of typing destinations one at a time. +- **Delete a destination** with the × on its row in the list, or clear the whole + list at once. Both offer an Undo instead of asking you to confirm first — a + confirm box is friction while tidying a list and still leaves you stuck if you + confirm by mistake. +- **Refuses to ship a blank destination.** Tapping "+" one time too many leaves + an entry with nothing in it; those are flagged in the list and the export + stops with a one-click "Remove and export". A new destination starts genuinely + empty rather than pre-filled with placeholder text, because placeholder text + is worse than blank here — "NEW DESTINATION" will happily print itself on the + front of a bus. +- **Keeps the original artwork** for any destination you have not edited, so + re-exporting an existing list changes nothing about it. +- **Exports `.td5`** for the SD card, and **`.tp5`** so the old software can + still open the list during the changeover. + +## How the file formats were worked out + +Everything below was derived from the sample files and from `TP5(En).exe` +itself, then verified byte-for-byte. + +### `.td5` — the file the bus reads + +``` +FILE + 0x000 16 magic, GBK "广州通达图形线路" + 0x010 4 "V5.0" + 0x018 12 export timestamp, ASCII YYMMDDhhmmss + 0x030 2 company count + 0x040 2 sign height, sign width in bytes (16, 14 = 112x16) + 0x080 16 company name \ repeats every 0x20 per company + 0x090 6 pointer-table offset, destination count + 0x200 .. u32 offset per destination, terminated by 0xffffffff + 0x400 .. destination records; unused space is filled with 0xff + +DESTINATION record header is 0x80 bytes; its block always starts at +0x200 + +0x00 16 name + +0x10 16 name again — THIS is the copy the driver's controller displays, + and TP5 always writes it identical to the first (verified across + all 36 records). If the two are allowed to differ, the sign shows + the right words but the controller shows the wrong ones. See + setName() in app/main.mjs; export forces them equal, and opening + a file repairs a pair that has already drifted. + +0x30 8 block offset, block length + records are padded up to the next 0x200 boundary + +BLOCK + +0x00 1 0x43 'C' + +0x01 2 CRC-16/ARC over block[3 .. blockLength] + +0x03 4 block id — TP5 stores rand(), so it never exceeds 0x7fff + +0x07 4 block length + +0x0b 3 frame count, three times + +0x0f 1 0x84 + +0x10 6 per frame: u32 bitmap offset, u8 width in bytes, u8 height + .. two 0x80-byte action sections per frame + .. the frame bitmaps + header length = 0x10 + frames*6 + frames*0x100 + +BITMAP one 16-byte chunk per 8 pixels of width; each chunk is 8 columns x 16 + rows, row-major, high bit leftmost. Wider than the sign means it scrolls. +``` + +The checksum was the last piece. Bytes `+5` and `+6` are always zero, which hid +a field boundary: `+1` is a 2-byte CRC and `+3` is a 4-byte random id whose top +half is always zero because `RAND_MAX` is `0x7fff`. The CRC is CRC-16/ARC +(reflected, polynomial `0xa001`, init 0), taken over everything from `+3` to the +end of the block. TP5 computes it in `sub_409150`, picking polynomial index 1 +out of the table at `0x447020` = `{0x8480, 0xa001, 0x8621, 0xe950}`, and stores +it in `SetTypeAndCrc` at `0x4353c0`. + +Because the id is random and sits inside the CRC's range, two exports of the +same list are never byte-identical — but any id you choose is valid. + +### `.tp5` — the editable project + +Line-based text, CRLF, one statement per line. Strings are UTF-16BE hex. Each +destination has five screens (front, rear, side, inner, backside), each with +"up" and "down" action lists, each action holding five icons that can carry text +objects. Only the front screen is used in practice. + +### `.font` — the sign's built-in fonts + +`name`, then `cellWidth,height`, then per glyph: character code, advance width, +and `height * ceil(cellWidth/8)` bytes — **each byte XORed with the character +code**. All 18 shipped fonts are included in the app. + +### Rendering to match the existing signs + +The company's current signs were made with the Windows font Impact, which TP5 +hands to GDI. A browser canvas rasterises the same font differently, so the +renderer is calibrated against the real output: TP5's size 20 corresponds to +**15.77 canvas pixels** (scale 0.789, measured across all 36 destinations), and +the alpha threshold is **225**, because GDI lays down far less antialiasing than +canvas does. At those settings the rendered stroke weight, width and height all +land within 2% of TP5's own output. + +Those numbers were measured on a Mac, and macOS, Windows and Linux all rasterise +fonts slightly differently — so they are only the starting point. When both a +`.tp5` and a `.td5` are loaded the app knows the text *and* the exact artwork TP5 +produced for it, so it solves for the size and threshold that reproduce that +artwork **on whatever computer it is running on**, and says so in the preview. +On the development Mac it settles on 0.804 / 245 — a closer match than the +hand-tuned constants. Nothing about this depends on the machine being the one it +was written on. + +## Which computers it runs on + +Any reasonably current browser — Edge, Chrome, Brave or Firefox — on Windows, +macOS or Linux. Edge is already on every Windows 10/11 machine, so on a depot PC +there is nothing to install. Verified opening straight off the disk with no +server (`file://`). + +Two things are worth knowing: + +- **Nothing about the exported file depends on the operating system.** The + `.td5` writer is pure integer work: same list in, same bytes out, on any + machine. The sign's own built-in fonts are bitmaps, so they are identical + everywhere too. Only the system-font (Impact) path touches the OS rasteriser, + and that is what the calibration above corrects for. +- **Impact ships with Windows**, and always has. It is the font TP5 was using + via GDI, so on a Windows machine the lettering has a shorter distance to + travel than it did here. + +The floor is a browser from roughly 2020 (Chrome/Edge 80+, Firefox 75+). It +deliberately avoids the newest APIs — no `structuredClone`, and it falls back +gracefully if `` is unsupported — so an older depot PC is fine. + +## Verification + +```bash +node test/roundtrip.mjs # re-build a real TP5 export and compare byte-for-byte +node test/tp5roundtrip.mjs # same for the project format +node test/authoring.mjs # build a file from scratch, read it back, check CRCs +node test/sheet.mjs # spreadsheet column mapping +node test/fontpreview.mjs "SCHOOL BUS" # render sample text in every built-in font +``` + +`roundtrip.mjs` parses the real `EXPRESS 1.td5`, rebuilds all 46,080 bytes from +the parsed model with a **recomputed** CRC, and requires an exact match. All 36 +stored CRCs verify against the implementation. + +Driven through its own interface — open the project, merge the artwork, press +Export — the app reproduces the company's real file with **7 differing bytes, +all of them the export timestamp**, and no other difference anywhere. + +Retyping a single destination and exporting again changes **only that +destination's block** (plus the timestamp). The other 35 stay byte-identical, so +editing one entry cannot disturb the rest of the list. + +## On the road + +DestoGod files have been loaded onto a real bus. **The signs display correctly +and the driver's controller lists the destinations correctly** — the format is +confirmed against the actual hardware, not just against TP5's output. + +The first road test found the one thing no amount of byte-comparison would have +caught: the destination name is stored twice, and the driver's controller reads +the *second* copy. Renaming a destination updated only the first, so the signs +were right while the controller showed nothing useful. Fixed, forced to stay in +step on both export and import, and confirmed on the bus afterwards. + +Still worth doing when convenient: export the same list from DestoGod and from +TP5 and compare. They should differ only in the timestamp and the random block +ids (and the CRCs that follow from them). + +### What is on the SD card + +Only the `.td5`. TP5 also leaves a folder tree next to it — +`///`, one folder per destination — but +every one of those 36 folders is empty, so it is scratch output, not something +the controller reads. Checked directly rather than assumed, because it was the +obvious suspect when the controller names went missing. + +## Building + +```bash +node tools/bundle.mjs # everything -> dist/DestoGod.html +``` + +`src/fonts.js` is committed, so a fresh clone builds and runs without anything +else. Regenerating it needs the original `.font` files that ship beside TP5, +which are not in this repo: + +```bash +node tools/build-fonts.mjs # ../../yutongapp/*.font -> src/fonts.js +``` + +## Layout + +``` +src/codec/td5.mjs read and write the file the bus reads +src/codec/tp5.mjs read and write the TP5 project file +src/codec/bitfont.mjs the sign's built-in bitmap fonts +src/codec/sheet.mjs .xlsx / .csv reading, no dependencies +src/fonts.js generated from the 18 shipped .font files +app/ the interface +tools/ font conversion, single-file bundler +test/ the checks described above +re/ the reverse-engineering scripts, kept as working notes +dist/DestoGod.html ← the thing to actually use +``` diff --git a/app/index.html b/app/index.html new file mode 100644 index 0000000..cf6e6c2 --- /dev/null +++ b/app/index.html @@ -0,0 +1,198 @@ + + + + + +DestoGod — Bus Destination Sign Editor + + + + +
+
DestoGodbus destination signs
+
+
+
+ + + + + +
+ +
+ +
+
+ +
Drop a .td5, .tp5, spreadsheet or CSV to open
+
+ + +
+

Paste a list of destinations

+

One per line. This is the fast way to build a whole list — far quicker than adding them one at a time.

+ +
+
+ + +
+
+ + + + diff --git a/app/main.mjs b/app/main.mjs new file mode 100644 index 0000000..c72d6fb --- /dev/null +++ b/app/main.mjs @@ -0,0 +1,953 @@ +/** DestoGod — editor for Yutong / Guangzhou-Tongda bus destination signs. */ +import { FONTS } from '../src/fonts.js'; +import { fontFromJSON, renderText, trimX } from '../src/codec/bitfont.mjs'; +import { parseTd5, buildTd5, frameToPixels, pixelsToFrame, EFFECT } from '../src/codec/td5.mjs'; +import { parseTp5, buildTp5, toSimple } from '../src/codec/tp5.mjs'; +import { readXlsx, readCsv, mapRows } from '../src/codec/sheet.mjs'; + +// Sign sizes from the Yutong programming manual (section 2.2). +const MODELS = [ + { label: 'ZK6930 / ZK6938 — 112 × 16', width: 112, height: 16 }, + { label: 'ZK6760 — 80 × 16', width: 80, height: 16 }, + { label: 'ZK6129 / T12 — 144 × 16', width: 144, height: 16 }, + { label: 'Other — 128 × 16', width: 128, height: 16 }, + { label: 'Other — 160 × 16', width: 160, height: 16 }, + { label: 'Other — 192 × 16', width: 192, height: 16 }, + { label: 'Other — 256 × 16', width: 256, height: 16 }, +]; + +const SYSTEM_FONTS = ['Impact', 'Arial Narrow', 'Arial Black', 'Arial', 'Helvetica', 'Verdana', 'Tahoma']; + +/** + * TP5 hands the point size to Windows GDI, which maps it to a smaller em size + * than a browser canvas does for the same number. Measured against all 36 + * destinations in the sample project: TP5 size 20 renders at 15.77 canvas px + * (spread 14.7-16.2, which is just per-string hinting noise). Keeping sizes in + * TP5's units means a .tp5 we write stays compatible with the old software. + */ +const TP5_SIZE_SCALE = 0.789; + +let state = { + company: '', + screen: { ...MODELS[0] }, + destinations: [], + selected: null, + tp5Template: null, // keeps the untouched project structure when a .tp5 was opened +}; + +let uid = 1; +const nextId = () => `d${uid++}`; + +// --------------------------------------------------------------- rendering + +/** Rasterise one page into {width, height, pixels}. */ +function renderPage(page, screen) { + if (page.bitmap && page.pristine) return page.bitmap; + if (!page.text.trim()) return { width: 0, height: screen.height, pixels: new Uint8Array(0) }; + + const bmp = page.fontKind === 'builtin' + ? renderBuiltin(page, screen) + : renderSystemFont(page, screen); + + page.bitmap = bmp; + return bmp; +} + +function renderBuiltin(page, screen) { + const json = FONTS[page.font] ?? FONTS.ASC1609; + const font = fontFromJSON(json); + const bmp = renderText(font, page.text, { tracking: page.tracking ?? 1 }); + return fitVertically(trimX(bmp, 0), screen.height); +} + +/** + * Render with a Windows/macOS font through a canvas and threshold to 1 bit. + * This is how the existing signs were made (TP5 hands the text to GDI), so it + * keeps a converted list looking like what the company already runs. + */ +function renderSystemFont(page, screen) { + const cal = state.calibration; + const useCal = cal && cal.family === page.ttfName; + const scale = useCal ? cal.scale : TP5_SIZE_SCALE; + const threshold = useCal ? cal.threshold : (page.threshold ?? 225); + const ink = rasteriseSystem(page.text, page.ttfName, (page.ptSize ?? 20) * scale, threshold, page.bold); + return fitVertically(ink, screen.height); +} + +/** Draw text with a system font and cut it to 1 bit, trimmed to the ink. */ +function rasteriseSystem(text, family, px, threshold, bold = false) { + const pad = 8; + const css = `${bold ? 'bold ' : ''}${px}px "${family}", sans-serif`; + const cv = document.createElement('canvas'); + let ctx = cv.getContext('2d', { willReadFrequently: true }); + ctx.font = css; + cv.width = Math.max(1, Math.ceil(ctx.measureText(text).width) + pad * 2); + cv.height = Math.max(1, Math.ceil(px * 3)); + + ctx = cv.getContext('2d', { willReadFrequently: true }); + ctx.font = css; + ctx.textBaseline = 'middle'; + ctx.fillStyle = '#fff'; + ctx.fillText(text, pad, cv.height / 2); + + const img = ctx.getImageData(0, 0, cv.width, cv.height).data; + const pixels = new Uint8Array(cv.width * cv.height); + for (let i = 0; i < pixels.length; i++) pixels[i] = img[i * 4 + 3] > threshold ? 1 : 0; + return trimX(trimY({ width: cv.width, height: cv.height, pixels }), 0); +} + +const countLit = (b) => b.pixels.reduce((n, v) => n + v, 0); + +function inkStats(bmp) { + const t = trimX(trimY(bmp), 0); + return { w: t.width, h: t.height, lit: countLit(t) }; +} + +/** + * The one part of this that depends on the computer is how the browser turns a + * system font into pixels — macOS, Windows and Linux all rasterise slightly + * differently, and TP5 itself used Windows GDI. So rather than trust a constant + * measured on one machine, measure against the company's own signs: we know the + * text (from the .tp5) and the exact artwork TP5 produced (from the .td5), so we + * can solve for the size and threshold that reproduce it on THIS machine. + */ +function calibrateSystemFont(samples) { + if (samples.length < 3) return null; + const family = samples[0].family; + let best = null; + + for (const threshold of [128, 170, 200, 225, 245]) { + // first pass: what size reproduces the original widths? + const ratios = samples + .map((s) => { + const r = rasteriseSystem(s.text, family, s.ptSize * TP5_SIZE_SCALE, threshold); + return r.width ? s.w / r.width : 0; + }) + .filter(Boolean) + .sort((a, b) => a - b); + if (!ratios.length) continue; + const scale = TP5_SIZE_SCALE * ratios[Math.floor(ratios.length / 2)]; + + // second pass: at that size, how close is the stroke weight? + let lit = 0, wErr = 0, hErr = 0, n = 0; + for (const s of samples) { + const r = rasteriseSystem(s.text, family, s.ptSize * scale, threshold); + if (!r.width || !s.lit) continue; + lit += countLit(r) / s.lit; + wErr += Math.abs(r.width - s.w) / s.w; + hErr += Math.abs(r.height - s.h) / s.h; + n++; + } + if (!n) continue; + const litRatio = lit / n; + const score = Math.abs(litRatio - 1) + wErr / n + hErr / n; + if (!best || score < best.score) { + best = { family, scale, threshold, litRatio, widthErr: wErr / n, heightErr: hErr / n, score, samples: n }; + } + } + return best; +} + +function trimY(b) { + let lo = b.height, hi = -1; + for (let y = 0; y < b.height; y++) { + for (let x = 0; x < b.width; x++) { + if (b.pixels[y * b.width + x]) { if (y < lo) lo = y; if (y > hi) hi = y; break; } + } + } + if (hi < 0) return { width: 0, height: 0, pixels: new Uint8Array(0) }; + const h = hi - lo + 1; + const out = new Uint8Array(b.width * h); + out.set(b.pixels.subarray(lo * b.width, (hi + 1) * b.width)); + return { width: b.width, height: h, pixels: out }; +} + +/** Centre a bitmap in a canvas `height` rows tall, cropping if it is too tall. */ +function fitVertically(b, height) { + if (b.height === height) return b; + const out = new Uint8Array(b.width * height); + const y0 = Math.floor((height - b.height) / 2); + for (let y = 0; y < b.height; y++) { + const ty = y + y0; + if (ty < 0 || ty >= height) continue; + out.set(b.pixels.subarray(y * b.width, (y + 1) * b.width), ty * b.width); + } + return { width: b.width, height, pixels: out }; +} + +// ------------------------------------------------------------- LED display + +/** Draw a bitmap as an LED matrix, windowed to the sign and optionally scrolling. */ +function drawLED(canvas, bmp, screen, { scale = 4, offset = 0, gap = 1 } = {}) { + const cw = screen.width, chh = screen.height; + const cell = scale + gap; + canvas.width = cw * cell; + canvas.height = chh * cell; + const ctx = canvas.getContext('2d'); + ctx.fillStyle = '#000'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + const scrolling = bmp.width > cw; + // A message that fits is centred, exactly as the sign controller shows it. + const x0 = scrolling ? -offset : Math.floor((cw - bmp.width) / 2); + + for (let y = 0; y < chh; y++) { + for (let x = 0; x < cw; x++) { + let sx = x - x0; + if (scrolling) sx = ((sx % (bmp.width + cw)) + (bmp.width + cw)) % (bmp.width + cw); + const on = sx >= 0 && sx < bmp.width && bmp.height > 0 && bmp.pixels[y * bmp.width + sx]; + ctx.fillStyle = on ? '#ffb000' : '#1a1508'; + ctx.fillRect(x * cell, y * cell, scale, scale); + } + } + return scrolling; +} + +let animTimer = null; +function startPreview(canvas, bmp, screen, effect) { + clearInterval(animTimer); + const scrolls = bmp.width > screen.width && effect === EFFECT.SCROLL; + if (!scrolls) { drawLED(canvas, bmp, screen, { scale: 6 }); return; } + let off = 0; + const span = bmp.width + screen.width; + animTimer = setInterval(() => { + drawLED(canvas, bmp, screen, { scale: 6, offset: off }); + off = (off + 1) % span; + }, 45); +} + +// ------------------------------------------------------------------ import + +function newPage(text = '') { + return { + text, + fontKind: 'system', + ttfName: 'Impact', + ptSize: 20, + bold: false, + threshold: 225, + font: 'ASC1609', + tracking: 1, + effect: EFFECT.SCROLL, + bitmap: null, + pristine: false, + }; +} + +function newDestination(name = 'NEW DESTINATION') { + return { id: nextId(), name: name.slice(0, 16), lineName: name.slice(0, 16), pages: [newPage(name)], blockId: null }; +} + +function loadTd5(bytes) { + const doc = parseTd5(bytes); + state.company = doc.company; + state.screen = { ...(MODELS.find((m) => m.width === doc.screen.width) ?? doc.screen) }; + state.tp5Template = null; + state.destinations = doc.destinations.map((d) => ({ + id: nextId(), + name: d.name, + lineName: d.name, // repairs a file whose two name copies drifted apart + blockId: d.blockId, + pages: d.frames.map((f, i) => { + const p = newPage(''); + p.bitmap = { width: f.width, height: f.height, pixels: frameToPixels(f) }; + p.pristine = true; // keep TP5's exact artwork until edited + p.effect = d.effects[i] ?? EFFECT.SCROLL; + return p; + }), + })); + return `Opened ${state.destinations.length} destinations from the bus file.`; +} + +function loadTp5(text) { + const doc = parseTp5(text); + const simple = toSimple(doc); + state.company = simple.company; + state.screen = { ...(MODELS.find((m) => m.width === simple.screen.width) ?? simple.screen) }; + state.tp5Template = doc; + state.destinations = simple.destinations.map((d) => ({ + id: nextId(), + name: d.name, + lineName: d.name, // repairs a file whose two name copies drifted apart + blockId: null, + pages: d.frames.map((f) => { + const p = newPage(f.text); + p.effect = f.effect; + if (f.useTrueType && f.ttfName) { p.fontKind = 'system'; p.ttfName = f.ttfName; p.ptSize = f.ptSize; } + else { p.fontKind = 'builtin'; p.font = FONTS[f.font] ? f.font : 'ASC1609'; } + return p; + }), + })); + return `Opened ${state.destinations.length} destinations, with their text, from the project file.`; +} + +/** A .tp5 carries the text; a .td5 carries the exact artwork. Merge by name. */ +function mergeArtworkFromTd5(bytes) { + const doc = parseTd5(bytes); + let hits = 0; + const samples = []; + for (const d of doc.destinations) { + const target = state.destinations.find((x) => x.name.trim() === d.name.trim()); + if (!target) continue; + d.frames.forEach((f, i) => { + const page = target.pages[i]; + if (!page) return; + const bitmap = { width: f.width, height: f.height, pixels: frameToPixels(f) }; + page.bitmap = bitmap; + page.pristine = true; + hits++; + if (page.fontKind === 'system' && page.text.trim()) { + const s = inkStats(bitmap); + if (s.w && s.lit) samples.push({ text: page.text, family: page.ttfName, ptSize: page.ptSize, ...s }); + } + }); + target.blockId = d.blockId; + } + + // Calibrate the lettering against the signs this company already runs. + const dominant = samples.filter((s) => s.family === (samples[0]?.family ?? '')); + state.calibration = calibrateSystemFont(dominant.slice(0, 24)); + return hits; +} + +// ------------------------------------------------------------------ export + +function toTd5Doc() { + const dests = state.destinations.map((d) => { + const frames = d.pages.map((p) => { + const bmp = renderPage(p, state.screen); + const width = Math.max(8, bmp.width); + const padded = bmp.width === width ? bmp + : { width, height: state.screen.height, pixels: new Uint8Array(width * state.screen.height) }; + return pixelsToFrame(padded.pixels, padded.width, state.screen.height); + }); + const nf = frames.length; + const effects = []; + for (let k = 0; k < nf * 2; k++) effects.push(d.pages[k % nf].effect); + return { + // Both copies always carry the same text, as TP5 writes them. The second + // is what the driver's controller displays. + name: d.name, lineName: d.name, + frames, effects, blockId: d.blockId ?? undefined, + }; + }); + return { company: state.company, screen: state.screen, destinations: dests }; +} + +function exportTd5() { + if (!state.destinations.length) return toast('Nothing to export yet.', true); + + // Don't let a stray empty destination reach a bus: the driver would see a + // selectable entry that displays nothing. + const blanks = state.destinations.filter(isBlank); + if (blanks.length) { + return toast( + `${blanks.length} destination${blanks.length === 1 ? '' : 's'} ${blanks.length === 1 ? 'has' : 'have'} no text — ` + + `${blanks.length === 1 ? 'it' : 'they'} would show as blank on the sign.`, + true, + 'Remove and export', + () => { for (const b of blanks) state.destinations.splice(state.destinations.indexOf(b), 1); renderAll(); exportTd5(); }, + ); + } + // TP5 makes you create a company before you can add destinations, so a file + // with a blank one is a shape the controller has never been given. Cheap to + // avoid: fill it in rather than shipping an empty field. + if (!state.company.trim()) { + state.company = 'DESTINATIONS'; + renderHeader(); + toast("Company name was empty, so 'DESTINATIONS' was used — change it at the top if you want something else."); + } + const bytes = buildTd5(toTd5Doc()); + download(bytes, `${safeName(state.company || 'destinations')}.td5`, 'application/octet-stream'); + toast(`Exported ${state.destinations.length} destinations. Copy this file to the SD card.`); +} + +/** + * Save a .tp5 so the old TP5 software can still open the list. When the project + * was opened from a .tp5 we edit that structure in place, which keeps every + * field we do not model (icons, rear/side screens) exactly as it was. + */ +function exportTp5() { + if (!state.destinations.length) return toast('Nothing to save yet.', true); + const template = state.tp5Template ?? blankProject(); + const proto = template.companies[0].destinations[0]; + const company = template.companies[0]; + company.name = state.company; + company.destinations = state.destinations.map((d) => { + const rec = deepClone(proto); + rec.lineName = d.name; + rec.lineName2 = d.name; + const protoAct = deepClone(proto.screens.fore.up[0]); + rec.screens.fore.up = d.pages.map((p) => { + const act = deepClone(protoAct); + const icon = act.icons[0]; + icon.fields[4] = String(p.effect); + icon.fields[6] = '1'; + const obj = icon.objs[0] ?? { + type: 'S', unknown1: '-3', x: 0, y: 0, font: 'ASC0704', + text: '', useTrueType: 1, ttfName: 'Impact', unknown2: '0', ptSize: 20, rest: [], + }; + obj.text = p.text; + if (p.fontKind === 'system') { obj.useTrueType = 1; obj.ttfName = p.ttfName; obj.ptSize = p.ptSize; } + else { obj.useTrueType = 0; obj.font = p.font; } + const bmp = renderPage(p, state.screen); + obj.x = Math.max(0, Math.floor((state.screen.width - bmp.width) / 2)); + icon.objs = [obj]; + return act; + }); + return rec; + }); + download(new TextEncoder().encode(buildTp5(template)), `${safeName(state.company || 'destinations')}.tp5`, 'text/plain'); + toast('Project saved. The old TP5 software can open this file too.'); +} + +function blankProject() { + const w = state.screen.width, h = state.screen.height; + const screen = (width) => ({ params: [String(width), String(h), '11316396', '0', '255', '1', '10', '6'], up: [], down: [] }); + const act = { + fields: ['0', '10', '5', '3', '0', '0', '0'], + icons: [ + { fields: ['0', '0', String(w), String(h), '9', '0', '1'], objs: [] }, + { fields: ['0', '0', '0', '0', '3', '0', '0'], objs: [] }, + { fields: ['0', '0', '0', '0', '3', '0', '0'], objs: [] }, + { fields: ['0', '0', '256', String(h), '3', '0', '0'], objs: [] }, + { fields: ['0', '0', '256', String(h), '3', '0', '0'], objs: [] }, + ], + }; + const dest = { + actIconFiles: [], srnIconFiles: [], lineName: '', lineName2: '', + screens: { + fore: { ...screen(w), up: [act] }, + bcak: screen(256), side: screen(256), inner: screen(256), backside: screen(256), + }, + }; + return { + version: 'V1.5.3', flag: '0', + screenPara: ['1', '0', String(h), String(w), '0', '0', '8', '4', '0', + '1', String(h), '256', '0', '0', '8', '4', '0', '0', + '2', String(h), '256', '0', '0', '8', '4', '0', '0', + '4', String(h), '256', '0', '0', '8', '4', '0', + '3', String(h), '256', '0', '0', '8', '4'], + companies: [{ name: state.company, destinations: [dest] }], + }; +} + +// --------------------------------------------------------------------- UI + +const $ = (s) => document.querySelector(s); +const el = (tag, props = {}, ...kids) => { + const n = Object.assign(document.createElement(tag), props); + for (const k of kids) n.append(k); + return n; +}; + +/** Depot PCs can be old, so avoid structuredClone (2022) and dialog fallbacks. */ +function deepClone(v) { + if (v === null || typeof v !== 'object') return v; + if (v instanceof Uint8Array) return new Uint8Array(v); + if (Array.isArray(v)) return v.map(deepClone); + const out = {}; + for (const k of Object.keys(v)) out[k] = deepClone(v[k]); + return out; +} + +function showDialog(dlg) { + if (typeof dlg.showModal === 'function') return dlg.showModal(); + dlg.setAttribute('open', ''); + dlg.style.cssText = 'display:block;position:fixed;top:12%;left:50%;transform:translateX(-50%);z-index:200'; +} + +function closeDialog(dlg) { + if (typeof dlg.close === 'function' && dlg.open !== undefined && typeof dlg.showModal === 'function') return dlg.close(); + dlg.removeAttribute('open'); + dlg.style.display = 'none'; +} + +/** + * A destination with nothing to display — usually left behind by tapping "+" + * one time too many. It would still reach the bus as a selectable but blank + * entry, so it is flagged in the list and caught on export. + */ +const isBlank = (d) => + d.pages.every((p) => !p.text.trim() && !(p.pristine && p.bitmap && p.bitmap.width)); + +function renderAll() { renderHeader(); renderList(); renderEditor(); } + +function renderHeader() { + $('#company').value = state.company; + const sel = $('#model'); + if (!sel.options.length) { + for (const m of MODELS) sel.append(el('option', { value: m.width, textContent: m.label })); + } + const known = MODELS.some((m) => m.width === state.screen.width); + if (!known) { + const label = `Custom — ${state.screen.width} × ${state.screen.height}`; + let opt = [...sel.options].find((o) => o.dataset.custom); + if (!opt) { opt = el('option', { value: state.screen.width }); opt.dataset.custom = '1'; sel.append(opt); } + opt.value = String(state.screen.width); + opt.textContent = label; + } + sel.value = String(state.screen.width); + const has = state.destinations.length > 0; + $('#btn-export').disabled = !has; + $('#btn-export-tp5').disabled = !has; + $('#count').textContent = state.destinations.length; +} + +function renderList() { + const list = $('#list'); + list.textContent = ''; + if (!state.destinations.length) { + list.append(el('div', { className: 'empty', textContent: 'No destinations yet. Open a file, paste a list, or add one.' })); + return; + } + state.destinations.forEach((d, i) => { + const item = el('div', { className: 'item' + (d.id === state.selected ? ' sel' : '') }); + item.onclick = () => { state.selected = d.id; renderAll(); }; + const body = el('div', { className: 'body' }); + body.append(el('div', { className: 'nm', textContent: d.name || '(new — type a name)' })); + const cv = el('canvas'); + const bmp = renderPage(d.pages[0], state.screen); + drawLED(cv, bmp, state.screen, { scale: 1, gap: 0 }); + cv.style.width = Math.min(230, state.screen.width) + 'px'; + body.append(cv); + if (d.pages.length > 1) body.append(el('div', { className: 'tag', textContent: `${d.pages.length} pages` })); + if (isBlank(d)) body.append(el('div', { className: 'tag warn', textContent: 'empty — nothing to show' })); + + const del = el('button', { className: 'del', textContent: '×', title: `Delete ${d.name}` }); + del.onclick = (e) => { e.stopPropagation(); remove(d); }; + + item.append(el('div', { className: 'num', textContent: i + 1 }), body, del); + list.append(item); + }); +} + +/** + * A destination record stores its name twice, and the driver's controller reads + * the second copy. TP5 always writes the two identically (verified across every + * record in the sample file), so they must never be allowed to drift apart — + * if they do, the sign shows the right words but the controller shows the wrong + * ones, or none at all. + */ +function setName(d, value) { + d.name = value.slice(0, 16); + d.lineName = d.name; +} + +/** The preview is its own card so it can be redrawn without rebuilding the form. */ +function buildPreviewCard(d) { + const prev = el('div', { className: 'card', id: 'preview-card' }); + prev.append(el('h3', { textContent: 'What the sign will show' })); + const stage = el('div', { className: 'led-stage' }); + const canvas = el('canvas'); + stage.append(canvas); + prev.append(stage); + + const page0 = d.pages[state.previewPage ?? 0] ?? d.pages[0]; + const bmp = renderPage(page0, state.screen); + startPreview(canvas, bmp, state.screen, page0.effect); + + const meta = el('div', { className: 'meta' }); + const over = bmp.width - state.screen.width; + meta.append(el('span', {}, el('b', { textContent: `${state.screen.width} × ${state.screen.height}` }), ' sign')); + meta.append(el('span', {}, el('b', { textContent: `${bmp.width}px` }), ' wide')); + if (over > 0) { + meta.append(el('span', { className: 'pill ' + (page0.effect === EFFECT.SCROLL ? 'scroll' : 'bad') }, + page0.effect === EFFECT.SCROLL ? `Too long — scrolls (${over}px over)` : `Too long by ${over}px — set it to Scroll, or shorten it`)); + } else { + meta.append(el('span', { className: 'pill ok', textContent: 'Fits on the sign' })); + } + if (page0.pristine) meta.append(el('span', { className: 'pill ok', textContent: 'Original artwork kept' })); + const cal = state.calibration; + if (cal && cal.family === page0.ttfName && !page0.pristine) { + meta.append(el('span', { className: 'pill ok', + title: `Matched to your existing signs on this computer: size ×${cal.scale.toFixed(3)}, threshold ${cal.threshold}, ` + + `from ${cal.samples} of them. Width within ${(cal.widthErr * 100).toFixed(1)}%, weight within ${(Math.abs(cal.litRatio - 1) * 100).toFixed(1)}%.`, + textContent: `Lettering matched to your signs (±${(cal.widthErr * 100).toFixed(1)}%)` })); + } + prev.append(meta); + + if (d.pages.length > 1) { + const row = el('div', { className: 'meta' }); + row.append(el('span', { textContent: 'Preview page:' })); + d.pages.forEach((_, i) => { + const b = el('button', { textContent: String(i + 1), style: 'padding:2px 10px' }); + if ((state.previewPage ?? 0) === i) b.className = 'primary'; + b.onclick = () => { state.previewPage = i; updatePreview(d); }; + row.append(b); + }); + prev.append(row); + } + return prev; +} + +/** Redraw only the preview — typing must never rebuild the box being typed in. */ +function updatePreview(d) { + const old = $('#preview-card'); + if (old) old.replaceWith(buildPreviewCard(d)); +} + +/** Refresh one sidebar row in place, for the same reason. */ +function updateListItem(d) { + const i = state.destinations.indexOf(d); + const item = $('#list').children[i]; + if (!item) return renderList(); + item.querySelector('.nm').textContent = d.name || '(new — type a name)'; + const cv = item.querySelector('canvas'); + if (cv) drawLED(cv, renderPage(d.pages[0], state.screen), state.screen, { scale: 1, gap: 0 }); + + const warn = item.querySelector('.tag.warn'); + const blank = isBlank(d); + if (blank && !warn) { + item.querySelector('.body').append(el('div', { className: 'tag warn', textContent: 'empty — nothing to show' })); + } else if (!blank && warn) { + warn.remove(); + } +} + +function renderEditor() { + const host = $('#editor'); + host.textContent = ''; + const d = state.destinations.find((x) => x.id === state.selected); + + if (!d) { + const w = el('div', { className: 'welcome' }); + w.append( + el('h1', { textContent: state.destinations.length ? 'Pick a destination' : 'Welcome' }), + el('p', { textContent: state.destinations.length + ? 'Choose one from the list on the left to edit it.' + : 'Open the destination file from the bus (.td5), or the TP5 project (.tp5), and everything will load here. You can also start from scratch.' }), + ); + if (!state.destinations.length) { + const btns = el('div', { className: 'btns' }); + btns.append( + el('button', { className: 'primary', textContent: 'Open a file…', onclick: () => $('#file').click() }), + el('button', { textContent: 'Import a spreadsheet', onclick: () => $('#file').click() }), + el('button', { textContent: 'Paste a list', onclick: openBulk }), + el('button', { textContent: 'Add one destination', onclick: addOne }), + ); + w.append(btns); + } + host.append(el('div', { className: 'wrap' }, w)); + return; + } + + const wrap = el('div', { className: 'wrap' }); + wrap.append(buildPreviewCard(d)); + + // ---- name + const nameCard = el('div', { className: 'card' }); + nameCard.append(el('h3', { textContent: 'Destination' })); + const nrow = el('div', { className: 'row' }); + const nameBox = el('div'); + nameBox.append(el('label', { textContent: "Name on the driver's controller (16 characters)" })); + const nameIn = el('input', { type: 'text', value: d.name, maxLength: 16 }); + // The controller reads its own copy of this name from the file, so both + // copies have to move together — see setName(). + nameIn.oninput = () => { setName(d, nameIn.value); updateListItem(d); }; + nameBox.append(nameIn); + nrow.append(nameBox); + nameCard.append(nrow); + + const acts = el('div', { className: 'row', style: 'margin-top:14px' }); + acts.append( + el('button', { textContent: 'Duplicate', onclick: () => duplicate(d) }), + el('button', { textContent: 'Move up', disabled: state.destinations.indexOf(d) === 0, onclick: () => move(d, -1) }), + el('button', { textContent: 'Move down', disabled: state.destinations.indexOf(d) === state.destinations.length - 1, onclick: () => move(d, 1) }), + el('button', { className: 'danger', textContent: 'Delete', onclick: () => remove(d) }), + ); + nameCard.append(acts); + wrap.append(nameCard); + + // ---- pages + const pagesCard = el('div', { className: 'card' }); + pagesCard.append(el('h3', { textContent: d.pages.length > 1 ? 'Pages (the sign alternates between these)' : 'Message' })); + d.pages.forEach((p, i) => pagesCard.append(pageEditor(d, p, i))); + const addRow = el('div', { className: 'row' }); + addRow.append(el('button', { textContent: '+ Add another page', onclick: () => { d.pages.push(newPage('')); renderAll(); } })); + pagesCard.append(addRow); + wrap.append(pagesCard); + + host.append(wrap); +} + +function pageEditor(d, p, i) { + const box = el('div', { className: 'page' }); + const head = el('div', { className: 'page-head' }); + if (d.pages.length > 1) head.append(el('span', { className: 'lbl', textContent: `Page ${i + 1}` })); + head.append(el('div', { style: 'flex:1' })); + if (d.pages.length > 1) { + head.append(el('button', { className: 'danger ghost', textContent: 'Remove page', onclick: () => { d.pages.splice(i, 1); renderAll(); } })); + } + box.append(head); + + const textBox = el('div'); + textBox.append(el('label', { textContent: 'Text shown on the sign' })); + const ti = el('input', { type: 'text', value: p.text }); + // Typing must not re-render the editor, or the input is destroyed mid-keystroke + // and the caret is lost — redraw only the preview and the sidebar row. + ti.oninput = () => { p.text = ti.value; p.pristine = false; p.bitmap = null; refreshLight(d); }; + textBox.append(ti); + box.append(textBox); + + if (p.pristine && !p.text) { + box.append(el('div', { className: 'hint', + textContent: 'This came from the bus file, which only stores the finished picture — not the words. The sign will keep showing exactly what it shows now. Type the text above if you want to change it.' })); + } + + const row = el('div', { className: 'row', style: 'margin-top:12px' }); + + const fontBox = el('div'); + fontBox.append(el('label', { textContent: 'Lettering' })); + const fsel = el('select'); + const og1 = el('optgroup', { label: 'Computer fonts (match your current signs)' }); + for (const f of SYSTEM_FONTS) og1.append(el('option', { value: `sys:${f}`, textContent: f })); + const og2 = el('optgroup', { label: "Sign's own built-in fonts (sharpest)" }); + for (const [name, j] of Object.entries(FONTS)) { + if (j.height > state.screen.height) continue; + og2.append(el('option', { value: `bi:${name}`, textContent: `${name} (${j.cellWidth}×${j.height})` })); + } + fsel.append(og1, og2); + fsel.value = p.fontKind === 'system' ? `sys:${p.ttfName}` : `bi:${p.font}`; + fsel.onchange = () => { + const [kind, val] = fsel.value.split(':'); + if (kind === 'sys') { p.fontKind = 'system'; p.ttfName = val; } else { p.fontKind = 'builtin'; p.font = val; } + p.pristine = false; p.bitmap = null; refresh(); + }; + fontBox.append(fsel); + row.append(fontBox); + + if (p.fontKind === 'system') { + const szBox = el('div'); + szBox.append(el('label', { textContent: 'Size' })); + const si = el('input', { type: 'number', value: p.ptSize, min: 6, max: 60 }); + si.oninput = () => { p.ptSize = Number(si.value) || 20; p.pristine = false; p.bitmap = null; refreshLight(d); }; + szBox.append(si); + row.append(szBox); + } + + const fxBox = el('div'); + fxBox.append(el('label', { textContent: 'If the text is too long' })); + const seg = el('div', { className: 'seg' }); + const mk = (label, value) => { + const b = el('button', { textContent: label, className: p.effect === value ? 'on' : '' }); + b.onclick = () => { p.effect = value; refresh(); }; + return b; + }; + seg.append(mk('Hold still', EFFECT.HOLD), mk('Scroll', EFFECT.SCROLL)); + fxBox.append(seg); + row.append(fxBox); + + box.append(row); + return box; +} + +/** Full rebuild — for changes that alter which controls are on screen. */ +const refresh = () => { renderList(); renderEditor(); }; + +/** Preview-only update — for anything driven by typing. */ +const refreshLight = (d) => { updatePreview(d); updateListItem(d); }; + +// ------------------------------------------------------------------ actions + +/** + * A new destination starts genuinely empty rather than pre-filled with + * placeholder text — placeholder text is worse than blank here, because + * "NEW DESTINATION" will cheerfully print itself on the front of a bus. + * Empty means it gets flagged in the list and refused at export. + */ +function addOne() { + const d = newDestination(''); + state.destinations.push(d); + state.selected = d.id; + renderAll(); + const input = $('#editor input[type=text]'); + if (input) input.focus(); +} + +function duplicate(d) { + const copy = deepClone({ ...d, id: undefined, blockId: null }); + copy.id = nextId(); + copy.pages.forEach((p) => { p.pristine = false; p.bitmap = null; }); + state.destinations.splice(state.destinations.indexOf(d) + 1, 0, copy); + state.selected = copy.id; + renderAll(); +} + +function move(d, dir) { + const i = state.destinations.indexOf(d); + const j = i + dir; + if (j < 0 || j >= state.destinations.length) return; + state.destinations.splice(j, 0, state.destinations.splice(i, 1)[0]); + renderAll(); +} + +/** + * Deleting offers an undo rather than asking first. A confirm box on every + * delete is friction when you are tidying a list, and it still leaves you stuck + * if you confirm by mistake; undo covers both. + */ +function remove(d) { + const i = state.destinations.indexOf(d); + if (i < 0) return; + state.destinations.splice(i, 1); + if (state.selected === d.id) { + state.selected = state.destinations[Math.min(i, state.destinations.length - 1)]?.id ?? null; + } + renderAll(); + toast(`Deleted "${d.name || 'unnamed'}".`, false, 'Undo', () => { + state.destinations.splice(Math.min(i, state.destinations.length), 0, d); + state.selected = d.id; + renderAll(); + toast(`Put "${d.name || 'unnamed'}" back.`); + }); +} + +/** Remove every destination, undoably — for starting a list over. */ +function removeAll() { + if (!state.destinations.length) return; + const previous = state.destinations; + const n = previous.length; + state.destinations = []; + state.selected = null; + renderAll(); + toast(`Cleared all ${n} destinations.`, false, 'Undo', () => { + state.destinations = previous; + state.selected = previous[0].id; + renderAll(); + toast(`Put all ${n} back.`); + }); +} + +function openBulk() { $('#bulk-text').value = ''; showDialog($('#bulk')); } + +// ------------------------------------------------------------------- files + +/** Bring in destinations from a spreadsheet, appending to whatever is loaded. */ +function importRows(rows, label) { + const { company, destinations } = mapRows(rows); + if (!destinations.length) { + return toast(`No destinations found in that ${label}. Expected a column of destination text.`, true); + } + if (company && !state.company) state.company = company; + + let added = 0, updated = 0; + for (const r of destinations) { + const existing = state.destinations.find((x) => x.name.trim().toUpperCase() === r.name.toUpperCase()); + if (existing) { + setName(existing, r.name); + const p = existing.pages[0]; + p.text = r.text; p.pristine = false; p.bitmap = null; + updated++; + } else { + const d = newDestination(r.name); + setName(d, r.name); + d.pages[0].text = r.text; + d.pages[0].bitmap = null; + state.destinations.push(d); + added++; + } + } + state.selected = state.destinations[state.destinations.length - 1]?.id ?? state.selected; + renderAll(); + toast(`${label}: added ${added}${updated ? `, updated ${updated}` : ''} destination${added === 1 && !updated ? '' : 's'}.`); +} + +async function openFile(file) { + try { + const name = file.name.toLowerCase(); + if (name.endsWith('.xlsx') || name.endsWith('.xlsm')) { + const rows = await readXlsx(new Uint8Array(await file.arrayBuffer())); + return importRows(rows, 'Spreadsheet'); + } + if (name.endsWith('.csv') || name.endsWith('.txt')) { + return importRows(readCsv(await file.text()), 'CSV'); + } + if (name.endsWith('.tp5')) { + const text = await file.text(); + toast(loadTp5(text)); + } else if (name.endsWith('.td5')) { + const bytes = new Uint8Array(await file.arrayBuffer()); + if (state.destinations.length && state.tp5Template) { + const hits = mergeArtworkFromTd5(bytes); + toast(hits ? `Matched the original artwork for ${hits} pages.` : 'No matching destinations found in that file.'); + } else { + toast(loadTd5(bytes)); + } + } else { + return toast('Open a .td5, .tp5, .xlsx or .csv file.', true); + } + state.selected = state.destinations[0]?.id ?? null; + state.previewPage = 0; + renderAll(); + } catch (err) { + toast(`Could not open that file: ${err.message}`, true); + } +} + +function download(bytes, filename, type) { + const url = URL.createObjectURL(new Blob([bytes], { type })); + const a = el('a', { href: url, download: filename }); + document.body.append(a); + a.click(); + a.remove(); + setTimeout(() => URL.revokeObjectURL(url), 1000); +} + +const safeName = (s) => s.replace(/[^\w.-]+/g, '_').replace(/^_+|_+$/g, '') || 'destinations'; + +let toastTimer; +function toast(msg, isError = false, actionLabel = null, actionFn = null) { + const t = $('#toast'); + t.textContent = msg; + if (actionLabel && actionFn) { + const b = el('button', { textContent: actionLabel }); + b.onclick = () => { t.className = ''; actionFn(); }; + t.append(b); + } + t.className = 'on' + (isError ? ' err' : ''); + clearTimeout(toastTimer); + toastTimer = setTimeout(() => { t.className = ''; }, actionLabel ? 9000 : 4200); +} + +// -------------------------------------------------------------------- wire + +$('#btn-open').onclick = () => $('#file').click(); +$('#btn-sheet').onclick = () => { $('#file').dataset.sheet = '1'; $('#file').click(); }; +$('#file').onchange = (e) => { if (e.target.files[0]) openFile(e.target.files[0]); e.target.value = ''; }; +$('#btn-add').onclick = addOne; +$('#btn-clear').onclick = removeAll; +$('#btn-bulk').onclick = openBulk; +$('#btn-export').onclick = exportTd5; +$('#btn-export-tp5').onclick = exportTp5; + +$('#company').oninput = (e) => { state.company = e.target.value; }; +$('#model').onchange = (e) => { + const m = MODELS.find((x) => String(x.width) === e.target.value); + if (m) state.screen = { ...m }; + for (const d of state.destinations) for (const p of d.pages) if (!p.pristine) p.bitmap = null; + renderAll(); +}; + +$('#bulk-cancel').onclick = () => closeDialog($('#bulk')); +$('#bulk-ok').onclick = () => { + const lines = $('#bulk-text').value.split('\n').map((s) => s.trim()).filter(Boolean); + for (const line of lines) state.destinations.push(newDestination(line)); + if (lines.length) state.selected = state.destinations[state.destinations.length - lines.length].id; + closeDialog($('#bulk')); + renderAll(); + toast(`Added ${lines.length} destination${lines.length === 1 ? '' : 's'}.`); +}; + +let dragDepth = 0; +addEventListener('dragenter', (e) => { e.preventDefault(); if (++dragDepth === 1) $('#drop').classList.add('on'); }); +addEventListener('dragleave', (e) => { e.preventDefault(); if (--dragDepth <= 0) { dragDepth = 0; $('#drop').classList.remove('on'); } }); +addEventListener('dragover', (e) => e.preventDefault()); +addEventListener('drop', (e) => { + e.preventDefault(); + dragDepth = 0; + $('#drop').classList.remove('on'); + if (e.dataTransfer.files[0]) openFile(e.dataTransfer.files[0]); +}); + +renderAll(); diff --git a/dist/DestoGod.html b/dist/DestoGod.html new file mode 100644 index 0000000..04c4262 --- /dev/null +++ b/dist/DestoGod.html @@ -0,0 +1,2072 @@ + + + + + + +DestoGod — Bus Destination Sign Editor + + + + +
+
DestoGodbus destination signs
+
+
+
+ + + + + +
+ +
+ +
+
+ +
Drop a .td5, .tp5, spreadsheet or CSV to open
+
+ + +
+

Paste a list of destinations

+

One per line. This is the fast way to build a whole list — far quicker than adding them one at a time.

+ +
+
+ + +
+
+ + + + diff --git a/re/affine.py b/re/affine.py new file mode 100644 index 0000000..3741a5a --- /dev/null +++ b/re/affine.py @@ -0,0 +1,38 @@ +import struct +from parse import records + +rs = records() +oc = [r for r in rs if r['name'].startswith('OC')] +assert len(oc)==8 +L = set(len(r['blk']) for r in oc) +print('lengths', L) + +b = [r['blk'] for r in oc] +f = [r['field'] for r in oc] + +# show differing byte positions +n = len(b[0]) +diff = [i for i in range(n) if len(set(x[i] for x in b))>1] +print('differing byte offsets:', [hex(i) for i in diff]) +for i in diff: + print(hex(i), [hex(x[i]) for x in b]) + +# GF(2) affine test: for equal-length messages, if h is affine over GF(2), +# then h(A)^h(B)^h(C)^h(D) == 0 whenever A^B^C^D == 0. +# Find quadruples among the 8 whose message-XOR is 0. +import itertools +def bx(x,y): + return bytes(p^q for p,q in zip(x,y)) +found=0 +for c in itertools.combinations(range(8),4): + m = bytes(n) + for i in c: m = bx(m,b[i]) + if m == bytes(n): + v = 0 + for i in c: v ^= f[i] + print('quad', c, 'msgxor=0 fieldxor=%08x' % v) + found+=1 +print('quads with zero msg xor:', found) + +# Alternative affine test using the base-block trick: +# Build a matrix over the differing bytes only. diff --git a/re/chk16.py b/re/chk16.py new file mode 100644 index 0000000..0cc0dd9 --- /dev/null +++ b/re/chk16.py @@ -0,0 +1,28 @@ +import struct +from parse import records + +rs = records() +for r in rs: + b = r['blk'] + r['chk'] = struct.unpack_from('H', b, 1)[0] + r['rnd'] = struct.unpack_from('3 else '/Users/jing/Documents/yutong/yutongapp/TP5(En).exe' +d = open(EXE,'rb').read() +BASE = 0x400000 +pe = pefile.PE(EXE) + +# import name map +imp = {} +for e in pe.DIRECTORY_ENTRY_IMPORT: + for i in e.imports: + imp[i.address] = (e.dll.decode(), i.name.decode() if i.name else 'ord%d'%i.ordinal) + +md = Cs(CS_ARCH_X86, CS_MODE_32) +md.detail = True + +start = int(sys.argv[1],16) +n = int(sys.argv[2],16) if len(sys.argv)>2 else 0x200 + +off = start - BASE +for ins in md.disasm(d[off:off+n], start): + s = "%08x %-24s %s %s" % (ins.address, ins.bytes.hex(), ins.mnemonic, ins.op_str) + # annotate indirect calls + if ins.mnemonic in ('call','jmp') and 'dword ptr [0x' in ins.op_str: + try: + a = int(ins.op_str.split('[')[1].split(']')[0],16) + if a in imp: s += ' ; %s!%s' % imp[a] + except: pass + print(s) diff --git a/re/find_const.py b/re/find_const.py new file mode 100644 index 0000000..3f9e6cf --- /dev/null +++ b/re/find_const.py @@ -0,0 +1,14 @@ +import re +d = open('/Users/jing/Documents/yutong/yutongapp/TP5(En).exe','rb').read() +def hits(pat, lo=0x1000, hi=0x46000): + return [m.start() for m in re.finditer(re.escape(pat), d[lo:hi])] +for name,pat in [('0x11684', b'\x84\x16\x01\x00'), ('0x11684 push', b'\x68\x84\x16\x01\x00')]: + h = hits(pat) + print(name, [hex(x+0x1000) for x in h]) +print('--- MSVCRT imports ---') +import pefile +p = pefile.PE('/Users/jing/Documents/yutong/yutongapp/TP5(En).exe') +for e in p.DIRECTORY_ENTRY_IMPORT: + if b'MSVCRT' in e.dll.upper() or b'KERNEL32' in e.dll.upper(): + for imp in e.imports: + print(e.dll.decode(), '%08x' % imp.address, imp.name.decode() if imp.name else imp.ordinal) diff --git a/re/findtab.py b/re/findtab.py new file mode 100644 index 0000000..1ecb802 --- /dev/null +++ b/re/findtab.py @@ -0,0 +1,46 @@ +import struct, re +EXES = ['/Users/jing/Documents/yutong/yutongapp/TP5(En).exe', + '/Users/jing/Documents/yutong/yutongapp/TP5(Cn).exe'] + +def crc_table16(poly, reflect): + t=[] + for i in range(256): + if reflect: + c=i + for _ in range(8): + c = (c>>1) ^ (poly if c&1 else 0) + else: + c=i<<8 + for _ in range(8): + c = ((c<<1)^poly)&0xffff if c&0x8000 else (c<<1)&0xffff + t.append(c&0xffff) + return t + +def crc_table32(poly, reflect): + t=[] + for i in range(256): + if reflect: + c=i + for _ in range(8): + c = (c>>1) ^ (poly if c&1 else 0) + else: + c=i<<24 + for _ in range(8): + c = ((c<<1)^poly)&0xffffffff if c&0x80000000 else (c<<1)&0xffffffff + t.append(c&0xffffffff) + return t + +polys16 = [0x1021,0x8408,0x8005,0xa001,0x3d65,0xa6bc,0xc867,0x0589,0x8bb7,0x8d95,0x1DCF,0x755B] +for path in EXES: + d=open(path,'rb').read() + print('===', path, len(d)) + for p in polys16: + for refl in (0,1): + t = crc_table16(p, refl) + blob = b''.join(struct.pack(' %s' % (p,refl, hex(i) if i>=0 else '-')) + # generic immediate search for 16-bit polys used in bitwise loop: + # look for "xor ax, imm16" = 66 35 xx xx or "xor eax, imm32" = 35 xx xx xx xx + for m in re.finditer(rb'\x66\x35(..)', d, re.S): + pass diff --git a/re/parse.py b/re/parse.py new file mode 100644 index 0000000..5192f80 --- /dev/null +++ b/re/parse.py @@ -0,0 +1,33 @@ +import struct, sys + +PATH = "/Users/jing/Documents/yutong/test files/EXPRESS 1.td5" + +def load(): + return open(PATH, 'rb').read() + +def records(d=None): + if d is None: d = load() + out = [] + off = 0x200 + ptrs = [] + while True: + p = struct.unpack_from('> 1) ^ poly if c & 1 else c >> 1 + return c & 0xffff + +rs = records() +for r in rs: + r['chk'] = struct.unpack_from('> 1) ^ poly if c & 1 else c >> 1 + return c & 0xFFFF + +def td5_block_checksum(block): + """block: the full block bytes (length == u32 at block[7:11]). Returns u16 for block[1:3].""" + blen = struct.unpack_from(' + pat = b'\xff\x15' + struct.pack('5*#$P~u3hTHq^PYy5p+c>TaHCnvQ+uZL;@($m1HOBjKXWsdK)}h ziWW<2WJ#`ra2Qgm>xLqpC9BU>+@*R(l@9z73-=FV|#y+@n^XI>uz4_jMj7KK_?bk=2 z|8kk~OGVkUuWaw0{k!*+Rg{;lnxIHAAvoP(x8m!?yj^XteY>NhGzH2QMS0fr8>!Dz zNAdot&r?U)ZXJQtxTKEO*40(z2}Mz!M6sj%9?DZF&!Ci`{2|I0P`-pxhO!Ukt0-SX zc>%?T(t`30l;bGhK{<)?8cG*RH%cGMn<#&c@;1s3Q2rj}9h3o-AEW#f<>x5xDyAnd z_Eh4Z#0Yp_KHTJOZZ1{*wQ3z+5)n_mH>snt6|Vtk)Z7<5xd-B@&r?V9cjS3~jDUKq zys24xq(wzV%9~FWfeq|nj-c3g{4PB@MJYBNN_Q}(AGC(t*Tj)F^7qf2X>V)m?rv*q zZ}06zSMJ=obNhBOdHXg_?f{>|DL$CHMgV+qd17K>c@Y(W@Z{9gRB|zj`nSu#6S)4` zRKhblI+{!nzIf}_STx}o85vn5++9;yS?Tehs;nW->*yaG9CW!*4LTga1A*{Of?Jc5 zw}^8(+-MsZ7({O-Bfx{>0EdI>AaU}j8Gy&qu(SkxU|`_l#fw81E?hW&{ygwV1mENe zHSqo+m&~j_0;=O1U6|78ksAE}Ny-1KdQP8i>#DN1wzl?E;r*@d z?d!pLns7%E{U08iYje> z&?XYDmr0WDTArScCc2#Xgz((GJ8E5RQ;WALMc$<%ZZzm8$__>Ok);3JJ%A0qllsJS z&z#mCsX_mrlJswL*i+!J5&aVN+H6!aJ-4^FcXZ6pcbx9*JdNQ4eTPpFR4go1Q~-}J z;EK5^Q~=-$3%DW$z#|ddo-6yz4Dd0Q*R(pCOZv|CdNtP_X=(9!4FN#JGwA=ba{kXY zriIk5_J8Kt7v=m9610FY-zqZ%D=T~;dL=};xv%O(zllP_4)T9q$7x4`G zcPh#UQvT02tOe7q{`v6g%i1F~=zp7(|3!POdkD(C+f5q2%LRJvXM1Ts(=*viM@RQR z?V`5ru)zXH!^Fffz{_184{+FPUCt5**;CyP;J9`WoB=hC+VylVf(Y@0+1a$jK=|rv zAPvALCJb?d`3sq<*VKsHCY?3p|4mE(q;FTUJGQch z=4Xe4CTg0k2id-V_AGmTxjKvXlxYvgg$3YlH+u%C;`!O>O#54(rt4XL2w#IsH$wh^ zASQmh9rYL-xa*m}KkmE6#Q(MC!%g*#FTUjW9}@8l`hN}~K{Ed1;|_b?d|%z)Gz0pl zM{3CbpO*4JneBBrbB5^~1gb%p=vDXUL4Ch>?>g|^<>i+zU!F_ACD1|k{^;E0U%dJ6 z|MmLoufH}w1-$!vMW@GuZ@>Nh!Ih#wH=^rmDzA*ZcQF8a-c_<6Rtz-tKS z3P<2h1Z)fpX!QVa{OtYv_gC)Ty^Br(uj+xNklmA=y>xv?M@M<(R{_tIIH9p4-eZt_ zi=(5%b8T(F7xhpT@+U3=@9x%2o2YxxJ*9{Iof`D6OP|P_7h87Zp+-QhZEEoP5dP=$ zd0+Ix{v+TS{Qt{}a$nki9%^h0;Ucy2);7ELNDcmPm;7J!|Au0*d-t|(y*E8OdxN5w z`}>0&FV{LV)7lC=Oy=CUR>y(A2ha>aumJ&2ay%eanm(CU4#(L4(@n0QXq1>R$9q&) zr`T_24cU`L`*PYkr1_I#nm$T4q6N}VLov(%#VD{^Uj{Dto6>Klzp0_Vxy9d@;s7F^ zLH~OYA}-~>^Z*w4nHu{1g+}d>8uY(I(!WT5qC8eY(X&IELO2Nef>mgqsf{Y&{je7g zT?|yTg*LNt*{^1I= z;u-uO-V7=K75rm2{(u@j_HCqV(IYkZzm)%=Pg~=ESsLrrm13~UYSPrX^3>D-7x^#q zYvR^x=+~Gf-gx7U-czSeA!rcacay&V5&0iMh?_b@Xg)E?gYUl&T+27n%=(STpDuw!5=u$%2=N0vGLfqCN=yG#KP6I6I5$DWA(VFv$HI2kxNI1qXFf z%lFc=yU@Z?VBx>l@*h0h2sHScyrljgtbK)N01?mN{|9CM@57C3VO*vD=Zzn{B=diX z@tFa_*&AohzH_TTIS zTOe1c-VfWqqdihX|Noq{{}vkmcTm0}SE#|iLzG|;KtYlL#Sh2hG&8Z4Vvgbw+B;qa zTpJ4kt^toGE?q+U0Vba|)-S|-=Lf_yaYO>DAdW}t1dd>=504y#2w+6W1S<(-6I14_v32%Rd|pWnBDrmLyVI%pgNr$Tyja<~b*qiUhfw^)3AlGyAqxR#dtosL z=&^n_0>(d@OcL)N89}?~-x1?|NKwCPrFy=~kPWyw--PUsePa~t zL-p8LpH+I#+gDX~>=PIPwbpmEwh6wUx+cVd*4K)72LC@M?SG%ZW7x1;s$cn|-{>Y? zga1qWU&eZWqW=Z)0c21klN!fc!m$pa{mPa1f4#u@z>vI85Tw@w!?pA2^}yQNjsuwe za@1uuO=O7HAK(f{@b4hIqO_X)`Qm)_fc1wsR|)a!(-aQ`|4#&P^Dq8q`s7LA)SKhS z5hFwZe8b|=H;fQA=?|-_T3X+T6(H5xhQmjicZ+xi|NoYZ|9YHW74^x3FF>WEln#Uc z%lQ8c{_hs!Gl?HNou~$RJr_;~FDjy_{j+3A0efctdh_Ou8#jQTIz|2#JxUAs>?}C2 z7N5oWUe_Z}Q3s_kAPy@^?;`-3p;&tP3Bch7qwN_Utlu8HO zFCI4{c-M?4=Jn|2VRypyZ2zaPAWDq<0h#&)=^BwpN}oU`3T-#^{he<2&Yid(_AhpV zLxq@}1P;$O1t6cizOr)tI&g7)JE1+VNnlfVj`;o2dbP#hR9{UR{^`X+vxs!z6txE!@!Zwc;LVR za$1*{mjl0w^(hPhuFL*7V|bk2c==z)(dw}r$-+|md{K{n!1{6ljOXo`3cp87m>G+!gn^J4_mZ~f$7zMwr) zgZ`f|^nVIn1DFq+WB-?8Er5@r_=@S@Pnq&(r+_CGuZ)cJ^lK$5k%R>BIz4Nb~+a&!Hmoo!-LUgc^{uI(xFH)dFz^633 z8ny@T;P~BWf}(MwQAE?ydY*;zCivBI3yF)krIG0WElK}{^K`TKRNede-+@b?L^?VD zr#ZmG&_CAcXoGbIOn zTI&Cg=Gi=oUr{|LP9pwG&i_^p;BcheF)sGU1jrwwK_q}nSsk$1qEH9I=a~L=89-Y< zD~F@OGr!F#Ts)euk>~j_0_xWJvZu61YS{m` z8UFuGyZ#S?BM($p(*~*lyvHQy8vvaBe<5-LVpRv=S>gdQ;^T#Q9`k-Kyi%O;TXcf^ zi~9-YEZ!dUzK1sbtZzJ2>%;z^BA!A2|0wr=$`8uTO)u0BedS-5`#*uiEKmY))1EE_ zXc71KOC*tLKSkAO68`>-{rS3}V^2C>!Pg$u)1uMY3V^{JI7p`~d1L#?&{ z6X?CXRP}j}dVRHxhnW5?cn1CdOwxaDC@(+mqBa~#maIwt=Dh|(DMg89$b(48&f)R zeE$SHf&=g1{TR~uz30!LzkU05607H=2%1P_iJbSlD?l@JKDva0r0KM1iO}&cI`s@UBJxDyBYxR o#{R{pPxo|FhtMxEe+=tYW4qz15*reLYasp?&Zf1!>%NB{r; literal 0 HcmV?d00001 diff --git a/samples/EXPRESS.tp5 b/samples/EXPRESS.tp5 new file mode 100644 index 0000000..b40b834 --- /dev/null +++ b/samples/EXPRESS.tp5 @@ -0,0 +1,1091 @@ +V1.5.3; +0; +screen para:1,0,16,112,0,0,8,4,0,1,16,256,0,0,8,4,0,0,2,16,256,0,0,8,4,0,0,4,16,256,0,0,8,4,0,3,16,256,0,0,8,4; +Company Sum:1; +0045007800700072006500730073,36; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004500580050005200450053005300200043004f004100430048,004500580050005200450053005300200043004f004100430048,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,0,0,0041005300430030003700300034,004500580050005200450053005300200043004f0041004300480020004c0049004e00450053,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:005300430048004f004f004c0020004200550053,005300430048004f004f004c0020004200550053,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,16,0,0041005300430030003700300034,005300430048004f004f004c0020004200550053,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:0043004800410052005400450052,0043004800410052005400450052,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,27,0,0041005300430030003700300034,0043004800410052005400450052,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:00530048005500540054004c00450020004200550053,00530048005500540054004c00450020004200550053,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,15,0,0041005300430030003700300034,00530048005500540054004c00450020004200550053,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:005200410049004c0020004200550053,005200410049004c0020004200550053,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,3,0,1; +Obj #0:S,-3,28,0,0041005300430030003700300034,005200410049004c0020004200550053,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:0043005200450057002000530048005500540054004c00450020,0043005200450057002000530048005500540054004c00450020,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,9,0,0041005300430030003700300034,0043005200450057002000530048005500540054004c0045,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004500560045004e00540020004200550053,004500560045004e00540020004200550053,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,23,0,0041005300430030003700300034,004500560045004e00540020004200550053,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:0047004f0020004c0049004f004e00530021,0047004f0020004c0049004f004e00530021,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,25,0,0041005300430030003700300034,0047004f0020004c0049004f004e00530021,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004c0049004f004e005300400053005000520049004e00470057004f004f0044,004c0049004f004e005300400053005000520049004e00470057004f004f0044,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,0,0,0041005300430030003700300034,004c0049004f004e005300400053005000520049004e00470057004f004f0044,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004300410052004d0045004c00200043004f004c004c004500470045,004300410052004d0045004c00200043004f004c004c004500470045,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,3,0,0041005300430030003700300034,004300410052004d0045004c00200043004f004c004c004500470045,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004f0052004d004900530054004f004e00200043004f004c004c004500470045,004f0052004d004900530054004f004e00200043004f004c004c004500470045,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,0,0,0041005300430030003700300034,004f0052004d004900530054004f004e00200043004f004c004c004500470045,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:005300480045004c0044004f004e00200043004f004c004c004500470045,005300480045004c0044004f004e00200043004f004c004c004500470045,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,0,0,0041005300430030003700300034,005300480045004c0044004f004e0043004f004c004c004500470045,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:00480049004c004c0043005200450053005400200043004f004c004c00450047,00480049004c004c0043005200450053005400200043004f004c004c00450047,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,0,0,0041005300430030003700300034,00480049004c004c0043005200450053005400200043004f004c004c004500470045,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004d004f005200450054004f004e002000420041005900200043004f004c004c,004d004f005200450054004f004e002000420041005900200043004f004c004c,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,0,0,0041005300430030003700300034,004d004f005200450054004f004e002000420041005900200043004f004c004c004500470045,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004d004f005200450054004f004e002000420041005900200042004f00590053,004d004f005200450054004f004e002000420041005900200042004f00590053,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,0,0,0041005300430030003700300034,004d004f005200450054004f004e002000420041005900200042004f0059005300200043004f004c004c004500470045,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:0043004800490053004f004c004d00200043004100540048004f004c00490043,0043004800490053004f004c004d00200043004100540048004f004c00490043,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:2; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,3,0,0; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +act #1:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,0,0,0041005300430030003700300034,00430048004900530048004f004c004d00200043004100540048004f004c0049004300200043004f004c004c004500470045,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:00530033003900320020005300430048004f004f004c00200042005500530020,00530033003900320020005300430048004f004f004c00200042005500530020,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,0,0,0041005300430030003700300034,00530033003900320020005300430048004f004f004c0020004200550053,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:00430041004c0056004100520059002000430048005200490053005400490041,00430041004c0056004100520059002000430048005200490053005400490041,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,0,0,0041005300430030003700300034,00430041004c0056004100520059002000430048005200490053005400490041004e00200043004f004c004c004500470045,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:00430043004300200049004e00540045005200430041004d0050005500530031,00430043004300200049004e00540045005200430041004d0050005500530031,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,0,0,0041005300430030003700300034,00430043004300200049004e00540045005200430041004d00500055005300200031,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:00430043004300200049004e00540045005200430041004d0050005500530032,00430043004300200049004e00540045005200430041004d0050005500530032,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,0,0,0041005300430030003700300034,00430043004300200049004e00540045005200430041004d00500055005300200032,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:0043004300430020004d005400200043004f00540054004f004e002f00430041,0043004300430020004d005400200043004f00540054004f004e002f00430041,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,0,0,0041005300430030003700300034,0043004300430020004d005400200043004f00540054004f004e002f0043004100500041004c004100420041,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:00430043004300200056004900430054004f00520049004100200050004f0049,00430043004300200056004900430054004f00520049004100200050004f0049,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,0,0,0041005300430030003700300034,00430043004300200056004900430054004f00520049004100200050004f0049004e0054,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:0043004300430020004f0052004d004500410055,0043004300430020004f0052004d004500410055,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,15,0,0041005300430030003700300034,0043004300430020004f0052004d004500410055,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004f00430031,004f00430031,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,3,0,1; +Obj #0:S,-3,44,0,0041005300430030003700300034,004f00430031,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004f00430032,004f00430032,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,3,0,1; +Obj #0:S,-3,43,0,0041005300430030003700300034,004f00430032,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004f00430033,004f00430033,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,3,0,1; +Obj #0:S,-3,43,0,0041005300430030003700300034,004f00430033,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004f00430034,004f00430034,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,3,0,1; +Obj #0:S,-3,43,0,0041005300430030003700300034,004f00430034,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004f00430035,004f00430035,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,3,0,1; +Obj #0:S,-3,42,0,0041005300430030003700300034,004f00430035,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004f00430036,004f00430036,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,3,0,1; +Obj #0:S,-3,42,0,0041005300430030003700300034,004f00430036,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004f00430037,004f00430037,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,3,0,1; +Obj #0:S,-3,44,0,0041005300430030003700300034,004f00430037,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004f00430038,004f00430038,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,3,0,1; +Obj #0:S,-3,42,0,0041005300430030003700300034,004f00430038,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004d004200430031002000420055004c0049004d00420041,004d004200430031002000420055004c0049004d00420041,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,9,0,0041005300430030003700300034,004d004200430031002000420055004c0049004d00420041,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004d00420043003200200043004100520049004e00440041004c0045,004d00420043003200200043004100520049004e00440041004c0045,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,2,0,0041005300430030003700300034,004d00420043003200200043004100520049004e00440041004c0045,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004d00420043003300200043004c004500560045004c0041004e0044,004d00420043003300200043004c004500560045004c0041004e0044,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,3,0,1; +Obj #0:S,-3,2,0,0041005300430030003700300034,004d00420043003300200043004c004500560045004c0041004e0044,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004d004200430034002000570059004e004e0055004d0020004d0041004e004c,004d004200430034002000570059004e004e0055004d0020004d0041004e004c,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,0,0,0041005300430030003700300034,004d004200430034002000570059004e004e0055004d0020004d0041004e004c0059,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +ActIconFileNames:0; +SrnIconFileNames:0; +line name:004d0042004300200049004e00540045005200430041004d005000550053,004d0042004300200049004e00540045005200430041004d005000550053,; +fore screen: +112,16,11316396,0,255,1,10,6; +Up Act Sum:1; +act #0:0,10,5,3,0,0,0; +icon #0:0,0,112,16,9,0,1; +Obj #0:S,-3,0,0,0041005300430030003700300034,004d0042004300200049004e00540045005200430041004d005000550053,1,0049006d0070006100630074,0,20; +icon #1:0,0,0,0,3,0,0; +icon #2:0,0,0,0,3,0,0; +icon #3:0,0,256,16,3,0,0; +icon #4:0,0,256,16,3,0,0; +Down Act Sum:0; +bcak screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +side screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +inner screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; +backside screen: +256,16,11316396,0,255,1,10,6; +Up Act Sum:0; +Down Act Sum:0; diff --git a/samples/README.md b/samples/README.md new file mode 100644 index 0000000..6d53809 --- /dev/null +++ b/samples/README.md @@ -0,0 +1,14 @@ +# Samples + +Real files from the operator this was built for, used by the test suite as the +reference for "what TP5 actually produces". + +- `EXPRESS.tp5` — a TP5 project: the destination list with its text +- `EXPRESS 1.td5` — the same list exported for the bus, made by TP5 itself. + This is the file `test/roundtrip.mjs` rebuilds byte-for-byte. +- `desto-test.xlsx` — a small sheet in the layout of the template these buses + ship with, for exercising spreadsheet import. + +The `.td5`/`.tp5` pair is the ground truth for the whole format. Replacing them +with anything else means the round-trip test is no longer checking against a +known-good TP5 export. diff --git a/samples/desto-test.xlsx b/samples/desto-test.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..41172bcc8276dbb0d3921b2323d93fd691dc09b2 GIT binary patch literal 5127 zcmZ`-1yq#l)*iYg1_WsZhHjLh%b`5wj|kr0qB32|sf zDf!3ay2o?)@BMOpYt6Ig-OqYU2Swd zT-`hc@4LAP_&PgjA+<<4g-G$2+U|MRzlvrqxOP`2I=cla@F={F+d1rX7lkY5;^)`T zkS7pz3sjc|*%w%I78U6ulb>Wel^q<&jm}*Qs1TkHiLIh1FJNKD>B@7S3Jw=01rM#% zS8h!RD8nBiQx$#QN$P>RR-j^%Rh%rPbBx3k8~z-!F+dH8m%MnFG5Uiv`q}FPuIILR zZ3IoUy8m)PS_<@d8?*F5EC7J=pDx(Adcc19P?)Ht(F|b_~e>|_gT*Ey4kvBUrBkNl> z(^t_UZb}W`N7d=G_W=qeNz$wavE}Wz)cDmpxrVx&h6L2hAHse_qS!wYS3+XqsgfZ? z;;BM2NARxXA={iG%|{l-UdP>J3@Ll)n!s17$T$u5tWHfaQ+FD@7tbHGaAls=g}C;t zi=34P`wMF>d0lH5srcY{vDYwboA=-o58TJka0mKfJD}CP^?3!$g?R4q%(r2=SmO}8 zrKz?koqsd)$~=>e0v`a#E65g&cwXlIh% zJ0W#7Ku>$`*_u~jC~cN-N@$#upKW4TP@BpfiosMe)C;Yq3~5}xt+Nq`%El(x?{G)p z!Dd8c@`11QXTjOY=6b<}C1VkGN(G@CZxDO!#D$ucwWjufE^y!nAXV2~Xbho-FZJN0 z`H~sEAlu~En%Q}yFngz5Iu%>%KubP5`Hu#Armj}3HD6dnf6xpX#KH2hfW``-#>kq3 z6Q`c@nFU!Ga!Zm)B*aCC0m>RQ;`(My+ca|7ptHN(0WN3c*f%<+8XWo|Fd45&4V^zu zLnuNfC7A^s@qUc%M&-l~%y@}8(=V;=`-1BR$70+VahLR(vpchnoZ1Q$h-&ifKiW>C zXDSklO5*f&#=i$HZfDqK633b8j63?wkH<&#mVfjmYu(p*)>RSJTl&#!+aAPuQQ=sy zb=^);d3D>Lil+99{5UHB>gGK;G=`IE$$k1Lnf^0V^Spxj9&|?{it=Y~5ALM>*o{;% zWsj@`ylVjQ?omzM#|VYG=%vmVW9A8yNbXu)dcySnkYkn5n>}-gi{qEg9|}bMvjQbY z9Fv3WOR?Udnlq3|=boEuId77tv=m3&TKI_e&)CfFKjU5tHMi6DVMWZ`b( zkxcGby*OJSykk7ZY(d$@toy7&cxFmWfXl|%W4OQ10FlH!Es@jy?ZQqrdmnZBnLP}V zZoU>@^-9AHGc(1V)3hBR8 zYM@ta#c^)Rnv(=)rzJKLkKaph?K;`&@ugpp_>9{am}cy}F+DnZw$Y(inZGNY%G4Km zkau$@HzEz$rS!}%z$y)%(yJ>*?d(*>&Zk^&-7vM!N4;cg&HtLzwgA2D0D&$|JHmr^;lq#N} z=T}c%lB85MGyQrfvAiJIY$=TR*J(x8ba!4P-~+LSlRw~a zQZ6eI1mSxcp=~+FK}8*1Ns4!q)?;;dH_5>#cIuQb#5v4B#P$>IfKpV4jHrjwv;oIq z#SDlfe0IfPxS75cWUbNV?~=vOc{yu2AyY#uqkNeEYKgh}nP-N7Lnox{5<1CzBC;sq zHY>S+W4R9GQ!04U5f~jEOj-J%wF#kf!jUOe6R;n`nsGikTY#QlB7HnjYFcQ^ODU&8 z@R;QVzD=yzk(kwU>IdaTRpq@i!lZt|&_mMPhv3C$kaT*{PcdZ@am20)pX9&D%}$Wn zp~=J;6Y(sCU)9~oU(+U*p8L5=R@vDdzl@4$!}@mt=~HEHbi@V#EC~Pry59xF$JN8p z(+&pn@)Z2*^;Z>1NwIL95u%Q`j7}*<73K0 z#>60Z<||fg&SZA|oOn4U2~WyA0CARdUmPgbOligM7r$eqGosA{xER$bij-+_`Q-Aj zFKKZ+5NmO$ZMxYNM&NERJPc!ux~JcHUm%g^M%Tgd^N#nek?JTrF8k$;8#AQ2E)V^N* z4413+l_p0}yxLQ8{Zgm+#qrCsA6kAoP=#BYuJ1-qScQ6=tYb7vXjj|_REkDaqPQo) z@daR3`}?b0)i{p!cc@&*?cWx@;_OxHi?YUBN7zN*n7f;Mr*Jx4NQ&4su(R(TNvl#+ z`})EJ9A@`zo4x{b$-D)DyiIaut!TbcaLDQr^CT5ieqTu_lr_24fL}3MpIjWeGPMg= zoxN?}Jn)cZQ?@m>Gcc(HAgwm$w6wW4m} z4CzIf=<%%GE4p+#BFeK;I%1&60!FrSe)pc0Mf?jz-9=j%nIap)Ye-H5$DP= z$6$Ba_m%rrb%7hqgG|fBdeiB#?>C0GuY3<9P!(bTfb6$GJ-z&#V4lBvn|Y(z=vg6(3z}8(;-s|n26n&V zo4R&`ojXcaU!dB!O%3l_*e^~U*f;7jR_~n}Wh}jtkQ9X%2#=KbaDK=k&um^RF4&pc z>S)~2Pd#qNF)q8bmaLE&qc2T6teA%f!X+I2Z*jlke^`A>Uvtx|dhF>8&lIPWs9n7_ z-2R2LQTgc9N9j1Mwp8xy7Li76UJ%3?Vm_3d@P^JRtbat+2DtIbElo5^h1B>-MBhMogaZX77)+*~2#joNBDJvy7|izlm8r)^w#?ULKt2 zy)(B^%b=8zvR2L(L35@gl)1I3$FLE5tCZ5oRV+q*q-J!l(Z_rME{R}4S%lbRbjMwe zg~_~$M;001P8S~D)D4;S5FXa0ROc0LQcOc;K`64(P zrVPt^@`f^5T;i*OD(BHA)7^2mi&m+e#i|+d6AP@il4X8|f`G>0?IVYv)i^o>Ka=CT zadD*;Xk*C%KldzsE6vRa7C-&CiutJy&6efCcw_69@VaGe3R?(;JZ`6{F1{^jYv(a?JnyNAT(T#s0EZB(b_fg9ky^Sp^ZM-+@P83%6rYCN=QRRFq!8N z)O-lU5fZ)=lt$(|kYs~nmQANOeb7Au#6>57VMO?V$y?W?slj^e*b{krIAzjrJR^vg z9c8gnIKu2yJC3g7%@l{axLNLT3>?10r3jH%&Q^-FoF*b1V7*B(aQNe9;GxXm$YSHS z(<%2uhJRgxC_&f*eAwK4lENKoMb z!AF-+HO+0yv|u$t3Z(#~sZVWccgm_uC4y6pTXQ9xvu|qh!+XojNOGw>CG`EcLr(!i z-m&4sIb(h(3Q>$h`;e^a6P4-0#)KHOBf4ZEH0iM^b#-Qkoq|Ve&q!$qnk99Y&I@G* zsCp=G5~s~in_q1_axfT=Tk=ESApac%e6kom{ZQqoLFws%Ny*Wj*CH|3__K2b40A;m z$F>sbkZX4{w0dkc--5cVN%)`_iQ=Q{%?l?5kqhs8h6>YZu%=l9)EJ@=Pd~7QeaYPp z-x(#`QzwxYI%aAL>AiI2 z2;!eVr6?h$jpIlxni5Z%wQdz(7qAoDiSoKwb(H>kK=N;LMYmU*$}yy#VF;%GP3~_Y z{gdLqL{*z8bM2`RX*h!F3xKY8A{V`)&&t z6pId!(wmZ^IS1;G+v+vYjSCOS?&@l=*17t|&kJsJb@cOicN?7B^@k%W11Lre86k-&|rX%og>TGiRX^MkFxdk?p#a){;7 zCx`RGjNXqt_hL6-5>c4ct3wvKEQ654G-X)W)HwfpXoFGBUq>*8;r~Cvxr)9zFZqoH z0D`f){)zt2^yDi1>QLbid>-@n|7FB*)xg!J@^1qn%!Gd#_^&PIRV!C3|KC=y+AzHU zW*L9_fUD4}dHWAk8q-7kBa2@JUd@AlfE?HUUip8r;#Kg~IR66<#pG1Xg8xUXR(V+MHtC9rfg@GvtV0DuT{%3?Bj^{*ir;Qs&&0OQdB literal 0 HcmV?d00001 diff --git a/src/codec/bitfont.mjs b/src/codec/bitfont.mjs new file mode 100644 index 0000000..d774fa0 --- /dev/null +++ b/src/codec/bitfont.mjs @@ -0,0 +1,133 @@ +/** + * The .font files shipped alongside TP5 — the sign's own bitmap fonts. + * + * NAME, + * cellWidth,height, + * code, + * advance, + * 0xNN, x (height * ceil(cellWidth/8)) <- each byte XORed with (code & 0xff) + * ... + * + * Rows are stored MSB-first, leftmost pixel in the high bit, exactly like the + * bitmaps inside a .td5, which is why text rendered here can be written + * straight into a frame. + */ + +/** @returns {{name: string, cellWidth: number, height: number, glyphs: Map}} */ +export function parseFont(text) { + const tok = text.split(/[,\r\n]+/).map((s) => s.trim()).filter(Boolean); + const name = tok[0]; + const cellWidth = parseInt(tok[1], 10); + const height = parseInt(tok[2], 10); + const bytesPerRow = Math.ceil(cellWidth / 8); + + const glyphs = new Map(); + let i = 3; + while (i < tok.length) { + const code = parseInt(tok[i++], 10); + const advance = parseInt(tok[i++], 10); + if (!Number.isFinite(code) || !Number.isFinite(advance)) break; + const rows = []; + for (let y = 0; y < height; y++) { + let row = 0; + for (let b = 0; b < bytesPerRow; b++) { + const byte = parseInt(tok[i++], 16) ^ (code & 0xff); + row = (row << 8) | byte; + } + rows.push(row); + } + glyphs.set(code, { advance, rows }); + } + return { name, cellWidth, height, glyphs }; +} + +/** Serialisable form for embedding in the app. */ +export function fontToJSON(f) { + const glyphs = {}; + for (const [code, g] of f.glyphs) glyphs[code] = [g.advance, ...g.rows]; + return { name: f.name, cellWidth: f.cellWidth, height: f.height, glyphs }; +} + +export function fontFromJSON(j) { + const glyphs = new Map(); + for (const [code, arr] of Object.entries(j.glyphs)) { + glyphs.set(Number(code), { advance: arr[0], rows: arr.slice(1) }); + } + return { name: j.name, cellWidth: j.cellWidth, height: j.height, glyphs }; +} + +/** Width in pixels that `text` would occupy. */ +export function measureText(font, text, tracking = 1) { + let w = 0; + for (const ch of text) { + const g = font.glyphs.get(ch.codePointAt(0)); + if (g) w += g.advance + tracking; + } + return Math.max(0, w - tracking); +} + +/** + * Render `text` into a {width, height, pixels} bitmap. + * `tracking` is the gap in pixels inserted between glyphs. + */ +export function renderText(font, text, { tracking = 1 } = {}) { + const width = measureText(font, text, tracking); + const height = font.height; + const pixels = new Uint8Array(Math.max(1, width) * height); + let x = 0; + for (const ch of text) { + const g = font.glyphs.get(ch.codePointAt(0)); + if (!g) continue; + for (let y = 0; y < height; y++) { + const row = g.rows[y]; + for (let b = 0; b < g.advance; b++) { + // high bit of the cell is the leftmost pixel + if (row & (1 << (font.cellWidth - 1 - b))) { + const px = x + b; + if (px < width) pixels[y * width + px] = 1; + } + } + } + x += g.advance + tracking; + } + return { width, height, pixels }; +} + +/** Place a rendered bitmap onto a canvas of the given size. */ +export function layout(bitmap, { width, height, align = 'center', offsetX = 0, offsetY = 0 }) { + const pixels = new Uint8Array(width * height); + let x0 = offsetX; + if (align === 'center') x0 += Math.floor((width - bitmap.width) / 2); + else if (align === 'right') x0 += width - bitmap.width; + const y0 = offsetY + Math.floor((height - bitmap.height) / 2); + + for (let y = 0; y < bitmap.height; y++) { + const ty = y + y0; + if (ty < 0 || ty >= height) continue; + for (let x = 0; x < bitmap.width; x++) { + const tx = x + x0; + if (tx < 0 || tx >= width) continue; + if (bitmap.pixels[y * bitmap.width + x]) pixels[ty * width + tx] = 1; + } + } + return { width, height, pixels }; +} + +/** Trim blank columns from both sides, keeping `pad` columns of margin. */ +export function trimX(bitmap, pad = 0) { + const { width, height, pixels } = bitmap; + let lo = width, hi = -1; + for (let x = 0; x < width; x++) { + for (let y = 0; y < height; y++) { + if (pixels[y * width + x]) { if (x < lo) lo = x; if (x > hi) hi = x; break; } + } + } + if (hi < 0) return { width: 0, height, pixels: new Uint8Array(0) }; + lo = Math.max(0, lo - pad); hi = Math.min(width - 1, hi + pad); + const w = hi - lo + 1; + const out = new Uint8Array(w * height); + for (let y = 0; y < height; y++) { + for (let x = 0; x < w; x++) out[y * w + x] = pixels[y * width + lo + x]; + } + return { width: w, height, pixels: out }; +} diff --git a/src/codec/sheet.mjs b/src/codec/sheet.mjs new file mode 100644 index 0000000..b8677a1 --- /dev/null +++ b/src/codec/sheet.mjs @@ -0,0 +1,196 @@ +/** + * Read a spreadsheet without any library. + * + * .xlsx is a ZIP of XML, so this walks the ZIP central directory, inflates the + * two parts that matter with the browser's own DecompressionStream, and pulls + * the cells out of the XML. CSV is handled too, for anyone who would rather + * save as CSV or is on an older browser. + */ + +const dec = new TextDecoder(); + +// ------------------------------------------------------------------- zip + +function findEOCD(buf) { + // End-of-central-directory: 'PK\5\6', within the last 64KB. + for (let i = buf.length - 22; i >= Math.max(0, buf.length - 65558); i--) { + if (buf[i] === 0x50 && buf[i + 1] === 0x4b && buf[i + 2] === 0x05 && buf[i + 3] === 0x06) return i; + } + return -1; +} + +function listEntries(buf) { + const eocd = findEOCD(buf); + if (eocd < 0) throw new Error('not a zip file'); + const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength); + const count = dv.getUint16(eocd + 10, true); + let p = dv.getUint32(eocd + 16, true); + + const entries = new Map(); + for (let i = 0; i < count; i++) { + if (dv.getUint32(p, true) !== 0x02014b50) break; + const method = dv.getUint16(p + 10, true); + const compressedSize = dv.getUint32(p + 20, true); + const nameLen = dv.getUint16(p + 28, true); + const extraLen = dv.getUint16(p + 30, true); + const commentLen = dv.getUint16(p + 32, true); + const localOff = dv.getUint32(p + 42, true); + const name = dec.decode(buf.subarray(p + 46, p + 46 + nameLen)); + entries.set(name, { method, compressedSize, localOff }); + p += 46 + nameLen + extraLen + commentLen; + } + return entries; +} + +async function readEntry(buf, entry) { + const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength); + const { localOff } = entry; + if (dv.getUint32(localOff, true) !== 0x04034b50) throw new Error('bad zip entry'); + const nameLen = dv.getUint16(localOff + 26, true); + const extraLen = dv.getUint16(localOff + 28, true); + const start = localOff + 30 + nameLen + extraLen; + const data = buf.subarray(start, start + entry.compressedSize); + + if (entry.method === 0) return dec.decode(data); + if (entry.method !== 8) throw new Error(`unsupported compression (${entry.method})`); + if (typeof DecompressionStream !== 'function') { + throw new Error('this browser cannot open .xlsx files — save the sheet as CSV instead'); + } + const stream = new Blob([data]).stream().pipeThrough(new DecompressionStream('deflate-raw')); + return dec.decode(new Uint8Array(await new Response(stream).arrayBuffer())); +} + +// ----------------------------------------------------------------- xlsx + +const colIndex = (ref) => { + let n = 0; + for (const ch of ref) { + const c = ch.charCodeAt(0); + if (c < 65 || c > 90) break; + n = n * 26 + (c - 64); + } + return n - 1; +}; + +function parseXml(text) { + const doc = new DOMParser().parseFromString(text, 'application/xml'); + if (doc.querySelector('parsererror')) throw new Error('could not read the spreadsheet XML'); + return doc; +} + +/** @returns {Promise} rows of plain strings from the first sheet */ +export async function readXlsx(bytes) { + const entries = listEntries(bytes); + const sheetName = entries.has('xl/worksheets/sheet1.xml') + ? 'xl/worksheets/sheet1.xml' + : [...entries.keys()].find((n) => /^xl\/worksheets\/.*\.xml$/.test(n)); + if (!sheetName) throw new Error('no worksheet found in that file'); + + let shared = []; + if (entries.has('xl/sharedStrings.xml')) { + const doc = parseXml(await readEntry(bytes, entries.get('xl/sharedStrings.xml'))); + shared = [...doc.getElementsByTagName('si')].map((si) => + [...si.getElementsByTagName('t')].map((t) => t.textContent).join('')); + } + + const doc = parseXml(await readEntry(bytes, entries.get(sheetName))); + const rows = []; + for (const row of doc.getElementsByTagName('row')) { + const cells = []; + for (const c of row.getElementsByTagName('c')) { + const type = c.getAttribute('t'); + const ref = c.getAttribute('r') || ''; + const at = ref ? colIndex(ref) : cells.length; + let value = ''; + if (type === 'inlineStr') { + value = [...c.getElementsByTagName('t')].map((t) => t.textContent).join(''); + } else { + const v = c.getElementsByTagName('v')[0]; + const raw = v ? v.textContent : ''; + value = type === 's' ? (shared[Number(raw)] ?? '') : raw; + } + while (cells.length < at) cells.push(''); + cells[at] = (value ?? '').trim(); + } + rows.push(cells); + } + return rows; +} + +// ------------------------------------------------------------------ csv + +export function readCsv(text) { + const rows = []; + let row = [], field = '', quoted = false; + for (let i = 0; i < text.length; i++) { + const ch = text[i]; + if (quoted) { + if (ch === '"') { + if (text[i + 1] === '"') { field += '"'; i++; } else quoted = false; + } else field += ch; + continue; + } + if (ch === '"') quoted = true; + else if (ch === ',') { row.push(field.trim()); field = ''; } + else if (ch === '\n') { row.push(field.trim()); rows.push(row); row = []; field = ''; } + else if (ch !== '\r') field += ch; + } + if (field || row.length) { row.push(field.trim()); rows.push(row); } + return rows; +} + +// -------------------------------------------------------------- mapping + +const norm = (s) => String(s ?? '').toLowerCase().replace(/[^a-z]/g, ''); + +/** + * Work out which columns hold the controller name and the sign text. + * The template that ships with these buses uses + * `Line | Line Name | Description | Content (Display)`. + */ +export function mapRows(rows) { + let company = ''; + for (const r of rows.slice(0, 5)) { + const i = r.findIndex((c) => norm(c).startsWith('companyname')); + if (i >= 0) { company = (r[i + 1] || '').trim(); break; } + } + + const headerAt = rows.findIndex((r) => r.some((c) => { + const n = norm(c); + return n === 'linename' || n === 'content' || n.startsWith('contentdisplay') || n === 'destination'; + })); + + let nameCol = -1, textCol = -1, start = 0; + if (headerAt >= 0) { + const head = rows[headerAt].map(norm); + // Search by priority, not by column order: the shipped template has both a + // "Line" (a row number) and a "Line Name", and the name must win. + const pick = (...wanted) => { + for (const w of wanted) { const i = head.indexOf(w); if (i >= 0) return i; } + return -1; + }; + nameCol = pick('linename', 'name', 'destinationname', 'route', 'line'); + textCol = pick('contentdisplay', 'content', 'display', 'destination', 'text', 'message'); + start = headerAt + 1; + } + + const out = []; + for (const r of rows.slice(start)) { + if (!r.length || r.every((c) => !c)) continue; + const cells = r.filter((c) => c !== ''); + let name = nameCol >= 0 ? (r[nameCol] || '') : ''; + let text = textCol >= 0 ? (r[textCol] || '') : ''; + if (!name && !text) { // no recognisable header — take what is there + text = cells[cells.length - 1] || ''; + name = cells.length > 1 ? cells[0] : text; + } + if (!text) text = name; + // A bare row number is a line code, not something worth showing a driver. + if (/^\d+$/.test(name) && text && text !== name) name = text; + if (!name) name = text; + if (!text.trim()) continue; + if (norm(name) === 'linename' || norm(text).startsWith('contentdisplay')) continue; + out.push({ name: name.trim().slice(0, 16), text: text.trim() }); + } + return { company, destinations: out }; +} diff --git a/src/codec/td5.mjs b/src/codec/td5.mjs new file mode 100644 index 0000000..c2e5d14 --- /dev/null +++ b/src/codec/td5.mjs @@ -0,0 +1,351 @@ +/** + * .td5 — the binary file the Yutong / Guangzhou-Tongda bus destination sign + * controller reads off the SD card. + * + * Format reverse-engineered from TP5.exe output. Layout: + * + * FILE + * 0x000 16 magic, GBK "广州通达图形线路" + * 0x010 4 "V5.0" + * 0x014 4 00 b4 00 00 + * 0x018 12 export timestamp, ASCII "YYMMDDhhmmss" + * 0x030 2 company count (u16) + * 0x040 2 screen height, screen width in bytes (16, 14 => 112x16) + * 0x080 16 company[0] name, space padded + * 0x090 4 offset of company[0] pointer table (u32) \ repeats every + * 0x094 2 company[0] destination count (u16) / 0x20 per company + * 0x200 .. pointer table: u32 absolute offset per destination, + * terminated by 0xffffffff + * 0x400 .. destination records + * everything unused is filled with 0xff + * + * DESTINATION (record header is 0x80 bytes, block always starts at +0x200) + * +0x00 16 name, space padded + * +0x10 16 name again (the "line name" shown on the driver's controller) + * +0x20 16 spaces + * +0x30 4 block offset (u32) + * +0x34 4 block length (u32) + * +0x38 72 constant tail + * + * BLOCK + * +0x00 1 0x43 'C' + * +0x01 2 CRC-16/ARC over block[3 .. blockLength] (u16) + * +0x03 4 block id — TP5 stores rand(), so it is always <= 0x7fff, which + * is why bytes +5/+6 always read as zero + * +0x07 4 block length (u32, same as record header) + * +0x0b 3 frame count, three times + * +0x0e 1 0 + * +0x0f 1 0x84 marker + * +0x10 6 frame descriptor x frameCount: u32 offset, u8 widthBytes, u8 height + * .. action section x (frameCount*2), 0x80 bytes each + * .. frame bitmaps + * headerLength = 0x10 + frameCount*6 + frameCount*0x100 + * + * BITMAP widthBytes chunks of 16 bytes; each chunk is 8 columns x 16 rows, + * row-major, MSB = leftmost pixel. Chunks run left to right. + * A bitmap wider than the screen scrolls. + */ + +export const MAGIC = new Uint8Array([ + 0xb9, 0xe3, 0xd6, 0xdd, 0xcd, 0xa8, 0xb4, 0xef, + 0xcd, 0xbc, 0xd0, 0xce, 0xcf, 0xdf, 0xc2, 0xb7, +]); + +export const REC_HEADER_LEN = 0x80; +export const BLOCK_START = 0x200; // block offset relative to record start +export const PAGE = 0x200; // records are aligned to this +export const PTR_TABLE = 0x200; +export const FILL = 0xff; + +/** Display effect stored at section byte 8. */ +export const EFFECT = { HOLD: 3, SCROLL: 9 }; + +/** Constant tail of a destination record header (record +0x38 .. +0x80). */ +const REC_TAIL = hex( + 'ffffffffffffffffffffffff' + '00000000' + + 'ffffffffffffffffffffffff' + '00000000' + + 'ffffffff' + '00000000' + 'ffffffff' + '00000000' + + 'ffffffffffffffffffffffffffffffffffffffffffffffff' +); + +/** One 0x80-byte action section. Byte 2 = frame index, byte 8 = effect. */ +function buildSection(frameIndex, effect) { + const s = new Uint8Array(0x80); + s[1] = 0x0a; + s[2] = frameIndex; + s[6] = 0x0e; + s[7] = 0x10; + s[8] = effect; + s[15] = 0x03; + s[22] = 0x03; + s[27] = 0x20; s[28] = 0x10; s[29] = 0x03; + s[34] = 0x20; s[35] = 0x10; s[36] = 0x03; + return s; +} + +function hex(s) { + const out = new Uint8Array(s.length / 2); + for (let i = 0; i < out.length; i++) out[i] = parseInt(s.substr(i * 2, 2), 16); + return out; +} + +const alignUp = (n, a) => Math.ceil(n / a) * a; + +function ascii(bytes) { + let s = ''; + for (const b of bytes) s += String.fromCharCode(b); + return s; +} + +/** Write `text` into `buf` at `off`, space padded / truncated to `len`. */ +function putPadded(buf, off, text, len) { + for (let i = 0; i < len; i++) { + const c = i < text.length ? text.charCodeAt(i) : 0x20; + buf[off + i] = c < 0x100 ? c : 0x3f; // '?' for anything non latin-1 + } +} + +// ---------------------------------------------------------------- checksum + +/** + * CRC-16/ARC — reflected, poly 0xa001, init 0, no final xor. TP5 calls this + * with polynomial index 1 out of the table {0x8480, 0xa001, 0x8621, 0xe950}. + */ +export function crc16arc(data, init = 0) { + let crc = init; + for (let i = 0; i < data.length; i++) { + crc ^= data[i]; + for (let b = 0; b < 8; b++) crc = crc & 1 ? (crc >>> 1) ^ 0xa001 : crc >>> 1; + } + return crc & 0xffff; +} + +/** The CRC covers everything from the block id onward, so write it last. */ +function sealBlock(block) { + const crc = crc16arc(block.subarray(3)); + block[1] = crc & 0xff; + block[2] = (crc >> 8) & 0xff; + return crc; +} + +/** TP5 uses MSVCRT rand(), whose RAND_MAX is 0x7fff. */ +const randomBlockId = () => Math.floor(Math.random() * 0x8000); + +// ------------------------------------------------------------------- parse + +/** + * Parse a .td5 file. + * @param {Uint8Array} bytes + * @returns {{company: string, timestamp: string, screen: {width: number, height: number}, + * destinations: Array<{name: string, lineName: string, effects: number[], + * frames: Array<{width: number, height: number, widthBytes: number, + * data: Uint8Array}>, checksum: number}>}} + */ +export function parseTd5(bytes) { + const dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + + for (let i = 0; i < MAGIC.length; i++) { + if (bytes[i] !== MAGIC[i]) throw new Error('Not a .td5 file (bad magic)'); + } + + const version = ascii(bytes.subarray(0x10, 0x14)); + const timestamp = ascii(bytes.subarray(0x18, 0x24)); + const screen = { height: bytes[0x40], width: bytes[0x41] * 8 }; + const company = ascii(bytes.subarray(0x80, 0x90)).trimEnd(); + + const ptrTable = dv.getUint32(0x90, true); + const count = dv.getUint16(0x94, true); + + const destinations = []; + for (let i = 0; i < count; i++) { + const ptr = dv.getUint32(ptrTable + i * 4, true); + if (ptr === 0xffffffff) break; + destinations.push(parseRecord(bytes, dv, ptr)); + } + + return { version, company, timestamp, screen, destinations }; +} + +function parseRecord(bytes, dv, ptr) { + const name = ascii(bytes.subarray(ptr, ptr + 16)).trimEnd(); + const lineName = ascii(bytes.subarray(ptr + 0x10, ptr + 0x20)).trimEnd(); + const blockOff = dv.getUint32(ptr + 0x30, true); + const blockLen = dv.getUint32(ptr + 0x34, true); + const block = bytes.subarray(blockOff, blockOff + blockLen); + + if (block[0] !== 0x43) throw new Error(`${name}: bad block tag`); + if (block[0x0f] !== 0x84) throw new Error(`${name}: bad frame-table marker`); + + const crc = block[1] | (block[2] << 8); + const blockId = dv.getUint32(blockOff + 3, true); + const crcOk = crc === crc16arc(block.subarray(3)); + const frameCount = block[0x0b]; + + const frames = []; + for (let f = 0; f < frameCount; f++) { + const p = 0x10 + f * 6; + const off = block[p] | (block[p + 1] << 8) | (block[p + 2] << 16) | (block[p + 3] << 24); + const widthBytes = block[p + 4]; + const height = block[p + 5]; + frames.push({ + width: widthBytes * 8, + height, + widthBytes, + data: block.slice(off, off + widthBytes * 16), + }); + } + + const base = 0x10 + frameCount * 6; + const effects = []; + for (let k = 0; k < frameCount * 2; k++) effects.push(block[base + k * 0x80 + 8]); + + return { name, lineName, effects, frames, blockId, crc, crcOk }; +} + +// ------------------------------------------------------------------- build + +/** + * Build a .td5 file. + * @param {object} doc same shape parseTd5 returns; `checksum` on a destination + * is reused when we cannot compute one. + * @param {{timestamp?: string}} [opts] + */ +export function buildTd5(doc, opts = {}) { + const dests = doc.destinations; + const screenWidthBytes = Math.round((doc.screen?.width ?? 112) / 8); + const screenHeight = doc.screen?.height ?? 16; + + // Lay records out first so we know the file size. + const layout = []; + let cursor = 0x400; + for (const d of dests) { + const frameCount = d.frames.length; + const headerLen = 0x10 + frameCount * 6 + frameCount * 0x100; + const blockLen = headerLen + d.frames.reduce((n, f) => n + f.widthBytes * 16, 0); + layout.push({ recOff: cursor, blockOff: cursor + BLOCK_START, blockLen, headerLen }); + cursor = alignUp(cursor + BLOCK_START + blockLen, PAGE); + } + + const size = cursor; + const out = new Uint8Array(size).fill(FILL); + const dv = new DataView(out.buffer); + + out.set(MAGIC, 0); + putAscii(out, 0x10, 'V5.0'); + out[0x14] = 0x00; out[0x15] = 0xb4; out[0x16] = 0x00; out[0x17] = 0x00; + putAscii(out, 0x18, opts.timestamp ?? doc.timestamp ?? nowStamp()); + out.fill(FILL, 0x24, 0x30); + dv.setUint16(0x30, 1, true); + out.fill(FILL, 0x32, 0x40); + out[0x40] = screenHeight; + out[0x41] = screenWidthBytes; + out.fill(FILL, 0x42, 0x80); + + putPadded(out, 0x80, doc.company ?? '', 16); + dv.setUint32(0x90, PTR_TABLE, true); + dv.setUint16(0x94, dests.length, true); + out.fill(FILL, 0x96, 0xa0); + // remaining company slots stay 0x20/0xff as TP5 leaves them + for (let off = 0xa0; off < 0x200; off += 0x20) { + out.fill(0x20, off, off + 0x10); + out.fill(FILL, off + 0x10, off + 0x20); + } + + let p = PTR_TABLE; + for (const l of layout) { dv.setUint32(p, l.recOff, true); p += 4; } + dv.setUint32(p, 0xffffffff, true); + + dests.forEach((d, i) => { + const l = layout[i]; + putPadded(out, l.recOff, d.name, 16); + putPadded(out, l.recOff + 0x10, d.lineName ?? d.name, 16); + out.fill(0x20, l.recOff + 0x20, l.recOff + 0x30); + dv.setUint32(l.recOff + 0x30, l.blockOff, true); + dv.setUint32(l.recOff + 0x34, l.blockLen, true); + out.set(REC_TAIL, l.recOff + 0x38); + + const block = buildBlock(d, l); + out.set(block, l.blockOff); + }); + + return out; +} + +function buildBlock(d, l) { + const frameCount = d.frames.length; + const block = new Uint8Array(l.blockLen); + const bv = new DataView(block.buffer); + + block[0] = 0x43; + bv.setUint32(0x03, d.blockId ?? randomBlockId(), true); + bv.setUint32(0x07, l.blockLen, true); + block[0x0b] = frameCount; + block[0x0c] = frameCount; + block[0x0d] = frameCount; + block[0x0f] = 0x84; + + let dataOff = l.headerLen; + d.frames.forEach((f, fi) => { + const p = 0x10 + fi * 6; + bv.setUint32(p, dataOff, true); + block[p + 4] = f.widthBytes; + block[p + 5] = f.height; + block.set(f.data, dataOff); + dataOff += f.widthBytes * 16; + }); + + // Two action groups, each holding one section per frame. + const base = 0x10 + frameCount * 6; + for (let k = 0; k < frameCount * 2; k++) { + const effect = d.effects?.[k] ?? d.effects?.[0] ?? EFFECT.SCROLL; + block.set(buildSection(k % frameCount, effect), base + k * 0x80); + } + + sealBlock(block); + return block; +} + +function putAscii(buf, off, s) { + for (let i = 0; i < s.length; i++) buf[off + i] = s.charCodeAt(i); +} + +function nowStamp(date = new Date()) { + const p = (n) => String(n).padStart(2, '0'); + return p(date.getFullYear() % 100) + p(date.getMonth() + 1) + p(date.getDate()) + + p(date.getHours()) + p(date.getMinutes()) + p(date.getSeconds()); +} + +// ------------------------------------------------------------------ pixels + +/** Unpack a frame into a width*height Uint8Array of 0/1. */ +export function frameToPixels(frame) { + const { widthBytes, height } = frame; + const w = widthBytes * 8; + const px = new Uint8Array(w * height); + for (let c = 0; c < widthBytes; c++) { + for (let y = 0; y < height; y++) { + const byte = frame.data[c * 16 + y]; + for (let b = 0; b < 8; b++) { + if (byte & (0x80 >> b)) px[y * w + c * 8 + b] = 1; + } + } + } + return px; +} + +/** Pack a width*height 0/1 array into a frame. Width is padded up to a multiple of 8. */ +export function pixelsToFrame(px, width, height) { + const widthBytes = Math.ceil(width / 8); + const data = new Uint8Array(widthBytes * 16); + for (let c = 0; c < widthBytes; c++) { + for (let y = 0; y < height; y++) { + let byte = 0; + for (let b = 0; b < 8; b++) { + const x = c * 8 + b; + if (x < width && px[y * width + x]) byte |= 0x80 >> b; + } + data[c * 16 + y] = byte; + } + } + return { width: widthBytes * 8, height, widthBytes, data }; +} diff --git a/src/codec/tp5.mjs b/src/codec/tp5.mjs new file mode 100644 index 0000000..507e343 --- /dev/null +++ b/src/codec/tp5.mjs @@ -0,0 +1,215 @@ +/** + * .tp5 — TP5's editable project file. Plain text, CRLF, one statement per line, + * each terminated with ';'. Strings are stored as UTF-16BE hex. + * + * Unlike a .td5 (which only holds rendered bitmaps) this keeps the actual text, + * so importing one gives us something we can edit. + * + * V1.5.3; + * 0; + * screen para:...; + * Company Sum:1; + * ,; + * ... then per destination: + * ActIconFileNames:0; + * SrnIconFileNames:0; + * line name:,,; + * fore screen: <- then bcak / side / inner / backside, in that order + * 112,16,11316396,0,255,1,10,6; + * Up Act Sum:1; + * act #0:0,10,5,3,0,0,0; + * icon #0:0,0,112,16,,0,1; + * Obj #0:S,-3,,,,,,,<0>,; + * icon #1:... (five icons per act, each may carry Obj lines) + * Down Act Sum:0; + */ + +const SCREENS = ['fore', 'bcak', 'side', 'inner', 'backside']; // 'bcak' typo is TP5's + +export function hexToStr(hex) { + let s = ''; + for (let i = 0; i + 3 < hex.length; i += 4) s += String.fromCharCode(parseInt(hex.substr(i, 4), 16)); + return s; +} + +export function strToHex(s) { + let out = ''; + for (const ch of s) out += ch.charCodeAt(0).toString(16).padStart(4, '0'); + return out; +} + +class Reader { + constructor(text) { + this.lines = text.split(/\r?\n/); + this.i = 0; + } + peek() { return this.lines[this.i]; } + next() { return this.lines[this.i++]; } + /** Strip the trailing ';' and return the payload after `label:`. */ + expect(label) { + const line = this.next(); + if (!line?.startsWith(label)) throw new Error(`line ${this.i}: expected "${label}", got "${line}"`); + return line.slice(label.length).replace(/;$/, ''); + } +} + +const stripSemi = (l) => (l ?? '').replace(/;$/, ''); + +export function parseTp5(text) { + const r = new Reader(text); + const version = stripSemi(r.next()); + const flag = stripSemi(r.next()); + const screenPara = r.expect('screen para:').split(','); + const companySum = Number(r.expect('Company Sum:')); + + const companies = []; + for (let c = 0; c < companySum; c++) { + const [nameHex, count] = stripSemi(r.next()).split(','); + companies.push({ name: hexToStr(nameHex), count: Number(count), destinations: [] }); + } + + for (const company of companies) { + for (let d = 0; d < company.count; d++) company.destinations.push(readDestination(r)); + } + + return { version, flag, screenPara, companies }; +} + +function readDestination(r) { + const actIcons = readFileNameList(r, 'ActIconFileNames:'); + const srnIcons = readFileNameList(r, 'SrnIconFileNames:'); + const parts = r.expect('line name:').split(','); + const dest = { + actIconFiles: actIcons, + srnIconFiles: srnIcons, + lineName: hexToStr(parts[0] ?? ''), + lineName2: hexToStr(parts[1] ?? ''), + screens: {}, + }; + for (const key of SCREENS) { + const header = r.next(); + if (header !== `${key} screen:`) throw new Error(`line ${r.i}: expected "${key} screen:", got "${header}"`); + dest.screens[key] = { + params: stripSemi(r.next()).split(','), + up: readActs(r, 'Up Act Sum:'), + down: readActs(r, 'Down Act Sum:'), + }; + } + return dest; +} + +function readFileNameList(r, label) { + const n = Number(r.expect(label)); + const files = []; + for (let i = 0; i < n; i++) files.push(stripSemi(r.next())); + return files; +} + +function readActs(r, label) { + const n = Number(r.expect(label)); + const acts = []; + for (let a = 0; a < n; a++) { + const fields = stripSemi(r.next()).split(':')[1].split(','); + const icons = []; + while (/^icon #\d+:/.test(r.peek() ?? '')) { + const icon = { fields: stripSemi(r.next()).split(':')[1].split(','), objs: [] }; + while (/^Obj #\d+:/.test(r.peek() ?? '')) icon.objs.push(parseObj(stripSemi(r.next()).split(':')[1])); + icons.push(icon); + } + acts.push({ fields, icons }); + } + return acts; +} + +function parseObj(payload) { + const f = payload.split(','); + return { + type: f[0], // 'S' = string + unknown1: f[1], // always -3 in the wild + x: Number(f[2]), + y: Number(f[3]), + font: hexToStr(f[4]), // built-in bitmap font, e.g. ASC0704 + text: hexToStr(f[5]), + useTrueType: Number(f[6]), // 1 = render with the Windows font below + ttfName: hexToStr(f[7]), // e.g. Impact + unknown2: f[8], + ptSize: Number(f[9]), + rest: f.slice(10), + }; +} + +function objToLine(o, i) { + const f = [ + o.type, o.unknown1, o.x, o.y, strToHex(o.font), strToHex(o.text), + o.useTrueType, strToHex(o.ttfName), o.unknown2, o.ptSize, ...(o.rest ?? []), + ]; + return `Obj #${i}:${f.join(',')};`; +} + +export function buildTp5(doc) { + const out = []; + out.push(`${doc.version};`); + out.push(`${doc.flag};`); + out.push(`screen para:${doc.screenPara.join(',')};`); + out.push(`Company Sum:${doc.companies.length};`); + for (const c of doc.companies) out.push(`${strToHex(c.name)},${c.destinations.length};`); + + for (const c of doc.companies) { + for (const d of c.destinations) { + out.push(`ActIconFileNames:${d.actIconFiles.length};`, ...d.actIconFiles.map((f) => `${f};`)); + out.push(`SrnIconFileNames:${d.srnIconFiles.length};`, ...d.srnIconFiles.map((f) => `${f};`)); + out.push(`line name:${strToHex(d.lineName)},${strToHex(d.lineName2)},;`); + for (const key of SCREENS) { + const s = d.screens[key]; + out.push(`${key} screen:`); + out.push(`${s.params.join(',')};`); + emitActs(out, 'Up Act Sum:', s.up); + emitActs(out, 'Down Act Sum:', s.down); + } + } + } + out.push(''); + return out.join('\r\n'); +} + +function emitActs(out, label, acts) { + out.push(`${label}${acts.length};`); + acts.forEach((a, ai) => { + out.push(`act #${ai}:${a.fields.join(',')};`); + a.icons.forEach((icon, ii) => { + out.push(`icon #${ii}:${icon.fields.join(',')};`); + icon.objs.forEach((o, oi) => out.push(objToLine(o, oi))); + }); + }); +} + +/** + * Flatten a parsed project into the simple per-destination view the editor uses: + * one entry per destination, with the front-screen text of each frame (act). + */ +export function toSimple(doc) { + const company = doc.companies[0]; + const [, , h, w] = doc.screenPara.map(Number); + return { + company: company?.name ?? '', + screen: { width: w || 112, height: h || 16 }, + destinations: (company?.destinations ?? []).map((d) => ({ + name: d.lineName, + lineName: d.lineName2 || d.lineName, + frames: d.screens.fore.up.map((act) => { + const icon = act.icons[0]; + const obj = icon?.objs?.[0]; + return { + text: obj?.text ?? '', + font: obj?.font ?? 'ASC0704', + useTrueType: obj?.useTrueType ?? 0, + ttfName: obj?.ttfName ?? '', + ptSize: obj?.ptSize ?? 20, + x: obj?.x ?? 0, + effect: Number(icon?.fields?.[4] ?? 9), + }; + }), + _raw: d, + })), + }; +} diff --git a/src/fonts.js b/src/fonts.js new file mode 100644 index 0000000..396861b --- /dev/null +++ b/src/fonts.js @@ -0,0 +1,3 @@ +// Generated by tools/build-fonts.mjs from the .font files shipped with TP5. +// Each glyph is [advance, ...rows]; rows are MSB-first, cellWidth bits wide. +export const FONTS = {"ASC0704":{"name":"ASC0704","cellWidth":8,"height":7,"glyphs":{"32":[4,0,0,0,0,0,0,0],"33":[1,128,128,128,128,128,0,128],"34":[3,160,160,0,0,0,0,0],"35":[5,80,80,248,80,248,80,80],"36":[5,32,120,160,112,40,240,32],"37":[5,192,200,16,32,64,152,24],"38":[5,64,160,160,64,168,144,104],"39":[1,128,128,128,0,0,0,0],"40":[3,32,64,128,128,128,64,32],"41":[3,128,64,32,32,32,64,128],"42":[5,32,168,112,248,112,168,32],"43":[5,0,32,32,248,32,32,0],"44":[2,0,0,0,0,64,64,128],"45":[5,0,0,0,248,0,0,0],"46":[1,0,0,0,0,0,0,128],"47":[5,0,8,16,32,64,128,0],"48":[5,112,136,136,136,136,136,112],"49":[3,64,192,64,64,64,64,224],"50":[5,112,136,8,48,64,128,248],"51":[5,112,136,8,48,8,136,112],"52":[5,16,48,80,144,248,16,16],"53":[5,248,128,240,8,8,136,112],"54":[5,56,64,128,240,136,136,112],"55":[5,248,8,16,32,64,64,64],"56":[5,112,136,136,112,136,136,112],"57":[5,112,136,136,120,8,136,112],"58":[1,0,0,128,0,0,128,0],"59":[2,0,0,64,0,64,64,128],"60":[5,160,168,168,72,8,0,0],"61":[5,0,0,248,0,248,0,0],"62":[3,0,0,0,64,160,224,160],"63":[5,112,136,8,16,32,0,32],"64":[5,112,136,184,168,184,128,120],"65":[4,96,144,144,144,240,144,144],"66":[4,224,144,144,240,144,144,224],"67":[4,96,144,128,128,128,144,96],"68":[4,224,144,144,144,144,144,224],"69":[4,240,128,128,224,128,128,240],"70":[4,240,128,128,224,128,128,128],"71":[4,96,144,128,128,176,144,96],"72":[4,144,144,144,240,144,144,144],"73":[3,224,64,64,64,64,64,224],"74":[4,112,32,32,32,32,160,64],"75":[4,144,144,160,192,192,160,144],"76":[4,128,128,128,128,128,128,240],"77":[5,136,216,168,168,136,136,136],"78":[4,144,144,208,176,144,144,144],"79":[4,96,144,144,144,144,144,96],"80":[4,224,144,144,224,128,128,128],"81":[5,112,136,136,136,168,144,104],"82":[4,224,144,144,224,192,160,144],"83":[4,96,144,128,96,16,144,96],"84":[5,248,32,32,32,32,32,32],"85":[4,144,144,144,144,144,144,96],"86":[5,136,136,136,136,136,80,32],"87":[5,136,136,136,168,168,168,80],"88":[5,136,136,80,32,80,136,136],"89":[5,136,136,80,32,32,32,32],"90":[5,248,8,16,32,64,128,248],"91":[2,192,128,128,128,128,128,192],"92":[5,0,128,64,32,16,8,0],"93":[2,192,64,64,64,64,64,192],"94":[5,32,80,136,0,0,0,0],"95":[4,0,0,0,0,0,0,240],"96":[2,128,64,0,0,0,0,0],"97":[4,0,0,96,16,112,144,112],"98":[4,128,128,224,144,144,144,224],"99":[4,0,0,112,128,128,128,112],"100":[4,16,16,112,144,144,144,112],"101":[4,0,0,96,144,240,128,112],"102":[4,48,64,64,224,64,64,64],"103":[4,0,0,112,144,112,16,224],"104":[4,128,128,224,144,144,144,144],"105":[1,128,0,128,128,128,128,128],"106":[2,64,0,64,64,64,64,128],"107":[4,128,128,144,160,192,160,144],"108":[1,128,128,128,128,128,128,128],"109":[5,0,0,208,168,168,168,168],"110":[4,0,0,224,144,144,144,144],"111":[4,0,0,96,144,144,144,96],"112":[4,0,0,224,144,224,128,128],"113":[5,0,112,144,112,16,24,16],"114":[4,0,0,160,208,128,128,128],"115":[4,0,0,112,128,96,16,224],"116":[4,64,64,224,64,64,64,48],"117":[4,0,0,144,144,144,144,112],"118":[5,0,0,136,136,136,80,32],"119":[5,0,0,136,168,168,168,80],"120":[5,0,0,136,80,32,80,136],"121":[4,0,0,144,144,112,144,96],"122":[4,0,0,240,16,32,64,240],"123":[5,136,168,168,168,168,168,136]}},"ASC0705":{"name":"ASC0705","cellWidth":8,"height":7,"glyphs":{"32":[4,0,0,0,0,0,0,0],"33":[3,64,64,64,64,0,64,0],"34":[4,80,80,80,0,0,0,0],"35":[6,40,40,124,40,124,40,40],"36":[6,16,60,80,56,20,120,16],"37":[6,12,76,32,16,8,100,96],"38":[6,48,72,80,32,84,72,52],"39":[4,96,32,64,0,0,0,0],"40":[4,16,32,64,64,64,32,16],"41":[4,64,32,16,16,16,32,64],"42":[6,0,16,84,56,84,16,0],"43":[6,0,16,16,124,16,16,0],"44":[4,0,0,0,0,96,32,64],"45":[6,0,0,0,124,0,0,0],"46":[4,0,0,0,0,0,96,96],"47":[6,0,4,8,16,32,64,0],"48":[6,56,68,76,84,100,68,56],"49":[4,32,96,32,32,32,32,112],"50":[6,56,68,4,8,16,32,124],"51":[6,124,8,16,8,4,68,56],"52":[6,8,24,40,72,124,8,8],"53":[6,124,64,120,4,4,68,56],"54":[6,24,32,64,120,68,68,56],"55":[6,124,4,8,16,32,32,32],"56":[6,56,68,68,56,68,68,56],"57":[6,56,68,68,60,4,8,48],"58":[3,0,96,96,0,96,96,0],"59":[3,0,96,96,0,96,32,64],"60":[6,8,16,32,64,32,16,8],"61":[6,0,0,124,0,124,0,0],"62":[6,32,16,8,4,8,16,32],"63":[6,56,68,4,8,16,0,16],"64":[6,56,68,4,52,92,68,56],"65":[6,16,40,68,68,124,68,68],"66":[6,120,68,68,120,68,68,120],"67":[6,56,68,64,64,64,68,56],"68":[7,120,68,66,66,66,68,120],"69":[6,124,64,64,120,64,64,124],"70":[6,124,64,64,120,64,64,64],"71":[7,60,66,64,78,66,66,60],"72":[6,68,68,68,124,68,68,68],"73":[4,112,32,32,32,32,32,112],"74":[5,56,16,16,16,16,16,96],"75":[6,68,72,80,96,80,72,68],"76":[6,64,64,64,64,64,64,124],"77":[8,65,99,85,73,65,65,65],"78":[7,66,98,82,74,70,66,66],"79":[6,56,68,68,68,68,68,56],"80":[6,120,68,68,120,64,64,64],"81":[6,56,68,68,68,84,72,52],"82":[6,120,68,68,120,80,72,68],"83":[7,62,64,64,60,2,2,124],"84":[8,127,8,8,8,8,8,8],"85":[6,68,68,68,68,68,68,56],"86":[6,68,68,68,68,68,40,16],"87":[8,65,65,65,73,73,42,20],"88":[8,65,34,20,8,20,34,65],"89":[8,65,65,34,20,8,8,8],"90":[8,127,2,4,8,16,32,127],"91":[4,112,64,64,64,64,64,112],"92":[7,64,96,48,24,12,6,2],"93":[4,112,16,16,16,16,16,112],"94":[6,16,40,68,0,0,0,0],"95":[6,0,0,0,0,0,0,124],"96":[4,64,32,16,0,0,0,0],"97":[5,0,0,48,8,56,72,56],"98":[5,64,64,64,112,72,72,112],"99":[5,0,0,56,64,64,64,56],"100":[5,8,8,8,56,72,72,56],"101":[6,0,0,56,68,124,64,56],"102":[5,16,40,32,112,32,32,32],"103":[5,0,56,72,72,56,8,48],"104":[5,64,64,80,104,72,72,72],"105":[2,64,0,64,64,64,64,64],"106":[3,32,0,96,32,32,32,64],"107":[5,64,64,72,80,96,80,72],"108":[2,64,64,64,64,64,64,64],"109":[6,0,0,104,84,84,68,68],"110":[5,0,0,112,72,72,72,72],"111":[6,0,0,56,68,68,68,56],"112":[5,0,112,72,72,112,64,64],"113":[5,0,56,72,72,56,8,8],"114":[5,0,0,80,104,72,64,64],"115":[6,0,0,56,64,56,4,120],"116":[5,32,32,112,32,32,40,16],"117":[5,0,0,72,72,72,72,48],"118":[6,0,0,68,68,68,40,16],"119":[8,0,0,65,73,73,42,20],"120":[6,0,0,68,40,16,40,68],"121":[5,0,72,72,72,56,8,112],"122":[5,0,0,120,8,16,32,120],"123":[3,32,64,64,32,64,64,32],"124":[2,64,64,64,64,64,64,64],"125":[3,64,32,32,64,32,32,64],"126":[5,0,0,64,168,16,0,0],"127":[0,0,0,0,0,0,0,0]}},"ASC0708":{"name":"ASC0708","cellWidth":8,"height":7,"glyphs":{"32":[4,0,0,0,0,0,0,0],"33":[2,192,192,192,192,192,0,192],"34":[5,216,216,216,0,0,0,0],"35":[7,108,254,108,108,254,108,108],"36":[5,32,248,224,248,56,248,32],"37":[5,208,208,48,32,96,88,88],"38":[5,64,160,160,64,168,144,104],"39":[2,192,192,192,0,0,0,0],"40":[3,96,192,192,192,192,192,96],"41":[3,192,96,96,96,96,96,192],"42":[7,16,84,56,254,56,84,16],"43":[6,0,48,48,252,48,48,0],"44":[3,0,0,0,0,96,96,192],"45":[5,0,0,0,248,0,0,0],"46":[2,0,0,0,0,0,192,192],"47":[4,48,48,112,96,224,192,192],"48":[6,120,204,204,204,204,204,120],"49":[4,32,96,224,96,96,96,240],"50":[6,120,204,12,24,48,96,252],"51":[6,120,204,12,56,12,204,120],"52":[7,28,60,108,204,254,12,12],"53":[6,252,192,192,248,12,140,120],"54":[6,120,192,192,248,204,204,120],"55":[6,252,12,24,48,96,96,96],"56":[6,120,204,204,120,204,204,120],"57":[6,120,204,204,124,12,12,120],"58":[2,0,192,192,0,192,192,0],"59":[3,0,96,96,0,96,96,128],"60":[5,24,48,96,192,96,48,24],"61":[5,0,0,248,0,248,0,0],"62":[5,192,96,48,24,48,96,192],"63":[5,112,216,152,48,96,0,96],"64":[7,124,198,222,214,222,192,120],"65":[5,112,216,216,248,216,216,216],"66":[5,240,216,216,240,216,216,240],"67":[5,112,216,192,192,192,216,112],"68":[5,240,216,216,216,216,216,240],"69":[5,248,192,192,240,192,192,248],"70":[5,248,192,192,240,192,192,192],"71":[6,120,204,192,192,220,204,120],"72":[5,216,216,216,248,216,216,216],"73":[2,192,192,192,192,192,192,192],"74":[5,24,24,24,24,24,216,112],"75":[6,204,216,240,224,240,216,204],"76":[4,192,192,192,192,192,192,240],"77":[7,198,238,214,198,198,198,198],"78":[6,204,236,236,220,220,204,204],"79":[5,112,216,216,216,216,216,112],"80":[5,240,216,216,240,192,192,192],"81":[7,120,204,204,204,204,220,126],"82":[5,240,216,216,240,240,216,216],"83":[5,112,216,192,112,24,216,112],"84":[4,240,96,96,96,96,96,96],"85":[5,216,216,216,216,216,216,112],"86":[5,216,216,216,216,216,112,32],"87":[7,198,198,198,198,214,238,198],"88":[7,198,198,108,56,108,198,198],"89":[6,204,204,204,120,48,48,48],"90":[6,252,12,24,48,96,192,252],"91":[3,224,192,192,192,192,192,224],"92":[4,192,192,224,96,112,48,48],"93":[3,224,96,96,96,96,96,224],"94":[7,16,56,108,198,0,0,0],"95":[5,0,0,0,0,0,0,248],"96":[3,192,96,0,0,0,0,0],"97":[5,0,0,240,24,120,216,120],"98":[5,192,192,240,216,216,216,240],"99":[4,0,0,112,192,192,192,112],"100":[5,24,24,120,216,216,216,120],"101":[5,0,0,112,216,248,192,120],"102":[4,48,96,96,240,96,96,96],"103":[5,0,0,120,216,120,24,240],"104":[5,192,192,240,216,216,216,216],"105":[2,192,0,192,192,192,192,192],"106":[3,96,0,96,96,96,96,192],"107":[5,192,192,216,240,224,240,216],"108":[2,192,192,192,192,192,192,192],"109":[8,0,0,182,255,219,219,219],"110":[5,0,0,176,248,216,216,216],"111":[5,0,0,112,216,216,216,112],"112":[5,0,0,248,216,248,192,192],"113":[5,0,0,248,216,248,24,24],"114":[4,0,0,176,240,192,192,192],"115":[4,0,0,240,192,240,48,240],"116":[4,96,96,240,96,96,96,48],"117":[5,0,0,216,216,216,216,104],"118":[6,0,0,204,204,204,120,48],"119":[7,0,0,198,198,198,214,108],"120":[6,0,0,204,120,48,120,204],"121":[6,0,0,204,120,48,96,192],"122":[5,0,0,248,48,96,192,248]}},"ASC0809":{"name":"ASC0809","cellWidth":8,"height":9,"glyphs":{"32":[4,0,0,0,0,0,0,0,0,0],"33":[2,192,192,192,192,192,192,192,0,192],"34":[5,216,216,216,0,0,0,0,0,0],"35":[8,102,255,255,102,102,102,255,255,102],"36":[5,32,120,160,160,248,40,40,240,32],"37":[7,192,198,12,24,48,96,192,140,12],"38":[5,64,160,160,160,64,224,168,144,104],"39":[2,192,192,192,0,0,0,0,0,0],"40":[3,96,192,192,192,192,192,192,192,96],"41":[4,192,96,96,96,96,96,96,96,192],"42":[7,16,146,84,56,254,56,84,146,16],"43":[8,0,24,24,24,255,255,24,24,24],"44":[3,0,0,0,0,0,0,96,96,192],"45":[4,0,0,0,0,240,240,0,0,0],"46":[2,0,0,0,0,0,0,0,192,192],"47":[8,1,3,6,12,24,48,96,192,128],"48":[6,120,252,204,204,204,204,204,252,120],"49":[4,32,96,224,96,96,96,96,96,240],"50":[7,124,198,6,12,24,48,96,192,254],"51":[6,120,204,12,12,56,12,12,204,120],"52":[8,14,30,54,102,198,198,255,6,6],"53":[6,252,192,192,192,248,12,12,140,120],"54":[7,60,96,192,192,252,198,198,198,124],"55":[6,252,12,24,48,96,96,96,96,96],"56":[6,120,204,204,204,120,204,204,204,120],"57":[7,124,198,198,198,126,6,6,12,120],"58":[2,0,0,0,192,192,0,192,192,0],"59":[3,0,0,0,96,96,0,96,96,128],"60":[4,0,16,32,64,128,64,32,16,0],"61":[5,0,0,248,248,0,248,248,0,0],"62":[4,0,128,64,32,16,32,64,128,0],"63":[5,112,248,152,48,96,96,0,96,96],"64":[7,124,198,198,222,214,222,192,192,124],"65":[7,16,56,108,198,198,198,254,198,198],"66":[6,248,204,204,204,248,204,204,204,248],"67":[6,120,204,192,192,192,192,192,204,120],"68":[6,248,204,204,204,204,204,204,204,248],"69":[6,252,192,192,192,248,192,192,192,252],"70":[6,252,192,192,192,248,192,192,192,192],"71":[6,120,204,192,192,192,220,204,204,120],"72":[6,204,204,204,204,252,204,204,204,204],"73":[4,240,96,96,96,96,96,96,96,240],"74":[6,60,24,24,24,24,24,24,216,112],"75":[7,198,204,216,240,224,240,216,204,198],"76":[5,192,192,192,192,192,192,192,192,248],"77":[7,130,198,238,254,214,198,198,198,198],"78":[7,134,198,230,246,222,206,198,198,198],"79":[6,120,204,204,204,204,204,204,204,120],"80":[6,248,204,204,204,248,192,192,192,192],"81":[7,120,204,204,204,204,204,204,220,126],"82":[6,248,204,204,204,248,240,216,204,204],"83":[6,120,204,192,192,120,12,12,204,120],"84":[6,252,252,48,48,48,48,48,48,48],"85":[6,204,204,204,204,204,204,204,252,120],"86":[7,198,198,198,198,198,198,108,56,16],"87":[7,198,198,198,198,198,214,214,254,108],"88":[7,198,198,198,108,56,108,198,198,198],"89":[6,204,204,204,204,120,48,48,48,48],"90":[6,252,252,12,24,48,96,192,252,252],"91":[3,224,192,192,192,192,192,192,192,224],"92":[8,128,192,96,48,24,12,6,3,1],"93":[3,224,96,96,96,96,96,96,96,224],"94":[7,16,56,108,198,0,0,0,0,0],"95":[7,0,0,0,0,0,0,0,254,254],"96":[3,192,96,0,0,0,0,0,0,0],"97":[5,0,0,240,24,24,120,216,216,120],"98":[5,192,192,192,240,248,216,216,248,240],"99":[5,0,0,120,248,192,192,192,248,120],"100":[5,24,24,24,120,248,216,216,248,120],"101":[5,0,0,112,216,216,248,192,192,120],"102":[4,48,112,96,96,240,96,96,96,96],"103":[5,0,0,120,216,216,120,24,24,240],"104":[5,192,192,192,240,248,216,216,216,216],"105":[2,192,192,0,192,192,192,192,192,192],"106":[3,96,96,0,96,96,96,96,96,192],"107":[6,192,192,204,216,240,224,240,216,204],"108":[2,192,192,192,192,192,192,192,192,192],"109":[8,0,0,182,255,219,219,219,219,219],"110":[5,0,0,176,248,216,216,216,216,216],"111":[6,0,0,120,204,204,204,204,204,120],"112":[5,0,0,248,216,216,248,192,192,192],"113":[6,0,0,248,216,216,248,24,28,24],"114":[5,0,0,184,248,192,192,192,192,192],"115":[4,0,0,240,208,192,240,48,176,240],"116":[4,96,96,96,240,240,96,96,112,48],"117":[6,0,0,204,204,204,204,204,252,116],"118":[7,0,0,198,198,198,198,108,56,16],"119":[7,0,0,198,198,198,214,214,254,108],"120":[6,0,0,204,204,120,48,120,204,204],"121":[6,0,0,204,204,120,48,96,192,128],"122":[5,0,0,248,248,16,32,64,248,248]}},"ASC1108":{"name":"ASC1108","cellWidth":8,"height":11,"glyphs":{"32":[4,0,0,0,0,0,0,0,0,0,0,0],"33":[2,192,192,192,192,192,192,192,192,0,192,192],"34":[5,216,216,216,216,0,0,0,0,0,0,0],"35":[8,102,102,255,255,102,102,102,255,255,102,102],"36":[5,32,120,160,160,160,248,40,40,40,240,32],"37":[7,192,192,6,12,24,48,96,192,128,12,12],"38":[5,64,160,160,160,160,64,224,160,168,144,104],"39":[2,192,192,192,192,0,0,0,0,0,0,0],"40":[3,96,192,192,192,192,192,192,192,192,192,96],"41":[3,192,96,96,96,96,96,96,96,96,96,192],"42":[7,0,16,146,84,56,254,56,84,146,16,0],"43":[8,24,24,24,24,24,255,255,24,24,24,24],"44":[3,0,0,0,0,0,0,0,96,96,96,192],"45":[4,0,0,0,0,0,240,240,0,0,0,0],"46":[2,0,0,0,0,0,0,0,0,0,192,192],"47":[8,0,1,3,6,12,24,48,96,192,128,0],"48":[7,124,254,198,198,198,198,198,198,198,254,124],"49":[4,32,96,224,96,96,96,96,96,96,96,240],"50":[7,124,198,6,6,12,24,48,96,192,192,254],"51":[6,120,204,12,12,12,56,12,12,12,204,120],"52":[7,12,28,60,108,76,204,204,254,12,12,12],"53":[6,252,192,192,192,248,12,12,12,12,140,120],"54":[7,60,96,192,192,192,252,198,198,198,198,124],"55":[7,254,6,6,12,24,48,48,48,48,48,48],"56":[7,124,198,198,198,198,124,198,198,198,198,124],"57":[7,124,198,198,198,198,126,6,6,6,12,120],"58":[2,0,0,0,0,192,192,0,192,192,0,0],"59":[3,0,0,0,0,96,96,0,96,96,64,128],"60":[5,0,8,16,32,64,128,64,32,16,8,0],"61":[5,0,0,0,248,248,0,248,248,0,0,0],"62":[5,0,128,64,32,16,8,16,32,64,128,0],"63":[5,112,248,152,24,24,48,96,96,0,96,96],"64":[7,124,198,198,222,214,214,214,222,192,192,124],"65":[7,16,56,108,198,198,198,254,198,198,198,198],"66":[6,248,204,204,204,204,248,204,204,204,204,248],"67":[6,120,204,192,192,192,192,192,192,192,204,120],"68":[6,248,204,204,204,204,204,204,204,204,204,248],"69":[6,252,192,192,192,192,248,192,192,192,192,252],"70":[6,252,192,192,192,192,248,192,192,192,192,192],"71":[6,120,204,192,192,192,192,220,204,204,204,120],"72":[6,204,204,204,204,204,252,204,204,204,204,204],"73":[4,240,96,96,96,96,96,96,96,96,96,240],"74":[6,60,24,24,24,24,24,24,24,24,216,112],"75":[8,195,198,204,216,240,224,240,216,204,198,195],"76":[5,192,192,192,192,192,192,192,192,192,192,248],"77":[7,130,198,238,254,214,198,198,198,198,198,198],"78":[7,198,198,230,246,222,206,198,198,198,198,198],"79":[6,120,204,204,204,204,204,204,204,204,204,120],"80":[6,248,204,204,204,204,248,192,192,192,192,192],"81":[7,120,204,204,204,204,204,204,204,204,220,126],"82":[6,248,204,204,204,204,248,240,216,200,204,204],"83":[6,120,204,192,192,192,120,12,12,12,204,120],"84":[6,252,252,48,48,48,48,48,48,48,48,48],"85":[6,204,204,204,204,204,204,204,204,204,252,120],"86":[7,198,198,198,198,198,198,198,198,108,56,16],"87":[7,198,198,198,198,198,198,198,214,214,254,108],"88":[7,198,198,198,198,108,56,108,198,198,198,198],"89":[6,204,204,204,204,204,120,48,48,48,48,48],"90":[8,255,255,3,6,12,24,48,96,192,255,255],"91":[3,224,192,192,192,192,192,192,192,192,192,224],"92":[8,0,128,192,96,48,24,12,6,3,1,0],"93":[3,224,96,96,96,96,96,96,96,96,96,224],"94":[7,16,56,108,198,0,0,0,0,0,0,0],"95":[7,0,0,0,0,0,0,0,0,0,254,254],"96":[3,192,96,0,0,0,0,0,0,0,0,0],"97":[6,0,0,0,248,12,12,124,204,204,204,124],"98":[6,192,192,192,248,252,204,204,204,204,252,248],"99":[6,0,0,0,124,252,192,192,192,192,252,124],"100":[6,12,12,12,124,252,204,204,204,204,252,124],"101":[6,0,0,0,120,204,204,204,252,192,192,124],"102":[4,48,112,96,96,240,96,96,96,96,96,96],"103":[6,0,0,0,124,204,204,204,124,12,12,248],"104":[6,192,192,192,248,252,204,204,204,204,204,204],"105":[2,192,192,0,192,192,192,192,192,192,192,192],"106":[3,96,96,0,96,96,96,96,96,96,96,192],"107":[6,192,192,192,192,204,216,240,224,240,216,204],"108":[2,192,192,192,192,192,192,192,192,192,192,192],"109":[8,0,0,0,182,255,219,219,219,219,219,219],"110":[6,0,0,0,184,252,204,204,204,204,204,204],"111":[6,0,0,0,120,204,204,204,204,204,204,120],"112":[6,0,0,0,248,204,204,204,248,192,192,192],"113":[7,0,0,0,124,204,204,204,124,12,14,12],"114":[5,0,0,0,184,248,192,192,192,192,192,192],"115":[5,0,0,0,120,200,192,240,24,24,152,240],"116":[4,96,96,96,240,240,96,96,96,96,112,48],"117":[6,0,0,0,204,204,204,204,204,204,252,116],"118":[7,0,0,0,198,198,198,198,198,108,56,16],"119":[7,0,0,0,198,198,198,198,214,214,254,108],"120":[6,0,0,0,204,204,120,48,120,204,204,204],"121":[6,0,0,0,204,204,204,120,48,96,192,128],"122":[6,0,0,0,252,252,8,16,96,192,252,252],"123":[5,56,96,96,96,96,192,96,96,96,96,56],"124":[1,128,128,128,128,128,128,128,128,128,128,128],"125":[4,192,96,96,96,96,48,96,96,96,96,192],"126":[6,0,0,0,100,152,0,0,0,0,0,0],"127":[4,0,0,0,0,0,0,0,0,0,0,0]}},"ASC1608":{"name":"ASC1608","cellWidth":8,"height":16,"glyphs":{"32":[3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"33":[2,192,192,192,192,192,192,192,192,192,192,192,192,0,0,192,192],"34":[5,216,216,216,0,0,0,0,0,0,0,0,0,0,0,0,0],"35":[8,0,102,102,102,255,255,102,102,102,102,255,255,102,102,102,0],"36":[8,24,126,255,219,216,216,216,254,127,27,27,27,219,255,126,24],"37":[7,66,162,166,70,12,12,24,24,48,48,96,96,196,202,138,132],"38":[8,120,252,204,204,204,120,120,248,221,207,199,194,199,239,125,56],"39":[2,192,192,192,0,0,0,0,0,0,0,0,0,0,0,0,0],"40":[5,24,48,96,192,192,192,192,192,192,192,192,192,192,96,48,24],"41":[5,192,96,48,24,24,24,24,24,24,24,24,24,24,48,96,192],"42":[7,0,0,16,16,146,84,56,254,56,84,146,16,16,0,0,0],"43":[8,0,0,0,0,24,24,24,255,255,24,24,24,0,0,0,0],"44":[3,0,0,0,0,0,0,0,0,0,0,0,0,96,96,64,128],"45":[5,0,0,0,0,0,0,0,248,248,0,0,0,0,0,0,0],"46":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,192],"47":[8,0,0,0,1,3,6,12,24,48,96,192,128,0,0,0,0],"48":[7,56,124,198,198,198,198,198,198,198,198,198,198,198,198,124,56],"49":[6,48,112,240,48,48,48,48,48,48,48,48,48,48,48,252,252],"50":[7,120,252,206,134,6,6,14,12,24,48,112,224,192,192,254,254],"51":[7,56,124,198,6,6,6,6,60,60,6,6,6,6,198,124,56],"52":[7,12,28,28,60,108,236,204,204,254,254,12,12,12,12,12,12],"53":[7,254,254,192,192,192,192,248,252,14,6,6,6,198,206,124,56],"54":[7,28,60,112,224,192,192,192,252,254,198,198,198,198,198,254,124],"55":[7,254,254,6,6,14,12,24,24,24,24,24,24,24,24,24,24],"56":[7,124,254,198,198,198,198,198,124,124,198,198,198,198,198,254,124],"57":[7,124,254,198,198,198,198,198,254,126,6,6,6,14,28,248,240],"58":[2,0,0,0,0,0,0,0,0,0,192,192,0,0,192,192,0],"59":[3,0,0,0,0,0,0,0,0,0,0,96,96,0,96,96,192],"60":[7,0,2,6,12,24,48,96,192,192,96,48,24,12,6,2,0],"61":[7,0,0,0,0,0,254,254,0,0,254,254,0,0,0,0,0],"62":[7,0,64,96,48,24,12,6,3,3,6,12,24,48,96,64,0],"63":[7,56,124,238,198,198,198,12,24,24,24,24,24,24,0,24,24],"64":[8,60,126,231,195,195,195,219,215,214,220,192,192,195,231,126,60],"65":[7,56,124,238,198,198,198,198,254,254,198,198,198,198,198,198,198],"66":[7,248,252,206,198,198,198,198,252,252,198,198,198,198,206,252,248],"67":[7,124,254,198,198,192,192,192,192,192,192,192,192,198,198,254,124],"68":[7,248,252,206,198,198,198,198,198,198,198,198,198,198,206,252,248],"69":[7,254,254,192,192,192,192,192,252,252,192,192,192,192,192,254,254],"70":[7,254,254,192,192,192,192,192,252,252,192,192,192,192,192,192,192],"71":[7,56,124,238,198,198,192,192,222,222,198,198,198,198,238,124,56],"72":[7,198,198,198,198,198,198,198,254,254,198,198,198,198,198,198,198],"73":[4,240,240,96,96,96,96,96,96,96,96,96,96,96,96,240,240],"74":[6,12,12,12,12,12,12,12,12,12,12,12,12,204,204,252,120],"75":[8,193,195,199,206,220,216,240,240,248,220,204,204,198,199,195,193],"76":[7,192,192,192,192,192,192,192,192,192,192,192,192,192,192,254,254],"77":[7,130,198,238,254,254,214,214,198,198,198,198,198,198,198,198,198],"78":[7,198,198,230,246,254,222,206,198,198,198,198,198,198,198,198,198],"79":[7,124,254,198,198,198,198,198,198,198,198,198,198,198,198,254,124],"80":[7,252,254,198,198,198,198,198,254,252,192,192,192,192,192,192,192],"81":[8,124,254,198,198,198,198,198,198,198,198,198,214,222,206,255,125],"82":[8,254,255,195,195,195,195,195,255,254,248,220,206,199,195,193,193],"83":[8,126,255,195,195,192,192,192,254,127,3,3,3,195,195,255,126],"84":[8,255,255,24,24,24,24,24,24,24,24,24,24,24,24,24,24],"85":[7,198,198,198,198,198,198,198,198,198,198,198,198,198,198,254,124],"86":[7,198,198,198,198,198,198,198,198,198,198,198,198,198,108,56,16],"87":[7,198,198,198,198,198,198,198,198,198,198,214,214,254,254,238,68],"88":[8,195,195,195,195,195,102,60,24,60,102,195,195,195,195,195,195],"89":[8,195,195,195,195,195,102,60,24,24,24,24,24,24,24,24,24],"90":[8,255,255,3,3,3,7,14,28,56,112,224,192,192,192,255,255],"91":[4,240,240,192,192,192,192,192,192,192,192,192,192,192,192,240,240],"92":[7,0,0,0,0,128,192,96,48,24,12,6,2,0,0,0,0],"93":[4,240,240,48,48,48,48,48,48,48,48,48,48,48,48,240,240],"94":[7,16,56,108,198,130,0,0,0,0,0,0,0,0,0,0,0],"95":[5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,248],"96":[4,192,96,48,0,0,0,0,0,0,0,0,0,0,0,0,0],"97":[7,0,0,0,0,0,124,254,6,6,126,254,198,198,198,254,118],"98":[7,192,192,192,192,192,252,254,198,198,198,198,198,198,198,254,188],"99":[7,0,0,0,0,0,124,254,198,192,192,192,192,192,198,254,124],"100":[7,6,6,6,6,6,126,254,198,198,198,198,198,198,198,254,122],"101":[7,0,0,0,0,0,124,254,198,198,198,254,252,192,192,254,124],"102":[6,28,60,48,48,48,48,252,252,48,48,48,48,48,48,48,48],"103":[7,0,0,0,0,0,122,254,198,198,198,254,126,6,6,254,124],"104":[7,192,192,192,192,192,252,254,198,198,198,198,198,198,198,198,198],"105":[2,192,192,0,192,192,192,192,192,192,192,192,192,192,192,192,192],"106":[5,24,24,0,24,24,24,24,24,24,24,24,24,24,48,224,192],"107":[7,192,192,192,192,194,198,206,220,248,240,248,220,206,198,194,194],"108":[2,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192],"109":[8,0,0,0,0,0,182,255,255,219,219,219,219,219,219,219,219],"110":[7,0,0,0,0,0,188,254,198,198,198,198,198,198,198,198,198],"111":[7,0,0,0,0,0,124,254,198,198,198,198,198,198,198,254,124],"112":[7,0,0,0,0,0,188,254,198,198,198,254,248,192,192,192,192],"113":[7,0,0,0,0,0,116,252,204,204,204,252,124,12,12,14,12],"114":[6,0,0,0,0,0,204,220,248,240,224,192,192,192,192,192,192],"115":[7,0,0,0,0,0,124,254,198,192,252,126,6,6,198,254,124],"116":[6,0,0,48,48,48,252,252,48,48,48,48,48,48,48,60,24],"117":[7,0,0,0,0,0,198,198,198,198,198,198,198,198,198,254,122],"118":[7,0,0,0,0,0,198,198,198,198,198,198,198,198,108,56,16],"119":[7,0,0,0,0,0,198,198,198,198,198,198,214,214,254,124,40],"120":[8,0,0,0,0,0,195,195,231,60,24,24,24,60,231,195,195],"121":[7,0,0,0,0,0,198,198,198,198,198,254,126,6,6,254,124],"122":[7,0,0,0,0,0,254,254,6,12,12,24,56,112,224,254,254],"123":[8,30,63,48,48,48,48,48,224,224,48,48,48,48,48,63,30],"124":[2,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192,192],"125":[8,120,252,12,12,12,12,12,7,7,12,12,12,12,12,252,120],"126":[7,0,0,0,0,0,0,0,34,118,220,0,0,0,0,0,0],"127":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},"ASC1609":{"name":"ASC1609","cellWidth":16,"height":16,"glyphs":{"32":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"33":[3,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,0,0,57344,57344],"34":[7,60928,60928,60928,60928,0,0,0,0,0,0,0,0,0,0,0,0],"35":[10,0,29568,29568,65472,65472,65472,29568,29568,29568,65472,65472,65472,29568,29568,29568,0],"36":[10,3072,32640,65472,65472,60864,60416,65408,65472,32704,3520,3520,60864,65472,65472,32640,3072],"37":[8,19968,60928,60928,19968,7168,7168,7168,7168,14336,14336,14336,14336,29184,30464,30464,29184],"38":[10,32256,65280,65280,59136,59136,32256,32256,65024,65216,59328,58240,58304,59328,65472,32576,15936],"39":[3,57344,57344,57344,57344,0,0,0,0,0,0,0,0,0,0,0,0],"40":[5,14336,28672,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,28672,14336],"41":[5,57344,28672,14336,14336,14336,14336,14336,14336,14336,14336,14336,14336,14336,14336,28672,57344],"42":[8,0,0,56064,56064,23040,15360,65280,65280,15360,23040,56064,56064,0,0,0,0],"43":[7,0,0,0,14336,14336,14336,65024,65024,65024,14336,14336,14336,14336,0,0,0],"44":[5,0,0,0,0,0,0,0,0,0,0,0,14336,14336,14336,12288,57344],"45":[7,0,0,0,0,0,0,65024,65024,65024,0,0,0,0,0,0,0],"46":[3,0,0,0,0,0,0,0,0,0,0,0,0,0,57344,57344,57344],"47":[6,7168,7168,7168,7168,14336,14336,14336,14336,28672,28672,28672,28672,57344,57344,57344,57344],"48":[8,15360,32256,65280,59136,59136,59136,59136,59136,59136,59136,59136,59136,59136,65280,32256,15360],"49":[7,14336,63488,63488,63488,14336,14336,14336,14336,14336,14336,14336,14336,14336,65024,65024,65024],"50":[8,32256,65280,65280,59136,59136,1792,1792,3584,7168,14336,28672,57344,57344,65280,65280,65280],"51":[8,32256,65280,65280,59136,59136,1792,15872,15872,15872,1792,1792,1792,59136,65280,65280,32256],"52":[10,896,1920,3968,8064,16256,31616,62336,58240,58240,65472,65472,65472,896,896,896,896],"53":[8,65280,65280,65280,57344,57344,65024,65280,65280,1792,1792,1792,1792,59136,65280,65280,32256],"54":[8,32256,65280,65280,59136,57344,57344,65024,65280,65280,59136,59136,59136,59136,65280,65280,32256],"55":[8,65280,65280,65280,1792,1792,3840,7680,15360,14336,14336,14336,14336,14336,14336,14336,14336],"56":[8,32256,65280,65280,59136,59136,59136,65280,32256,32256,59136,59136,59136,59136,65280,65280,32256],"57":[8,32256,65280,65280,59136,59136,59136,65280,65280,32512,1792,1792,1792,59136,65280,65280,32256],"58":[3,0,0,0,0,0,0,0,57344,57344,57344,0,0,57344,57344,57344,0],"59":[5,0,0,0,0,0,0,0,14336,14336,14336,0,0,14336,14336,14336,61440],"60":[8,768,1792,3840,7680,15360,30720,61440,57344,57344,61440,30720,15360,7680,3840,1792,768],"61":[8,0,0,0,0,65280,65280,65280,0,0,65280,65280,65280,0,0,0,0],"62":[8,49152,57344,61440,30720,15360,7680,3840,1792,1792,3840,7680,15360,30720,61440,57344,49152],"63":[8,32256,65280,65280,59136,59136,59136,1792,3840,7680,15360,14336,14336,0,14336,14336,14336],"64":[11,32640,65472,49344,49344,52928,57024,57024,56000,56256,56256,57088,52832,49248,49376,65472,32640],"65":[8,15360,32256,65280,59136,59136,59136,59136,65280,65280,65280,59136,59136,59136,59136,59136,59136],"66":[8,65024,65280,65280,59136,59136,59136,65024,65024,65024,59136,59136,59136,59136,65280,65280,65024],"67":[8,32256,65280,65280,59136,59136,57344,57344,57344,57344,57344,57344,59136,59136,65280,65280,32256],"68":[8,65024,65280,65280,59136,59136,59136,59136,59136,59136,59136,59136,59136,59136,65280,65280,65024],"69":[8,65280,65280,65280,57344,57344,57344,65024,65024,65024,57344,57344,57344,57344,65280,65280,65280],"70":[8,65280,65280,65280,57344,57344,57344,65024,65024,65024,57344,57344,57344,57344,57344,57344,57344],"71":[8,32256,65280,65280,59136,59136,57344,57344,61184,61184,61184,59136,59136,59136,65280,65280,32256],"72":[8,59136,59136,59136,59136,59136,59136,65280,65280,65280,59136,59136,59136,59136,59136,59136,59136],"73":[5,63488,63488,63488,28672,28672,28672,28672,28672,28672,28672,28672,28672,28672,63488,63488,63488],"74":[8,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,1792,59136,65280,65280,32256],"75":[10,57792,58304,59264,61184,65024,64512,63488,61440,63488,64512,65024,61184,59264,58304,57792,57792],"76":[7,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,65024,65024,65024],"77":[10,57792,62400,62400,65472,65472,60864,60864,57792,57792,57792,57792,57792,57792,57792,57792,57792],"78":[9,58240,58240,62336,62336,64384,64384,65408,61312,61312,59264,59264,58240,58240,58240,58240,58240],"79":[8,32256,65280,65280,59136,59136,59136,59136,59136,59136,59136,59136,59136,59136,65280,65280,32256],"80":[8,65024,65280,65280,59136,59136,59136,65280,65280,65024,57344,57344,57344,57344,57344,57344,57344],"81":[9,32512,65408,65408,58240,58240,58240,58240,58240,58240,58240,61312,61312,59264,65408,65408,32384],"82":[8,65024,65280,65280,59136,59136,59136,65280,65280,65024,64512,65024,60928,60928,59136,59136,59136],"83":[8,32256,65280,65280,59136,59136,57344,61440,31744,15872,7936,1792,1792,59136,65280,65280,32256],"84":[7,65024,65024,65024,14336,14336,14336,14336,14336,14336,14336,14336,14336,14336,14336,14336,14336],"85":[8,59136,59136,59136,59136,59136,59136,59136,59136,59136,59136,59136,59136,59136,65280,65280,32256],"86":[9,58240,58240,58240,58240,58240,58240,58240,58240,58240,58240,58240,58240,63360,32512,15872,7168],"87":[10,57792,57792,57792,57792,57792,57792,57792,57792,57792,57792,60864,60864,65472,62400,62400,57792],"88":[9,58240,58240,58240,58240,58240,63360,32512,15872,15872,32512,63360,58240,58240,58240,58240,58240],"89":[9,58240,58240,58240,58240,58240,63360,32512,15872,7168,7168,7168,7168,7168,7168,7168,7168],"90":[9,65408,65408,65408,896,896,1920,3840,7680,15360,30720,61440,57344,57344,65408,65408,65408],"91":[5,63488,63488,63488,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,63488,63488,63488],"92":[6,57344,57344,57344,57344,28672,28672,28672,28672,14336,14336,14336,14336,7168,7168,7168,7168],"93":[6,63488,63488,63488,14336,14336,14336,14336,14336,14336,14336,14336,14336,14336,63488,63488,63488],"94":[9,2048,7168,15872,30464,58240,58240,0,0,0,0,0,0,0,0,0,0],"95":[7,0,0,0,0,0,0,0,0,0,0,0,0,0,65024,65024,65024],"96":[5,57344,28672,14336,0,0,0,0,0,0,0,0,0,0,0,0,0],"97":[8,0,0,0,0,31744,65024,65280,1792,32512,65280,65280,59136,59136,65280,65280,31488],"98":[8,57344,57344,57344,57344,64512,65024,65280,59136,59136,59136,59136,59136,59136,65280,65024,64512],"99":[7,0,0,0,0,31744,65024,65024,60928,57344,57344,57344,57344,60928,65024,65024,31744],"100":[8,1792,1792,1792,1792,16128,32512,65280,59136,59136,59136,59136,59136,59136,65280,32512,16128],"101":[8,0,0,0,0,15360,32256,65280,59136,59136,65280,65280,65024,57344,65280,32512,16128],"102":[7,7680,15872,15872,14336,14336,14336,65024,65024,65024,14336,14336,14336,14336,14336,14336,14336],"103":[8,0,0,0,0,32000,65280,65280,59136,59136,65280,65280,32512,1792,65280,65280,32256],"104":[8,57344,57344,57344,57344,65024,65280,65280,59136,59136,59136,59136,59136,59136,59136,59136,59136],"105":[3,57344,57344,0,0,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344],"106":[6,7168,7168,0,0,7168,7168,7168,7168,7168,7168,7168,7168,7168,14336,61440,57344],"107":[8,57344,57344,57344,57344,59136,61184,65024,64512,63488,61440,63488,64512,65024,61184,59136,58112],"108":[3,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344,57344],"109":[11,0,0,0,0,48000,65472,65504,65504,61152,61152,61152,61152,61152,61152,61152,61152],"110":[8,0,0,0,0,48128,65024,65280,59136,59136,59136,59136,59136,59136,59136,59136,59136],"111":[8,0,0,0,0,32256,65280,65280,59136,59136,59136,59136,59136,59136,65280,65280,32256],"112":[8,0,0,0,0,48640,65280,65280,59136,59136,65280,65280,65024,57344,57344,57344,57344],"113":[8,0,0,0,0,32000,65280,65280,59136,59136,65280,65280,32512,1792,1792,1792,1792],"114":[7,0,0,0,0,60928,65024,65024,63488,61440,57344,57344,57344,57344,57344,57344,57344],"115":[8,0,0,0,0,32256,65280,65280,59136,57344,65024,32512,1792,59136,65280,65280,32256],"116":[7,14336,14336,14336,14336,65024,65024,65024,14336,14336,14336,14336,14336,14336,15872,15872,7168],"117":[8,0,0,0,0,59136,59136,59136,59136,59136,59136,59136,59136,59136,65280,32512,15616],"118":[9,0,0,0,0,58240,58240,58240,58240,58240,58240,58240,58240,63360,32512,15872,7168],"119":[11,0,0,0,0,57568,57568,57568,57568,57568,57568,61152,61152,65504,65504,29120,8320],"120":[10,0,0,0,0,57792,57792,62400,32640,16128,7680,7680,16128,32640,62400,57792,57792],"121":[8,0,0,0,0,59136,59136,59136,59136,59136,65280,65280,32512,1792,65280,65280,32256],"122":[8,0,0,0,0,65280,65280,65280,3840,7680,15360,30720,61440,57344,65280,65280,65280]}},"ASC1610":{"name":"ASC1610","cellWidth":16,"height":16,"glyphs":{"32":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"33":[2,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,0,0,49152,49152],"34":[5,55296,55296,55296,0,0,0,0,0,0,0,0,0,0,0,0,0],"35":[10,0,13056,13056,13056,65472,65472,13056,13056,13056,13056,65472,65472,13056,13056,13056,0],"36":[10,3072,32640,65472,52416,52224,52224,52224,65408,32704,3264,3264,3264,52416,65472,32640,3072],"37":[7,16896,41472,42496,17920,3072,3072,6144,6144,12288,12288,24576,24576,50176,51712,35328,33792],"38":[10,31744,65024,50688,50688,50688,31744,31744,64512,52800,51136,50048,49600,50112,59200,32256,15360],"39":[2,49152,49152,49152,0,0,0,0,0,0,0,0,0,0,0,0,0],"40":[5,6144,12288,24576,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,24576,12288,6144],"41":[5,49152,24576,12288,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,12288,24576,49152],"42":[8,0,0,2048,2048,18688,10752,7168,65280,7168,10752,18688,2048,2048,0,0,0],"43":[8,0,0,0,0,6144,6144,6144,65280,65280,6144,6144,6144,0,0,0,0],"44":[3,0,0,0,0,0,0,0,0,0,0,0,0,24576,24576,16384,32768],"45":[5,0,0,0,0,0,0,0,63488,63488,0,0,0,0,0,0,0],"46":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49152,49152],"47":[9,0,0,0,128,384,768,1536,3072,6144,12288,24576,49152,32768,0,0,0],"48":[9,15872,32512,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,32512,15872],"49":[6,12288,28672,61440,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,64512,64512],"50":[9,32256,65280,50048,384,384,384,896,1792,3584,7168,14336,28672,57344,57344,65408,65408],"51":[9,15360,32256,49920,768,768,768,768,7680,7680,768,768,768,768,49920,32256,15360],"52":[10,384,896,1920,3968,7552,14720,29056,57728,49536,65472,65472,384,384,384,384,384],"53":[9,65408,65408,49152,49152,49152,49152,65024,65280,896,384,384,384,49536,50048,32512,15872],"54":[8,7936,16128,28672,57344,49152,49152,49152,65024,65280,49920,49920,49920,49920,49920,65280,32256],"55":[9,65408,65408,384,384,896,1792,3584,7168,6144,6144,6144,6144,6144,6144,6144,6144],"56":[8,32256,65280,49920,49920,49920,49920,49920,32256,32256,49920,49920,49920,49920,49920,65280,32256],"57":[8,32256,65280,49920,49920,49920,49920,49920,65280,32512,768,768,768,1792,3584,64512,63488],"58":[2,0,0,0,0,0,49152,49152,49152,0,0,49152,49152,49152,0,0,0],"59":[3,0,0,0,0,0,0,0,0,0,0,24576,24576,0,24576,24576,49152],"60":[9,384,768,1536,3072,6144,12288,24576,49152,49152,24576,12288,6144,3072,1536,768,384],"61":[8,0,0,0,0,0,65280,65280,0,0,65280,65280,0,0,0,0,0],"62":[16,0,64,96,112,120,32764,32766,0,0,32766,16382,7168,3072,1024,0,0],"63":[10,16128,32640,57792,49344,49344,49600,896,1792,3584,3072,3072,3072,3072,0,3072,3072],"64":[10,16128,32640,49344,49344,49344,52928,57024,56000,56000,56192,57088,52800,49344,24960,16128,7680],"65":[10,3072,7680,16128,29568,57792,49344,49344,49344,49344,65472,65472,49344,49344,49344,49344,49344],"66":[9,65024,65280,50048,49536,49536,49536,49536,65280,65280,49536,49536,49536,49536,50048,65280,65024],"67":[9,15872,32512,58240,49536,49536,49152,49152,49152,49152,49152,49152,49536,49536,58240,32512,15872],"68":[9,65024,65280,50048,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,50048,65280,65024],"69":[8,65280,65280,49152,49152,49152,49152,49152,65024,65024,49152,49152,49152,49152,49152,65280,65280],"70":[8,65280,65280,49152,49152,49152,49152,49152,65024,65024,49152,49152,49152,49152,49152,49152,49152],"71":[10,15872,32512,58240,49536,49536,49152,49152,49152,51136,51136,49536,49536,49536,58240,32512,15872],"72":[9,49536,49536,49536,49536,49536,49536,49536,65408,65408,49536,49536,49536,49536,49536,49536,49536],"73":[6,64512,64512,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,64512,64512],"74":[8,16128,16128,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,50688,50688,65024,31744],"75":[10,49344,49600,50048,50944,52736,56320,63488,61440,63488,56320,52736,50944,50048,49600,49344,49344],"76":[8,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,65280,65280],"77":[10,32832,49344,57792,62400,57024,52416,52416,52416,49344,49344,49344,49344,49344,49344,49344,49344],"78":[10,49344,49344,49344,57536,61632,63680,56512,52928,51136,50112,49600,49344,49344,49344,49344,49344],"79":[9,15872,32512,58240,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,58240,32512,15872],"80":[10,65280,65408,49600,49344,49344,49344,49600,65408,65280,49152,49152,49152,49152,49152,49152,49152],"81":[9,15872,32512,58240,49536,49536,49536,49536,49536,49536,49536,49536,52608,51072,58240,32640,16000],"82":[10,65280,65408,49600,49344,49344,49344,49600,65408,65280,56320,52736,50944,50048,49600,49344,49344],"83":[10,16128,32640,57792,49344,49152,49152,57344,32512,16256,448,192,192,49344,57792,32640,16128],"84":[10,65472,65472,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072],"85":[9,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,58240,32512,15872],"86":[9,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,58240,30464,15872,7168,2048],"87":[10,49344,49344,49344,49344,49344,49344,49344,49344,52416,52416,52416,52416,57024,65472,29568,8448],"88":[10,49344,49344,49344,49344,57792,29568,16128,7680,7680,16128,29568,57792,49344,49344,49344,49344],"89":[10,49344,49344,49344,49344,57792,29568,16128,7680,3072,3072,3072,3072,3072,3072,3072,3072],"90":[10,65472,65472,192,192,448,896,1792,3584,7168,14336,28672,57344,49152,49152,65472,65472],"91":[4,61440,61440,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,61440,61440],"92":[9,0,0,0,32768,49152,24576,12288,6144,3072,1536,768,384,128,0,0,0],"93":[4,61440,61440,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,61440,61440],"94":[9,2048,7168,13824,25344,49536,0,0,0,0,0,0,0,0,0,0,0],"95":[6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64512,64512],"96":[4,49152,24576,12288,0,0,0,0,0,0,0,0,0,0,0,0,0],"97":[8,0,0,0,0,31744,65024,1792,768,32512,59136,49920,49920,49920,49920,65280,31488],"98":[8,49152,49152,49152,49152,64512,65024,50944,49920,49920,49920,49920,49920,49920,50944,65024,48128],"99":[7,0,0,0,0,31744,65024,50688,49152,49152,49152,49152,49152,49152,50688,65024,31744],"100":[8,768,768,768,768,16128,32512,58112,49920,49920,49920,49920,49920,49920,58112,32512,15616],"101":[8,0,0,0,0,15360,32256,59136,49920,49920,49920,65280,65024,49152,57344,32512,16128],"102":[6,7168,15360,12288,12288,12288,12288,12288,64512,64512,12288,12288,12288,12288,12288,12288,12288],"103":[8,0,0,0,0,32000,65280,49920,49920,49920,49920,65280,32512,768,768,65280,32256],"104":[8,49152,49152,49152,49152,65024,65280,59136,49920,49920,49920,49920,49920,49920,49920,49920,49920],"105":[2,49152,49152,0,0,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"106":[5,6144,6144,0,0,6144,6144,6144,6144,6144,6144,6144,6144,6144,12288,57344,49152],"107":[8,49152,49152,49152,49152,49920,50944,52736,56320,63488,61440,63488,56320,52736,50944,49920,49408],"108":[2,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"109":[10,0,0,0,0,45952,65472,52416,52416,52416,52416,52416,52416,52416,52416,52416,52416],"110":[8,0,0,0,0,48128,65024,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920],"111":[8,0,0,0,0,32256,65280,59136,49920,49920,49920,49920,49920,49920,59136,65280,32256],"112":[8,0,0,0,0,48640,65280,49920,49920,49920,49920,65280,65024,49152,49152,49152,49152],"113":[8,0,0,0,0,32000,65280,49920,49920,49920,49920,65280,32512,768,768,768,768],"114":[7,0,0,0,0,52736,56832,63488,61440,57344,49152,49152,49152,49152,49152,49152,49152],"115":[8,0,0,0,0,32256,65280,49920,49152,49152,65024,16128,768,768,49920,65280,32256],"116":[6,12288,12288,12288,12288,64512,64512,12288,12288,12288,12288,12288,12288,12288,12288,15360,6144],"117":[8,0,0,0,0,49920,49920,49920,49920,49920,49920,49920,49920,49920,59136,32512,15616],"118":[9,0,0,0,0,49536,49536,49536,49536,49536,49536,49536,58240,30464,15872,7168,2048],"119":[10,0,0,0,0,49344,49344,52416,52416,52416,52416,52416,52416,57024,65472,29568,8448],"120":[10,0,0,0,0,49344,49344,57792,29568,16128,7680,7680,16128,29568,57792,49344,49344],"121":[8,0,0,0,0,49920,49920,49920,49920,49920,49920,65280,32512,768,768,65280,32256],"122":[9,0,0,0,0,65408,65408,896,1792,3584,7168,14336,28672,57344,49152,65408,65408],"123":[6,3072,6144,6144,6144,6144,6144,6144,12288,49152,12288,12288,6144,6144,6144,6144,3072],"124":[2,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"125":[6,49152,24576,24576,24576,24576,24576,24576,12288,3072,12288,12288,24576,24576,24576,24576,49152],"126":[13,0,0,0,0,0,0,15384,32280,59192,50160,49376,0,0,0,0,0],"127":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},"ASC2010":{"name":"ASC2010","cellWidth":16,"height":20,"glyphs":{"32":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"33":[2,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,0,0,49152,49152,0,0,0,0],"34":[5,55296,55296,55296,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"35":[10,0,13056,13056,13056,65472,65472,13056,13056,13056,13056,65472,65472,13056,13056,13056,0,0,0,0,0],"36":[10,3072,32640,65472,52416,52224,52224,52224,65408,32704,3264,3264,3264,52416,65472,32640,3072,0,0,0,0],"37":[7,16896,41472,42496,17920,3072,3072,6144,6144,12288,12288,24576,24576,50176,51712,35328,33792,0,0,0,0],"38":[10,31744,65024,50688,50688,50688,31744,31744,64512,52800,51136,50048,49600,50112,59200,32256,15360,0,0,0,0],"39":[2,49152,49152,49152,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"40":[5,6144,12288,24576,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,24576,12288,6144,0,0,0,0],"41":[5,49152,24576,12288,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,12288,24576,49152,0,0,0,0],"42":[9,0,0,2048,2048,18688,10752,7168,65408,7168,10752,18688,2048,2048,0,0,0,0,0,0,0],"43":[8,0,0,0,0,6144,6144,6144,65280,65280,6144,6144,6144,0,0,0,0,0,0,0,0],"44":[3,0,0,0,0,0,0,0,0,0,0,0,0,24576,24576,16384,32768,0,0,0,0],"45":[5,0,0,0,0,0,0,0,63488,63488,0,0,0,0,0,0,0,0,0,0,0],"46":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49152,49152,0,0,0,0],"47":[9,0,0,0,128,384,768,1536,3072,6144,12288,24576,49152,32768,0,0,0,0,0,0,0],"48":[9,15872,32512,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,32512,15872],"49":[6,12288,28672,61440,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,64512,64512],"50":[9,32256,65280,50048,384,384,384,384,384,896,1792,3584,7168,14336,28672,57344,57344,57344,57344,65408,65408],"51":[8,15360,32256,49920,768,768,768,768,768,768,7680,7680,768,768,768,768,768,768,49920,32256,15360],"52":[10,128,384,896,1920,3968,7552,14720,29056,57728,49536,65472,65472,384,384,384,384,384,384,384,384],"53":[9,65408,65408,49152,49152,49152,49152,49152,49152,65024,65280,896,384,384,384,384,384,49536,50048,32512,15872],"54":[8,7936,16128,28672,57344,49152,49152,49152,49152,49152,65024,65280,49920,49920,49920,49920,49920,49920,49920,65280,32256],"55":[9,65408,65408,384,384,384,384,896,1792,3584,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072],"56":[8,32256,65280,49920,49920,49920,49920,49920,49920,49920,32256,32256,49920,49920,49920,49920,49920,49920,49920,65280,32256],"57":[8,32256,65280,49920,49920,49920,49920,49920,49920,49920,65280,32512,768,768,768,768,768,1792,3584,64512,63488],"58":[2,0,0,0,0,0,0,0,0,0,49152,49152,0,0,49152,49152,0,0,0,0,0],"59":[3,0,0,0,0,0,0,0,0,0,0,24576,24576,0,24576,24576,49152,16384,0,0,0],"60":[9,384,768,1536,3072,6144,12288,24576,49152,49152,24576,12288,6144,3072,1536,768,384,0,0,0,0],"61":[8,0,0,0,0,0,65280,65280,0,0,65280,65280,0,0,0,0,0,0,0,0,0],"62":[9,49152,24576,12288,6144,3072,1536,768,384,384,768,1536,3072,6144,12288,24576,49152,0,0,0,0],"63":[10,16128,32640,57792,49344,49344,49600,896,1792,3584,3072,3072,3072,3072,0,0,3072,3072,0,0,0],"64":[10,16128,32640,49344,49344,49344,52928,57024,56000,56000,56192,57088,52800,49344,24960,16128,7680,0,0,0,0],"65":[10,3072,7680,16128,29568,57792,49344,49344,49344,49344,49344,49344,65472,65472,49344,49344,49344,49344,49344,49344,49344],"66":[9,65024,65280,50048,49536,49536,49536,49536,50048,65408,65280,65408,50048,49536,49536,49536,49536,49536,50048,65280,65024],"67":[9,15872,32512,58240,49536,49536,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49536,49536,58240,32512,15872],"68":[9,65024,65280,50048,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,50048,65280,65024],"69":[8,65280,65280,49152,49152,49152,49152,49152,49152,49152,65024,65024,49152,49152,49152,49152,49152,49152,49152,65280,65280],"70":[8,65280,65280,49152,49152,49152,49152,49152,49152,49152,65024,65024,49152,49152,49152,49152,49152,49152,49152,49152,49152],"71":[10,15872,32512,58240,49536,49536,49152,49152,49152,49152,49152,49152,51136,51136,49536,49536,49536,49536,58240,32512,15872],"72":[9,49536,49536,49536,49536,49536,49536,49536,49536,49536,65408,65408,49536,49536,49536,49536,49536,49536,49536,49536,49536],"73":[6,64512,64512,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,64512,64512],"74":[9,32640,32640,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,50688,50688,50688,31744,14336],"75":[10,49344,49344,49600,50048,50944,52736,56320,63488,61440,61440,61440,63488,56320,52736,50944,50048,49600,49344,49344,49344],"76":[8,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,65280,65280],"77":[10,32832,49344,57792,62400,65472,57024,52416,52416,52416,52416,52416,49344,49344,49344,49344,49344,49344,49344,49344,49344],"78":[10,49344,49344,49344,57536,61632,63680,56512,52928,51136,50112,49600,49344,49344,49344,49344,49344,49344,49344,49344,49344],"79":[9,15872,32512,58240,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,58240,32512,15872],"80":[10,65280,65408,49600,49344,49344,49344,49344,49344,49600,65408,65280,49152,49152,49152,49152,49152,49152,49152,49152,49152],"81":[9,15872,32512,58240,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,52608,51072,58240,32640,16000],"82":[10,65280,65408,49600,49344,49344,49344,49344,49344,49344,49600,65408,65280,63488,56320,52736,50944,50048,49600,49344,49344],"83":[10,16128,32640,57792,49344,49344,49152,49152,49152,57344,32512,16256,448,192,192,192,49344,49344,57792,32640,16128],"84":[10,65472,65472,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072],"85":[9,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,58240,32512,15872],"86":[9,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,58240,30464,15872,7168,2048],"87":[10,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,52416,52416,52416,52416,57024,65472,29568,8448],"88":[10,49344,49344,49344,49344,49344,49344,57792,29568,16128,7680,7680,16128,29568,57792,49344,49344,49344,49344,49344,49344],"89":[10,49344,49344,49344,49344,49344,49344,57792,29568,16128,7680,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072],"90":[10,65472,65472,192,192,192,192,448,896,1792,3584,7168,14336,28672,57344,49152,49152,49152,49152,65472,65472],"91":[4,61440,61440,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,61440,61440,0,0,0,0],"92":[9,0,0,0,32768,49152,24576,12288,6144,3072,1536,768,384,128,0,0,0,0,0,0,0],"93":[4,61440,61440,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,61440,61440,0,0,0,0],"94":[9,2048,7168,13824,25344,49536,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"95":[6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64512,64512,0,0,0,0],"96":[4,49152,24576,12288,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"97":[8,0,0,0,0,31744,65024,1792,768,768,768,32512,65280,49920,49920,49920,49920,49920,65280,32512,15104],"98":[8,49152,49152,49152,49152,49152,49152,64512,65024,50944,49920,49920,49920,49920,49920,49920,49920,49920,50944,65024,48128],"99":[7,0,0,0,0,0,31744,65024,50688,49152,49152,49152,49152,49152,49152,49152,49152,49152,50688,65024,31744],"100":[8,768,768,768,768,768,768,16128,32512,58112,49920,49920,49920,49920,49920,49920,49920,49920,58112,32512,15616],"101":[8,0,0,0,0,0,15360,32256,59136,49920,49920,49920,49920,65280,65024,49152,49152,49152,49152,32512,15872],"102":[6,7168,15360,12288,12288,12288,12288,12288,64512,64512,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288],"103":[8,0,0,0,0,0,32000,65280,49920,49920,49920,49920,65280,32512,768,768,768,768,1792,65280,32256],"104":[8,49152,49152,49152,49152,49152,49152,65024,65280,59136,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920],"105":[2,49152,49152,0,0,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"106":[5,6144,6144,0,0,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,12288,57344,49152],"107":[8,49152,49152,49152,49152,49152,49152,49920,50944,52736,56320,63488,61440,61440,63488,56320,52736,50944,49920,49920,49920],"108":[2,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"109":[10,0,0,0,0,0,45952,65472,52416,52416,52416,52416,52416,52416,52416,52416,52416,52416,52416,52416,52416],"110":[8,0,0,0,0,0,48128,65024,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920],"111":[8,0,0,0,0,0,32256,65280,59136,49920,49920,49920,49920,49920,49920,49920,49920,49920,59136,65280,32256],"112":[8,0,0,0,0,0,48640,65280,49920,49920,49920,49920,65280,65024,49152,49152,49152,49152,49152,49152,49152],"113":[9,0,0,0,0,0,32000,65280,49920,49920,49920,49920,65280,32512,768,768,768,768,896,896,768],"114":[7,0,0,0,0,0,52736,56832,63488,61440,57344,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"115":[8,0,0,0,0,0,15872,32512,49920,49152,49152,49152,32256,16128,768,768,768,768,49920,65024,31744],"116":[6,12288,12288,12288,12288,12288,12288,64512,64512,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,15360,6144],"117":[8,0,0,0,0,0,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,59136,32512,15616],"118":[9,0,0,0,0,0,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,58240,30464,15872,7168,2048],"119":[10,0,0,0,0,0,49344,49344,49344,49344,49344,52416,52416,52416,52416,52416,52416,57024,65472,29568,8448],"120":[10,0,0,0,0,0,49344,49344,49344,57792,29568,16128,7680,7680,16128,29568,57792,49344,49344,49344,49344],"121":[8,0,0,0,0,0,49920,49920,49920,49920,49920,49920,65280,32512,768,768,768,768,50944,65280,32256],"122":[9,0,0,0,0,0,65408,65408,384,384,896,1792,3584,7168,14336,28672,57344,49152,49152,65408,65408]}},"ASC2410":{"name":"ASC2410","cellWidth":16,"height":24,"glyphs":{"32":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"33":[2,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,0,0,49152,49152,49152,0,0,0,0,0],"34":[5,55296,55296,55296,55296,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"35":[10,0,0,13056,13056,13056,13056,65472,65472,13056,13056,13056,13056,65472,65472,13056,13056,13056,13056,0,0,0,0,0,0],"36":[10,0,0,3072,3072,32640,65472,52416,52224,52224,52224,65408,32704,3264,3264,3264,52416,65472,32640,3072,3072,0,0,0,0],"37":[7,0,0,16896,41472,42496,42496,17920,3072,3072,6144,6144,12288,12288,24576,24576,50176,51712,51712,35328,33792,0,0,0,0],"38":[10,0,0,31744,65024,50688,50688,50688,50688,31744,31744,64512,52736,50752,51136,50048,49600,50112,59200,32256,15360,0,0,0,0],"39":[2,49152,49152,49152,49152,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"40":[5,0,0,6144,12288,24576,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,24576,12288,6144,0,0,0,0],"41":[5,0,0,49152,24576,12288,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,12288,24576,49152,0,0,0,0],"42":[9,0,0,0,0,2048,2048,18688,10752,7168,65408,7168,10752,18688,2048,2048,0,0,0,0,0,0,0,0,0],"43":[8,0,0,0,0,0,6144,6144,6144,6144,65280,65280,6144,6144,6144,6144,0,0,0,0,0,0,0,0,0],"44":[3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24576,24576,24576,16384,32768,0,0,0,0,0],"45":[5,0,0,0,0,0,0,0,0,0,63488,63488,0,0,0,0,0,0,0,0,0,0,0,0,0],"46":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49152,49152,0,0,0,0,0],"47":[9,0,0,0,0,0,128,384,768,1536,3072,6144,12288,24576,49152,32768,0,0,0,0,0,0,0,0,0],"48":[9,15872,32512,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,32512,15872],"49":[6,12288,28672,61440,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,64512,64512],"50":[9,32256,65280,50048,384,384,384,384,384,384,384,896,1792,3584,7168,14336,28672,57344,57344,57344,57344,57344,57344,65408,65408],"51":[8,15360,32256,49920,768,768,768,768,768,768,768,768,15872,15872,768,768,768,768,768,768,768,768,49920,32256,15360],"52":[10,128,384,896,1920,3968,7552,14720,29056,57728,49536,49536,65472,65472,384,384,384,384,384,384,384,384,384,384,384],"53":[9,65408,65408,49152,49152,49152,49152,49152,49152,49152,49152,49152,65024,65280,384,384,384,384,384,384,384,49536,50048,32512,15872],"54":[8,7936,16128,28672,57344,49152,49152,49152,49152,49152,49152,49152,65024,65280,49920,49920,49920,49920,49920,49920,49920,49920,49920,65280,32256],"55":[9,65408,65408,384,384,384,384,384,896,1792,3584,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072],"56":[8,32256,65280,49920,49920,49920,49920,49920,49920,49920,49920,49920,32256,32256,49920,49920,49920,49920,49920,49920,49920,49920,49920,65280,32256],"57":[8,32256,65280,49920,49920,49920,49920,49920,49920,49920,49920,49920,65280,32512,768,768,768,768,768,768,768,1792,3584,64512,63488],"58":[3,0,0,0,0,0,0,0,57344,57344,57344,0,0,0,0,0,57344,57344,57344,0,0,0,0,0,0],"59":[3,0,0,0,0,0,0,0,0,0,0,0,0,24576,24576,0,24576,24576,24576,16384,32768,0,0,0,0],"60":[9,0,0,384,768,1536,3072,6144,12288,24576,49152,49152,24576,12288,6144,3072,1536,768,384,0,0,0,0,0,0],"61":[8,0,0,0,0,0,0,0,65280,65280,0,0,65280,65280,0,0,0,0,0,0,0,0,0,0,0],"62":[9,0,0,49152,24576,12288,6144,3072,1536,768,384,384,768,1536,3072,6144,12288,24576,49152,0,0,0,0,0,0],"63":[10,0,0,16128,32640,57792,49344,49344,49600,896,1792,3584,3072,3072,3072,3072,3072,0,0,3072,3072,0,0,0,0],"64":[10,0,0,16128,32640,49344,49344,49344,52928,57024,56000,56000,56192,57088,52800,49344,24960,16128,7680,0,0,0,0,0,0],"65":[10,3072,7680,16128,29568,57792,49344,49344,49344,49344,49344,49344,65472,65472,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344],"66":[9,65024,65280,50048,49536,49536,49536,49536,49536,49536,49536,50048,65280,65408,50048,49536,49536,49536,49536,49536,49536,49536,50048,65280,65024],"67":[9,15872,32512,58240,49536,49536,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49536,49536,58240,32512,15872],"68":[9,65024,65280,50048,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,50048,65280,65024],"69":[8,65280,65280,49152,49152,49152,49152,49152,49152,49152,49152,49152,65024,65024,49152,49152,49152,49152,49152,49152,49152,49152,49152,65280,65280],"70":[8,65280,65280,49152,49152,49152,49152,49152,49152,49152,49152,49152,65024,65024,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"71":[10,15872,32512,58240,49536,49536,49152,49152,49152,49152,49152,49152,51136,51136,49536,49536,49536,49536,49536,49536,49536,49536,58240,32512,15872],"72":[9,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,65408,65408,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536],"73":[6,64512,64512,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,64512,64512],"74":[9,32640,32640,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,50688,50688,50688,31744,14336],"75":[10,49344,49600,49600,50048,49920,50944,50688,52736,52224,56320,63488,63488,61440,63488,56320,52224,52736,50688,50944,49920,50048,49600,49600,49344],"76":[8,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,65280,65280],"77":[10,32832,49344,57792,62400,65472,57024,52416,52416,52416,52416,52416,52416,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344],"78":[10,49344,49344,57536,57536,61632,61632,55488,55488,52416,52416,50880,50880,50112,50112,49600,49600,49344,49344,49344,49344,49344,49344,49344,49344],"79":[9,15872,32512,58240,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,58240,32512,15872],"80":[10,65280,65408,49600,49344,49344,49344,49344,49344,49344,49344,49600,65408,65280,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"81":[9,15872,32512,58240,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,52608,51072,58240,32640,16000],"82":[10,65280,65408,49600,49344,49344,49344,49344,49344,49344,49344,49600,65472,65280,61440,63488,56320,52224,52736,50688,50944,49920,50048,49600,49600],"83":[10,16128,32640,57792,49344,49344,49152,49152,49152,49152,49152,57344,32512,16256,448,192,192,192,192,192,49344,49344,57792,32640,16128],"84":[10,65472,65472,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072],"85":[9,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,58240,32512,15872],"86":[9,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,58240,30464,15872,7168,2048],"87":[10,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,52416,52416,52416,52416,52416,57024,65472,29568,8448],"88":[10,49344,49344,49344,49344,49344,49344,49344,49344,57792,29568,16128,7680,7680,16128,29568,57792,49344,49344,49344,49344,49344,49344,49344,49344],"89":[10,49344,49344,49344,49344,49344,49344,49344,49344,57792,29568,16128,7680,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072],"90":[10,65472,65472,192,192,192,192,192,192,448,896,1792,3584,7168,14336,28672,57344,49152,49152,49152,49152,49152,49152,65472,65472],"91":[4,61440,61440,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,61440,61440,0,0,0,0],"92":[9,0,32768,32768,49152,16384,24576,8192,12288,4096,6144,2048,3072,1024,1536,512,768,256,384,128,128,0,0,0,0],"93":[4,61440,61440,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,61440,61440,0,0,0,0],"94":[9,2048,7168,13824,25344,49536,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"95":[6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64512,64512,0,0,0,0],"96":[4,49152,57344,28672,12288,4096,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"97":[8,0,0,0,0,0,0,0,31744,65024,1792,768,768,768,32512,65280,49920,49920,49920,49920,49920,49920,65280,32512,15104],"98":[8,49152,49152,49152,49152,49152,49152,49152,64512,65024,50944,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,50944,65024,48128],"99":[7,0,0,0,0,0,0,0,31744,65024,50688,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,50688,65024,31744],"100":[8,768,768,768,768,768,768,768,16128,32512,58112,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,58112,32512,15616],"101":[8,0,0,0,0,0,0,0,15360,65024,59136,49920,49920,49920,49920,49920,65280,65024,49152,49152,49152,49152,49152,32512,15872],"102":[6,7168,15360,12288,12288,12288,12288,12288,64512,64512,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288],"103":[8,0,0,0,0,0,0,0,32000,65280,49920,49920,49920,49920,49920,49920,65280,32512,768,768,768,768,1792,65280,32256],"104":[8,49152,49152,49152,49152,49152,49152,49152,65024,65280,59136,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920],"105":[2,0,49152,49152,0,0,0,0,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"106":[5,0,6144,6144,0,0,0,0,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,12288,57344,49152],"107":[8,49152,49152,49152,49152,49152,49152,49152,49152,49920,50944,52736,56320,63488,61440,61440,61440,55296,55296,52224,52224,50688,50944,49920,49920],"108":[2,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"109":[10,0,0,0,0,0,0,0,45952,65472,52416,52416,52416,52416,52416,52416,52416,52416,52416,52416,52416,52416,52416,52416,52416],"110":[8,0,0,0,0,0,0,0,48128,65024,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920],"111":[8,0,0,0,0,0,0,0,32256,65280,59136,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,59136,65280,32256],"112":[8,0,0,0,0,0,0,0,48640,65280,49920,49920,49920,49920,49920,65280,65024,49152,49152,49152,49152,49152,49152,49152,49152],"113":[9,0,0,0,0,0,0,0,32000,65280,49920,49920,49920,49920,49920,65280,32512,768,768,768,768,768,896,896,768],"114":[7,0,0,0,0,0,0,0,52736,56832,63488,61440,57344,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"115":[8,0,0,0,0,0,0,0,15872,32512,49920,49152,49152,49152,49152,49152,32256,16128,768,768,768,768,49920,65024,31744],"116":[6,0,12288,12288,12288,12288,12288,12288,64512,64512,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,15360,6144],"117":[8,0,0,0,0,0,0,0,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920,59136,32512,15616],"118":[9,0,0,0,0,0,0,0,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,58240,30464,15872,7168,2048],"119":[10,0,0,0,0,0,0,0,49344,49344,49344,49344,49344,49344,52416,52416,52416,52416,52416,52416,52416,57024,65472,29568,8448],"120":[10,0,0,0,0,0,0,0,49344,49344,49344,49344,49344,57792,29568,16128,7680,7680,16128,29568,57792,49344,49344,49344,49344],"121":[8,0,0,0,0,0,0,0,49920,49920,49920,49920,49920,49920,49920,65280,32512,768,768,768,768,768,50944,65280,32256],"122":[9,0,0,0,0,0,0,0,65408,65408,384,384,384,896,1792,3584,7168,14336,28672,57344,49152,49152,49152,65408,65408]}},"ASC2412":{"name":"ASC2412","cellWidth":16,"height":24,"glyphs":{"32":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"33":[2,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,0,0,49152,49152,49152,0,0,0,0,0],"34":[5,55296,55296,55296,55296,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"35":[12,0,0,12480,12480,12480,12480,65520,65520,12480,12480,12480,12480,65520,65520,12480,12480,12480,12480,0,0,0,0,0,0],"36":[12,0,0,1536,1536,32736,65520,50736,50688,50688,50688,65504,32752,1584,1584,1584,50736,65520,32736,1536,1536,0,0,0,0],"37":[14,12312,30768,27696,52272,52320,52320,52320,52416,52416,27840,31104,12672,768,816,888,1624,1740,1740,3276,3276,3276,6232,6264,6192],"38":[15,1984,4064,7280,6192,6192,6192,6192,3168,3776,1920,3840,4864,9088,25036,16588,49388,49260,49208,49208,57464,28924,16364,8132,0],"39":[2,49152,49152,49152,49152,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"40":[5,0,0,6144,12288,24576,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,24576,12288,6144,0,0,0,0],"41":[5,0,0,49152,24576,12288,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,12288,24576,49152,0,0,0,0],"42":[11,0,0,0,0,1024,17472,9344,5376,3584,65504,3584,5376,9344,17472,1024,0,0,0,0,0,0,0,0,0],"43":[12,0,0,0,1536,1536,1536,1536,1536,1536,1536,1536,65520,65520,1536,1536,1536,1536,1536,1536,1536,1536,0,0,0],"44":[3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24576,24576,24576,16384,32768,0,0,0,0,0],"45":[7,0,0,0,0,0,0,0,0,0,0,0,65024,65024,0,0,0,0,0,0,0,0,0,0,0],"46":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49152,49152,0,0,0,0,0],"47":[9,0,0,0,0,0,128,384,768,1536,3072,6144,12288,24576,49152,32768,0,0,0,0,0,0,0,0,0],"48":[11,16256,32704,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,32704,16256],"49":[6,12288,28672,61440,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,64512,64512],"50":[11,32640,65472,49376,96,96,96,96,96,96,96,224,448,896,1792,3584,7168,14336,28672,57344,57344,57344,57344,65504,65504],"51":[10,16128,32640,49344,192,192,192,192,192,192,192,192,8064,8064,192,192,192,192,192,192,192,192,49344,32640,16128],"52":[12,64,192,448,960,1984,3776,7360,14528,28864,57536,49344,65520,65520,192,192,192,192,192,192,192,192,192,192,192],"53":[11,65504,65504,49152,49152,49152,49152,49152,49152,49152,49152,49152,65408,65472,224,96,96,96,96,96,96,49248,49376,32704,16256],"54":[10,8128,16320,28672,57344,49152,49152,49152,49152,49152,49152,49152,65408,65472,49344,49344,49344,49344,49344,49344,49344,49344,49344,65472,32640],"55":[10,65472,65472,192,192,192,192,192,448,896,1792,3584,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072],"56":[10,32640,65472,49344,49344,49344,49344,49344,49344,49344,49344,49344,32640,32640,49344,49344,49344,49344,49344,49344,49344,49344,49344,65472,32640],"57":[10,32640,65472,49344,49344,49344,49344,49344,49344,49344,49344,49344,65472,32704,192,192,192,192,192,192,192,448,896,65280,65024],"58":[3,0,0,0,0,0,0,0,57344,57344,57344,0,0,0,0,57344,57344,57344,0,0,0,0,0,0,0],"59":[3,0,0,0,0,0,0,0,0,0,0,0,0,24576,24576,0,24576,24576,24576,16384,32768,0,0,0,0],"60":[9,0,0,384,768,1536,3072,6144,12288,24576,49152,49152,24576,12288,6144,3072,1536,768,384,0,0,0,0,0,0],"61":[8,0,0,0,0,0,0,0,65280,65280,0,0,65280,65280,0,0,0,0,0,0,0,0,0,0,0],"62":[9,0,0,49152,24576,12288,6144,3072,1536,768,384,384,768,1536,3072,6144,12288,24576,49152,0,0,0,0,0,0],"63":[11,0,0,16256,32704,57568,49248,49248,49376,448,896,1792,1536,1536,1536,1536,1536,0,0,1536,1536,0,0,0,0],"64":[12,0,0,16320,32736,49200,49200,49200,53168,57264,55472,55472,55520,57280,53136,49200,24672,16320,8064,0,0,0,0,0,0],"65":[12,1536,3840,8064,14784,28896,57456,49200,49200,49200,49200,49200,65520,65520,49200,49200,49200,49200,49200,49200,49200,49200,49200,49200,49200],"66":[11,65408,65472,49376,49248,49248,49248,49248,49248,49248,49248,49376,65472,65504,49376,49248,49248,49248,49248,49248,49248,49248,49376,65472,65408],"67":[11,16256,32704,57568,49248,49248,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49248,49248,57568,32704,16256],"68":[11,65408,65472,49376,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49376,65472,65408],"69":[10,65472,65472,49152,49152,49152,49152,49152,49152,49152,49152,49152,65408,65408,49152,49152,49152,49152,49152,49152,49152,49152,49152,65472,65472],"70":[10,65472,65472,49152,49152,49152,49152,49152,49152,49152,49152,49152,65408,65408,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"71":[12,16256,32704,57568,49248,49248,49152,49152,49152,49152,49152,49152,49648,49648,49248,49248,49248,49248,49248,49248,49248,49248,57568,32704,16256],"72":[11,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,65504,65504,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248],"73":[6,64512,64512,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,64512,64512],"74":[10,32704,32704,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,50688,50688,50688,31744,14336],"75":[12,49376,49600,49600,50048,49920,50944,50688,52736,52224,56320,63488,63488,61440,63488,56320,52224,52736,50688,50944,49920,50048,49600,49648,49392],"76":[9,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,65408,65408],"77":[12,32784,49200,57456,61680,63984,57264,53040,50736,50736,50736,50736,50736,49200,49200,49200,49200,49200,49200,49200,49200,49200,49200,49200,49200],"78":[12,49200,49200,57392,57392,61488,61488,55344,55344,52272,52272,50736,50736,49968,49968,49584,49584,49392,49392,49264,49264,49200,49200,49200,49200],"79":[11,16256,32704,57568,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,57568,32704,16256],"80":[12,65472,65504,49264,49200,49200,49200,49200,49200,49200,49200,49264,65504,65472,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"81":[11,16256,32704,57568,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,50016,49632,57568,32736,16288],"82":[12,65472,65504,49264,49200,49200,49200,49200,49200,49200,49200,49264,65520,65504,64512,52736,50944,49920,50048,49536,49600,49344,49392,49264,49264],"83":[12,16320,32736,57456,49200,49200,49152,49152,49152,49152,49152,57344,32704,16352,112,48,48,48,48,48,49200,49200,57456,32736,16320],"84":[12,65520,65520,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536],"85":[11,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,57568,32704,16256],"86":[11,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,57568,29120,15232,7936,3584,1024],"87":[12,49200,49200,49200,49200,49200,49200,49200,49200,49200,49200,49200,49200,49200,49200,49200,50736,50736,50736,50736,53040,57264,63984,28896,8256],"88":[12,49200,49200,49200,49200,49200,49200,49200,57456,28896,14784,8064,3840,3840,8064,14784,28896,57456,49200,49200,49200,49200,49200,49200,49200],"89":[12,49200,49200,49200,49200,49200,49200,49200,49200,57456,28896,14784,8064,3840,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536],"90":[12,65520,65520,48,48,48,48,48,112,224,448,896,1792,3584,7168,14336,28672,57344,49152,49152,49152,49152,49152,65520,65520],"91":[4,61440,61440,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,61440,61440,0,0,0,0],"92":[9,0,32768,32768,49152,16384,24576,8192,12288,4096,6144,2048,3072,1024,1536,512,768,256,384,128,128,0,0,0,0],"93":[4,61440,61440,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,61440,61440,0,0,0,0],"94":[9,2048,7168,13824,25344,49536,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"95":[6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64512,64512,0,0,0,0],"96":[4,49152,57344,28672,12288,4096,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"97":[10,0,0,0,0,0,0,0,32512,65408,448,192,192,192,32704,65472,49344,49344,49344,49344,49344,49344,65472,32704,16064],"98":[10,49152,49152,49152,49152,49152,49152,49152,65280,65408,49600,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49600,65408,48896],"99":[9,0,0,0,0,0,0,0,32512,65408,49536,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49536,65408,32512],"100":[10,192,192,192,192,192,192,192,16320,32704,24768,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,57536,32704,16192],"101":[10,0,0,0,0,0,0,0,16128,32640,57792,49344,49344,49344,49344,49344,65472,65408,49152,49152,49152,49152,49152,32704,16256],"102":[8,3840,7936,6144,6144,6144,6144,6144,65280,65280,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144],"103":[10,0,0,0,0,0,0,0,32576,65472,49344,49344,49344,49344,49344,65472,32704,192,192,192,192,192,448,65472,32640],"104":[10,49152,49152,49152,49152,49152,49152,49152,65408,65472,57792,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344],"105":[2,49152,49152,0,0,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"106":[5,6144,6144,0,0,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,12288,57344,49152],"107":[10,49152,49152,49152,49152,49152,49152,49152,49536,50048,50944,52736,56320,63488,61440,61440,63488,55296,52224,52224,50688,50688,49920,50048,49600],"108":[2,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"109":[12,0,0,0,0,0,0,0,47584,65520,50736,50736,50736,50736,50736,50736,50736,50736,50736,50736,50736,50736,50736,50736,50736],"110":[10,0,0,0,0,0,0,0,48896,65408,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344],"111":[10,0,0,0,0,0,0,0,32640,65472,57792,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,57792,65472,32640],"112":[10,0,0,0,0,0,0,0,49024,65472,49344,49344,49344,49344,49344,65472,65408,49152,49152,49152,49152,49152,49152,49152,49152],"113":[11,0,0,0,0,0,0,0,32576,65472,49344,49344,49344,49344,49344,65472,32704,192,192,192,192,192,224,224,192],"114":[8,0,0,0,0,0,0,0,52992,57088,63488,61440,57344,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"115":[10,0,0,0,0,0,0,0,16256,32704,49344,49152,49152,49152,49152,49152,32640,16320,192,192,192,192,49344,65408,32512],"116":[8,0,0,6144,6144,6144,6144,6144,65280,65280,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,7680,3072],"117":[10,0,0,0,0,0,0,0,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,57792,32704,16192],"118":[11,0,0,0,0,0,0,0,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,57568,29120,15232,7936,3584,1024],"119":[12,0,0,0,0,0,0,0,49200,49200,49200,49200,49200,49200,50736,50736,50736,50736,50736,50736,53040,57264,63984,28896,8256],"120":[12,0,0,0,0,0,0,0,49200,49200,49200,57456,28896,14784,8064,3840,3840,8064,14784,28896,57456,49200,49200,49200,49200],"121":[10,0,0,0,0,0,0,0,49344,49344,49344,49344,49344,49344,49344,65472,32704,192,192,192,192,192,49600,65472,32640],"122":[11,0,0,0,0,0,0,0,65504,65504,96,96,224,448,896,1792,3584,7168,14336,28672,57344,49152,49152,65504,65504]}},"ASCE0705":{"name":"ASCE0705","cellWidth":8,"height":7,"glyphs":{"32":[4,0,0,0,0,0,0,0],"33":[1,128,128,128,128,128,0,128],"34":[3,160,160,0,0,0,0,0],"35":[5,80,80,248,80,248,80,80],"36":[5,32,120,160,112,40,240,32],"37":[5,192,200,16,32,64,152,24],"38":[5,64,160,160,64,168,144,104],"39":[1,128,128,128,0,0,0,0],"40":[3,32,64,128,128,128,64,32],"41":[3,128,64,32,32,32,64,128],"42":[5,32,168,112,248,112,168,32],"43":[5,0,32,32,248,32,32,0],"44":[2,0,0,0,0,64,64,128],"45":[5,0,0,0,248,0,0,0],"46":[1,0,0,0,0,0,0,128],"47":[5,0,8,16,32,64,128,0],"48":[5,112,136,136,136,136,136,112],"49":[3,64,192,64,64,64,64,224],"50":[5,112,136,8,48,64,128,248],"51":[5,112,136,8,48,8,136,112],"52":[5,16,48,80,144,248,16,16],"53":[5,248,128,240,8,8,136,112],"54":[5,56,64,128,240,136,136,112],"55":[5,248,8,16,32,64,64,64],"56":[5,112,136,136,112,136,136,112],"57":[5,112,136,136,120,8,136,112],"58":[1,0,0,128,0,0,128,0],"59":[2,0,0,64,0,64,64,128],"60":[5,160,168,168,72,8,0,0],"61":[5,0,0,248,0,248,0,0],"62":[3,0,0,0,64,160,224,160],"63":[5,112,136,8,16,32,0,32],"64":[5,112,136,184,168,184,128,120],"65":[4,96,144,144,144,240,144,144],"66":[4,224,144,144,240,144,144,224],"67":[4,96,144,128,128,128,144,96],"68":[4,224,144,144,144,144,144,224],"69":[4,240,128,128,224,128,128,240],"70":[4,240,128,128,224,128,128,128],"71":[4,96,144,128,128,176,144,96],"72":[4,144,144,144,240,144,144,144],"73":[3,224,64,64,64,64,64,224],"74":[4,112,32,32,32,32,160,64],"75":[4,144,144,160,192,192,160,144],"76":[4,128,128,128,128,128,128,240],"77":[5,136,216,168,168,136,136,136],"78":[4,144,144,208,176,144,144,144],"79":[4,96,144,144,144,144,144,96],"80":[4,224,144,144,224,128,128,128],"81":[5,112,136,136,136,168,144,104],"82":[4,224,144,144,224,192,160,144],"83":[4,96,144,128,96,16,144,96],"84":[5,248,32,32,32,32,32,32],"85":[4,144,144,144,144,144,144,96],"86":[5,136,136,136,136,136,80,32],"87":[5,136,136,136,168,168,168,80],"88":[5,136,136,80,32,80,136,136],"89":[5,136,136,80,32,32,32,32],"90":[5,248,8,16,32,64,128,248],"91":[2,192,128,128,128,128,128,192],"92":[5,0,128,64,32,16,8,0],"93":[2,192,64,64,64,64,64,192],"94":[5,32,80,136,0,0,0,0],"95":[4,0,0,0,0,0,0,240],"96":[2,128,64,0,0,0,0,0],"97":[4,0,0,96,16,112,144,112],"98":[4,128,128,224,144,144,144,224],"99":[4,0,0,112,128,128,128,112],"100":[4,16,16,112,144,144,144,112],"101":[4,0,0,96,144,240,128,112],"102":[4,48,64,64,224,64,64,64],"103":[4,0,0,112,144,112,16,224],"104":[4,128,128,224,144,144,144,144],"105":[1,128,0,128,128,128,128,128],"106":[2,64,0,64,64,64,64,128],"107":[4,128,128,144,160,192,160,144],"108":[1,128,128,128,128,128,128,128],"109":[5,0,0,208,168,168,168,168],"110":[4,0,0,224,144,144,144,144],"111":[4,0,0,96,144,144,144,96],"112":[4,0,0,224,144,224,128,128],"113":[5,0,112,144,112,16,24,16],"114":[4,0,0,160,208,128,128,128],"115":[4,0,0,112,128,96,16,224],"116":[4,64,64,224,64,64,64,48],"117":[4,0,0,144,144,144,144,112],"118":[5,0,0,136,136,136,80,32],"119":[5,0,0,136,168,168,168,80],"120":[5,0,0,136,80,32,80,136],"121":[4,0,0,144,144,112,144,96],"122":[4,0,0,240,16,32,64,240],"123":[5,136,168,168,168,168,168,136],"192":[5,48,8,112,136,248,136,136],"193":[5,96,128,112,136,248,136,136],"194":[5,112,136,112,136,248,136,136],"195":[5,248,0,112,136,248,136,136],"196":[5,136,32,80,136,248,136,136],"197":[5,112,80,112,136,248,136,136],"198":[5,56,96,160,184,224,160,184],"199":[5,112,136,136,136,112,32,96],"200":[4,192,32,240,128,224,128,240],"201":[4,48,64,240,128,224,128,240],"202":[4,32,80,240,128,224,128,240],"203":[4,80,0,240,128,224,128,240],"204":[3,192,32,0,224,64,64,224],"205":[3,96,128,0,224,64,64,224],"206":[3,64,160,0,224,64,64,224],"207":[3,160,0,0,224,64,64,224],"208":[5,112,72,72,232,72,72,112],"209":[5,248,0,136,200,168,152,136],"210":[5,48,8,112,136,136,136,112],"211":[5,96,128,112,136,136,136,112],"212":[5,112,136,112,136,136,136,112],"213":[5,248,0,112,136,136,136,112],"214":[5,136,0,112,136,136,136,112],"215":[5,112,136,168,104,240,32,32],"216":[5,0,112,152,168,200,136,112],"217":[5,64,32,136,136,136,136,112],"218":[5,16,32,136,136,136,136,112],"219":[5,32,80,0,136,136,136,120],"220":[5,80,0,136,136,136,136,120],"221":[5,24,32,136,112,32,32,32],"222":[4,128,224,144,144,144,224,128],"223":[5,224,144,144,240,136,200,176],"224":[4,32,16,96,16,112,144,112],"225":[4,64,128,96,16,112,144,112],"226":[5,96,144,96,16,112,144,120],"227":[5,240,0,96,16,112,144,120],"228":[5,80,0,96,16,112,144,120],"229":[4,96,96,96,16,112,144,112],"230":[5,0,0,208,40,120,160,120],"231":[4,0,112,128,128,112,32,96],"232":[5,48,8,112,136,240,128,112],"233":[5,96,128,112,136,240,128,112],"234":[5,112,136,112,136,240,128,112],"235":[5,80,0,112,136,240,128,112],"236":[2,0,128,64,128,128,128,128],"237":[2,0,64,128,64,64,64,64],"238":[3,0,64,160,64,64,64,64],"239":[3,0,160,0,64,64,64,64],"240":[5,208,32,80,48,72,72,48],"241":[5,248,0,176,200,136,136,136],"242":[5,48,8,112,136,136,136,112],"243":[5,96,128,112,136,136,136,112],"244":[5,112,136,112,136,136,136,112],"245":[5,248,0,112,136,136,136,112],"246":[5,80,0,112,136,136,136,112],"247":[5,16,120,16,112,144,144,112],"248":[5,0,0,112,152,168,200,112],"249":[5,64,32,136,136,136,136,120],"250":[5,16,32,136,136,136,136,120],"251":[5,32,80,136,136,136,136,120],"252":[5,80,0,136,136,136,136,120],"253":[5,16,32,136,136,112,32,192],"254":[4,128,128,224,144,224,128,128],"255":[4,144,0,144,144,112,32,192]}},"ASCE0708":{"name":"ASCE0708","cellWidth":8,"height":7,"glyphs":{"32":[4,0,0,0,0,0,0,0],"33":[2,192,192,192,192,192,0,192],"34":[5,216,216,216,0,0,0,0],"35":[7,108,254,108,108,254,108,108],"36":[5,32,248,224,248,56,248,32],"37":[5,208,208,48,32,96,88,88],"38":[5,64,160,160,64,168,144,104],"39":[2,192,192,192,0,0,0,0],"40":[3,96,192,192,192,192,192,96],"41":[3,192,96,96,96,96,96,192],"42":[7,16,84,56,254,56,84,16],"43":[6,0,48,48,252,48,48,0],"44":[3,0,0,0,0,96,96,192],"45":[5,0,0,0,248,0,0,0],"46":[2,0,0,0,0,0,192,192],"47":[4,48,48,112,96,224,192,192],"48":[6,120,204,204,204,204,204,120],"49":[4,32,96,224,96,96,96,240],"50":[6,120,204,12,24,48,96,252],"51":[6,120,204,12,56,12,204,120],"52":[7,28,60,108,204,254,12,12],"53":[6,252,192,192,248,12,140,120],"54":[6,120,192,192,248,204,204,120],"55":[6,252,12,24,48,96,96,96],"56":[6,120,204,204,120,204,204,120],"57":[6,120,204,204,124,12,12,120],"58":[2,0,192,192,0,192,192,0],"59":[3,0,96,96,0,96,96,128],"60":[5,24,48,96,192,96,48,24],"61":[5,0,0,248,0,248,0,0],"62":[5,192,96,48,24,48,96,192],"63":[5,112,216,152,48,96,0,96],"64":[7,124,198,222,214,222,192,120],"65":[5,112,216,216,248,216,216,216],"66":[5,240,216,216,240,216,216,240],"67":[5,112,216,192,192,192,216,112],"68":[5,240,216,216,216,216,216,240],"69":[5,248,192,192,240,192,192,248],"70":[5,248,192,192,240,192,192,192],"71":[6,120,204,192,192,220,204,120],"72":[5,216,216,216,248,216,216,216],"73":[2,192,192,192,192,192,192,192],"74":[5,24,24,24,24,24,216,112],"75":[6,204,216,240,224,240,216,204],"76":[4,192,192,192,192,192,192,240],"77":[7,198,238,214,198,198,198,198],"78":[6,204,236,236,220,220,204,204],"79":[5,112,216,216,216,216,216,112],"80":[5,240,216,216,240,192,192,192],"81":[7,120,204,204,204,204,220,126],"82":[5,240,216,216,240,240,216,216],"83":[5,112,216,192,112,24,216,112],"84":[4,240,96,96,96,96,96,96],"85":[5,216,216,216,216,216,216,112],"86":[5,216,216,216,216,216,112,32],"87":[7,198,198,198,198,214,238,198],"88":[7,198,198,108,56,108,198,198],"89":[6,204,204,204,120,48,48,48],"90":[6,252,12,24,48,96,192,252],"91":[3,224,192,192,192,192,192,224],"92":[4,192,192,224,96,112,48,48],"93":[3,224,96,96,96,96,96,224],"94":[7,16,56,108,198,0,0,0],"95":[5,0,0,0,0,0,0,248],"96":[3,192,96,0,0,0,0,0],"97":[5,0,0,240,24,120,216,120],"98":[5,192,192,240,216,216,216,240],"99":[4,0,0,112,192,192,192,112],"100":[5,24,24,120,216,216,216,120],"101":[5,0,0,112,216,248,192,120],"102":[4,48,96,96,240,96,96,96],"103":[5,0,0,120,216,120,24,240],"104":[5,192,192,240,216,216,216,216],"105":[2,192,0,192,192,192,192,192],"106":[3,96,0,96,96,96,96,192],"107":[5,192,192,216,240,224,240,216],"108":[2,192,192,192,192,192,192,192],"109":[8,0,0,182,255,219,219,219],"110":[5,0,0,176,248,216,216,216],"111":[5,0,0,112,216,216,216,112],"112":[5,0,0,248,216,248,192,192],"113":[5,0,0,248,216,248,24,24],"114":[4,0,0,176,240,192,192,192],"115":[4,0,0,240,192,240,48,240],"116":[4,96,96,240,96,96,96,48],"117":[5,0,0,216,216,216,216,104],"118":[6,0,0,204,204,204,120,48],"119":[7,0,0,198,198,198,214,108],"120":[6,0,0,204,120,48,120,204],"121":[6,0,0,204,120,48,96,192],"122":[5,0,0,248,48,96,192,248],"192":[7,12,58,124,198,254,254,198],"193":[7,96,184,124,198,254,254,198],"194":[7,124,186,124,198,254,254,198],"195":[7,254,56,124,198,254,254,198],"196":[7,68,56,124,198,254,254,198],"197":[7,56,40,124,198,254,254,198],"198":[7,62,126,216,222,254,248,222],"199":[5,112,248,192,248,112,32,96],"200":[5,96,16,248,192,240,192,248],"201":[5,48,64,248,192,240,192,248],"202":[5,48,72,248,192,240,192,248],"203":[5,216,0,248,192,240,192,248],"204":[3,192,32,192,192,192,192,192],"205":[3,96,128,96,96,96,96,96],"206":[4,96,144,96,96,96,96,96],"207":[4,144,0,96,96,96,96,96],"208":[8,124,126,99,243,99,126,124],"209":[6,252,0,204,236,252,220,204],"210":[7,12,2,124,254,198,254,124],"211":[7,96,128,124,254,198,254,124],"212":[7,124,198,56,254,198,254,124],"213":[7,124,0,124,254,198,254,124],"214":[7,40,0,124,254,198,254,124],"215":[8,98,137,153,189,137,137,98],"216":[7,58,124,206,214,230,124,184],"217":[7,96,16,198,198,198,254,124],"218":[7,12,16,198,198,198,254,124],"219":[7,16,40,198,198,198,254,124],"220":[7,108,0,198,198,198,254,124],"221":[6,12,16,204,120,48,48,48],"222":[6,192,248,252,204,252,248,192],"223":[6,248,252,204,248,204,236,216],"224":[6,24,4,120,12,124,204,124],"225":[6,48,64,120,12,124,204,124],"226":[6,48,72,112,12,124,204,124],"227":[6,120,0,120,12,124,204,124],"228":[6,40,0,120,12,124,204,124],"229":[6,56,56,124,12,124,204,124],"230":[6,0,52,74,62,72,74,52],"231":[5,120,248,192,248,120,16,48],"232":[6,24,4,120,204,252,192,120],"233":[6,96,128,120,204,252,192,120],"234":[6,120,132,120,204,252,192,120],"235":[6,108,0,120,204,252,192,120],"236":[3,0,192,32,192,192,192,192],"237":[3,0,96,128,96,96,96,96],"238":[4,0,96,144,96,96,96,96],"239":[6,0,204,204,48,48,48,48],"240":[5,208,32,80,120,216,216,112],"241":[6,252,0,248,252,204,204,204],"242":[6,24,4,120,204,204,204,120],"243":[6,96,128,120,204,204,204,120],"244":[6,120,132,120,204,204,204,120],"245":[6,252,0,120,204,204,204,120],"246":[6,204,0,120,204,204,204,120],"247":[7,24,126,24,120,216,216,120],"248":[6,0,120,124,220,236,252,120],"249":[6,96,16,204,204,204,252,124],"250":[6,24,32,204,204,204,252,124],"251":[6,48,72,204,204,204,252,124],"252":[6,204,0,204,204,204,252,124],"253":[5,24,32,216,248,112,48,224],"254":[5,192,240,248,152,248,240,192],"255":[5,80,0,216,248,112,48,224]}},"ASCE1610":{"name":"ASCE1610","cellWidth":16,"height":16,"glyphs":{"32":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"33":[2,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,0,0,49152,49152],"34":[5,55296,55296,55296,0,0,0,0,0,0,0,0,0,0,0,0,0],"35":[10,0,13056,13056,13056,65472,65472,13056,13056,13056,13056,65472,65472,13056,13056,13056,0],"36":[10,3072,32640,65472,52416,52224,52224,52224,65408,32704,3264,3264,3264,52416,65472,32640,3072],"37":[7,16896,41472,42496,17920,3072,3072,6144,6144,12288,12288,24576,24576,50176,51712,35328,33792],"38":[10,31744,65024,50688,50688,50688,31744,31744,64512,52800,51136,50048,49600,50112,59200,32256,15360],"39":[2,49152,49152,49152,0,0,0,0,0,0,0,0,0,0,0,0,0],"40":[5,6144,12288,24576,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,24576,12288,6144],"41":[5,49152,24576,12288,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,12288,24576,49152],"42":[8,0,0,2048,2048,18688,10752,7168,65280,7168,10752,18688,2048,2048,0,0,0],"43":[8,0,0,0,0,6144,6144,6144,65280,65280,6144,6144,6144,0,0,0,0],"44":[3,0,0,0,0,0,0,0,0,0,0,0,0,24576,24576,16384,32768],"45":[5,0,0,0,0,0,0,0,63488,63488,0,0,0,0,0,0,0],"46":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49152,49152],"47":[9,0,0,0,128,384,768,1536,3072,6144,12288,24576,49152,32768,0,0,0],"48":[9,15872,32512,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,32512,15872],"49":[6,12288,28672,61440,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,64512,64512],"50":[9,32256,65280,50048,384,384,384,896,1792,3584,7168,14336,28672,57344,57344,65408,65408],"51":[9,15360,32256,49920,768,768,768,768,7680,7680,768,768,768,768,49920,32256,15360],"52":[10,384,896,1920,3968,7552,14720,29056,57728,49536,65472,65472,384,384,384,384,384],"53":[9,65408,65408,49152,49152,49152,49152,65024,65280,896,384,384,384,49536,50048,32512,15872],"54":[8,7936,16128,28672,57344,49152,49152,49152,65024,65280,49920,49920,49920,49920,49920,65280,32256],"55":[9,65408,65408,384,384,896,1792,3584,7168,6144,6144,6144,6144,6144,6144,6144,6144],"56":[8,32256,65280,49920,49920,49920,49920,49920,32256,32256,49920,49920,49920,49920,49920,65280,32256],"57":[8,32256,65280,49920,49920,49920,49920,49920,65280,32512,768,768,768,1792,3584,64512,63488],"58":[2,0,0,0,0,0,49152,49152,49152,0,0,49152,49152,49152,0,0,0],"59":[3,0,0,0,0,0,0,0,0,0,0,24576,24576,0,24576,24576,49152],"60":[9,384,768,1536,3072,6144,12288,24576,49152,49152,24576,12288,6144,3072,1536,768,384],"61":[8,0,0,0,0,0,65280,65280,0,0,65280,65280,0,0,0,0,0],"62":[16,0,64,96,112,120,32764,32766,0,0,32766,16382,7168,3072,1024,0,0],"63":[10,16128,32640,57792,49344,49344,49600,896,1792,3584,3072,3072,3072,3072,0,3072,3072],"64":[10,16128,32640,49344,49344,49344,52928,57024,56000,56000,56192,57088,52800,49344,24960,16128,7680],"65":[10,3072,7680,16128,29568,57792,49344,49344,49344,49344,65472,65472,49344,49344,49344,49344,49344],"66":[9,65024,65280,50048,49536,49536,49536,49536,65280,65280,49536,49536,49536,49536,50048,65280,65024],"67":[9,15872,32512,58240,49536,49536,49152,49152,49152,49152,49152,49152,49536,49536,58240,32512,15872],"68":[9,65024,65280,50048,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,50048,65280,65024],"69":[8,65280,65280,49152,49152,49152,49152,49152,65024,65024,49152,49152,49152,49152,49152,65280,65280],"70":[8,65280,65280,49152,49152,49152,49152,49152,65024,65024,49152,49152,49152,49152,49152,49152,49152],"71":[10,15872,32512,58240,49536,49536,49152,49152,49152,51136,51136,49536,49536,49536,58240,32512,15872],"72":[9,49536,49536,49536,49536,49536,49536,49536,65408,65408,49536,49536,49536,49536,49536,49536,49536],"73":[6,64512,64512,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,64512,64512],"74":[8,16128,16128,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,50688,50688,65024,31744],"75":[10,49344,49600,50048,50944,52736,56320,63488,61440,63488,56320,52736,50944,50048,49600,49344,49344],"76":[8,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,65280,65280],"77":[10,32832,49344,57792,62400,57024,52416,52416,52416,49344,49344,49344,49344,49344,49344,49344,49344],"78":[10,49344,49344,49344,57536,61632,63680,56512,52928,51136,50112,49600,49344,49344,49344,49344,49344],"79":[9,15872,32512,58240,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,58240,32512,15872],"80":[10,65280,65408,49600,49344,49344,49344,49600,65408,65280,49152,49152,49152,49152,49152,49152,49152],"81":[9,15872,32512,58240,49536,49536,49536,49536,49536,49536,49536,49536,52608,51072,58240,32640,16000],"82":[10,65280,65408,49600,49344,49344,49344,49600,65408,65280,56320,52736,50944,50048,49600,49344,49344],"83":[10,16128,32640,57792,49344,49152,49152,57344,32512,16256,448,192,192,49344,57792,32640,16128],"84":[10,65472,65472,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072],"85":[9,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,58240,32512,15872],"86":[9,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,49536,58240,30464,15872,7168,2048],"87":[10,49344,49344,49344,49344,49344,49344,49344,49344,52416,52416,52416,52416,57024,65472,29568,8448],"88":[10,49344,49344,49344,49344,57792,29568,16128,7680,7680,16128,29568,57792,49344,49344,49344,49344],"89":[10,49344,49344,49344,49344,57792,29568,16128,7680,3072,3072,3072,3072,3072,3072,3072,3072],"90":[10,65472,65472,192,192,448,896,1792,3584,7168,14336,28672,57344,49152,49152,65472,65472],"91":[4,61440,61440,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,61440,61440],"92":[9,0,0,0,32768,49152,24576,12288,6144,3072,1536,768,384,128,0,0,0],"93":[4,61440,61440,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,61440,61440],"94":[9,2048,7168,13824,25344,49536,0,0,0,0,0,0,0,0,0,0,0],"95":[6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64512,64512],"96":[4,49152,24576,12288,0,0,0,0,0,0,0,0,0,0,0,0,0],"97":[8,0,0,0,0,31744,65024,1792,768,32512,59136,49920,49920,49920,49920,65280,31488],"98":[8,49152,49152,49152,49152,64512,65024,50944,49920,49920,49920,49920,49920,49920,50944,65024,48128],"99":[7,0,0,0,0,31744,65024,50688,49152,49152,49152,49152,49152,49152,50688,65024,31744],"100":[8,768,768,768,768,16128,32512,58112,49920,49920,49920,49920,49920,49920,58112,32512,15616],"101":[8,0,0,0,0,15360,32256,59136,49920,49920,49920,65280,65024,49152,57344,32512,16128],"102":[6,7168,15360,12288,12288,12288,12288,12288,64512,64512,12288,12288,12288,12288,12288,12288,12288],"103":[8,0,0,0,0,32000,65280,49920,49920,49920,49920,65280,32512,768,768,65280,32256],"104":[8,49152,49152,49152,49152,65024,65280,59136,49920,49920,49920,49920,49920,49920,49920,49920,49920],"105":[2,49152,49152,0,0,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"106":[5,6144,6144,0,0,6144,6144,6144,6144,6144,6144,6144,6144,6144,12288,57344,49152],"107":[8,49152,49152,49152,49152,49920,50944,52736,56320,63488,61440,63488,56320,52736,50944,49920,49408],"108":[2,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"109":[10,0,0,0,0,45952,65472,52416,52416,52416,52416,52416,52416,52416,52416,52416,52416],"110":[8,0,0,0,0,48128,65024,49920,49920,49920,49920,49920,49920,49920,49920,49920,49920],"111":[8,0,0,0,0,32256,65280,59136,49920,49920,49920,49920,49920,49920,59136,65280,32256],"112":[8,0,0,0,0,48640,65280,49920,49920,49920,49920,65280,65024,49152,49152,49152,49152],"113":[8,0,0,0,0,32000,65280,49920,49920,49920,49920,65280,32512,768,768,768,768],"114":[7,0,0,0,0,52736,56832,63488,61440,57344,49152,49152,49152,49152,49152,49152,49152],"115":[8,0,0,0,0,32256,65280,49920,49152,49152,65024,16128,768,768,49920,65280,32256],"116":[6,12288,12288,12288,12288,64512,64512,12288,12288,12288,12288,12288,12288,12288,12288,15360,6144],"117":[8,0,0,0,0,49920,49920,49920,49920,49920,49920,49920,49920,49920,59136,32512,15616],"118":[9,0,0,0,0,49536,49536,49536,49536,49536,49536,49536,58240,30464,15872,7168,2048],"119":[10,0,0,0,0,49344,49344,52416,52416,52416,52416,52416,52416,57024,65472,29568,8448],"120":[9,0,0,0,0,49344,49344,57792,29568,16128,7680,7680,16128,29568,57792,49344,49344],"121":[8,0,0,0,0,49920,49920,49920,49920,49920,49920,65280,32512,768,768,65280,32256],"122":[9,0,0,0,0,65408,65408,896,1792,3584,7168,14336,28672,57344,49152,65408,65408],"123":[6,3072,6144,6144,6144,6144,6144,6144,12288,49152,12288,12288,6144,6144,6144,6144,3072],"124":[2,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"125":[6,49152,24576,24576,24576,24576,24576,24576,12288,3072,12288,12288,24576,24576,24576,24576,49152],"126":[13,0,0,0,0,0,0,15384,32280,59192,50160,49376,0,0,0,0,0],"127":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"192":[10,3072,512,16128,32640,49344,49344,49344,49344,49344,65472,65472,49344,49344,49344,49344,49344],"193":[10,1536,2048,16128,32640,49344,49344,49344,49344,49344,65472,65472,49344,49344,49344,49344,49344],"194":[10,3072,4608,16128,32640,49344,49344,49344,49344,49344,65472,65472,49344,49344,49344,49344,49344],"195":[10,7680,0,16128,32640,49344,49344,49344,49344,49344,65472,65472,49344,49344,49344,49344,49344],"196":[10,49344,0,16128,32640,49344,49344,49344,49344,49344,65472,65472,49344,49344,49344,49344,49344],"197":[10,3072,13056,16128,32640,49344,49344,49344,49344,49344,65472,65472,49344,49344,49344,49344,49344],"198":[10,8128,16320,26112,50688,50688,50688,50688,51136,51136,65024,65024,50688,50688,50688,51136,51136],"199":[10,16128,32640,49344,49152,49152,49152,49152,49152,49152,49344,32640,16128,3072,3072,1536,7168],"200":[10,3072,512,65472,65472,49152,49152,49152,49152,65024,65024,49152,49152,49152,49152,65472,65472],"201":[10,3072,4096,65472,65472,49152,49152,49152,49152,65024,65024,49152,49152,49152,49152,65472,65472],"202":[10,3072,4608,65472,65472,49152,49152,49152,49152,65024,65024,49152,49152,49152,49152,65472,65472],"203":[10,13056,0,65472,65472,49152,49152,49152,49152,65024,65024,49152,49152,49152,49152,65472,65472],"204":[3,49152,8192,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"205":[3,24576,32768,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576],"206":[4,24576,36864,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576],"207":[4,36864,0,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576],"208":[9,32256,32512,24960,24960,24960,24960,24960,63872,63872,24960,24960,24960,24960,24960,32512,32256],"209":[10,7680,49344,49344,49344,57536,61632,55488,52416,50880,50112,49600,49344,49344,49344,49344,49344],"210":[10,768,128,16128,32640,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,32640,16128],"211":[10,12288,16384,16128,32640,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,32640,16128],"212":[10,3072,4608,16128,32640,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,32640,16128],"213":[10,7680,0,16128,32640,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,32640,16128],"214":[10,49344,0,16128,32640,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,32640,16128],"215":[11,0,0,0,16448,57568,29120,15232,7936,3584,7936,15232,29120,57568,16448,0,0],"216":[10,16128,32640,57792,49344,49600,50112,50880,52416,55488,61632,57536,49344,49344,57792,32640,16128],"217":[10,2048,1024,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,57792,32640,16128],"218":[10,1024,2048,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,57792,32640,16128],"219":[10,3072,4608,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,57792,32640,16128],"220":[10,49344,0,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,57792,32640,16128],"221":[10,1536,35904,49344,24960,13056,7680,3072,3072,3072,3072,3072,3072,3072,3072,3072,3072],"222":[10,0,0,49152,49152,65280,65408,49344,49344,49344,49344,65408,65280,49152,49152,0,0],"223":[10,64512,65024,50688,50688,50688,52224,55296,55296,52224,50688,49920,49536,49344,53440,57280,53120],"224":[8,4096,2048,0,31744,65024,1792,768,768,32512,65280,49920,49920,49920,49920,65280,32000],"225":[8,2048,4096,0,31744,65024,1792,768,768,32512,65280,49920,49920,49920,49920,65280,32000],"226":[8,6144,9216,0,31744,65024,1792,768,768,32512,65280,49920,49920,49920,49920,65280,32000],"227":[8,5120,10240,0,31744,65024,1792,768,768,32512,65280,49920,49920,49920,49920,65280,32000],"228":[8,0,17408,0,31744,65024,1792,768,768,32512,65280,49920,49920,49920,49920,65280,32000],"229":[8,2048,5120,2048,31744,65024,1792,768,768,32512,65280,49920,49920,49920,49920,65280,32000],"230":[10,0,0,0,31616,65472,3264,3264,3264,32704,65472,52224,52224,52224,52224,65472,30592],"231":[10,0,0,0,16256,32704,57408,49152,49152,49152,49152,57408,32704,16256,3072,1536,7168],"232":[10,2048,1024,0,16128,32640,57792,49344,49344,65472,65472,49152,49152,49152,57408,32704,16256],"233":[10,1024,2048,0,16128,32640,57792,49344,49344,65472,65472,49152,49152,49152,57408,32704,16256],"234":[10,3072,4608,0,16128,32640,57792,49344,49344,65472,65472,49152,49152,49152,57408,32704,16256],"235":[10,13056,13056,0,16128,32640,57792,49344,49344,65472,65472,49152,49152,49152,57408,32704,16256],"236":[3,16384,8192,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"237":[3,16384,32768,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576],"238":[4,24576,36864,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576],"239":[4,36864,0,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576],"240":[10,13312,2048,5120,512,16128,32640,57536,49344,49344,49344,49344,49344,49344,49344,32640,16128],"241":[10,0,2560,5120,0,49152,65280,65408,49344,49344,49344,49344,49344,49344,49344,49344,49344],"242":[10,2048,1024,0,16128,32640,49344,49344,49344,49344,49344,49344,49344,49344,49344,32640,16128],"243":[10,1024,2048,0,16128,32640,49344,49344,49344,49344,49344,49344,49344,49344,49344,32640,16128],"244":[10,3072,4608,0,16128,32640,49344,49344,49344,49344,49344,49344,49344,49344,49344,32640,16128],"245":[10,2560,5120,0,16128,32640,49344,49344,49344,49344,49344,49344,49344,49344,49344,32640,16128],"246":[10,0,8448,0,16128,32640,49344,49344,49344,49344,49344,49344,49344,49344,49344,32640,16128],"247":[10,768,768,768,16320,16320,768,16128,32512,49920,49920,49920,49920,49920,49920,32512,16128],"248":[10,0,0,0,16128,32640,49344,49600,50112,50880,52416,55488,61632,57536,49344,32640,16128],"249":[10,2048,1024,0,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,65472,32704],"250":[10,1024,2048,0,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,65472,32704],"251":[10,3072,4608,0,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,65472,32704],"252":[10,0,8448,0,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,65472,32704],"253":[10,1024,2048,0,49344,49344,49344,24768,12672,6912,3584,3072,6144,12288,24576,49152,32768],"254":[7,49152,49152,49152,49152,64512,65024,50688,50688,50688,50688,65024,64512,49152,49152,49152,49152],"255":[10,0,8448,0,49344,49344,49344,24768,12672,6912,3584,3072,6144,12288,24576,49152,32768]}},"ASCEV12":{"name":"ASCEV12","cellWidth":16,"height":16,"glyphs":{"32":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"33":[2,0,0,0,0,49152,49152,49152,49152,49152,49152,49152,49152,49152,0,49152,49152],"34":[6,0,0,0,0,27648,18432,18432,55296,36864,0,0,0,0,0,0,0],"35":[11,0,0,0,0,1152,1152,2304,32736,2304,2304,4608,4608,65472,4608,9216,9216],"36":[7,0,0,0,0,4096,31744,37376,36864,36864,28672,7168,4608,4608,37376,31744,4096],"37":[12,0,0,0,0,24768,36992,37248,37120,37632,37472,25744,3216,2192,6288,4240,12384],"38":[11,0,0,0,0,15360,26112,26112,26112,15360,14528,27840,50880,50048,49600,25440,15392],"39":[3,0,0,0,0,24576,16384,16384,49152,32768,0,0,0,0,0,0,0],"40":[3,0,0,0,0,8192,16384,16384,32768,32768,32768,32768,32768,32768,16384,16384,8192],"41":[3,0,0,0,0,32768,16384,16384,8192,8192,8192,8192,8192,8192,16384,16384,32768],"42":[7,0,0,0,0,4096,37376,21504,14336,14336,21504,37376,4096,0,0,0,0],"43":[11,0,0,0,0,0,1024,1024,1024,1024,1024,65504,1024,1024,1024,1024,1024],"44":[4,0,0,0,0,0,0,0,0,0,0,0,28672,24576,24576,49152,49152],"45":[5,0,0,0,0,0,0,0,0,0,0,63488,0,0,0,0,0],"46":[2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49152,49152],"47":[5,0,0,0,0,2048,2048,4096,4096,4096,8192,8192,8192,16384,16384,16384,32768],"48":[9,0,0,0,0,15872,25344,16640,49536,49536,49536,49536,49536,49536,16640,25344,15872],"49":[6,0,0,0,0,12288,12288,61440,12288,12288,12288,12288,12288,12288,12288,12288,64512],"50":[8,0,0,0,0,31744,50688,33536,768,768,1536,3584,7168,14336,28672,49152,65280],"51":[9,0,0,0,0,32256,50048,33152,384,768,7168,768,384,384,33152,49920,32256],"52":[10,0,0,0,0,768,1792,2816,4864,13056,25344,49920,33536,65472,768,768,768],"53":[7,0,0,0,0,65024,49152,49152,49152,63488,3072,1536,1536,1536,1536,3072,63488],"54":[9,0,0,0,0,3840,12288,24576,16384,56832,58112,49536,49536,49536,16768,25344,15872],"55":[8,0,0,0,0,65280,768,1536,1536,3072,3072,7168,6144,6144,12288,12288,24576],"56":[9,0,0,0,0,15872,25344,49536,49536,25344,15872,26368,49536,49536,49536,25344,15872],"57":[9,0,0,0,0,15872,25344,49536,49536,49536,49536,25472,15744,256,768,1536,30720],"58":[2,0,0,0,0,0,0,0,49152,49152,0,0,0,49152,49152,0,0],"59":[3,0,0,0,0,0,0,0,24576,24576,0,0,0,24576,24576,16384,32768],"60":[9,0,0,0,0,0,128,768,3072,12288,49152,49152,12288,3072,768,128,0],"61":[8,0,0,0,0,0,0,0,0,65280,0,0,65280,0,0,0,0],"62":[9,0,0,0,0,0,32768,24576,6144,1536,384,384,1536,6144,24576,32768,0],"63":[7,0,0,0,0,63488,36352,1536,1536,3584,7168,14336,12288,12288,0,12288,12288],"64":[12,0,0,0,0,3968,12352,16416,20368,37008,37008,37008,37008,20448,16384,12288,3968],"65":[12,0,0,0,0,1536,3840,3840,6528,6528,6528,12480,16320,24672,24672,24672,49200],"66":[9,0,0,0,0,65024,49920,49920,49920,50688,65024,49920,49536,49536,49536,49920,65024],"67":[10,0,0,0,0,8064,12480,24640,49152,49152,49152,49152,49152,49152,24640,12480,8064],"68":[10,0,0,0,0,65024,49920,49536,49344,49344,49344,49344,49344,49344,49536,49920,65024],"69":[8,0,0,0,0,65280,49152,49152,49152,49152,65280,49152,49152,49152,49152,49152,65280],"70":[8,0,0,0,0,65280,49152,49152,49152,49152,65024,49152,49152,49152,49152,49152,49152],"71":[11,0,0,0,0,4032,12384,24608,49152,49152,49152,50144,49248,49248,24672,12384,8064],"72":[9,0,0,0,0,49536,49536,49536,49536,49536,65408,49536,49536,49536,49536,49536,49536],"73":[4,0,0,0,0,61440,24576,24576,24576,24576,24576,24576,24576,24576,24576,24576,61440],"74":[5,0,0,0,0,30720,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,57344],"75":[9,0,0,0,0,49408,49920,50688,52224,55296,61440,63488,52224,52736,50944,50048,49536],"76":[7,0,0,0,0,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,65024],"77":[10,0,0,0,0,57792,57792,57792,53952,53952,56000,52416,52416,52416,49344,49344,49344],"78":[9,0,0,0,0,57728,57728,53632,53632,55680,51584,52608,50560,50560,50048,50048,50048],"79":[11,0,0,0,0,7936,12672,24768,49248,49248,49248,49248,49248,49248,24768,12672,7936],"80":[7,0,0,0,0,63488,52224,50688,50688,50688,50688,52224,63488,49152,49152,49152,49152],"81":[11,0,0,0,0,7936,12672,24768,49248,49248,49248,49248,49248,50016,25536,12736,8032],"82":[9,0,0,0,0,64512,50944,49920,49920,49920,50688,64512,52224,50688,49920,49536,49536],"83":[10,0,0,0,0,16128,24960,49280,49152,57344,32256,8064,448,192,32960,49536,32512],"84":[8,0,0,0,0,65280,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144],"85":[10,0,0,0,0,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,24960,16128],"86":[12,0,0,0,0,49200,16416,24672,24672,12480,12480,4224,6528,6528,3840,3840,1536],"87":[15,0,0,0,0,49414,50054,25228,25228,25228,26316,9288,13400,13400,7288,6192,6192],"88":[10,0,0,0,0,57792,24960,13056,13056,7680,3072,3072,7680,13056,13056,24960,57792],"89":[10,0,0,0,0,57792,24960,29568,13056,7680,7680,3072,3072,3072,3072,3072,3072],"90":[10,0,0,0,0,65472,448,384,768,1536,3072,3072,6144,12288,24576,57344,65472],"91":[3,0,0,0,0,57344,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,57344],"92":[5,0,0,0,0,32768,32768,16384,16384,16384,8192,8192,4096,4096,4096,2048,2048],"93":[3,0,0,0,0,57344,8192,8192,8192,8192,8192,8192,8192,8192,8192,8192,57344],"94":[9,0,0,0,0,2048,7168,13824,25344,16640,49536,0,0,0,0,0,0],"95":[10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65472],"96":[2,0,0,0,0,32768,16384,0,0,0,0,0,0,0,0,0,0],"97":[7,0,0,0,0,0,0,0,0,31744,17920,1536,15872,26112,50688,50688,32256],"98":[8,0,0,0,0,49152,49152,49152,49152,56320,58880,49920,49920,49920,49920,50688,64512],"99":[7,0,0,0,0,0,0,0,0,15872,25088,49152,49152,49152,49152,26112,15360],"100":[8,0,0,0,0,768,768,768,768,16128,25344,49920,49920,49920,49920,26368,15104],"101":[8,0,0,0,0,0,0,0,0,15360,26112,49920,49920,65280,49152,57600,32256],"102":[6,0,0,0,0,15360,24576,24576,24576,63488,24576,24576,24576,24576,24576,24576,24576],"103":[7,0,0,0,0,0,0,0,0,31744,50688,50688,50688,32256,1536,3072,31744],"104":[7,0,0,0,0,49152,49152,49152,49152,56320,58880,50688,50688,50688,50688,50688,50688],"105":[2,0,0,0,0,49152,49152,0,0,49152,49152,49152,49152,49152,49152,49152,49152],"106":[4,0,0,0,0,12288,12288,0,0,28672,12288,12288,12288,12288,12288,12288,57344],"107":[8,0,0,0,0,49152,49152,49152,49152,50688,52224,55296,63488,64512,52736,50688,49920],"108":[2,0,0,0,0,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"109":[12,0,0,0,0,0,0,0,0,56544,59184,50736,50736,50736,50736,50736,50736],"110":[7,0,0,0,0,0,0,0,0,56320,58880,50688,50688,50688,50688,50688,50688],"111":[8,0,0,0,0,0,0,0,0,15360,26112,49920,49920,49920,49920,26112,15360],"112":[7,0,0,0,0,0,0,0,56320,58880,50688,50688,52224,63488,49152,49152,49152],"113":[7,0,0,0,0,0,0,0,31744,50688,50688,50688,17920,15872,1536,1536,1536],"114":[6,0,0,0,0,0,0,0,0,56320,57344,49152,49152,49152,49152,49152,49152],"115":[7,0,0,0,0,0,0,0,15360,26112,49664,57344,31744,3584,34304,52224,30720],"116":[6,0,0,0,0,24576,24576,24576,24576,64512,24576,24576,24576,24576,24576,24576,15360],"117":[7,0,0,0,0,0,0,0,0,50688,50688,50688,50688,50688,50688,52736,30208],"118":[8,0,0,0,0,0,0,0,0,49920,26112,26112,9216,15360,15360,6144,6144],"119":[12,0,0,0,0,0,0,0,0,50736,50736,17952,28512,26976,14784,12480,12480],"120":[8,0,0,0,0,0,0,0,59136,26112,15360,14336,6144,15360,15360,26112,59136],"121":[8,0,0,0,0,0,0,0,49920,26112,26112,9216,15360,6144,4096,12288,24576],"122":[7,0,0,0,0,0,0,0,0,65024,1536,3072,6144,4096,24576,49152,65024],"123":[4,0,0,0,0,12288,16384,16384,16384,16384,32768,16384,16384,16384,16384,16384,12288],"124":[1,0,0,0,0,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768],"125":[4,0,0,0,0,49152,8192,8192,8192,8192,4096,8192,8192,8192,8192,8192,49152],"126":[8,0,0,0,0,0,0,0,0,0,24832,39168,34304,0,0,0,0],"127":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"128":[10,0,0,0,0,4032,6208,12288,24576,65024,24576,24576,64512,24576,12288,6208,4032],"129":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"130":[2,0,0,0,0,0,0,0,0,0,0,49152,49152,49152,16384,16384,32768],"131":[9,0,0,0,0,896,1536,1536,3072,16128,3072,3072,6144,6144,6144,12288,57344],"132":[5,0,0,0,0,0,0,0,0,0,0,55296,55296,55296,18432,18432,36864],"133":[7,0,0,0,0,0,0,0,0,0,0,0,0,37376,37376,0,0],"134":[4,0,0,0,0,4096,4096,4096,65024,4096,4096,4096,4096,4096,4096,4096,4096],"135":[5,0,0,0,0,8192,8192,8192,63488,8192,8192,8192,8192,63488,8192,8192,8192],"136":[6,0,0,12288,30720,52224,0,0,0,0,0,0,0,0,0,0,0],"137":[15,0,0,0,0,384,24832,37632,37376,38400,25600,2444,6738,4690,8786,24972,16384],"138":[9,13056,7680,3072,0,16128,24960,49280,49152,57344,32256,8064,448,192,32960,49536,32512],"139":[5,0,0,0,0,0,0,0,2048,6144,12288,49152,49152,12288,6144,2048,0],"140":[15,0,0,0,0,4094,12480,24768,49344,49344,49406,49344,49344,49344,24768,12480,4094],"141":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"142":[10,13056,7680,3072,0,65472,448,384,768,1536,3072,3072,6144,12288,24576,57344,65472],"143":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"144":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"145":[3,49152,16384,16384,24576,8192,0,0,0,0,0,0,0,0,0,0,0],"146":[3,24576,16384,16384,49152,32768,0,0,0,0,0,0,0,0,0,0,0],"147":[6,9216,18432,36864,55296,55296,55296,0,0,0,0,0,0,0,0,0,0],"148":[5,55296,55296,55296,18432,18432,36864,0,0,0,0,0,0,0,0,0,0],"149":[6,0,0,0,0,0,0,0,30720,64512,64512,64512,64512,30720,0,0,0],"150":[7,0,0,0,0,0,0,0,0,0,0,65024,0,0,0,0,0],"151":[13,0,0,0,0,0,0,0,0,0,0,65528,0,0,0,0,0],"152":[7,0,29184,39936,0,0,0,0,0,0,0,0,0,0,0,0,0],"153":[14,0,0,0,0,63884,8532,8532,8484,8484,8452,0,0,0,0,0,0],"154":[8,0,0,26112,15360,6144,0,0,15872,25344,49408,57344,32256,1792,33536,50688,31744],"155":[5,0,0,0,0,0,0,32768,49152,24576,6144,6144,24576,49152,32768,0,0],"156":[13,0,0,0,0,0,0,0,0,15472,26520,49928,49928,50168,49920,26496,15480],"157":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"158":[7,0,0,0,26112,15360,6144,0,0,65024,1536,3072,6144,12288,24576,49152,65024],"159":[10,6912,6912,0,0,57792,24960,29568,13056,7680,7680,3072,3072,3072,3072,3072,3072],"160":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"161":[2,0,0,0,0,49152,49152,0,0,49152,49152,49152,49152,49152,49152,49152,49152],"162":[8,0,0,0,0,2048,2048,16128,26880,51200,51200,51200,51200,26880,16128,2048,2048],"163":[8,0,0,0,0,3840,6144,12288,12288,12288,12288,65024,12288,12288,8192,16384,65280],"164":[10,0,0,0,0,4032,6208,12288,24576,65024,24576,24576,64512,24576,12288,6208,4032],"165":[10,0,0,0,0,49344,24960,24960,13056,7680,7680,3072,32640,3072,3072,3072,3072],"166":[2,0,0,0,0,49152,49152,49152,49152,49152,0,0,49152,49152,49152,49152,49152],"167":[8,0,0,0,0,15872,57856,49152,49152,31744,50688,50688,31744,7168,1536,35840,63488],"168":[5,0,0,55296,55296,0,0,0,0,0,0,0,0,0,0,0,0],"169":[13,0,0,0,3968,6336,8224,18320,52376,38920,38920,38920,52376,18320,8224,6336,3968],"170":[7,0,0,0,0,31744,17920,1536,15872,58880,50688,50688,32256,0,0,0,0],"171":[9,0,0,0,0,0,0,0,2176,6528,13056,50688,50688,13056,6528,2176,0],"172":[8,0,0,0,0,0,0,0,0,0,0,65280,768,768,768,768,768],"173":[5,0,0,0,0,0,0,0,0,0,0,63488,0,0,0,0,0],"174":[13,0,0,0,3968,6336,8224,20240,52632,36232,36616,36616,52632,19920,8224,6336,3968],"175":[9,0,65408,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"176":[7,0,0,0,0,14336,27648,50688,50688,50688,27648,14336,0,0,0,0,0],"177":[9,0,0,0,0,2048,2048,2048,2048,2048,65408,2048,2048,2048,2048,65408,0],"178":[6,0,0,0,0,63488,35840,3072,3072,6144,12288,24576,64512,0,0,0,0],"179":[7,0,0,0,0,63488,35840,3072,12288,3072,3072,35840,63488,0,0,0,0],"180":[4,28672,24576,49152,0,0,0,0,0,0,0,0,0,0,0,0,0],"181":[7,0,0,0,0,0,50688,50688,50688,50688,50688,50688,50688,60928,54784,49152,49152],"182":[8,0,0,0,0,16128,31488,64256,64256,64256,31488,6912,6912,6912,6912,6912,6912],"183":[2,0,0,0,0,0,0,0,0,0,0,49152,49152,0,0,0,0],"184":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,12288,12288,57344],"185":[6,0,0,0,0,12288,61440,12288,12288,12288,12288,12288,64512,0,0,0,0],"186":[7,0,0,0,0,14336,27648,50688,50688,50688,50688,27648,14336,0,0,0,0],"187":[8,0,0,0,0,0,0,0,34816,52224,26112,12544,12544,26112,52224,34816,0],"188":[14,0,0,0,0,24768,57472,24960,24832,25440,25312,26080,28000,2656,7152,4192,12384],"189":[14,0,0,0,0,24768,57472,24960,24832,25464,25164,25612,27660,2072,6192,4192,12412],"190":[14,0,0,0,0,61536,38976,6336,28800,6552,6456,39544,63064,1176,3324,2072,6168],"191":[7,0,0,0,0,6144,6144,0,0,6144,14336,28672,57344,49152,49152,57856,15872],"192":[12,3072,1536,768,0,1536,3840,3840,6528,6528,6528,12480,16320,24672,24672,24672,49200],"193":[12,1536,3072,6144,0,1536,3840,3840,6528,6528,6528,12480,16320,24672,24672,24672,49200],"194":[12,1536,3840,6528,0,1536,3840,3840,6528,6528,6528,12480,16320,24672,24672,24672,49200],"195":[12,3648,4992,0,0,1536,3840,3840,6528,6528,6528,12480,16320,24672,24672,24672,49200],"196":[12,6528,6528,0,0,1536,3840,3840,6528,6528,6528,12480,16320,24672,24672,24672,49200],"197":[12,3840,4224,3840,0,1536,3840,3840,2304,6528,6528,12480,16320,24672,24672,24672,49200],"198":[15,0,0,0,0,2046,3264,3264,3264,6336,6398,16320,12480,28864,24768,24768,49406],"199":[10,0,8064,12480,24640,49152,49152,49152,49152,49152,49152,24640,12480,8064,768,768,3584],"200":[8,12288,6144,3072,0,65280,49152,49152,49152,49152,65280,49152,49152,49152,49152,49152,65280],"201":[8,3072,6144,12288,0,65280,49152,49152,49152,49152,65280,49152,49152,49152,49152,49152,65280],"202":[8,6144,15360,26112,0,65280,49152,49152,49152,49152,65280,49152,49152,49152,49152,49152,65280],"203":[8,26112,26112,0,0,65280,49152,49152,49152,49152,65280,49152,49152,49152,49152,49152,65280],"204":[6,24576,12288,6144,0,64512,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,64512],"205":[6,6144,12288,24576,0,64512,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,64512],"206":[6,12288,30720,52224,0,64512,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,64512],"207":[6,52224,52224,0,0,64512,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,64512],"208":[11,0,0,0,0,32512,24960,24768,24672,24672,63584,24672,24672,24672,24768,24960,32512],"209":[9,14592,19968,0,0,57728,57728,53632,53632,55680,51584,52608,50560,50560,50048,50048,50048],"210":[11,6144,3072,1536,0,7936,12672,24768,49248,49248,49248,49248,49248,49248,24768,12672,7936],"211":[11,768,1536,3072,0,7936,12672,24768,49248,49248,49248,49248,49248,49248,24768,12672,7936],"212":[11,3072,7680,13056,0,7936,12672,24768,49248,49248,49248,49248,49248,49248,24768,12672,7936],"213":[11,7296,9984,0,0,7936,12672,24768,49248,49248,49248,49248,49248,49248,24768,12672,7936],"214":[11,13056,13056,0,0,7936,12672,24768,49248,49248,49248,49248,49248,49248,24768,12672,7936],"215":[8,0,0,0,0,0,0,33024,49920,26112,15360,6144,15360,26112,49920,33024,0],"216":[11,0,0,0,32,8000,12672,25024,49760,49760,50272,50272,51296,53344,28864,12672,24320],"217":[10,6144,3072,1536,0,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,24960,16128],"218":[10,1536,3072,6144,0,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,24960,16128],"219":[10,3072,7680,13056,0,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,24960,16128],"220":[10,13056,13056,0,0,49344,49344,49344,49344,49344,49344,49344,49344,49344,49344,24960,16128],"221":[10,1536,3072,6144,0,57792,24960,29568,13056,7680,7680,3072,3072,3072,3072,3072,3072],"222":[7,0,0,0,0,49152,49152,63488,52224,50688,50688,50688,52224,63488,49152,49152,49152],"223":[9,0,0,0,0,15872,25344,49920,49920,50688,52224,49920,49536,49536,49536,49920,56832],"224":[8,0,0,0,14336,6144,3072,0,0,32256,17152,768,16128,25344,49920,50944,31488],"225":[8,0,0,0,1792,1536,3072,0,0,32256,17152,768,16128,25344,49920,50944,31488],"226":[8,0,0,0,3072,7680,13056,0,0,32256,17152,768,16128,25344,49920,50944,31488],"227":[8,0,0,0,0,14592,19968,0,0,32256,17152,768,16128,25344,49920,50944,31488],"228":[8,0,0,0,0,26112,26112,0,0,32256,17152,768,16128,25344,49920,50944,31488],"229":[8,0,0,15360,26112,26112,15360,0,0,32256,17152,768,16128,25344,49920,50944,31488],"230":[12,0,0,0,0,0,0,0,0,30912,18272,1584,16368,58880,50944,53008,29168],"231":[8,0,0,0,0,16128,24832,49152,49152,49152,49152,49152,25344,15872,1536,1536,7168],"232":[8,0,0,0,14336,6144,3072,0,0,15360,26112,49920,49920,65280,49152,24832,7936],"233":[8,0,0,0,1792,1536,3072,0,0,15360,26112,49920,49920,65280,49152,24832,7936],"234":[8,0,0,0,6144,15360,26112,0,0,15360,26112,49920,49920,65280,49152,24832,7936],"235":[8,0,0,0,0,26112,26112,0,0,15360,26112,49920,49920,65280,49152,24832,7936],"236":[4,0,0,0,57344,24576,12288,0,0,12288,12288,12288,12288,12288,12288,12288,12288],"237":[4,0,0,0,28672,24576,49152,0,0,49152,49152,49152,49152,49152,49152,49152,49152],"238":[6,0,0,0,12288,30720,52224,0,0,12288,12288,12288,12288,12288,12288,12288,12288],"239":[6,0,0,0,0,52224,52224,0,0,12288,12288,12288,12288,12288,12288,12288,12288],"240":[8,0,0,0,0,13824,7168,9728,1536,15104,26368,49920,49920,49920,49920,26112,15360],"241":[7,0,0,0,0,29184,39936,0,0,56320,58880,50688,50688,50688,50688,50688,50688],"242":[8,0,0,0,28672,12288,6144,0,0,15360,26112,49920,49920,49920,49920,26112,15360],"243":[8,0,0,0,7168,6144,12288,0,0,15360,26112,49920,49920,49920,49920,26112,15360],"244":[8,0,0,0,6144,15360,26112,0,0,15360,26112,49920,49920,49920,49920,26112,15360],"245":[8,0,0,0,0,29184,39936,0,0,15360,26112,49920,49920,49920,49920,26112,15360],"246":[8,0,0,0,0,26112,26112,0,0,15360,26112,49920,49920,49920,49920,26112,15360],"247":[10,0,0,0,0,0,3072,3072,0,0,0,65472,0,0,0,3072,3072],"248":[9,0,0,0,0,0,128,16128,25344,50560,50560,51584,53632,53632,25344,32256,32768],"249":[8,0,0,0,14336,6144,3072,0,0,49920,49920,49920,49920,49920,49920,50944,31488],"250":[8,0,0,0,3584,3072,6144,0,0,49920,49920,49920,49920,49920,49920,50944,31488],"251":[8,0,0,0,6144,15360,26112,0,0,49920,49920,49920,49920,49920,49920,50944,31488],"252":[8,0,0,0,0,26112,26112,0,0,49920,49920,49920,49920,49920,49920,50944,31488],"253":[8,0,0,0,3072,6144,12288,0,0,49920,26112,26112,15360,6144,4096,12288,24576],"254":[8,0,0,0,0,49152,49152,56320,58880,49920,49920,49920,49920,50688,64512,49152,49152],"255":[8,0,0,0,0,26112,26112,0,0,49920,26112,26112,15360,6144,4096,12288,24576]}},"ASCEV14":{"name":"ASCEV14","cellWidth":16,"height":16,"glyphs":{"32":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"33":[2,0,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,0,0,49152,49152],"34":[8,30464,26112,26112,52224,52224,0,0,0,0,0,0,0,0,0,0,0],"35":[12,1088,1088,2176,2176,65520,2176,4352,4352,4352,65504,8704,8704,8704,17408,17408,0],"36":[9,2048,16128,32640,51328,51200,51200,63488,16128,3968,2432,2432,35200,65280,32256,2048,2048],"37":[14,0,48,28704,34912,34880,35008,34944,35072,35128,29508,580,1604,2116,6212,4164,12344],"38":[13,0,7680,16128,25344,25344,25344,13824,7264,27744,18016,50016,49600,49344,57824,32560,15896],"39":[4,28672,24576,24576,49152,49152,0,0,0,0,0,0,0,0,0,0,0],"40":[4,12288,28672,24576,24576,49152,49152,49152,49152,49152,49152,49152,49152,24576,24576,28672,12288],"41":[4,49152,57344,24576,24576,12288,12288,12288,12288,12288,12288,12288,12288,24576,24576,57344,49152],"42":[9,34944,60288,15872,2048,15872,60288,34944,2048,0,0,0,0,0,0,0,0],"43":[12,0,0,0,1536,1536,1536,1536,1536,65520,65520,1536,1536,1536,1536,1536,0],"44":[4,0,0,0,0,0,0,0,0,0,0,28672,24576,24576,16384,49152,49152],"45":[6,0,0,0,0,0,0,0,0,64512,64512,0,0,0,0,0,0],"46":[2,0,0,0,0,0,0,0,0,0,0,0,0,49152,49152,49152,0],"47":[7,1536,1536,3072,3072,7168,6144,6144,14336,12288,12288,24576,24576,24576,49152,49152,49152],"48":[11,0,7936,16256,24768,24768,49248,49248,49248,49248,49248,49248,49248,24768,25024,16256,7936],"49":[8,0,6144,6144,63488,63488,6144,6144,6144,6144,6144,6144,6144,6144,6144,65280,65280],"50":[10,0,32256,65280,33664,384,384,384,768,768,1536,3072,6144,28672,57344,65472,65472],"51":[9,0,32256,65280,33664,384,384,768,7680,7936,896,384,384,384,33664,65280,32256],"52":[11,0,384,896,1920,3968,7552,6528,12672,29056,57728,65504,65504,384,384,384,384],"53":[10,0,32704,32704,24576,24576,24576,24576,32512,32640,448,192,192,192,33152,65408,32256],"54":[11,0,3968,8064,12288,24576,16384,57088,65472,57568,49248,49248,49248,57440,28864,16256,7936],"55":[10,0,65472,65472,192,384,384,768,1792,1536,3584,3072,7168,6144,14336,12288,28672],"56":[11,0,8064,32704,57568,49248,49248,24768,15744,16256,25024,49248,49248,49248,57568,32704,7936],"57":[11,0,7936,16256,25024,49376,49248,49248,49248,57568,32736,8032,64,192,384,16128,15872],"58":[2,0,0,0,0,49152,49152,49152,0,0,0,0,0,49152,49152,49152,0],"59":[4,0,0,0,0,24576,24576,24576,0,0,0,28672,24576,24576,16384,49152,49152],"60":[11,0,0,0,96,480,1920,7680,30720,57344,30720,7680,1920,480,96,0,0],"61":[12,0,0,0,0,0,0,65520,65520,0,0,65520,65520,0,0,0,0],"62":[11,0,0,0,49152,61440,15360,3840,960,224,960,3840,15360,61440,49152,0,0],"63":[8,31744,65024,34560,768,768,768,1536,7168,14336,12288,12288,0,0,12288,12288,0],"64":[14,4032,14384,24600,49164,51180,35940,39012,39012,39012,39012,39012,52452,51064,24576,14368,4064],"65":[14,0,768,1920,1920,3264,3264,3264,6240,6240,14448,16368,16368,24600,24600,24600,49164],"66":[11,0,65280,65408,49600,49344,49344,49536,65280,65472,49376,49248,49248,49248,49376,65472,65280],"67":[12,0,4032,16368,30768,24592,57344,49152,49152,49152,49152,49152,57344,24592,30768,16368,4032],"68":[12,0,65024,65408,49600,49248,49264,49200,49200,49200,49200,49200,49248,49248,49600,65408,65024],"69":[10,0,65472,65472,49152,49152,49152,49152,65472,65472,49152,49152,49152,49152,49152,65472,65472],"70":[10,0,65472,65472,49152,49152,49152,49152,65408,65408,49152,49152,49152,49152,49152,49152,49152],"71":[14,0,2032,8188,14364,28676,24576,49152,49152,49404,49404,49164,57356,28684,14348,8188,2016],"72":[11,0,49248,49248,49248,49248,49248,49248,65504,65504,49248,49248,49248,49248,49248,49248,49248],"73":[6,0,64512,64512,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,64512,64512],"74":[7,0,15872,15872,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,3584,64512,63488],"75":[9,0,49280,49536,49920,50688,52224,55296,61440,61440,63488,56320,52736,50944,50048,49536,49280],"76":[9,0,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,65280,65280],"77":[13,0,57400,57400,61560,53336,55512,55448,52632,52632,50968,50968,49688,49176,49176,49176,49176],"78":[11,0,57440,61536,61536,55392,55392,52320,52320,50784,50784,50016,50016,49632,49632,49376,49376],"79":[14,0,4032,16368,30840,24600,57372,49164,49164,49164,49164,49164,57372,24600,30840,16368,4032],"80":[9,0,65024,65280,50048,49536,49536,49536,50048,65280,64512,49152,49152,49152,49152,49152,49152],"81":[15,4032,16368,30840,24600,57372,49164,49164,49164,49164,49164,57756,25048,30968,16376,4092,14],"82":[12,0,65280,65408,49600,49344,49344,49344,49536,65280,65024,50688,49920,49536,49344,49376,49248],"83":[12,0,8128,32736,57376,49152,49152,57344,31744,16320,480,48,48,32816,57440,65504,16256],"84":[12,0,65520,65520,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536],"85":[11,0,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,57568,24768,32704,7936],"86":[14,0,49164,24600,24600,24600,12336,12336,14448,6240,6240,3264,3264,3264,1920,1920,1920],"87":[16,0,33153,33153,50115,50115,50115,49731,26214,26214,26214,25638,13364,15420,15420,14364,6168],"88":[12,0,57456,24672,12480,12480,6528,3840,3840,1536,3840,3840,6528,12480,12480,24672,57456],"89":[12,0,57456,24672,28896,12480,6528,6528,3840,3840,1536,1536,1536,1536,1536,1536,1536],"90":[12,0,65520,65520,112,224,448,896,768,1536,3072,7168,14336,28672,57344,65520,65520],"91":[5,63488,63488,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,63488,63488],"92":[8,49152,24576,24576,28672,12288,12288,14336,6144,6144,3072,3072,3072,1536,1536,1536,768],"93":[5,63488,63488,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,63488,63488],"94":[11,1024,3584,6912,12672,12672,24768,49248,32800,0,0,0,0,0,0,0,0],"95":[12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65520],"96":[4,57344,24576,8192,12288,0,0,0,0,0,0,0,0,0,0,0,0],"97":[9,0,0,0,0,0,32256,32512,896,384,8064,32640,57728,49536,50048,65408,15744],"98":[9,0,49152,49152,49152,49152,56832,65280,58240,49536,49536,49536,49536,49536,49920,65280,56832],"99":[8,0,0,0,0,0,7680,32512,24832,49152,49152,49152,49152,49152,24832,32512,7680],"100":[9,0,384,384,384,384,16256,32640,24960,49536,49536,49536,49536,49536,58240,32640,15744],"101":[10,0,0,0,0,0,7936,32640,25024,49344,65472,65472,49152,49152,28736,32704,8064],"102":[7,7680,15872,28672,24576,24576,64512,64512,24576,24576,24576,24576,24576,24576,24576,24576,24576],"103":[9,0,0,0,0,16128,32640,49536,49536,49536,49536,58240,32640,15744,384,32640,32512],"104":[9,0,49152,49152,49152,49152,56832,65280,58240,49536,49536,49536,49536,49536,49536,49536,49536],"105":[2,49152,49152,0,0,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"106":[4,12288,12288,0,0,61440,61440,12288,12288,12288,12288,12288,12288,12288,12288,28672,57344],"107":[9,0,49152,49152,49152,49152,49408,49920,50688,52224,55296,63488,64512,52736,50944,50048,49536],"108":[2,0,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"109":[16,0,0,0,0,0,56892,65406,58311,49539,49539,49539,49539,49539,49539,49539,49539],"110":[9,0,0,0,0,0,56832,65280,58240,49536,49536,49536,49536,49536,49536,49536,49536],"111":[10,0,0,0,0,0,7680,32640,24960,49344,49344,49344,49344,49344,24960,32640,7680],"112":[9,0,0,0,0,57088,65408,58240,49536,49536,49536,49920,65280,65024,49152,49152,49152],"113":[9,0,0,0,0,16128,32640,57728,49536,49536,57728,32640,16256,384,384,384,384],"114":[7,0,0,0,0,0,52736,65024,57344,49152,49152,49152,49152,49152,49152,49152,49152],"115":[8,0,0,0,0,0,15872,32512,49408,49152,61440,32256,3840,768,33536,65024,31744],"116":[7,0,0,24576,24576,24576,65024,65024,24576,24576,24576,24576,24576,24576,24576,32256,15872],"117":[9,0,0,0,0,0,49536,49536,49536,49536,49536,49536,49536,49536,58240,32640,15744],"118":[10,0,0,0,0,0,49344,24960,24960,24960,13056,13056,13056,7680,7680,3072,3072],"119":[14,0,0,0,0,0,49932,49932,25496,26520,25752,25752,11472,14416,14448,14448,6176],"120":[10,0,0,0,0,0,57792,24960,13056,7680,7680,3072,7680,7680,13056,24960,57792],"121":[10,0,0,0,0,49344,24960,24960,24960,13056,13056,7680,3072,3072,2048,6144,12288],"122":[9,0,0,0,0,0,65408,65408,896,1792,3584,7168,14336,28672,57344,65408,65408],"123":[6,3072,6144,6144,6144,6144,6144,6144,12288,49152,12288,12288,6144,6144,6144,6144,3072],"124":[2,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"125":[6,49152,24576,24576,24576,24576,24576,24576,12288,3072,12288,12288,24576,24576,24576,24576,49152],"126":[13,0,0,0,0,0,0,15384,32280,59192,50160,49376,0,0,0,0,0],"127":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"128":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"129":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"130":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"131":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"132":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"133":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"134":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"135":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"136":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"137":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"138":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"139":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"140":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"141":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"142":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"143":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"144":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"145":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"146":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"147":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"148":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"149":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"150":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"151":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"152":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"153":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"154":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"155":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"156":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"157":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"158":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"159":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"160":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"161":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"162":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"163":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"164":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"165":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"166":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"167":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"168":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"169":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"170":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"171":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"172":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"173":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"174":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"175":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"176":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"177":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"178":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"179":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"180":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"181":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"182":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"183":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"184":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"185":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"186":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"187":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"188":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"189":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"190":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"191":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"192":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"193":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"194":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"195":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"196":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"197":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"198":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"199":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"200":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"201":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"202":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"203":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"204":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"205":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"206":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"207":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"208":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"209":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"210":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"211":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"212":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"213":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"214":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"215":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"216":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"217":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"218":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"219":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"220":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"221":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"222":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"223":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"224":[9,12288,6144,3072,0,0,32256,32512,896,384,8064,32640,57728,49536,50048,65408,15744],"225":[9,768,1536,3072,0,0,32256,32512,896,384,8064,32640,57728,49536,50048,65408,15744],"226":[9,7168,13824,25344,0,0,32256,32512,896,384,8064,32640,57728,49536,50048,65408,15744],"227":[9,0,14464,18176,0,0,32256,32512,896,384,8064,32640,57728,49536,50048,65408,15744],"228":[9,0,13056,13056,0,0,32256,32512,896,384,8064,32640,57728,49536,50048,65408,15744],"229":[9,7680,8448,8448,7680,0,32256,32512,896,384,8064,32640,57728,49536,50048,65408,15744],"230":[15,0,0,0,0,0,64636,65278,902,770,32766,65532,49920,33536,34688,65020,30846],"231":[8,0,0,7680,32512,24832,49152,49152,49152,49152,49152,24832,32512,7680,1536,15872,15360],"232":[10,12288,6144,3072,0,0,7936,32640,25024,49344,65472,65472,49152,49152,28736,32704,8064],"233":[10,384,768,1536,0,0,7936,32640,25024,49344,65472,65472,49152,49152,28736,32704,8064],"234":[10,3584,6912,12672,0,0,7936,32640,25024,49344,65472,65472,49152,49152,28736,32704,8064],"235":[10,0,13056,13056,0,0,7936,32640,25024,49344,65472,65472,49152,49152,28736,32704,8064],"236":[4,49152,24576,12288,0,0,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288],"237":[4,12288,24576,49152,0,0,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"238":[6,12288,30720,52224,0,0,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288],"239":[6,0,52224,52224,0,0,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288],"240":[10,0,6528,3840,3840,4352,384,16000,32704,57792,49344,49344,49344,49600,24960,32640,7680],"241":[9,14464,19584,18176,0,0,56832,65280,58240,49536,49536,49536,49536,49536,49536,49536,49536],"242":[10,6144,3072,1536,0,0,7680,32640,24960,49344,49344,49344,49344,49344,24960,32640,7680],"243":[10,768,1536,3072,0,0,7680,32640,24960,49344,49344,49344,49344,49344,24960,32640,7680],"244":[10,3584,6912,12672,0,0,7680,32640,24960,49344,49344,49344,49344,49344,24960,32640,7680],"245":[10,14464,19584,18176,0,0,7680,32640,24960,49344,49344,49344,49344,49344,24960,32640,7680],"246":[10,0,13056,13056,0,0,7680,32640,24960,49344,49344,49344,49344,49344,24960,32640,7680],"247":[10,0,0,0,0,3072,3072,3072,0,0,65472,65472,0,0,3072,3072,3072],"248":[10,0,0,0,64,7808,32640,24960,49856,50368,51392,51392,53440,24960,32640,24064,32768],"249":[9,12288,6144,3072,0,0,49536,49536,49536,49536,49536,49536,49536,49536,58240,32640,15744],"250":[9,768,1536,3072,0,0,49536,49536,49536,49536,49536,49536,49536,49536,58240,32640,15744],"251":[9,7168,13824,25344,0,0,49536,49536,49536,49536,49536,49536,49536,49536,58240,32640,15744],"252":[9,0,13056,13056,0,0,49536,49536,49536,49536,49536,49536,49536,49536,58240,32640,15744],"253":[10,1536,3072,6144,49344,24960,24960,24960,13056,13056,4608,7680,7680,3072,3072,2048,14336],"254":[9,49152,49152,49152,56832,65280,58240,49536,49536,49536,49536,49536,49920,65280,65024,49152,49152],"255":[10,13056,13056,0,49344,24960,24960,24960,13056,13056,4608,7680,7680,3072,3072,2048,14336]}},"ASCV14":{"name":"ASCV14","cellWidth":16,"height":16,"glyphs":{"32":[4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"33":[2,0,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,0,0,49152,49152],"34":[8,30464,26112,26112,52224,52224,0,0,0,0,0,0,0,0,0,0,0],"35":[12,1088,1088,2176,2176,65520,2176,4352,4352,4352,65504,8704,8704,8704,17408,17408,0],"36":[9,2048,16128,32640,51328,51200,51200,63488,16128,3968,2432,2432,35200,65280,32256,2048,2048],"37":[14,0,48,28704,34912,34880,35008,34944,35072,35128,29508,580,1604,2116,6212,4164,12344],"38":[13,0,7680,16128,25344,25344,25344,13824,7264,27744,18016,50016,49600,49344,57824,32560,15896],"39":[4,28672,24576,24576,49152,49152,0,0,0,0,0,0,0,0,0,0,0],"40":[4,12288,28672,24576,24576,49152,49152,49152,49152,49152,49152,49152,49152,24576,24576,28672,12288],"41":[4,49152,57344,24576,24576,12288,12288,12288,12288,12288,12288,12288,12288,24576,24576,57344,49152],"42":[9,34944,60288,15872,2048,15872,60288,34944,2048,0,0,0,0,0,0,0,0],"43":[12,0,0,0,1536,1536,1536,1536,1536,65520,65520,1536,1536,1536,1536,1536,0],"44":[4,0,0,0,0,0,0,0,0,0,0,28672,24576,24576,16384,49152,49152],"45":[6,0,0,0,0,0,0,0,0,64512,64512,0,0,0,0,0,0],"46":[2,0,0,0,0,0,0,0,0,0,0,0,0,49152,49152,49152,0],"47":[7,1536,1536,3072,3072,7168,6144,6144,14336,12288,12288,24576,24576,24576,49152,49152,49152],"48":[11,0,7936,16256,24768,24768,49248,49248,49248,49248,49248,49248,49248,24768,25024,16256,7936],"49":[8,0,6144,6144,63488,63488,6144,6144,6144,6144,6144,6144,6144,6144,6144,65280,65280],"50":[10,0,32256,65280,33664,384,384,384,768,768,1536,3072,6144,28672,57344,65472,65472],"51":[9,0,32256,65280,33664,384,384,768,7680,7936,896,384,384,384,33664,65280,32256],"52":[11,0,384,896,1920,3968,7552,6528,12672,29056,57728,65504,65504,384,384,384,384],"53":[10,0,32704,32704,24576,24576,24576,24576,32512,32640,448,192,192,192,33152,65408,32256],"54":[11,0,3968,8064,12288,24576,16384,57088,65472,57568,49248,49248,49248,57440,28864,16256,7936],"55":[10,0,65472,65472,192,384,384,768,1792,1536,3584,3072,7168,6144,14336,12288,28672],"56":[11,0,8064,32704,57568,49248,49248,24768,15744,16256,25024,49248,49248,49248,57568,32704,7936],"57":[11,0,7936,16256,25024,49376,49248,49248,49248,57568,32736,8032,64,192,384,16128,15872],"58":[2,0,0,0,0,49152,49152,49152,0,0,0,0,0,49152,49152,49152,0],"59":[4,0,0,0,0,24576,24576,24576,0,0,0,28672,24576,24576,16384,49152,49152],"60":[11,0,0,0,96,480,1920,7680,30720,57344,30720,7680,1920,480,96,0,0],"61":[12,0,0,0,0,0,0,65520,65520,0,0,65520,65520,0,0,0,0],"62":[11,0,0,0,49152,61440,15360,3840,960,224,960,3840,15360,61440,49152,0,0],"63":[8,31744,65024,34560,768,768,768,1536,7168,14336,12288,12288,0,0,12288,12288,0],"64":[14,4032,14384,24600,49164,51180,35940,39012,39012,39012,39012,39012,52452,51064,24576,14368,4064],"65":[14,0,768,1920,1920,3264,3264,3264,6240,6240,14448,16368,16368,24600,24600,24600,49164],"66":[11,0,65280,65408,49600,49344,49344,49536,65280,65472,49376,49248,49248,49248,49376,65472,65280],"67":[12,0,4032,16368,30768,24592,57344,49152,49152,49152,49152,49152,57344,24592,30768,16368,4032],"68":[12,0,65024,65408,49600,49248,49264,49200,49200,49200,49200,49200,49248,49248,49600,65408,65024],"69":[10,0,65472,65472,49152,49152,49152,49152,65472,65472,49152,49152,49152,49152,49152,65472,65472],"70":[10,0,65472,65472,49152,49152,49152,49152,65408,65408,49152,49152,49152,49152,49152,49152,49152],"71":[14,0,2032,8188,14364,28676,24576,49152,49152,49404,49404,49164,57356,28684,14348,8188,2016],"72":[11,0,49248,49248,49248,49248,49248,49248,65504,65504,49248,49248,49248,49248,49248,49248,49248],"73":[6,0,64512,64512,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,12288,64512,64512],"74":[7,0,15872,15872,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,3584,64512,63488],"75":[9,0,49280,49536,49920,50688,52224,55296,61440,61440,63488,56320,52736,50944,50048,49536,49280],"76":[9,0,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,65280,65280],"77":[13,0,57400,57400,61560,53336,55512,55448,52632,52632,50968,50968,49688,49176,49176,49176,49176],"78":[11,0,57440,61536,61536,55392,55392,52320,52320,50784,50784,50016,50016,49632,49632,49376,49376],"79":[14,0,4032,16368,30840,24600,57372,49164,49164,49164,49164,49164,57372,24600,30840,16368,4032],"80":[9,0,65024,65280,50048,49536,49536,49536,50048,65280,64512,49152,49152,49152,49152,49152,49152],"81":[15,4032,16368,30840,24600,57372,49164,49164,49164,49164,49164,57756,25048,30968,16376,4092,14],"82":[12,0,65280,65408,49600,49344,49344,49344,49536,65280,65024,50688,49920,49536,49344,49376,49248],"83":[12,0,8128,32736,57376,49152,49152,57344,31744,16320,480,48,48,32816,57440,65504,16256],"84":[12,0,65520,65520,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536,1536],"85":[11,0,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,49248,57568,24768,32704,7936],"86":[14,0,49164,24600,24600,24600,12336,12336,14448,6240,6240,3264,3264,3264,1920,1920,1920],"87":[16,0,33153,33153,50115,50115,50115,49731,26214,26214,26214,25638,13364,15420,15420,14364,6168],"88":[12,0,57456,24672,12480,12480,6528,3840,3840,1536,3840,3840,6528,12480,12480,24672,57456],"89":[12,0,57456,24672,28896,12480,6528,6528,3840,3840,1536,1536,1536,1536,1536,1536,1536],"90":[12,0,65520,65520,112,224,448,896,768,1536,3072,7168,14336,28672,57344,65520,65520],"91":[5,63488,63488,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,63488,63488],"92":[8,49152,24576,24576,28672,12288,12288,14336,6144,6144,3072,3072,3072,1536,1536,1536,768],"93":[5,63488,63488,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,6144,63488,63488],"94":[11,1024,3584,6912,12672,12672,24768,49248,32800,0,0,0,0,0,0,0,0],"95":[12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65520],"96":[4,57344,24576,8192,12288,0,0,0,0,0,0,0,0,0,0,0,0],"97":[9,0,0,0,0,0,32256,32512,896,384,8064,32640,57728,49536,50048,65408,15744],"98":[9,0,49152,49152,49152,49152,56832,65280,58240,49536,49536,49536,49536,49536,49920,65280,56832],"99":[8,0,0,0,0,0,7680,32512,24832,49152,49152,49152,49152,49152,24832,32512,7680],"100":[9,0,384,384,384,384,16256,32640,24960,49536,49536,49536,49536,49536,58240,32640,15744],"101":[10,0,0,0,0,0,7936,32640,25024,49344,65472,65472,49152,49152,28736,32704,8064],"102":[7,7680,15872,28672,24576,24576,64512,64512,24576,24576,24576,24576,24576,24576,24576,24576,24576],"103":[9,0,0,0,0,16128,32640,49536,49536,49536,49536,58240,32640,15744,384,32640,32512],"104":[9,0,49152,49152,49152,49152,56832,65280,58240,49536,49536,49536,49536,49536,49536,49536,49536],"105":[2,49152,49152,0,0,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"106":[4,12288,12288,0,0,61440,61440,12288,12288,12288,12288,12288,12288,12288,12288,28672,57344],"107":[9,0,49152,49152,49152,49152,49408,49920,50688,52224,55296,63488,64512,52736,50944,50048,49536],"108":[2,0,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"109":[16,0,0,0,0,0,56892,65406,58311,49539,49539,49539,49539,49539,49539,49539,49539],"110":[9,0,0,0,0,0,56832,65280,58240,49536,49536,49536,49536,49536,49536,49536,49536],"111":[10,0,0,0,0,0,7680,32640,24960,49344,49344,49344,49344,49344,24960,32640,7680],"112":[9,0,0,0,0,57088,65408,58240,49536,49536,49536,49920,65280,65024,49152,49152,49152],"113":[9,0,0,0,0,16128,32640,57728,49536,49536,57728,32640,16256,384,384,384,384],"114":[7,0,0,0,0,0,52736,65024,57344,49152,49152,49152,49152,49152,49152,49152,49152],"115":[8,0,0,0,0,0,15872,32512,49408,49152,61440,32256,3840,768,33536,65024,31744],"116":[7,0,0,24576,24576,24576,65024,65024,24576,24576,24576,24576,24576,24576,24576,32256,15872],"117":[9,0,0,0,0,0,49536,49536,49536,49536,49536,49536,49536,49536,58240,32640,15744],"118":[10,0,0,0,0,0,49344,24960,24960,24960,13056,13056,13056,7680,7680,3072,3072],"119":[14,0,0,0,0,0,49932,49932,25496,26520,25752,25752,11472,14416,14448,14448,6176],"120":[10,0,0,0,0,0,57792,24960,13056,7680,7680,3072,7680,7680,13056,24960,57792],"121":[10,0,0,0,0,49344,24960,24960,24960,13056,13056,7680,3072,3072,2048,6144,12288],"122":[9,0,0,0,0,0,65408,65408,896,1792,3584,7168,14336,28672,57344,65408,65408],"123":[6,3072,6144,6144,6144,6144,6144,6144,12288,49152,12288,12288,6144,6144,6144,6144,3072],"124":[2,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152,49152],"125":[6,49152,24576,24576,24576,24576,24576,24576,12288,3072,12288,12288,24576,24576,24576,24576,49152],"126":[13,0,0,0,0,0,0,15384,32280,59192,50160,49376,0,0,0,0,0]}},"ASCV20":{"name":"ASCV20","cellWidth":24,"height":20,"glyphs":{"32":[5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"33":[3,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,0,0,14680064,14680064,14680064,0],"34":[10,3784704,3244032,7569408,6488064,6488064,12976128,12976128,0,0,0,0,0,0,0,0,0,0,0,0,0],"35":[16,101376,101376,202752,202752,202752,8388352,8388352,405504,405504,421888,811008,811008,16776704,16776704,1622016,1622016,1622016,3244032,3244032,0],"36":[10,262144,262144,2064384,8372224,14958592,12845056,12845056,14942208,8126464,4161536,507904,286720,286720,286720,12902400,16760832,8323072,262144,262144,0],"37":[22,0,4064000,8323584,14910976,12682240,12684288,12683264,12687360,14915584,8335856,4076536,26396,26124,17932,50700,34316,100124,66552,197104,0],"38":[17,1015808,4177920,7987200,7397376,7397376,7397376,3784704,2067968,1969664,3640832,7589376,14740992,14711808,14695424,14687232,15744512,7894784,4187008,2064768,0],"39":[5,3670016,3145728,7340032,6291456,6291456,12582912,12582912,0,0,0,0,0,0,0,0,0,0,0,0,0],"40":[6,1835008,1572864,3145728,7340032,6291456,6291456,12582912,12582912,12582912,12582912,12582912,12582912,12582912,12582912,6291456,6291456,7340032,3145728,1572864,1835008],"41":[6,14680064,6291456,3145728,3670016,1572864,1572864,786432,786432,786432,786432,786432,786432,786432,786432,1572864,1572864,3670016,3145728,6291456,14680064],"42":[12,393216,393216,4599808,15101952,4177920,983040,983040,4177920,15101952,4599808,393216,393216,0,0,0,0,0,0,0,0],"43":[12,0,0,0,0,393216,393216,393216,393216,393216,393216,16773120,16773120,393216,393216,393216,393216,393216,393216,393216,0],"44":[5,0,0,0,0,0,0,0,0,0,0,0,3670016,3145728,7340032,7340032,6291456,6291456,12582912,12582912,0],"45":[8,0,0,0,0,0,0,0,0,0,0,16711680,16711680,0,0,0,0,0,0,0,0],"46":[3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14680064,14680064,14680064,14680064,0],"47":[9,98304,196608,196608,196608,393216,393216,917504,786432,786432,1835008,1572864,1572864,3145728,3145728,3145728,6291456,6291456,6291456,12582912,0],"48":[13,1015808,4186112,7925760,7368704,6303744,14694400,14694400,14694400,14694400,14694400,14694400,14694400,14694400,14694400,6303744,7368704,7925760,4186112,1015808,0],"49":[11,917504,917504,4063232,16646144,16646144,917504,917504,917504,917504,917504,917504,917504,917504,917504,917504,917504,917504,16769024,16769024,0],"50":[13,4161536,16760832,14737408,8417280,28672,28672,28672,61440,57344,122880,245760,491520,983040,1966080,3932160,7340032,14680064,16775168,16775168,0],"51":[13,2080768,8384512,7368704,4208640,14336,14336,12288,57344,507904,516096,28672,14336,14336,14336,14336,8419328,14741504,16769024,4161536,0],"52":[14,57344,122880,253952,253952,450560,974848,1892352,1630208,3203072,7397376,14737408,12640256,16776192,16776192,57344,57344,57344,57344,57344,0],"53":[12,16773120,16773120,14680064,14680064,14680064,14680064,14680064,16744448,16760832,122880,61440,28672,28672,28672,28672,57344,12705792,16760832,8323072,0],"54":[13,253952,1040384,1843200,3670016,7340032,7340032,14680064,15187968,16769024,15757312,14710784,14694400,14694400,14694400,6305792,7368704,7925760,4186112,1015808,0],"55":[13,16775168,16775168,14336,14336,28672,28672,57344,57344,114688,114688,229376,229376,458752,458752,983040,917504,1966080,1835008,3932160,0],"56":[13,1015808,4186112,3727360,7368704,7368704,7368704,7892992,3956736,2080768,4186112,7401472,6322176,14694400,14694400,14694400,15742976,7892992,4186112,2064384,0],"57":[13,1015808,4186112,7925760,7368704,14694400,14694400,14694400,14694400,15742976,7370752,4192256,2045952,14336,28672,28672,57344,2211840,4161536,4063232,0],"58":[3,0,0,0,0,14680064,14680064,14680064,14680064,0,0,0,0,14680064,14680064,14680064,14680064,0,0,0,0],"59":[5,0,0,0,0,7340032,7340032,7340032,7340032,0,0,0,0,3670016,3145728,7340032,7340032,6291456,6291456,12582912,0],"60":[15,0,0,0,512,3584,31744,126976,507904,1966080,16252928,14680064,16252928,1966080,507904,126976,31744,3584,512,0,0],"61":[15,0,0,0,0,0,0,0,16776704,16776704,0,0,0,16776704,16776704,0,0,0,0,0,0],"62":[15,0,0,0,8388608,14680064,8126464,2031616,507904,61440,15872,3584,15872,61440,507904,2031616,8126464,14680064,8388608,0,0],"63":[11,8323072,16760832,8503296,57344,57344,57344,57344,114688,245760,1015808,1966080,1835008,1835008,1835008,0,0,1835008,1835008,1835008,0],"64":[19,129024,1048064,1967872,3670912,3206592,6552768,6491360,12979296,12979296,12979296,12979296,12979296,12979296,6495424,6553536,7466944,3670016,1966080,1047552,130048],"65":[18,122880,122880,258048,258048,258048,473088,473088,924672,924672,924672,1838592,1838592,4194048,4194048,3671808,7340928,7340928,7340928,14680512,0],"66":[14,16744448,16769024,14741504,14708736,14708736,14708736,14708736,14737408,16760832,16773120,14694400,14687232,14687232,14687232,14687232,14695424,14710784,16773120,16760832,0],"67":[16,260096,1048064,1969920,3670784,7340288,7340032,14680064,14680064,14680064,14680064,14680064,14680064,14680064,7340032,7340288,3670784,4067072,1048064,260096,0],"68":[15,16744448,16769024,14741504,14694400,14687232,14687232,14683648,14683648,14683648,14683648,14683648,14683648,14683648,14687232,14687232,14694400,14741504,16769024,16744448,0],"69":[12,16773120,16773120,14680064,14680064,14680064,14680064,14680064,14680064,16773120,16773120,14680064,14680064,14680064,14680064,14680064,14680064,14680064,16773120,16773120,0],"70":[12,16773120,16773120,14680064,14680064,14680064,14680064,14680064,14680064,16769024,16769024,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,0],"71":[17,261120,1048320,1968000,3670400,7340160,7340032,14680064,14680064,14680064,14712704,14712704,14680960,14680960,7340928,7340928,3670912,1968000,1048320,260096,0],"72":[14,14687232,14687232,14687232,14687232,14687232,14687232,14687232,14687232,16776192,16776192,14687232,14687232,14687232,14687232,14687232,14687232,14687232,14687232,14687232,0],"73":[7,16646144,16646144,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,16646144,16646144,0],"74":[9,4161536,4161536,229376,229376,229376,229376,229376,229376,229376,229376,229376,229376,229376,229376,229376,229376,8847360,16711680,16515072,0],"75":[15,14687744,14695424,14710784,14741504,14802944,14925824,15171584,15663104,16646144,16646144,16711680,16220160,14925824,14794752,14802944,14741504,14710784,14695424,14687744,0],"76":[11,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,16769024,16769024,0],"77":[16,15732480,15732480,16260864,16260864,15480576,15480576,15480576,15099648,15099648,14894848,14927616,14927616,14780160,14780160,14681856,14681856,14681856,14681856,14681856,0],"78":[13,15742976,16267264,16267264,15218688,15480832,15480832,15087616,15087616,15087616,14891008,14891008,14759936,14792704,14792704,14743552,14743552,14710784,14710784,14710784,0],"79":[18,258048,1047552,3936000,3671808,7340928,7340928,14680512,14680512,14680512,14680512,14680512,14680512,14680512,7340928,7340928,3671808,4067072,1047552,258048,0],"80":[12,16711680,16760832,14802944,14741504,14708736,14708736,14708736,14708736,14737408,14802944,16760832,16711680,14680064,14680064,14680064,14680064,14680064,14680064,14680064,0],"81":[19,258048,1047552,3936000,3671808,7340928,7340928,14680512,14680512,14680512,14680512,14680512,14680512,14680512,7340928,7340928,3671808,4067072,1048320,262112,2016],"82":[16,16760832,16769024,14708736,14694400,14694400,14694400,14694400,14708736,14741504,16769024,16744448,14925824,14802944,14741504,14708736,14710784,14695424,14687744,14683904,0],"83":[13,1040384,4192256,7878656,14682112,14680064,14680064,14680064,16252928,8355840,4190208,260096,15360,7168,7168,8395776,12598272,15759360,16773120,2080768,0],"84":[15,16777088,16777088,114688,114688,114688,114688,114688,114688,114688,114688,114688,114688,114688,114688,114688,114688,114688,114688,114688,0],"85":[15,14683648,14683648,14683648,14683648,14683648,14683648,14683648,14683648,14683648,14683648,14683648,14683648,14683648,14683648,14683648,7347200,7879680,4192256,1040384,0],"86":[18,14680512,7340928,7340928,7866240,3671808,3671808,3936000,1838592,1838592,924672,924672,990208,473088,473088,258048,258048,258048,122880,122880,0],"87":[24,14686215,14695431,7355406,7355406,7355918,7366158,3696156,3696156,3696412,1622808,1884952,1884984,1885112,885168,885168,1016240,1016304,458976,458976,0],"88":[16,15732480,7867904,3677184,3947520,1849344,946176,946176,516096,245760,114688,245760,516096,946176,946176,1849344,3947520,3677184,7343616,15732480,0],"89":[17,15730560,7341824,3673600,3939840,1842176,931840,931840,487424,516096,253952,114688,114688,114688,114688,114688,114688,114688,114688,114688,0],"90":[15,16776704,16776704,7680,15360,30720,28672,61440,122880,245760,229376,491520,983040,1966080,1835008,3932160,7864320,15728640,16776704,16776704,0],"91":[5,16252928,12582912,12582912,12582912,12582912,12582912,12582912,12582912,12582912,12582912,12582912,12582912,12582912,12582912,12582912,12582912,12582912,12582912,16252928,0],"92":[11,12582912,6291456,6291456,6291456,3145728,3145728,3670016,1572864,1572864,1835008,786432,786432,393216,393216,393216,196608,196608,196608,98304,0],"93":[5,16252928,1572864,1572864,1572864,1572864,1572864,1572864,1572864,1572864,1572864,1572864,1572864,1572864,1572864,1572864,1572864,1572864,1572864,16252928,0],"94":[12,131072,458752,884736,1622016,1622016,3170304,6303744,12589056,0,0,0,0,0,0,0,0,0,0,0,0],"95":[12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16773120],"96":[4,14680064,6291456,2097152,3145728,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"97":[13,0,0,0,0,4177920,8384512,6320128,14336,14336,522240,4192256,8140800,15742976,14694400,14694400,14694400,15792128,8370176,2045952,0],"98":[13,14680064,14680064,14680064,14680064,15187968,15720448,16314368,14708736,14694400,14694400,14694400,14694400,14694400,14694400,14694400,14708736,15790080,16769024,15695872,0],"99":[12,0,0,0,0,1032192,2093056,3682304,7344128,15728640,14680064,14680064,14680064,14680064,14680064,15728640,7344128,7876608,4190208,1032192,0],"100":[13,14336,14336,14336,14336,1030144,4192256,7895040,7354368,14694400,14694400,14694400,14694400,14694400,14694400,14694400,7354368,7927808,4192256,2045952,0],"101":[13,0,0,0,0,1032192,4186112,3698688,7368704,14694400,14694400,16775168,16775168,14680064,14680064,15728640,7342080,3684352,2095104,1040384,0],"102":[10,1032192,2080768,3932160,3670016,3670016,16744448,16744448,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,0],"103":[11,0,0,0,0,2015232,8380416,15982592,14737408,12640256,12640256,12640256,14737408,15982592,8380416,3989504,57344,49152,114688,8355840,8257536],"104":[12,14680064,14680064,14680064,14680064,15187968,15720448,16310272,14708736,14708736,14708736,14708736,14708736,14708736,14708736,14708736,14708736,14708736,14708736,14708736,0],"105":[3,14680064,14680064,14680064,0,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,0],"106":[5,3670016,3670016,3670016,0,16252928,16252928,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,7864320,15728640,14680064],"107":[13,14680064,14680064,14680064,14680064,14710784,14741504,14802944,14925824,15171584,15663104,16646144,16646144,16711680,16187392,15171584,14925824,14802944,14741504,14710784,0],"108":[3,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,0],"109":[22,0,0,0,0,15188960,16773104,16317552,14708792,14708792,14708792,14708792,14708792,14708792,14708792,14708792,14708792,14708792,14708792,14708792,0],"110":[12,0,0,0,0,15187968,15720448,16310272,14708736,14708736,14708736,14708736,14708736,14708736,14708736,14708736,14708736,14708736,14708736,14708736,0],"111":[14,0,0,0,0,1032192,4190208,7895040,7354368,14687232,14687232,14687232,14687232,14687232,14687232,14687232,7354368,7895040,4190208,1032192,0],"112":[11,0,0,0,0,15138816,16744448,16498688,14794752,14737408,14737408,14737408,14737408,14794752,15974400,16744448,15597568,14680064,14680064,14680064,14680064],"113":[10,0,0,0,0,1949696,8372224,15974400,14794752,12697600,12697600,12697600,12697600,14794752,16236544,8372224,3784704,114688,114688,114688,114688],"114":[9,0,0,0,0,15171584,15695872,16252928,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,14680064,0],"115":[12,0,0,0,0,2064384,8380416,15785984,14688256,14680064,15728640,8257536,4177920,516096,61440,28672,8417280,14737408,16769024,4161536,0],"116":[9,3670016,3670016,3670016,3670016,16744448,16744448,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3670016,3932160,2064384,1015808,0],"117":[12,0,0,0,0,14708736,14708736,14708736,14708736,14708736,14708736,14708736,14708736,14708736,14708736,14708736,14708736,7467008,8351744,4091904,0],"118":[13,0,0,0,0,14694400,7368704,7368704,7368704,3170304,3727360,3727360,1622016,1949696,1949696,884736,884736,1015808,458752,458752,0],"119":[19,0,0,0,0,14737632,14737632,7397568,7385536,7451072,7451072,3258752,3348864,3873152,3874688,1969920,1969920,1969920,1836800,787968,0],"120":[13,0,0,0,0,15759360,7368704,3727360,3727360,1949696,1015808,983040,458752,491520,884736,1949696,3727360,3727360,7368704,15759360,0],"121":[13,0,0,0,0,14694400,7368704,7368704,7368704,3727360,3727360,1949696,884736,1015808,458752,458752,917504,917504,1835008,1835008,3670016],"122":[12,0,0,0,0,16773120,16773120,61440,57344,114688,229376,458752,983040,917504,1835008,3670016,7340032,15728640,16773120,16773120,0]}}}; diff --git a/test/authoring.mjs b/test/authoring.mjs new file mode 100644 index 0000000..581f984 --- /dev/null +++ b/test/authoring.mjs @@ -0,0 +1,83 @@ +/** + * End-to-end: build a .td5 from nothing but text, then read it back and check + * the sign would accept it (CRC), and that the pixels survive intact. + */ +import { writeFileSync } from 'node:fs'; +import { FONTS } from '../src/fonts.js'; +import { fontFromJSON, renderText, trimX } from '../src/codec/bitfont.mjs'; +import { parseTd5, buildTd5, pixelsToFrame, frameToPixels, crc16arc, EFFECT } from '../src/codec/td5.mjs'; + +const SCREEN = { width: 112, height: 16 }; +const font = fontFromJSON(FONTS.ASC1609); + +const LIST = [ + 'SCHOOL BUS', 'CHARTER', 'RAIL BUS', 'EXPRESS COACH LINES', + 'CARMEL COLLEGE', 'OC1', 'NOT IN SERVICE', 'GO LIONS!', +]; + +function fit(bmp, height) { + if (bmp.height === height) return bmp; + const out = new Uint8Array(bmp.width * height); + const y0 = Math.floor((height - bmp.height) / 2); + for (let y = 0; y < bmp.height; y++) { + const ty = y + y0; + if (ty >= 0 && ty < height) out.set(bmp.pixels.subarray(y * bmp.width, (y + 1) * bmp.width), ty * bmp.width); + } + return { width: bmp.width, height, pixels: out }; +} + +const destinations = LIST.map((text) => { + const bmp = fit(trimX(renderText(font, text)), SCREEN.height); + const frame = pixelsToFrame(bmp.pixels, bmp.width, SCREEN.height); + const effect = bmp.width > SCREEN.width ? EFFECT.SCROLL : EFFECT.HOLD; + return { name: text.slice(0, 16), lineName: text.slice(0, 16), frames: [frame], effects: [effect, effect] }; +}); + +// one two-page destination, to exercise the multi-frame path +const pages = ['MORETON BAY', 'BOYS COLLEGE'].map((t) => { + const b = fit(trimX(renderText(font, t)), SCREEN.height); + return pixelsToFrame(b.pixels, b.width, SCREEN.height); +}); +destinations.push({ name: 'MORETON BAY', lineName: 'MORETON BAY', frames: pages, effects: [EFFECT.HOLD, EFFECT.HOLD, EFFECT.HOLD, EFFECT.HOLD] }); + +const bytes = buildTd5({ company: 'Test Coaches', screen: SCREEN, destinations }); +writeFileSync(new URL('./authored.td5', import.meta.url), bytes); + +// ---- read it back as if we were the sign +const back = parseTd5(bytes); +let fails = 0; +const check = (cond, msg) => { if (!cond) { fails++; console.log(' ❌ ' + msg); } }; + +check(back.company === 'Test Coaches', 'company survived'); +check(back.destinations.length === destinations.length, 'destination count'); +check(back.screen.width === 112 && back.screen.height === 16, 'screen size'); +check(bytes.length % 0x200 === 0, 'file padded to a 512-byte boundary'); + +for (const d of back.destinations) { + check(d.crcOk, `${d.name}: CRC verifies`); + const src = destinations.find((x) => x.name === d.name); + check(src && d.frames.length === src.frames.length, `${d.name}: frame count`); + d.frames.forEach((f, i) => { + const a = Buffer.from(f.data), b = Buffer.from(src.frames[i].data); + check(a.equals(b), `${d.name}: frame ${i} pixels identical`); + }); +} + +// independent CRC verification, byte-for-byte against the stored value +const dv = new DataView(bytes.buffer); +for (let i = 0; i < back.destinations.length; i++) { + const ptr = dv.getUint32(0x200 + i * 4, true); + const off = dv.getUint32(ptr + 0x30, true); + const len = dv.getUint32(ptr + 0x34, true); + const stored = bytes[off + 1] | (bytes[off + 2] << 8); + check(stored === crc16arc(bytes.subarray(off + 3, off + len)), `block ${i}: CRC recomputes`); +} + +console.log(`authored ${destinations.length} destinations -> ${bytes.length} bytes (test/authored.td5)`); +for (const d of back.destinations) { + const w = d.frames[0].width; + console.log(` ${d.name.padEnd(17)} ${String(w).padStart(3)}px ${w > 112 ? 'scrolls' : 'fits '} ` + + `${d.frames.length > 1 ? d.frames.length + ' pages' : ''}`); +} +console.log(fails === 0 ? '\n✅ every check passed — a sign-ready file built from scratch' : `\n❌ ${fails} checks failed`); +process.exit(fails ? 1 : 0); diff --git a/test/fontpreview.mjs b/test/fontpreview.mjs new file mode 100644 index 0000000..7868fa8 --- /dev/null +++ b/test/fontpreview.mjs @@ -0,0 +1,53 @@ +/** Render sample text in every 16-row font, next to the original TP5 bitmap. */ +import { readFileSync, writeFileSync } from 'node:fs'; +import { FONTS } from '../src/fonts.js'; +import { fontFromJSON, renderText } from '../src/codec/bitfont.mjs'; +import { parseTd5, frameToPixels } from '../src/codec/td5.mjs'; +import { writePNG } from './png.mjs'; + +const SAMPLE = process.argv[2] ?? 'SCHOOL BUS'; + +const doc = parseTd5(new Uint8Array(readFileSync(new URL('../../test files/EXPRESS 1.td5', import.meta.url)))); +const orig = doc.destinations.find((d) => d.name.startsWith(SAMPLE.slice(0, 10))); + +const rows = []; +if (orig) { + const f = orig.frames[0]; + rows.push({ label: 'TP5 original (Impact 20)', width: f.width, height: f.height, pixels: frameToPixels(f) }); +} +for (const [name, j] of Object.entries(FONTS)) { + const font = fontFromJSON(j); + if (font.height > 16) continue; + const bmp = renderText(font, SAMPLE); + rows.push({ label: `${name} (${font.cellWidth}x${font.height})`, ...bmp }); +} + +const S = 3, PAD = 6, LABEL = 210, SCREEN = 112; +const maxW = Math.max(...rows.map((r) => r.width)); +const W = LABEL + maxW * S + PAD * 2; +const H = rows.length * (16 * S + PAD) + PAD; + +const px = new Map(); +rows.forEach((r, i) => { + const oy = PAD + i * (16 * S + PAD); + for (let y = 0; y < r.height; y++) + for (let x = 0; x < r.width; x++) + if (r.pixels[y * r.width + x]) + for (let dy = 0; dy < S; dy++) + for (let dx = 0; dx < S; dx++) + px.set((oy + y * S + dy) * W + LABEL + x * S + dx, 1); +}); + +writeFileSync(new URL('./fontpreview.png', import.meta.url), writePNG(W, H, (x, y) => { + if (px.has(y * W + x)) return [255, 176, 0]; + // mark where the 112px screen ends + if (x === LABEL + SCREEN * S) return [80, 30, 30]; + return [14, 14, 16]; +})); + +console.log(`sample: ${JSON.stringify(SAMPLE)} screen is ${SCREEN}px wide`); +for (const r of rows) { + const fits = r.width <= SCREEN ? 'fits' : `scrolls (+${r.width - SCREEN}px)`; + console.log(` ${r.label.padEnd(26)} ${String(r.width).padStart(3)}px ${fits}`); +} +console.log('wrote test/fontpreview.png'); diff --git a/test/png.mjs b/test/png.mjs new file mode 100644 index 0000000..640a741 --- /dev/null +++ b/test/png.mjs @@ -0,0 +1,44 @@ +/** Minimal PNG writer, just enough for test previews. */ +import { deflateSync } from 'node:zlib'; + +function crc32(buf) { + let c, crc = 0xffffffff; + for (let n = 0; n < buf.length; n++) { + c = (crc ^ buf[n]) & 0xff; + for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1; + crc = c ^ (crc >>> 8); + } + return (crc ^ 0xffffffff) >>> 0; +} + +function chunk(type, data) { + const len = Buffer.alloc(4); + len.writeUInt32BE(data.length); + const body = Buffer.concat([Buffer.from(type, 'latin1'), data]); + const crc = Buffer.alloc(4); + crc.writeUInt32BE(crc32(body)); + return Buffer.concat([len, body, crc]); +} + +/** rgb: (x,y) => [r,g,b] */ +export function writePNG(width, height, rgb) { + const raw = Buffer.alloc((width * 3 + 1) * height); + let p = 0; + for (let y = 0; y < height; y++) { + raw[p++] = 0; + for (let x = 0; x < width; x++) { + const [r, g, b] = rgb(x, y); + raw[p++] = r; raw[p++] = g; raw[p++] = b; + } + } + const ihdr = Buffer.alloc(13); + ihdr.writeUInt32BE(width, 0); + ihdr.writeUInt32BE(height, 4); + ihdr[8] = 8; ihdr[9] = 2; ihdr[10] = 0; ihdr[11] = 0; ihdr[12] = 0; + return Buffer.concat([ + Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), + chunk('IHDR', ihdr), + chunk('IDAT', deflateSync(raw)), + chunk('IEND', Buffer.alloc(0)), + ]); +} diff --git a/test/roundtrip.mjs b/test/roundtrip.mjs new file mode 100644 index 0000000..8f29d32 --- /dev/null +++ b/test/roundtrip.mjs @@ -0,0 +1,62 @@ +/** + * Proof of format: parse a real TP5 export, rebuild it from the parsed model, + * and require the result to be byte-identical. + */ +import { readFileSync } from 'node:fs'; +import { parseTd5, buildTd5, frameToPixels, pixelsToFrame } from '../src/codec/td5.mjs'; + +const path = process.argv[2] ?? new URL('../samples/EXPRESS 1.td5', import.meta.url); +const original = new Uint8Array(readFileSync(path)); + +const doc = parseTd5(original); +console.log(`company : ${doc.company}`); +console.log(`version : ${doc.version} exported ${doc.timestamp}`); +console.log(`screen : ${doc.screen.width}x${doc.screen.height}`); +console.log(`dests : ${doc.destinations.length}`); +const multi = doc.destinations.filter((d) => d.frames.length > 1); +console.log(`multiframe: ${multi.map((d) => `${d.name}(${d.frames.length})`).join(', ') || 'none'}`); + +const badCrc = doc.destinations.filter((d) => !d.crcOk); +console.log(`crc check : ${doc.destinations.length - badCrc.length}/${doc.destinations.length} verify` + + (badCrc.length ? ` — FAILED: ${badCrc.map((d) => d.name).join(', ')}` : '')); + +const rebuilt = buildTd5(doc, { timestamp: doc.timestamp }); + +let ok = rebuilt.length === original.length; +const diffs = []; +if (!ok) { + console.log(`\nSIZE MISMATCH: original ${original.length}, rebuilt ${rebuilt.length}`); +} else { + for (let i = 0; i < original.length; i++) { + if (original[i] !== rebuilt[i]) { + diffs.push(i); + if (diffs.length > 40) break; + } + } + ok = diffs.length === 0; +} + +if (ok) { + console.log('\n✅ BYTE-IDENTICAL round-trip over all ' + original.length + ' bytes'); +} else { + console.log(`\n❌ ${diffs.length}${diffs.length > 40 ? '+' : ''} differing bytes`); + for (const off of diffs.slice(0, 24)) { + console.log(` 0x${off.toString(16).padStart(5, '0')}: orig ${hx(original[off])} != ${hx(rebuilt[off])}`); + } +} + +// pixel round-trip +let pxOk = true; +for (const d of doc.destinations) { + for (const f of d.frames) { + const re = pixelsToFrame(frameToPixels(f), f.width, f.height); + if (Buffer.compare(Buffer.from(re.data), Buffer.from(f.data)) !== 0) { + pxOk = false; + console.log(` pixel round-trip failed: ${d.name}`); + } + } +} +console.log(pxOk ? '✅ pixel pack/unpack round-trips for every frame' : '❌ pixel round-trip failed'); + +function hx(b) { return '0x' + b.toString(16).padStart(2, '0'); } +process.exit(ok && pxOk ? 0 : 1); diff --git a/test/sheet.mjs b/test/sheet.mjs new file mode 100644 index 0000000..7c6dcb0 --- /dev/null +++ b/test/sheet.mjs @@ -0,0 +1,51 @@ +/** Column-mapping checks for spreadsheet import. */ +import { readCsv, mapRows } from '../src/codec/sheet.mjs'; + +let fails = 0; +function check(label, got, want) { + const ok = JSON.stringify(got) === JSON.stringify(want); + if (!ok) { fails++; console.log(` ❌ ${label}\n got ${JSON.stringify(got)}\n want ${JSON.stringify(want)}`); } + else console.log(` ✅ ${label}`); +} + +// The template shipped with these buses: a "Line" row number AND a "Line Name". +// The name must win over the number — this was a real bug. +check('shipped template layout', + mapRows(readCsv( + 'Company Name:,Yutong Coaches\n' + + '\n' + + 'Line,Line Name,Description,Content (Display)\n' + + '1,SCHOOL BUS,morning,SCHOOL BUS\n' + + '2,CARMEL,college run,CARMEL COLLEGE\n' + )), + { company: 'Yutong Coaches', destinations: [ + { name: 'SCHOOL BUS', text: 'SCHOOL BUS' }, + { name: 'CARMEL', text: 'CARMEL COLLEGE' }, + ] }); + +check('only a number column plus content — do not show the driver a bare number', + mapRows(readCsv('Line,Content (Display)\n1,DEPOT\n2,RAIL BUS\n')).destinations, + [{ name: 'DEPOT', text: 'DEPOT' }, { name: 'RAIL BUS', text: 'RAIL BUS' }]); + +check('no header at all', + mapRows(readCsv('SCHOOL BUS\nCHARTER\n')).destinations, + [{ name: 'SCHOOL BUS', text: 'SCHOOL BUS' }, { name: 'CHARTER', text: 'CHARTER' }]); + +check('destination column naming', + mapRows(readCsv('Name,Destination\nAIRPORT,AIRPORT SHUTTLE VIA CITY\n')).destinations, + [{ name: 'AIRPORT', text: 'AIRPORT SHUTTLE VIA CITY' }]); + +check('blank rows and stray whitespace ignored', + mapRows(readCsv('Line Name,Content\n\n DEPOT , DEPOT VIA YARD \n\n')).destinations, + [{ name: 'DEPOT', text: 'DEPOT VIA YARD' }]); + +check('controller name is capped at the 16 characters the record holds', + mapRows(readCsv('Line Name,Content\nA VERY LONG DESTINATION NAME,SOMETHING\n')).destinations[0].name, + 'A VERY LONG DEST'); + +check('quoted fields with commas', + mapRows(readCsv('Line Name,Content\n"CITY, VIA MALL","CITY, VIA MALL"\n')).destinations, + [{ name: 'CITY, VIA MALL', text: 'CITY, VIA MALL' }]); + +console.log(fails === 0 ? '\n✅ spreadsheet mapping correct' : `\n❌ ${fails} failed`); +process.exit(fails ? 1 : 0); diff --git a/test/structdiff.mjs b/test/structdiff.mjs new file mode 100644 index 0000000..41e4ca4 --- /dev/null +++ b/test/structdiff.mjs @@ -0,0 +1,27 @@ +/** Compare the structure of a TP5-made file against one we authored. */ +import { readFileSync } from 'node:fs'; + +function dump(buf, label) { + const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength); + const ptrs = []; + for (let i = 0x200; ; i += 4) { const v = dv.getUint32(i, true); if (v === 0xffffffff) break; ptrs.push(v); } + const hx = (a, b) => Buffer.from(buf.subarray(a, b)).toString('hex').replace(/(..)/g, '$1 ').trim(); + console.log(`=== ${label} (${buf.length} bytes, ${ptrs.length} destinations)`); + console.log(` 0x30..0x48 : ${hx(0x30, 0x48)}`); + console.log(` 0x80..0xa0 : ${hx(0x80, 0xa0)}`); + console.log(` 0xa0..0xb0 : ${hx(0xa0, 0xb0)}`); + for (let r = 0; r < 2 && r < ptrs.length; r++) { + const q = ptrs[r]; + console.log(` rec${r} name : ${JSON.stringify(String.fromCharCode(...buf.subarray(q, q + 0x10)))}`); + console.log(` rec${r} line : ${JSON.stringify(String.fromCharCode(...buf.subarray(q + 0x10, q + 0x20)))}`); + console.log(` rec${r} third : ${JSON.stringify(String.fromCharCode(...buf.subarray(q + 0x20, q + 0x30)))}`); + console.log(` rec${r} tail : ${hx(q + 0x30, q + 0x60)}`); + console.log(` rec${r} tail2 : ${hx(q + 0x60, q + 0x80)}`); + } +} + +const a = new Uint8Array(readFileSync(new URL('../samples/EXPRESS 1.td5', import.meta.url))); +const b = new Uint8Array(readFileSync(new URL('./authored.td5', import.meta.url))); +dump(a, 'TP5 original — works on the controller'); +console.log(); +dump(b, 'DestoGod, built from scratch'); diff --git a/test/tp5roundtrip.mjs b/test/tp5roundtrip.mjs new file mode 100644 index 0000000..4a1498d --- /dev/null +++ b/test/tp5roundtrip.mjs @@ -0,0 +1,20 @@ +import { readFileSync } from 'node:fs'; +import { parseTp5, buildTp5, toSimple } from '../src/codec/tp5.mjs'; + +const orig = readFileSync(new URL('../samples/EXPRESS.tp5', import.meta.url), 'latin1'); +const doc = parseTp5(orig); +const out = buildTp5(doc); +console.log('companies:', doc.companies.map(c => `${c.name}(${c.destinations.length})`).join(', ')); +console.log(out === orig ? '✅ .tp5 round-trip byte-identical' : '❌ .tp5 round-trip differs'); +if (out !== orig) { + const a = orig.split('\r\n'), b = out.split('\r\n'); + console.log(` lines ${a.length} vs ${b.length}`); + for (let i = 0, n = 0; i < Math.max(a.length, b.length) && n < 6; i++) + if (a[i] !== b[i]) { console.log(` L${i+1}\n orig: ${String(a[i]).slice(0,110)}\n new : ${String(b[i]).slice(0,110)}`); n++; } +} +const s = toSimple(doc); +console.log(`\nsimple view: company=${s.company} screen=${s.screen.width}x${s.screen.height}`); +for (const d of s.destinations.slice(0, 5)) + console.log(` ${d.name.padEnd(18)} -> ${d.frames.map(f => `"${f.text}" [${f.useTrueType ? f.ttfName + ' ' + f.ptSize : f.font}] fx=${f.effect}`).join(' + ')}`); +const ch = s.destinations.find(d => d.frames.length > 1); +if (ch) console.log(` ${ch.name.padEnd(18)} -> ${ch.frames.map(f => `"${f.text}"`).join(' + ')} (${ch.frames.length} frames)`); diff --git a/tools/build-fonts.mjs b/tools/build-fonts.mjs new file mode 100644 index 0000000..9005b42 --- /dev/null +++ b/tools/build-fonts.mjs @@ -0,0 +1,28 @@ +/** Convert the shipped .font files into a JS module the app can embed. */ +import { readFileSync, writeFileSync, readdirSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname, join } from 'node:path'; +import { parseFont, fontToJSON } from '../src/codec/bitfont.mjs'; + +const here = dirname(fileURLToPath(import.meta.url)); +const srcDir = join(here, '../../yutongapp'); +const outFile = join(here, '../src/fonts.js'); + +const fonts = {}; +for (const f of readdirSync(srcDir).filter((f) => f.endsWith('.font')).sort()) { + const parsed = parseFont(readFileSync(join(srcDir, f), 'latin1')); + fonts[parsed.name] = fontToJSON(parsed); + const codes = [...parsed.glyphs.keys()]; + console.log( + `${parsed.name.padEnd(9)} cell ${String(parsed.cellWidth).padStart(2)}x${parsed.height}` + + ` ${String(parsed.glyphs.size).padStart(3)} glyphs codes ${Math.min(...codes)}..${Math.max(...codes)}` + ); +} + +writeFileSync(outFile, + '// Generated by tools/build-fonts.mjs from the .font files shipped with TP5.\n' + + '// Each glyph is [advance, ...rows]; rows are MSB-first, cellWidth bits wide.\n' + + 'export const FONTS = ' + JSON.stringify(fonts) + ';\n'); + +const kb = (readFileSync(outFile).length / 1024).toFixed(0); +console.log(`\nwrote ${outFile} (${kb} KB, ${Object.keys(fonts).length} fonts)`); diff --git a/tools/bundle.mjs b/tools/bundle.mjs new file mode 100644 index 0000000..6cd71ec --- /dev/null +++ b/tools/bundle.mjs @@ -0,0 +1,59 @@ +/** + * Inline every module into one self-contained HTML file, so the whole tool is a + * single document that opens straight from the desktop with no install, no + * server and no internet. + */ +import { readFileSync, writeFileSync, mkdirSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname, join } from 'node:path'; + +const here = dirname(fileURLToPath(import.meta.url)); +const root = join(here, '..'); + +// Dependency order, leaves first. +const MODULES = [ + 'src/fonts.js', + 'src/codec/bitfont.mjs', + 'src/codec/td5.mjs', + 'src/codec/tp5.mjs', + 'src/codec/sheet.mjs', + 'app/main.mjs', +]; + +function strip(src, name) { + const out = src + // drop the import lines; every symbol ends up in one shared scope + .replace(/^\s*import\s+[^;]*?from\s*['"][^'"]+['"];?\s*$/gm, '') + // `export const X` -> `const X`, `export function f` -> `function f` + .replace(/^export\s+(const|let|var|function|class|async)\b/gm, '$1') + // bare re-export statements, if any ever appear + .replace(/^export\s*\{[^}]*\};?\s*$/gm, ''); + if (/^\s*(import|export)\b/m.test(out)) { + throw new Error(`${name}: an import/export slipped through the bundler`); + } + return `\n// ===== ${name} =====\n${out.trim()}\n`; +} + +const code = MODULES.map((m) => strip(readFileSync(join(root, m), 'utf8'), m)).join('\n'); + +const html = readFileSync(join(root, 'app/index.html'), 'utf8'); +if (!html.includes('')) { + throw new Error('index.html no longer has the expected script tag'); +} + +const banner = ` +`; + +const bundled = banner + html.replace( + '', + `` +); + +mkdirSync(join(root, 'dist'), { recursive: true }); +const outPath = join(root, 'dist/DestoGod.html'); +writeFileSync(outPath, bundled); +console.log(`wrote dist/DestoGod.html (${(bundled.length / 1024).toFixed(0)} KB, ${MODULES.length} modules inlined)`);