12/**3 * Fetches/verifies the Urizen 1-bit tileset used by the web map renderer.4 *5 * - Target: web/public/urizen/urizen_onebit_tileset__v2d0.png (2679x651).6 * - If the file is present and its SHA-256 matches the pin, does nothing.7 * - Otherwise downloads it from the community mirror and verifies the hash.8 * - Always (re)writes the CC0 license note next to the sheet.9 *10 * Sheet geometry (consumed by web/src/render/urizen.ts):11 * 12x12 tiles on a 13px stride (1px margin, 1px spacing);12 * tile (col,row) starts at pixel (1 + 13*col, 1 + 13*row); 206 cols x 50 rows.13 */14import { createHash } from 'node:crypto';15import { mkdir, readFile, writeFile } from 'node:fs/promises';16import { dirname, join } from 'node:path';17import { fileURLToPath } from 'node:url';1819const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');20const DEST_DIR = join(ROOT, 'web', 'public', 'urizen');21const DEST = join(DEST_DIR, 'urizen_onebit_tileset__v2d0.png');22const URL_PNG =23 'https://raw.githubusercontent.com/Coding-Lambda/urizen-tileset-info/main/public/urizen_onebit_tileset__v2d0.png';2425/** SHA-256 of the exact sheet revision the manifest coordinates were picked against. */26const SHA256_PIN = 'f5d927e5e9877a3c088bc488aa64d50f85ce09fd73ded3c1fd25ad715b3ac617';2728const LICENSE_NOTE = `Urizen 1Bit Tileset v2.0 (urizen_onebit_tileset__v2d0.png)29by vurmux — https://vurmux.itch.io/urizen-onebit-tileset3031License: CC0 1.0 Universal (public domain dedication).32The tileset may be copied, modified, and used for any purpose, including33commercially, without attribution (attribution kept here as a courtesy).3435Downloaded via the community tileset-info mirror:36${URL_PNG}3738SHA-256 (pinned by scripts/import-urizen.mjs):39${SHA256_PIN}40`;4142function sha256(buf) {43 return createHash('sha256').update(buf).digest('hex');44}4546async function currentHash() {47 try {48 return sha256(await readFile(DEST));49 } catch {50 return null;51 }52}5354async function main() {55 await mkdir(DEST_DIR, { recursive: true });5657 let hash = await currentHash();58 if (hash === SHA256_PIN) {59 console.log(`ok: ${DEST} already present, sha256 verified (${hash.slice(0, 12)}…)`);60 } else {61 if (hash !== null) {62 console.warn(`sha256 mismatch for existing file (${hash.slice(0, 12)}…), re-downloading`);63 }64 console.log(`downloading ${URL_PNG}`);65 const res = await fetch(URL_PNG);66 if (!res.ok) throw new Error(`download failed: HTTP ${res.status}`);67 const buf = Buffer.from(await res.arrayBuffer());68 hash = sha256(buf);69 if (hash !== SHA256_PIN) {70 throw new Error(71 `downloaded sheet hash ${hash} does not match pin ${SHA256_PIN}; ` +72 'the upstream file changed — re-verify manifest coordinates before updating the pin',73 );74 }75 await writeFile(DEST, buf);76 console.log(`wrote ${DEST} (${buf.length} bytes, sha256 verified)`);77 }7879 await writeFile(join(DEST_DIR, 'LICENSE.txt'), LICENSE_NOTE);80 console.log(`wrote ${join(DEST_DIR, 'LICENSE.txt')}`);81}8283main().catch((err) => {84 console.error(err.message ?? err);85 process.exit(1);86});