{"id":12783,"date":"2026-09-24T05:47:01","date_gmt":"2026-09-23T20:47:01","guid":{"rendered":"https:\/\/www.webcyou.com\/?p=12783"},"modified":"2026-09-24T06:04:46","modified_gmt":"2026-09-23T21:04:46","slug":"%e3%80%90r-chr%e3%80%91rust-egui-%e3%81%a7%e3%83%95%e3%82%a1%e3%83%9f%e3%82%b3%e3%83%b3%e7%94%a8%e3%82%b9%e3%83%97%e3%83%a9%e3%82%a4%e3%83%88%e3%82%a8%e3%83%87%e3%82%a3%e3%82%bf%e3%82%92%e4%bd%9c-2","status":"publish","type":"post","link":"https:\/\/www.webcyou.com\/?p=12783","title":{"rendered":"\u3010R-CHR\u3011Rust + egui \u3067\u30d5\u30a1\u30df\u30b3\u30f3\u7528\u30b9\u30d7\u30e9\u30a4\u30c8\u30a8\u30c7\u30a3\u30bf\u3092\u4f5c\u3063\u305f\u8a71 \u305d\u306e2 CHR \u306e 2BPP \u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3068 iNES \u30d8\u30c3\u30c0\u3001\u30d1\u30ec\u30c3\u30c8"},"content":{"rendered":"\n<p>\u3069\u3082\u3067\u3059\u3002<\/p>\n<p>\u524d\u56de\u300cR-CHR \u306e\u6982\u8981\u3068\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3\u300d\u3068\u306e\u3053\u3068\u3067\u8a18\u4e8b\u3092\u66f8\u304b\u305b\u3066\u3044\u305f\u3060\u304d\u307e\u3057\u305f\u304c\u3001\u4eca\u56de\u306f\u305d\u306e\u7d9a\u304d\u3067\u3001\u3044\u3088\u3044\u3088\u4e2d\u8eab\u306e\u30c7\u30fc\u30bf\u306e\u8a71\u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n<p>\u30d5\u30a1\u30df\u30b3\u30f3\u306e\u7d75\u3063\u3066\u3001\u305d\u3082\u305d\u3082\u3069\u3046\u3044\u3046\u30d0\u30a4\u30c8\u5217\u3067 ROM \u306b\u5165\u3063\u3066\u3044\u308b\u306e\u304b\u3002\u3053\u308c\u304c\u5206\u304b\u3063\u3066\u3044\u306a\u3044\u3068\u30a8\u30c7\u30a3\u30bf\u306f\u4f5c\u308c\u306a\u3044\u308f\u3051\u3067\u3057\u3066\u3002\u3068\u3044\u3046\u3053\u3068\u3067\u3001\u4eca\u56de\u306f R-CHR \u306e <code>io<\/code> \/ <code>model<\/code> \u5c64\u3001\u3064\u307e\u308a <strong>CHR \u306e 2BPP \u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3001iNES \u30d8\u30c3\u30c0\u306e\u30d1\u30fc\u30b9\u3001\u305d\u3057\u3066\u30d1\u30ec\u30c3\u30c8<\/strong>\u306e\u5b9f\u88c5\u3092\u307e\u3068\u3081\u305f\u5f62\u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n<p>egui \u306e\u8a71\u306f\u4e00\u5207\u51fa\u3066\u304d\u307e\u305b\u3093\u3002\u7d14\u7c8b\u306b\u30d0\u30a4\u30c8\u3068\u6226\u3046\u56de\u3067\u3059\u3002<\/p>\n<h4>\u9023\u8f09\u306e\u76ee\u6b21<\/h4>\n<ol>\n<li>\u6982\u8981\u3068\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3<\/li>\n<li><strong>NES \u306e CHR \u30d5\u30a9\u30fc\u30de\u30c3\u30c8\uff082BPP\uff09\u3068 iNES \u30d8\u30c3\u30c0\u3001\u30d1\u30ec\u30c3\u30c8\uff08\u3053\u306e\u8a18\u4e8b\uff09<\/strong><\/li>\n<li>egui \u3067\u7d44\u3080 3 \u30d1\u30cd\u30eb UI \u3068\u30d0\u30f3\u30af\u30d3\u30e5\u30fc<\/li>\n<li>\u30c9\u30c3\u30c8\u30a8\u30c7\u30a3\u30bf\u306e\u57fa\u790e\u3068\u30a2\u30f3\u30c9\u30a5\u8a2d\u8a08<\/li>\n<li>\u56f3\u5f62\u30c4\u30fc\u30eb\uff08Bresenham\u30fb\u6955\u5186\u30fb\u5857\u308a\u3064\u3076\u3057\u30fb\u30b9\u30bf\u30f3\u30d7\uff09<\/li>\n<li>PNG \u30a4\u30f3\u30dd\u30fc\u30c8\u3068 3 \u3064\u306e\u30de\u30c3\u30d4\u30f3\u30b0\u6226\u7565<\/li>\n<li>macOS \u30cd\u30a4\u30c6\u30a3\u30d6\u30e1\u30cb\u30e5\u30fc\u30fb\u591a\u8a00\u8a9e\u5bfe\u5fdc\u30fbGitHub Actions \u3067\u306e\u30ea\u30ea\u30fc\u30b9<\/li>\n<\/ol>\n<h4>\u30d5\u30a1\u30df\u30b3\u30f3\u306e\u30bf\u30a4\u30eb\u306f\u300c8\u00d78 \u30c9\u30c3\u30c8\u30014 \u8272\u300116 \u30d0\u30a4\u30c8\u300d<\/h4>\n<p>\u30d5\u30a1\u30df\u30b3\u30f3\u306e\u30b0\u30e9\u30d5\u30a3\u30c3\u30af\u306f <strong>8\u00d78 \u30c9\u30c3\u30c8\u306e\u30bf\u30a4\u30eb<\/strong>\u304c\u6700\u5c0f\u5358\u4f4d\u3067\u3059\u3002\u5404\u30c9\u30c3\u30c8\u306f 0\u301c3 \u306e <strong>4 \u8272<\/strong>\u3057\u304b\u6301\u3066\u305a\u30011 \u30c9\u30c3\u30c8\u3042\u305f\u308a 2 \u30d3\u30c3\u30c8\u3002\u306a\u306e\u3067 8\u00d78 = 64 \u30c9\u30c3\u30c8 \u00d7 2 \u30d3\u30c3\u30c8 = 128 \u30d3\u30c3\u30c8 = <strong>16 \u30d0\u30a4\u30c8<\/strong>\u3067 1 \u30bf\u30a4\u30eb\u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n<p>\u3053\u306e\u300c1 \u30c9\u30c3\u30c8 2 \u30d3\u30c3\u30c8\u300d\u3092 <strong>2BPP\uff082 bits per pixel\uff09<\/strong> \u3068\u547c\u3073\u307e\u3059\u3002<\/p>\n<p>\u305f\u3060\u3057\u3001\u305d\u306e 2 \u30d3\u30c3\u30c8\u304c\u7d20\u76f4\u306b\u4e26\u3093\u3067\u3044\u308b\u308f\u3051\u3067\u306f\u306a\u3044\u306e\u304c\u30dd\u30a4\u30f3\u30c8\u3067\u3001<strong>\u30d3\u30c3\u30c8\u30d7\u30ec\u30fc\u30f3<\/strong>\u3068\u3044\u3046\u5f62\u5f0f\u306b\u306a\u3063\u3066\u3044\u307e\u3059\u3002<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">1 \u30bf\u30a4\u30eb = 16 \u30d0\u30a4\u30c8\n  \u30d0\u30a4\u30c8 0\u301c7  : \u30d7\u30ec\u30fc\u30f3 0\uff08\u5404\u30c9\u30c3\u30c8\u306e\u4e0b\u4f4d\u30d3\u30c3\u30c8\uff09  1 \u30d0\u30a4\u30c8 = 1 \u884c\n  \u30d0\u30a4\u30c8 8\u301c15 : \u30d7\u30ec\u30fc\u30f3 1\uff08\u5404\u30c9\u30c3\u30c8\u306e\u4e0a\u4f4d\u30d3\u30c3\u30c8\uff09  1 \u30d0\u30a4\u30c8 = 1 \u884c\n  \u5404\u884c\u306e\u5de6\u7aef\u30c9\u30c3\u30c8\u304c\u30d3\u30c3\u30c8 7\u3001\u53f3\u7aef\u304c\u30d3\u30c3\u30c8 0<\/pre>\n<p>\u3064\u307e\u308a\u3001\u3042\u308b\u884c\u306e\u30c9\u30c3\u30c8\u306e\u8272\u3092\u77e5\u308a\u305f\u3051\u308c\u3070\u3001\u30d7\u30ec\u30fc\u30f3 0 \u306e\u8a72\u5f53\u30d0\u30a4\u30c8\u3068\u30d7\u30ec\u30fc\u30f3 1 \u306e\u8a72\u5f53\u30d0\u30a4\u30c8\u304b\u3089\u540c\u3058\u30d3\u30c3\u30c8\u4f4d\u7f6e\u3092\u53d6\u308a\u51fa\u3057\u3066\u3001<code>(\u4e0a\u4f4d &lt;&lt; 1) | \u4e0b\u4f4d<\/code> \u3067\u5408\u6210\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002<\/p>\n<p>\u5177\u4f53\u4f8b\u3092\u51fa\u3057\u305f\u307b\u3046\u304c\u65e9\u3044\u3067\u3059\u306d\u3002\u3042\u308b\u884c\u306e\u30d7\u30ec\u30fc\u30f3 0 \u304c <code>0b01100110<\/code>\u3001\u30d7\u30ec\u30fc\u30f3 1 \u304c <code>0b00111100<\/code> \u3060\u3063\u305f\u3068\u3057\u307e\u3059\u3002<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">\u5217:        0  1  2  3  4  5  6  7\n\u30d7\u30ec\u30fc\u30f30: 0  1  1  0  0  1  1  0   (0x66)\n\u30d7\u30ec\u30fc\u30f31: 0  0  1  1  1  1  0  0   (0x3C)\n------------------------------------\n\u8272 idx:    0  1  3  2  2  3  1  0<\/pre>\n<p>\u5de6\u304b\u3089\u300c0, 1, 3, 2, 2, 3, 1, 0\u300d\u3068\u3044\u3046\u8272\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306e\u884c\u306b\u306a\u308a\u307e\u3059\u3002\u3053\u308c\u304c 8 \u884c\u5206\u3042\u3063\u3066\u30011 \u30bf\u30a4\u30eb\u3067\u3059\u3002<\/p>\n<p>\u306a\u304a\u3001\u3053\u306e 0\u301c3 \u3068\u3044\u3046\u5024\u306f\u3042\u304f\u307e\u3067\u300c<strong>\u30d1\u30ec\u30c3\u30c8\u5185\u306e\u4f55\u756a\u76ee\u304b<\/strong>\u300d\u3067\u3042\u3063\u3066\u3001RGB \u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u5b9f\u969b\u306b\u4f55\u8272\u3067\u8868\u793a\u3055\u308c\u308b\u304b\u306f\u30d1\u30ec\u30c3\u30c8\u304c\u6c7a\u3081\u307e\u3059\u3002\u3053\u3053\u306f\u5f8c\u534a\u3067\u3002<\/p>\n<h4>\u30c7\u30b3\u30fc\u30c9\uff1a16 \u30d0\u30a4\u30c8 \u2192 8\u00d78 \u306e\u8272\u30a4\u30f3\u30c7\u30c3\u30af\u30b9<\/h4>\n<p>\u4e0a\u306e\u8a71\u3092\u305d\u306e\u307e\u307e\u30b3\u30fc\u30c9\u306b\u3057\u305f\u306e\u304c <code>src\/io\/chr.rs<\/code> \u306e <code>decode_tile()<\/code> \u3067\u3059\u3002<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">\/\/\/ 2BPP NES \u5f62\u5f0f\u3067 16\u30d0\u30a4\u30c8\u3092 8\u00d78 \u30d4\u30af\u30bb\u30eb\uff08\u30a4\u30f3\u30c7\u30c3\u30af\u30b9 0\u301c3\uff09\u306b\u30c7\u30b3\u30fc\u30c9\npub fn decode_tile(data: &amp;[u8]) -&gt; [[u8; 8]; 8] {\n    let mut pixels = [[0u8; 8]; 8];\n    for row in 0..8 {\n        let plane0 = data[row];\n        let plane1 = data[row + 8];\n        for col in 0..8 {\n            let bit = 7 - col;\n            let lo = (plane0 &gt;&gt; bit) &amp; 1;\n            let hi = (plane1 &gt;&gt; bit) &amp; 1;\n            pixels[row][col] = (hi &lt;&lt; 1) | lo;\n        }\n    }\n    pixels\n}<\/pre>\n<p><code>data[row]<\/code> \u304c\u30d7\u30ec\u30fc\u30f3 0\u3001<code>data[row + 8]<\/code> \u304c\u30d7\u30ec\u30fc\u30f3 1\u3002\u5de6\u7aef\u304c\u30d3\u30c3\u30c8 7 \u306a\u306e\u3067 <code>bit = 7 - col<\/code> \u3067\u30b7\u30d5\u30c8\u91cf\u3092\u6c42\u3081\u3066\u3044\u307e\u3059\u3002\u3053\u308c\u3060\u3051\u3067\u3059\u3002<\/p>\n<p>R-CHR \u306e\u30c9\u30c3\u30c8\u30a8\u30c7\u30a3\u30bf\u306f 1 \u30bf\u30a4\u30eb\u3060\u3051\u3067\u306a\u304f\u3001\u30d5\u30a9\u30fc\u30ab\u30b9\u30b5\u30a4\u30ba\u306b\u5fdc\u3058\u3066 2\u00d72 \u3084 4\u00d74 \u30bf\u30a4\u30eb\u3068\u3044\u3063\u305f <strong>N\u00d7N \u306e\u30d6\u30ed\u30c3\u30af<\/strong>\u3092\u307e\u3068\u3081\u3066\u7de8\u96c6\u3057\u307e\u3059\u3002\u305d\u306e\u305f\u3081\u306e <code>decode_block()<\/code> \u3082\u7528\u610f\u3057\u3066\u3042\u308a\u307e\u3059\u3002<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">pub fn decode_block(\n    chr_data: &amp;[u8],\n    top_left_tile: usize,\n    tiles_per_row: usize,\n    n: usize,\n) -&gt; Vec&lt;vec&gt; {\n    let block_px = n * 8;\n    let mut pixels = vec![vec![0u8; block_px]; block_px];\n\n    let top_row = top_left_tile \/ tiles_per_row;\n    let top_col = top_left_tile % tiles_per_row;\n\n    for by in 0..n {\n        for bx in 0..n {\n            let tile_idx = (top_row + by) * tiles_per_row + (top_col + bx);\n            let tile_offset = tile_idx * 16;\n            if tile_offset + 16 &gt; chr_data.len() {\n                continue; \/\/ \u7bc4\u56f2\u5916\u306f\u30b9\u30ad\u30c3\u30d7\uff08\u9ed2\u306e\u307e\u307e\uff09\n            }\n            let tile = decode_tile(&amp;chr_data[tile_offset..tile_offset + 16]);\n            for py in 0..8 {\n                for px in 0..8 {\n                    pixels[by * 8 + py][bx * 8 + px] = tile[py][px];\n                }\n            }\n        }\n    }\n    pixels\n}<\/pre>\n<p>R-CHR \u3067\u306f\u30d0\u30f3\u30af\u30d3\u30e5\u30fc\u3092 <strong>1 \u884c 16 \u30bf\u30a4\u30eb<\/strong>\u3067\u4e26\u3079\u3066\u3044\u308b\u306e\u3067\u3001\u30bf\u30a4\u30eb\u306e\u30b0\u30ed\u30fc\u30d0\u30eb\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u304b\u3089\u884c\u30fb\u5217\u306f <code>\/ 16<\/code> \u3068 <code>% 16<\/code> \u3067\u6c42\u307e\u308a\u307e\u3059\u30021 \u884c = 16 \u30bf\u30a4\u30eb \u00d7 16 \u30d0\u30a4\u30c8 = <strong>0x100 \u30d0\u30a4\u30c8<\/strong>\u3002\u3053\u306e\u300c1 \u884c = 0x100\u300d\u306f\u30a2\u30c9\u30ec\u30b9\u8868\u793a\u3084\u30b9\u30af\u30ed\u30fc\u30eb\u4f4d\u7f6e\u306e\u8a08\u7b97\u3067\u4f55\u5ea6\u3082\u51fa\u3066\u304f\u308b\u6570\u5b57\u3067\u3059\u3002<\/p>\n<h4>\u30a8\u30f3\u30b3\u30fc\u30c9\uff1a1 \u30c9\u30c3\u30c8\u3060\u3051\u66f8\u304d\u63db\u3048\u308b<\/h4>\n<p>\u30c9\u30c3\u30c8\u30a8\u30c7\u30a3\u30bf\u3067 1 \u30c9\u30c3\u30c8\u5857\u308b\u3068\u304d\u306f\u3001\u30bf\u30a4\u30eb 16 \u30d0\u30a4\u30c8\u5168\u90e8\u3092\u4f5c\u308a\u76f4\u3059\u5fc5\u8981\u306f\u306a\u304f\u3001\u8a72\u5f53\u3059\u308b 2 \u30d0\u30a4\u30c8\uff08\u30d7\u30ec\u30fc\u30f3 0 \u3068\u30d7\u30ec\u30fc\u30f3 1 \u306e\u540c\u3058\u884c\uff09\u306e\u540c\u3058\u30d3\u30c3\u30c8\u3060\u3051\u3092\u66f8\u304d\u63db\u3048\u308c\u3070 OK \u3067\u3059\u3002<\/p>\n<p>\u00a0<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">\/\/\/ \u30bf\u30a4\u30eb\u30c7\u30fc\u30bf\u306e\u6307\u5b9a\u30c9\u30c3\u30c8 (px, py) \u306b\u30ab\u30e9\u30fc\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3092\u66f8\u304d\u8fbc\u3080\uff082BPP NES \u5f62\u5f0f\uff09\npub fn encode_dot(data: &amp;mut [u8], px: usize, py: usize, color_idx: u8) {\n    let bit = 7 - px;\n    data[py]     = (data[py]     &amp; !(1 &lt;&lt; bit)) | ((color_idx &amp; 1)        &lt;&lt; bit);\n    data[py + 8] = (data[py + 8] &amp; !(1 &lt;&lt; bit)) | (((color_idx &gt;&gt; 1) &amp; 1) &lt;&lt; bit);\n}<\/pre>\n<p><code>&amp; !(1 &lt;&lt; bit)<\/code> \u3067\u8a72\u5f53\u30d3\u30c3\u30c8\u3092\u3044\u3063\u305f\u3093 0 \u306b\u30af\u30ea\u30a2\u3057\u3066\u304b\u3089\u3001\u8272\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306e\u4e0b\u4f4d\u30d3\u30c3\u30c8 \/ \u4e0a\u4f4d\u30d3\u30c3\u30c8\u3092\u305d\u308c\u305e\u308c <code>&lt;&lt; bit<\/code> \u3057\u3066 OR \u3059\u308b\u3001\u3068\u3044\u3046\u5178\u578b\u7684\u306a read-modify-write \u3067\u3059\u306d\u3002<\/p>\n<p>\u00a0<\/p>\n<p>\u3053\u306e <code>encode_dot()<\/code> \u304c\u3001\u30da\u30f3\u3082\u76f4\u7dda\u3082\u5857\u308a\u3064\u3076\u3057\u3082\u30b9\u30bf\u30f3\u30d7\u3082 PNG \u30a4\u30f3\u30dd\u30fc\u30c8\u3082\u3001<strong>\u5168\u3066\u306e\u66f8\u304d\u8fbc\u307f\u306e\u6700\u7d42\u51fa\u53e3<\/strong>\u306b\u306a\u3063\u3066\u3044\u307e\u3059\u3002\u3069\u306e\u30c4\u30fc\u30eb\u3082\u6700\u7d42\u7684\u306b\u306f\u300c(\u30bf\u30a4\u30eb\u30aa\u30d5\u30bb\u30c3\u30c8, px, py, \u8272)\u300d\u306e\u30ea\u30b9\u30c8\u3092\u4f5c\u3063\u3066\u3001\u3053\u306e\u95a2\u6570\u3092\u547c\u3076\u3060\u3051\u3002\u3053\u3053\u3092 1 \u7b87\u6240\u306b\u7d5e\u3063\u3066\u304a\u3044\u305f\u304a\u304b\u3052\u3067\u3001\u3042\u3068\u304b\u3089\u30c4\u30fc\u30eb\u3092\u5897\u3084\u3059\u306e\u304c\u304b\u306a\u308a\u697d\u3067\u3057\u305f\u3002<\/p>\n<h4>iNES \u30d8\u30c3\u30c0\u306e\u30d1\u30fc\u30b9<\/h4>\n<p>\u3055\u3066\u3001CHR \u30c7\u30fc\u30bf\u5358\u4f53\uff08<code>.bin<\/code>\uff09\u306a\u3089\u30d5\u30a1\u30a4\u30eb\u306e\u5148\u982d\u304b\u3089 16 \u30d0\u30a4\u30c8\u305a\u3064\u304c\u305d\u306e\u307e\u307e\u30bf\u30a4\u30eb\u306a\u306e\u3067\u3059\u304c\u3001<code>.nes<\/code> \u30d5\u30a1\u30a4\u30eb\u306b\u306f\u30d8\u30c3\u30c0\u3068 PRG-ROM\uff08\u30d7\u30ed\u30b0\u30e9\u30e0\uff09\u304c\u524d\u306b\u4ed8\u3044\u3066\u3044\u307e\u3059\u3002<\/p>\n<p>\u4e00\u822c\u7684\u306a <code>.nes<\/code> \u30d5\u30a1\u30a4\u30eb\u306f <strong>iNES \u5f62\u5f0f<\/strong>\u3067\u3001\u69cb\u9020\u306f\u3053\u3046\u306a\u3063\u3066\u3044\u307e\u3059\u3002<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\">[0x00-0x0F] \u30d8\u30c3\u30c0 16 \u30d0\u30a4\u30c8\n  [0x00-0x03] \u30de\u30b8\u30c3\u30af \"NES\\x1A\"\n  [0x04]      PRG-ROM \u30d0\u30f3\u30af\u6570\uff0816KB \u5358\u4f4d\uff09\n  [0x05]      CHR-ROM \u30d0\u30f3\u30af\u6570\uff088KB \u5358\u4f4d\uff09\u30010 = CHR-RAM\n  [0x06]      \u30d5\u30e9\u30b06\uff08\u30de\u30c3\u30d1\u30fc\u4e0b\u4f4d 4bit \/ \u30df\u30e9\u30fc\u30ea\u30f3\u30b0 \/ \u30d0\u30c3\u30c6\u30ea\u30fc \/ \u30c8\u30ec\u30fc\u30ca\u30fc\uff09\n  [0x07]      \u30d5\u30e9\u30b07\uff08\u30de\u30c3\u30d1\u30fc\u4e0a\u4f4d 4bit\uff09\n[\u30c8\u30ec\u30fc\u30ca\u30fc]  512 \u30d0\u30a4\u30c8\uff08\u30d5\u30e9\u30b06 \u306e bit2 \u304c\u7acb\u3063\u3066\u3044\u308b\u5834\u5408\u306e\u307f\uff09\n[PRG-ROM]   prg_banks \u00d7 16KB\n[CHR-ROM]   chr_banks \u00d7 8KB<\/pre>\n<p>\u30a8\u30c7\u30a3\u30bf\u3068\u3057\u3066\u6b32\u3057\u3044\u306e\u306f CHR-ROM \u306e\u4f4d\u7f6e\u3068\u30b5\u30a4\u30ba\u3060\u3051\u306a\u306e\u3067\u3059\u304c\u3001\u305d\u3053\u306b\u8fbf\u308a\u7740\u304f\u306b\u306f PRG-ROM \u306e\u30b5\u30a4\u30ba\u3068\u30c8\u30ec\u30fc\u30ca\u30fc\u306e\u6709\u7121\u3092\u898b\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059\u3002<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">pub fn parse_nes(data: &amp;[u8]) -&gt; Result&lt;nesrom, parseerror=\"\"&gt; {\n    if data.len() &lt; 16 {\n        return Err(ParseError::TooShort);\n    }\n\n    \/\/ \u30de\u30b8\u30c3\u30af\u30ca\u30f3\u30d0\u30fc: \"NES\" + 0x1A\n    if &amp;data[0..4] != b\"NES\\x1a\" {\n        return Err(ParseError::InvalidMagic);\n    }\n\n    let prg_rom_banks = data[4];\n    let chr_rom_banks = data[5];\n    let flags6 = data[6];\n    let flags7 = data[7];\n\n    let vertical_mirroring = flags6 &amp; 0x01 != 0;\n    let has_battery = flags6 &amp; 0x02 != 0;\n    let has_trainer = flags6 &amp; 0x04 != 0;\n    let mapper_lo = flags6 &gt;&gt; 4;\n    let mapper_hi = flags7 &amp; 0xF0;\n    let mapper = mapper_hi | mapper_lo;\n\n    let header = NesHeader {\n        prg_rom_banks,\n        chr_rom_banks,\n        mapper,\n        vertical_mirroring,\n        has_battery,\n    };\n\n    \/\/ \u30c8\u30ec\u30fc\u30ca\u30fc\uff08512\u30d0\u30a4\u30c8\uff09\u304c\u3042\u308b\u5834\u5408\u306f\u30b9\u30ad\u30c3\u30d7\n    let mut offset = 16;\n    if has_trainer {\n        offset += 512;\n    }\n\n    let prg_size = header.prg_rom_size();\n    let chr_size = header.chr_rom_size();\n\n    if data.len() &lt; offset + prg_size + chr_size {\n        return Err(ParseError::TooShort);\n    }\n\n    let chr_data_offset = offset + prg_size;\n    let prg_rom = data[offset..chr_data_offset].to_vec();\n    let chr_rom = data[chr_data_offset..chr_data_offset + chr_size].to_vec();\n\n    Ok(NesRom { header, prg_rom, chr_rom, chr_data_offset })\n}&lt;\/nesrom,&gt;<\/pre>\n<p>\u30de\u30b8\u30c3\u30af\u30ca\u30f3\u30d0\u30fc\u306e <code>\"NES\\x1A\"<\/code> \u3092\u30c1\u30a7\u30c3\u30af\u3057\u3066\u3001\u30d0\u30f3\u30af\u6570\u304b\u3089\u30b5\u30a4\u30ba\u3092\u8a08\u7b97\u3057\u3001\u30c8\u30ec\u30fc\u30ca\u30fc\u5206\u3092\u30b9\u30ad\u30c3\u30d7\u3057\u3066\u3001CHR-ROM \u3092\u5207\u308a\u51fa\u3059\u3002\u7d20\u76f4\u306a\u5b9f\u88c5\u3067\u3059\u3002<\/p>\n<h5>CHR-RAM \u306e\u30ab\u30fc\u30c8\u30ea\u30c3\u30b8<\/h5>\n<p><code>chr_rom_banks<\/code> \u304c 0 \u306e\u30ab\u30fc\u30c8\u30ea\u30c3\u30b8\u306f\u3001CHR-ROM \u3067\u306f\u306a\u304f <strong>CHR-RAM<\/strong> \u3092\u7a4d\u3093\u3067\u3044\u308b\u30bf\u30a4\u30d7\u3067\u3001\u30b0\u30e9\u30d5\u30a3\u30c3\u30af\u30c7\u30fc\u30bf\u306f PRG-ROM \u306e\u4e2d\u306b\u5727\u7e2e\u3055\u308c\u3066\u3044\u305f\u308a\u30d7\u30ed\u30b0\u30e9\u30e0\u3067\u751f\u6210\u3055\u308c\u305f\u308a\u3057\u307e\u3059\u3002\u3053\u306e\u5834\u5408\u3001<code>chr_rom<\/code> \u306f\u7a7a\u306e\u30d9\u30af\u30bf\u306b\u306a\u308b\u306e\u3067\u3001R-CHR \u3067\u306f\u300c\u3053\u306e\u30d5\u30a1\u30a4\u30eb\u306f CHR-RAM \u3092\u4f7f\u7528\u3057\u3066\u3044\u307e\u3059\u300d\u3068\u3044\u3046\u65e8\u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u3092\u8868\u793a\u3059\u308b\u3060\u3051\u3067\u3001\u7de8\u96c6\u5bfe\u8c61\u306b\u306f\u3057\u3066\u3044\u307e\u305b\u3093\u3002<\/p>\n<h5>\u4fdd\u5b58\u6642\u306e\u305f\u3081\u306b\u5143\u30d5\u30a1\u30a4\u30eb\u306e\u4f4d\u7f6e\u3092\u899a\u3048\u3066\u304a\u304f<\/h5>\n<p><code>NesRom<\/code> \u306b <code>chr_data_offset<\/code> \u3068\u3044\u3046\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u6301\u305f\u305b\u3066\u3044\u308b\u306e\u304c\u3061\u3087\u3063\u3068\u3057\u305f\u30dd\u30a4\u30f3\u30c8\u3067\u3001\u3053\u308c\u306f<strong>\u4fdd\u5b58\u306e\u305f\u3081<\/strong>\u3067\u3059\u3002<\/p>\n<p>R-CHR \u306f <code>.nes<\/code> \u3092\u958b\u304f\u3068\u304d\u3001\u30d1\u30fc\u30b9\u7d50\u679c\u3068\u306f\u5225\u306b<strong>\u5143\u30d5\u30a1\u30a4\u30eb\u306e\u30d0\u30a4\u30c8\u5217\u3092\u307e\u308b\u3054\u3068 <code>raw_file_data<\/code> \u3068\u3057\u3066\u4fdd\u6301<\/strong>\u3057\u3066\u3044\u307e\u3059\u3002\u4fdd\u5b58\u6642\u306f\u3001\u7de8\u96c6\u5f8c\u306e <code>chr_rom<\/code> \u3092\u5143\u30d5\u30a1\u30a4\u30eb\u306e <code>chr_data_offset<\/code> \u306e\u4f4d\u7f6e\u306b\u4e0a\u66f8\u304d\u30b3\u30d4\u30fc\u3057\u3066\u3001\u30d5\u30a1\u30a4\u30eb\u5168\u4f53\u3092\u66f8\u304d\u51fa\u3057\u307e\u3059\u3002<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">RomData::Nes(nes_rom) =&gt; {\n    let raw = self.raw_file_data.as_mut().ok_or(t.err_no_raw)?;\n    let start = nes_rom.chr_data_offset;\n    let end   = start + nes_rom.chr_rom.len();\n    if end &gt; raw.len() {\n        return Err(t.err_filesize.to_string());\n    }\n    raw[start..end].copy_from_slice(&amp;nes_rom.chr_rom);\n    std::fs::write(path, raw as &amp;[u8]).map_err(|e| format!(\"Save failed: {e}\"))?;\n}<\/pre>\n<p>\u30d8\u30c3\u30c0\u3084 PRG-ROM \u3092\u81ea\u524d\u3067\u518d\u69cb\u7bc9\u3057\u306a\u3044\u306e\u3067\u3001iNES 2.0 \u306e\u62e1\u5f35\u30d5\u30a3\u30fc\u30eb\u30c9\u3084\u30c8\u30ec\u30fc\u30ca\u30fc\u304c\u4ed8\u3044\u3066\u3044\u3066\u3082\u3001\u305d\u3053\u306f\u5143\u306e\u307e\u307e\u58ca\u3055\u305a\u4fdd\u5b58\u3067\u304d\u307e\u3059\u3002\u30a8\u30c7\u30a3\u30bf\u304c\u89e6\u308b\u306e\u306f CHR-ROM \u306e\u7bc4\u56f2\u3060\u3051\u3001\u3068\u3044\u3046\u5272\u308a\u5207\u308a\u3067\u3059\u306d\u3002<\/p>\n<h4>RomData\uff1a.nes \u3068 .bin \u3092\u540c\u3058\u3088\u3046\u306b\u6271\u3046<\/h4>\n<p>\u30a8\u30c7\u30a3\u30bf\u304b\u3089\u898b\u308b\u3068\u300c<code>.nes<\/code> \u306e CHR-ROM \u90e8\u5206\u300d\u3082\u300c<code>.bin<\/code> \u30d5\u30a1\u30a4\u30eb\u5168\u4f53\u300d\u3082\u3001\u3069\u3061\u3089\u3082\u300c16 \u30d0\u30a4\u30c8 \u00d7 N \u500b\u306e\u30bf\u30a4\u30eb\u5217\u300d\u3067\u3059\u3002\u306a\u306e\u3067\u3001\u3053\u306e 2 \u3064\u3092 enum \u3067\u30e9\u30c3\u30d7\u3057\u3066\u3001CHR \u30c7\u30fc\u30bf\u3078\u306e\u53c2\u7167\u3060\u3051\u3092\u5171\u901a\u3067\u53d6\u308c\u308b\u3088\u3046\u306b\u3057\u3066\u3044\u307e\u3059\u3002<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">pub enum RomData {\n    \/\/\/ iNES (.nes) \u30d5\u30a1\u30a4\u30eb\n    Nes(NesRom),\n    \/\/\/ \u751f CHR \u30d0\u30a4\u30ca\u30ea (.bin) \u30d5\u30a1\u30a4\u30eb\n    Bin(Vec),\n}\n\nimpl RomData {\n    pub fn chr_data(&amp;self) -&gt; &amp;[u8] {\n        match self {\n            RomData::Nes(rom) =&gt; &amp;rom.chr_rom,\n            RomData::Bin(data) =&gt; data,\n        }\n    }\n\n    pub fn chr_data_mut(&amp;mut self) -&gt; &amp;mut [u8] {\n        match self {\n            RomData::Nes(rom) =&gt; &amp;mut rom.chr_rom,\n            RomData::Bin(data) =&gt; data,\n        }\n    }\n}<\/pre>\n<p>UI \u5074\u306e\u30b3\u30fc\u30c9\u306f\u307b\u307c\u5168\u3066 <code>rom.chr_data()<\/code> \/ <code>rom.chr_data_mut()<\/code> \u7d4c\u7531\u3067\u89e6\u308b\u306e\u3067\u3001\u30d5\u30a1\u30a4\u30eb\u5f62\u5f0f\u306e\u9055\u3044\u3092\u610f\u8b58\u3059\u308b\u306e\u306f\u958b\u304f\u3068\u304d\u3068\u4fdd\u5b58\u3059\u308b\u3068\u304d\u3060\u3051\u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n<p>\u3061\u306a\u307f\u306b <code>.zip<\/code> \u3092\u958b\u3044\u305f\u5834\u5408\u306f\u3001<code>zip<\/code> \u30af\u30ec\u30fc\u30c8\u3067\u30a2\u30fc\u30ab\u30a4\u30d6\u5185\u3092\u8d70\u67fb\u3057\u3066\u6700\u521d\u306b\u898b\u3064\u304b\u3063\u305f <code>.nes<\/code> \u3092\u53d6\u308a\u51fa\u3057\u3001\u3042\u3068\u306f <code>.nes<\/code> \u3068\u540c\u3058\u6d41\u308c\u306b\u4e57\u305b\u3066\u3044\u307e\u3059\u3002\u300cZIP \u304b\u3089\u958b\u3044\u305f\u5834\u5408\u306f\u4e0a\u66f8\u304d\u4fdd\u5b58\u5148\u304c\u7121\u3044\u300d\u306e\u3067\u3001<code>file_path<\/code> \u3092 <code>None<\/code> \u306b\u3057\u3066\u5225\u540d\u4fdd\u5b58\u306b\u8a98\u5c0e\u3059\u308b\u5f62\u3067\u3059\u3002<\/p>\n<p>\u300c\u65b0\u898f\u4f5c\u6210\u300d\u306f 0x4000 \u30d0\u30a4\u30c8\uff0816KB\uff09\u306e\u30bc\u30ed\u57cb\u3081\u30d9\u30af\u30bf\u3092 <code>RomData::Bin<\/code> \u3068\u3057\u3066\u4f5c\u308b\u3060\u3051\u3002\u771f\u3063\u9ed2\u306a CHR \u304c 1024 \u30bf\u30a4\u30eb\u5206\u3067\u304d\u3042\u304c\u308a\u307e\u3059\u3002<\/p>\n<h4>\u30d1\u30ec\u30c3\u30c8\uff1a\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3092\u5b9f\u969b\u306e\u8272\u306b\u3059\u308b<\/h4>\n<p>\u3053\u3053\u307e\u3067\u3067\u300c0\u301c3 \u306e\u8272\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u300d\u307e\u3067\u306f\u53d6\u308a\u51fa\u305b\u307e\u3057\u305f\u3002\u6b21\u306f\u305d\u308c\u3092 RGB \u306b\u3059\u308b\u8a71\u3067\u3059\u3002<\/p>\n<p>\u30d5\u30a1\u30df\u30b3\u30f3\u306e\u30d1\u30ec\u30c3\u30c8\u306f 2 \u6bb5\u69cb\u3048\u306b\u306a\u3063\u3066\u3044\u307e\u3059\u3002<\/p>\n<ol>\n<li><strong>\u30de\u30b9\u30bf\u30fc\u30d1\u30ec\u30c3\u30c8<\/strong>\uff1a\u30cf\u30fc\u30c9\u30a6\u30a7\u30a2\uff08PPU\uff09\u304c\u51fa\u305b\u308b <strong>64 \u8272<\/strong>\u306e\u56fa\u5b9a\u30c6\u30fc\u30d6\u30eb<\/li>\n<li><strong>\u30d1\u30ec\u30c3\u30c8\u30bb\u30c3\u30c8<\/strong>\uff1a\u305d\u306e 64 \u8272\u306e\u4e2d\u304b\u3089 <strong>4 \u8272\u3092\u9078\u3093\u3060\u30bb\u30c3\u30c8<\/strong>\u3002\u30b2\u30fc\u30e0\u5074\u306f\u3053\u308c\u3092 4 \u30bb\u30c3\u30c8\u6301\u3066\u308b\uff08\u80cc\u666f\u7528\u306b 4\u3001\u30b9\u30d7\u30e9\u30a4\u30c8\u7528\u306b 4 \u3067\u3059\u304c\u3001\u30a8\u30c7\u30a3\u30bf\u3067\u306f 4 \u30bb\u30c3\u30c8 \u00d7 4 \u8272\u3068\u3057\u3066\u6271\u3044\u307e\u3059\uff09<\/li>\n<\/ol>\n<p>\u30bf\u30a4\u30eb\u306e\u8272\u30a4\u30f3\u30c7\u30c3\u30af\u30b9 0\u301c3 \u306f\u300c\u73fe\u5728\u306e\u30d1\u30ec\u30c3\u30c8\u30bb\u30c3\u30c8\u306e\u4f55\u756a\u76ee\u304b\u300d\u3092\u6307\u3057\u3001\u305d\u306e\u5024\u304c\u30de\u30b9\u30bf\u30fc\u30d1\u30ec\u30c3\u30c8\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\uff080x00\u301c0x3F\uff09\u306b\u306a\u3063\u3066\u3044\u3066\u3001\u305d\u308c\u304c\u6700\u7d42\u7684\u306a RGB \u3092\u6c7a\u3081\u307e\u3059\u3002<\/p>\n<h5>MasterPalette \u3068 .pal \u30d5\u30a1\u30a4\u30eb<\/h5>\n<p>\u30de\u30b9\u30bf\u30fc\u30d1\u30ec\u30c3\u30c8\u306f <code>[[u8; 3]; 64]<\/code> \u306e RGB \u914d\u5217\u3067\u3059\u3002\u30c7\u30d5\u30a9\u30eb\u30c8\u5024\u306f nesdev.org \u6e96\u62e0\u306e 64 \u8272\u3092\u5b9a\u6570\u3067\u6301\u3063\u3066\u3044\u307e\u3059\u3002<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">\/\/\/ NES \u30cf\u30fc\u30c9\u30a6\u30a7\u30a2\u306e\u6a19\u6e96 64\u8272\u30de\u30b9\u30bf\u30fc\u30d1\u30ec\u30c3\u30c8\uff08RGB\u5404 8bit\uff09\n\/\/\/ \u53c2\u8003: https:\/\/www.nesdev.org\/wiki\/PPU_palettes\npub const NES_PALETTE: [[u8; 3]; 64] = [\n    [84,  84,  84 ], \/\/ 0x00\n    [0,   30,  116], \/\/ 0x01\n    [8,   16,  144], \/\/ 0x02\n    \/\/ ...\uff08\u4e2d\u7565\uff09\n    [0,   0,   0  ], \/\/ 0x3F\n];\n\n#[derive(Clone)]\npub struct MasterPalette {\n    pub colors: [[u8; 3]; 64],\n}\n\nimpl MasterPalette {\n    \/\/\/ 192 \u30d0\u30a4\u30c8\u306e .pal \u30c7\u30fc\u30bf\uff0864\u8272 \u00d7 RGB 3\u30d0\u30a4\u30c8\uff09\u304b\u3089\u30d1\u30fc\u30b9\n    pub fn from_pal_bytes(data: &amp;[u8]) -&gt; Option {\n        if data.len() &lt; 192 {\n            return None;\n        }\n        let mut colors = [[0u8; 3]; 64];\n        for i in 0..64 {\n            colors[i] = [data[i * 3], data[i * 3 + 1], data[i * 3 + 2]];\n        }\n        Some(Self { colors })\n    }\n}<\/pre>\n<p><code>.pal<\/code> \u30d5\u30a1\u30a4\u30eb\u306f <strong>64 \u8272 \u00d7 RGB 3 \u30d0\u30a4\u30c8 = 192 \u30d0\u30a4\u30c8<\/strong>\u306e\u751f\u30d0\u30a4\u30ca\u30ea\u3067\u3001\u3053\u308c\u306f YY-CHR \u3084\u30a8\u30df\u30e5\u30ec\u30fc\u30bf\u754c\u9688\u3067\u5e83\u304f\u4f7f\u308f\u308c\u3066\u3044\u308b\u5f62\u5f0f\u3067\u3059\u3002\u30d5\u30a1\u30df\u30b3\u30f3\u306e\u5b9f\u6a5f\u306f NTSC \u306e\u4fe1\u53f7\u3067\u8272\u3092\u51fa\u3057\u3066\u3044\u308b\u306e\u3067\u300c\u6b63\u3057\u3044 RGB\u300d\u3068\u3044\u3046\u3082\u306e\u304c\u5b58\u5728\u305b\u305a\u3001\u30a8\u30df\u30e5\u30ec\u30fc\u30bf\u3084\u597d\u307f\u306b\u3088\u3063\u3066\u5fae\u5999\u306b\u9055\u3046\u30d1\u30ec\u30c3\u30c8\u304c\u51fa\u56de\u3063\u3066\u3044\u307e\u3059\u3002\u306a\u306e\u3067 R-CHR \u3067\u3082 <code>.pal<\/code> \u3092\u5dee\u3057\u66ff\u3048\u3089\u308c\u308b\u3088\u3046\u306b\u3057\u3066\u3042\u308a\u307e\u3059\u3002<\/p>\n<h5>DatPalette \u3068 .dat \u30d5\u30a1\u30a4\u30eb<\/h5>\n<p>\u30d1\u30ec\u30c3\u30c8\u30bb\u30c3\u30c8\u306e\u307b\u3046\u306f <code>[[u8; 4]; 4]<\/code>\u3001\u3064\u307e\u308a 4 \u30bb\u30c3\u30c8 \u00d7 4 \u8272\u3067\u3001\u5404\u5024\u306f\u30de\u30b9\u30bf\u30fc\u30d1\u30ec\u30c3\u30c8\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u3067\u3059\u3002<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">\/\/\/ DAT \u30d1\u30ec\u30c3\u30c8: 4\u30bb\u30c3\u30c8 \u00d7 4\u8272 \u306e NES \u30d1\u30ec\u30c3\u30c8\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\n#[derive(Clone)]\npub struct DatPalette {\n    \/\/\/ sets[set_index][color_index] = NES \u30d1\u30ec\u30c3\u30c8\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\n    pub sets: [[u8; 4]; 4],\n}\n\nimpl DatPalette {\n    \/\/\/ \u6307\u5b9a\u30bb\u30c3\u30c8\u30fb\u30ab\u30e9\u30fc\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306e RGB \u3092\u8fd4\u3059\n    pub fn color_rgb(&amp;self, set: usize, color_idx: usize, master: &amp;MasterPalette) -&gt; [u8; 3] {\n        let nes_idx = self.sets[set][color_idx] as usize &amp; 0x3F;\n        master.colors[nes_idx]\n    }\n\n    \/\/\/ 16\u30d0\u30a4\u30c8\u4ee5\u4e0a\u306e .dat \u30c7\u30fc\u30bf\u304b\u3089\u30d1\u30fc\u30b9\uff084\u30bb\u30c3\u30c8 \u00d7 4\u8272\uff09\n    pub fn from_dat_bytes(data: &amp;[u8]) -&gt; Option {\n        if data.len() &lt; 16 {\n            return None;\n        }\n        let mut sets = [[0u8; 4]; 4];\n        for s in 0..4 {\n            for c in 0..4 {\n                sets[s][c] = data[s * 4 + c] &amp; 0x3F;\n            }\n        }\n        Some(Self { sets })\n    }\n\n    \/\/\/ \u73fe\u5728\u306e\u30d1\u30ec\u30c3\u30c8\u3092 16\u30d0\u30a4\u30c8\u306e .dat \u5f62\u5f0f\u3067\u8fd4\u3059\n    pub fn to_dat_bytes(&amp;self) -&gt; [u8; 16] {\n        let mut out = [0u8; 16];\n        for s in 0..4 {\n            for c in 0..4 {\n                out[s * 4 + c] = self.sets[s][c];\n            }\n        }\n        out\n    }\n}<\/pre>\n<p><code>.dat<\/code> \u306f YY-CHR \u4e92\u63db\u306e\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3067\u3001<strong>\u5404\u30d0\u30a4\u30c8\u304c NES \u30d1\u30ec\u30c3\u30c8\u30a4\u30f3\u30c7\u30c3\u30af\u30b9<\/strong>\u3001\u5148\u982d 16 \u30d0\u30a4\u30c8\u3092 4 \u30bb\u30c3\u30c8 \u00d7 4 \u8272\u3068\u3057\u3066\u8aad\u307f\u307e\u3059\u3002<code>&amp; 0x3F<\/code> \u3067 0\u301c63 \u306b\u4e38\u3081\u3066\u3044\u308b\u306e\u306f\u3001\u4e0a\u4f4d\u30d3\u30c3\u30c8\u306b\u30b4\u30df\u304c\u5165\u3063\u3066\u3044\u3066\u3082\u843d\u3061\u306a\u3044\u3088\u3046\u306b\u3059\u308b\u305f\u3081\u3067\u3059\u3002<\/p>\n<p>\u8272\u3092\u5f15\u304f\u3068\u304d\u306f <code>color_rgb(set, color_idx, master)<\/code> \u3067\u3001\u300cDAT \u30d1\u30ec\u30c3\u30c8\u306e\u30bb\u30c3\u30c8 \u2192 NES \u30a4\u30f3\u30c7\u30c3\u30af\u30b9 \u2192 \u30de\u30b9\u30bf\u30fc\u30d1\u30ec\u30c3\u30c8\u306e RGB\u300d\u3068 2 \u6bb5\u968e\u3067\u89e3\u6c7a\u3057\u307e\u3059\u3002<\/p>\n<h5>\u30c7\u30d5\u30a9\u30eb\u30c8\u30d1\u30ec\u30c3\u30c8\u306f\u30d0\u30a4\u30ca\u30ea\u306b\u57cb\u3081\u8fbc\u3080<\/h5>\n<p>\u8d77\u52d5\u6642\u306b\u8aad\u307f\u8fbc\u3080\u30d1\u30ec\u30c3\u30c8\u306f <code>include_bytes!<\/code> \u3067\u30d0\u30a4\u30ca\u30ea\u306b\u57cb\u3081\u8fbc\u3093\u3067\u3044\u307e\u3059\u3002<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">const DEFAULT_PAL: &amp;[u8] = include_bytes!(\"..\/..\/assets\/rchr.pal\");\nconst DEFAULT_DAT: &amp;[u8] = include_bytes!(\"..\/..\/assets\/rchr.dat\");\n\/\/\/ NES \u6a19\u6e96 64\u8272\u30d1\u30ec\u30c3\u30c8\uff08\u30ea\u30bb\u30c3\u30c8\u7528\uff09\npub(super) const NES_PAL: &amp;[u8] = include_bytes!(\"..\/..\/assets\/nes.pal\");<\/pre>\n<p>\u914d\u5e03\u30d0\u30a4\u30ca\u30ea\u306e\u6a2a\u306b <code>assets\/<\/code> \u3092\u7f6e\u3044\u3066\u3082\u3089\u3046\u5fc5\u8981\u304c\u7121\u3044\u306e\u3067\u3001\u5358\u4e00\u30d5\u30a1\u30a4\u30eb\u3067\u30dd\u30f3\u3068\u6e21\u305b\u307e\u3059\u3002\u300c\u30de\u30b9\u30bf\u30fc\u30d1\u30ec\u30c3\u30c8\u3092\u30ea\u30bb\u30c3\u30c8\u300d\u30e1\u30cb\u30e5\u30fc\u306f <code>nes.pal<\/code> \u3092\u8aad\u307f\u76f4\u3059\u3060\u3051\u3067\u3059\u3002<\/p>\n<h4>CHR \u5168\u4f53\u3092 1 \u679a\u306e\u753b\u50cf\u306b\u3059\u308b<\/h4>\n<p>\u6700\u5f8c\u306b\u3001\u30d0\u30f3\u30af\u30d3\u30e5\u30fc\u7528\u306e\u30ec\u30f3\u30c0\u30ea\u30f3\u30b0\u95a2\u6570\u3067\u3059\u3002CHR \u30c7\u30fc\u30bf\u5168\u4f53\u3092 <strong>\u5e45 128px\uff0816 \u30bf\u30a4\u30eb \u00d7 8px\uff09\u306e\u7e26\u9577 RGBA \u753b\u50cf<\/strong>\u3068\u3057\u3066\u4e00\u6c17\u306b\u63cf\u304d\u307e\u3059\u3002<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"rust\">pub fn render_full_image(\n    chr_data: &amp;[u8],\n    palette: &amp;DatPalette,\n    palette_set: usize,\n    master: &amp;MasterPalette,\n) -&gt; ColorImage {\n    let total_tiles = chr_data.len() \/ 16;\n    let total_rows = (total_tiles + 15) \/ 16;\n    const W: usize = 128;\n    let h = (total_rows * 8).max(8);\n\n    let mut rgba = vec![0u8; W * h * 4];\n\n    for tile_idx in 0..total_tiles {\n        let tile_offset = tile_idx * 16;\n        let tile = decode_tile(&amp;chr_data[tile_offset..tile_offset + 16]);\n        let tile_col = tile_idx % 16;\n        let tile_row = tile_idx \/ 16;\n\n        for py in 0..8 {\n            for px in 0..8 {\n                let img_x = tile_col * 8 + px;\n                let img_y = tile_row * 8 + py;\n                let color_idx = tile[py][px] as usize;\n                let [r, g, b] = palette.color_rgb(palette_set, color_idx, master);\n                let i = (img_y * W + img_x) * 4;\n                rgba[i]     = r;\n                rgba[i + 1] = g;\n                rgba[i + 2] = b;\n                rgba[i + 3] = 255;\n            }\n        }\n    }\n\n    ColorImage::from_rgba_unmultiplied([W, h], &amp;rgba)\n}<\/pre>\n<p>8KB \u306e CHR-ROM \u306a\u3089 512 \u30bf\u30a4\u30eb\u300132 \u884c\u3001\u9ad8\u3055 256px \u306e\u753b\u50cf\u306b\u306a\u308a\u307e\u3059\u3002\u3053\u308c\u3092 egui \u306e\u30c6\u30af\u30b9\u30c1\u30e3\u3068\u3057\u3066 GPU \u306b\u8f09\u305b\u3066\u3001\u30b9\u30af\u30ed\u30fc\u30eb\u30a8\u30ea\u30a2\u306e\u4e2d\u306b\u8868\u793a\u3059\u308b\u306e\u304c\u6b21\u56de\u306e\u30d0\u30f3\u30af\u30d3\u30e5\u30fc\u306e\u8a71\u3067\u3059\u3002<\/p>\n<p>1 \u753b\u9762\u3076\u3093\u305a\u3064\u8868\u793a\u3092\u5207\u308a\u66ff\u3048\u3066\u898b\u308b\u65b9\u5f0f\u3068\u9055\u3063\u3066\u3001R-CHR \u306f\u3053\u306e\u300c\u5168\u90e8\u3092 1 \u679a\u306b\u3059\u308b\u300d\u65b9\u5f0f\u306e\u304a\u304b\u3052\u3067\u3001CHR-ROM \u306e\u5148\u982d\u304b\u3089\u672b\u5c3e\u307e\u3067<strong>\u9023\u7d9a\u30b9\u30af\u30ed\u30fc\u30eb<\/strong>\u3067\u773a\u3081\u3089\u308c\u307e\u3059\u3002\u3053\u308c\u3001\u5730\u5473\u306b\u6c17\u6301\u3061\u826f\u3044\u3093\u3067\u3059\u3088\u3002<\/p>\n<h4>\u307e\u3068\u3081<\/h4>\n<ul>\n<li>NES \u306e\u30bf\u30a4\u30eb\u306f 8\u00d78 \u30c9\u30c3\u30c8\u30fb4 \u8272\u30fb16 \u30d0\u30a4\u30c8\u306e <strong>2BPP \u30d3\u30c3\u30c8\u30d7\u30ec\u30fc\u30f3\u5f62\u5f0f<\/strong><\/li>\n<li>\u30d7\u30ec\u30fc\u30f3 0\uff08\u4e0b\u4f4d\uff09\u3068\u30d7\u30ec\u30fc\u30f3 1\uff08\u4e0a\u4f4d\uff09\u304b\u3089\u540c\u3058\u30d3\u30c3\u30c8\u4f4d\u7f6e\u3092\u53d6\u308a\u51fa\u3057\u3066\u5408\u6210\u3059\u308b<\/li>\n<li><code>decode_tile()<\/code> \/ <code>encode_dot()<\/code> \u306e 2 \u3064\u304c CHR \u64cd\u4f5c\u306e\u57fa\u672c\u3067\u3001\u5168\u30c4\u30fc\u30eb\u306e\u66f8\u304d\u8fbc\u307f\u306f <code>encode_dot()<\/code> \u306b\u96c6\u7d04<\/li>\n<li>iNES \u30d8\u30c3\u30c0\u306f\u30de\u30b8\u30c3\u30af\u3001PRG \/ CHR \u30d0\u30f3\u30af\u6570\u3001\u30c8\u30ec\u30fc\u30ca\u30fc\u306e\u6709\u7121\u3092\u898b\u3066 CHR-ROM \u306e\u4f4d\u7f6e\u3092\u5272\u308a\u51fa\u3059<\/li>\n<li>\u4fdd\u5b58\u6642\u306f\u5143\u30d5\u30a1\u30a4\u30eb\u306e\u30d0\u30a4\u30c8\u5217\u306b CHR-ROM \u3060\u3051\u3092\u66f8\u304d\u623b\u3059<\/li>\n<li>\u30d1\u30ec\u30c3\u30c8\u306f\u300c\u30de\u30b9\u30bf\u30fc 64 \u8272\u300d\u3068\u300c4 \u30bb\u30c3\u30c8 \u00d7 4 \u8272\u300d\u306e 2 \u6bb5\u69cb\u3048\u3002<code>.pal<\/code>\uff08192 \u30d0\u30a4\u30c8\uff09\u3068 <code>.dat<\/code>\uff0816 \u30d0\u30a4\u30c8\uff09\u306f YY-CHR \u4e92\u63db<\/li>\n<\/ul>\n<p>\u6614\u306e\u30b2\u30fc\u30e0\u3092\u89e3\u6790\u3057\u3066\u3044\u308b\u4eba\u306b\u306f\u5f53\u305f\u308a\u524d\u306e\u5185\u5bb9\u3060\u3068\u306f\u601d\u3046\u306e\u3067\u3059\u304c\u3001\u81ea\u5206\u3067\u66f8\u3044\u3066\u307f\u308b\u3068\u300c\u3042\u3041\u3001\u3060\u304b\u3089\u30d5\u30a1\u30df\u30b3\u30f3\u306e\u30ad\u30e3\u30e9\u306f 4 \u8272\uff08\u5b9f\u8cea 3 \u8272 + \u900f\u660e\uff09\u306a\u306e\u304b\u300d\u3068\u3044\u3046\u306e\u304c\u4f53\u3067\u5206\u304b\u3063\u3066\u304f\u308b\u306e\u304c\u9762\u767d\u3044\u3068\u3053\u308d\u3067\u3059\u3002<\/p>\n<p>\u6b21\u56de\u306f egui \u306e\u8a71\u306b\u5165\u308a\u307e\u3059\u3002\u3053\u306e <code>render_full_image()<\/code> \u3067\u4f5c\u3063\u305f\u753b\u50cf\u3092\u3069\u3046\u3084\u3063\u3066\u8868\u793a\u3057\u3066\u3001\u3069\u3046\u3084\u3063\u3066\u30bf\u30a4\u30eb\u3092\u30af\u30ea\u30c3\u30af\u3067\u9078\u629e\u3067\u304d\u308b\u3088\u3046\u306b\u3057\u3066\u3044\u308b\u306e\u304b\u3001\u3068\u3044\u3046 <strong>3 \u30d1\u30cd\u30eb UI \u3068\u30d0\u30f3\u30af\u30d3\u30e5\u30fc<\/strong>\u306e\u5b9f\u88c5\u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n<p>\u3067\u306f\u3067\u306f\u3041\u3002<\/p>\n<p>\u307e\u305f\u307e\u305f\u3041\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u3069\u3082\u3067\u3059\u3002 \u524d\u56de\u300cR-CHR \u306e\u6982\u8981\u3068\u30a2\u30fc\u30ad\u30c6\u30af\u30c1\u30e3\u300d\u3068\u306e\u3053\u3068\u3067\u8a18\u4e8b\u3092\u66f8\u304b\u305b\u3066\u3044\u305f\u3060\u304d\u307e\u3057\u305f\u304c\u3001\u4eca\u56de\u306f\u305d\u306e\u7d9a\u304d\u3067\u3001\u3044\u3088\u3044\u3088\u4e2d\u8eab\u306e\u30c7\u30fc\u30bf\u306e\u8a71\u306b\u306a\u308a\u307e\u3059\u3002 \u30d5\u30a1\u30df\u30b3\u30f3\u306e\u7d75\u3063\u3066\u3001\u305d\u3082\u305d\u3082\u3069\u3046\u3044\u3046\u30d0\u30a4\u30c8\u5217\u3067 ROM \u306b\u5165\u3063\u3066\u3044\u308b [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":12806,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[299],"tags":[308,169,381,95,39,390,389,388,78,118],"class_list":["post-12783","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-rust","tag-gui","tag-mac","tag-nes","tag-95","tag-39","tag-390","tag-389","tag-388","tag-78","tag-118"],"_links":{"self":[{"href":"https:\/\/www.webcyou.com\/index.php?rest_route=\/wp\/v2\/posts\/12783","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.webcyou.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.webcyou.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.webcyou.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.webcyou.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=12783"}],"version-history":[{"count":19,"href":"https:\/\/www.webcyou.com\/index.php?rest_route=\/wp\/v2\/posts\/12783\/revisions"}],"predecessor-version":[{"id":12822,"href":"https:\/\/www.webcyou.com\/index.php?rest_route=\/wp\/v2\/posts\/12783\/revisions\/12822"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.webcyou.com\/index.php?rest_route=\/wp\/v2\/media\/12806"}],"wp:attachment":[{"href":"https:\/\/www.webcyou.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=12783"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.webcyou.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=12783"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.webcyou.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=12783"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}