JSFiddle - React, Tailwind, and code Playground

HTML

<p>
<input type="file" onchange="onChange(event);"><br><br>
<a id="anchor" download="will.chr">will.chr</a>
</p>
<canvas id="canvas"></canvas><br>
<textarea id="textarea" cols="145" rows="25"></textarea>

CSS

canvas {
	border: 1px solid green;
}

JavaScript

"use strict";

let ctx;
let chr_rom;

onload = function() {
	ctx = canvas.getContext("2d");
};

function onChange(e) {
	let file = e.target.files[0];

	let r = new FileReader();
	r.onload = function(e) {
		let img = new Image();
		img.onload = function() {
			canvas.width = img.naturalWidth;
			canvas.height = img.naturalHeight;
			ctx.drawImage(img, 0, 0);
			picker();
		};
		img.src = e.target.result;
	};
	r.readAsDataURL(file);
}

function picker() {
	const code = {	// 0xAABBGGRR
		0xff000000:1, 0xffff0000:4, 0xff00ff00:5, 0xffffff00:6,
		0xff0000ff:2, 0xffff00ff:7, 0xff00ffff:3, 0xffffffff:0
	};
	const sox = 344;	// screen offset
	const soy = 120;
	const cox = 4;		// character offset
	const coy = 6;
	const cw = 25;		// character
	const ch = 18;

	let bgid = 0;	// BG pattern id
	let spid = 1;	// sprite pattern id ※0は飛ばす
	let hash = [];
	let sprite = "sprite_data:\n";

	// ネームテーブル
	let name_tbl = new Uint8Array(960);
	name_tbl.fill(0);

	// BG+スプライトパターン
	chr_rom = new Uint8Array(8192);
	chr_rom.fill(0);

	for (let cy = 0; cy < ch; cy++) {
		for (let cx = 0; cx < cw; cx++) {
			let imgData = ctx.getImageData(sox + cx * 8, soy + cy * 8, 8, 8);
			let pxl = new Uint32Array(imgData.data.buffer);

			// タイル文字列
			let tile = "";
			for (let i = 0; i < 64; i++) {
				tile += code[pxl[i]];
			}

			// BGパターンとネームテーブル
			{
				let t = tile.replace(/[4567]/g, "0");
				if (! hash.hasOwnProperty(t)) {
					setchr(bgid, t);
					hash[t] = bgid;
					bgid++;
				}
				name_tbl[(coy + cy) * 32 + (cox + cx)] = hash[t];
			}

			// スプライトパターンとスプライトデータ
			if (/[4567]/.test(tile)) {
				let x = (cox + cx) * 8;
				let y = (coy + cy) * 8 - 1;
				let c = (/7/.test(tile)) ? 1 : 0;
				sprite += `\tdb\t${y}, ${spid}, ${c}, ${x}\n`;
				let p = tile;
				p = p.replace(/[0123]/g, "0");
				p = p.replace(/4/g, "1");
				p = p.replace(/5/g, "2");
				p = p.replace(/[67]/g, "3");
				setchr(256 + spid, p);
				spid++;
			}
		}
	}

	// 出力範囲
	ctx.strokeStyle =...