code wiki / (root) / nx_games_publish.nx

nx_games_publish.nx source

↩ module page · 189 lines · 7390 B

1// nx_games_publish.nx -- sovereign emitter: content/games.cards -> 2// dist/games/index.html (the arcade page). 3// 4// Data-driven sibling of nx_hub_emit (see [[project-nishi-hub-games-publish-2026-06-30]]). 5// Each published game renders with a version badge (the value the game- 6// update API channels off) + a Play CTA; "soon" games render disabled. 7// File-I/O only -> imports the leaf x86_64 syscalls ONLY (builds clean). 8// Run from the nishi-pages checkout; dist/games must exist. 9// 10// Build: ./nxc2.exe --target x86_64 runtime/nx_games_publish.nx > /tmp/gp.s 11// wsl bash -c "gcc -no-pie /tmp/gp.s -o /tmp/nx_games_publish" 12// Run: cd nishi-pages && mkdir -p dist/games && /tmp/nx_games_publish 13// 14// nx_capability_claims: 15// needs: [x86_64_syscalls, pipe_delimited_parse] 16// provides: [games_arcade_emit, version_badge, data_driven_publish] 17// safety: [html_escape_text_fields, no_floating_point, fixed_output_path] 18// verdict: [exit_0_ok] 19// license: ORIGINAL 20// kind: nishi_pages_specialist 21// layer: L3 22// raci: [R=nishi_pages_builder, A=elder_west, C=games_cards_owner, I=conductor] 23 24import "nx_syscalls_x86_64.nx" 25const K_MAGIC_262144: i64 = 262144 26 27func gp_puts(buf: *u8, off: i64, s: *u8) -> i64 { 28 var o: i64 = off 29 var i: i64 = 0 30 while s[i] != 0 { 31 buf[o] = s[i] 32 o = o + 1 33 i = i + 1 34 } 35 return o 36} 37 38func gp_putb(buf: *u8, off: i64, src: *u8, n: i64) -> i64 { 39 var o: i64 = off 40 var i: i64 = 0 41 while i < n { 42 buf[o] = src[i] 43 o = o + 1 44 i = i + 1 45 } 46 return o 47} 48 49func gp_put_esc(buf: *u8, off: i64, src: *u8, n: i64) -> i64 { 50 var o: i64 = off 51 var i: i64 = 0 52 while i < n { 53 let c: i64 = src[i] as i64 54 if c == 0x26 { 55 o = gp_puts(buf, o, "&amp;" as *u8) 56 } else { 57 if c == 0x3c { 58 o = gp_puts(buf, o, "&lt;" as *u8) 59 } else { 60 if c == 0x3e { 61 o = gp_puts(buf, o, "&gt;" as *u8) 62 } else { 63 buf[o] = src[i] 64 o = o + 1 65 } 66 } 67 } 68 i = i + 1 69 } 70 return o 71} 72 73func gp_eq(buf: *u8, off: i64, n: i64, lit: *u8) -> i64 { 74 var i: i64 = 0 75 while lit[i] != 0 { 76 if i >= n { return 0 } 77 if (buf[off + i] as i64) != (lit[i] as i64) { return 0 } 78 i = i + 1 79 } 80 if i != n { return 0 } 81 return 1 82} 83 84func gp_split(buf: *u8, ls: i64, le: i64, foff: *i64, flen: *i64) -> i64 { 85 var fi: i64 = 0 86 var s: i64 = ls 87 var i: i64 = ls 88 while i < le { 89 if (buf[i] as i64) == 0x7c { 90 if fi < 6 { 91 foff[fi] = s 92 flen[fi] = i - s 93 fi = fi + 1 94 } 95 s = i + 1 96 } 97 i = i + 1 98 } 99 if fi < 6 { 100 foff[fi] = s 101 flen[fi] = le - s 102 fi = fi + 1 103 } 104 return fi 105} 106 107func main() -> i64 { 108 let len_p: *i64 = sys_mmap(8) as *i64 109 let data: *u8 = sys_read_file_x86_64("content/games.cards" as *u8, len_p) 110 if data == (0 as *u8) { return 5 } 111 let dn: i64 = len_p[0] 112 113 let out: *u8 = sys_mmap(K_MAGIC_262144) 114 var o: i64 = 0 115 116 o = gp_puts(out, o, "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><title>Games &middot; Nishi Family</title><link rel=\"stylesheet\" href=\"/assets/style.css\"><style>.app-soon{opacity:.55}.gver{display:inline-block;margin-left:8px;padding:1px 7px;font-size:.6em;vertical-align:middle;border:1px solid #2f6f4f;border-radius:999px;color:#7fd9a6;background:#10231a}.gplay{display:inline-block;margin-top:10px;padding:6px 14px;border-radius:8px;background:#2f6f4f;color:#eafff3;font-weight:600;font-size:.9em}.gnote{color:#8a8d98;max-width:640px;margin:0 auto 22px}</style></head><body><header class=\"site-header\"><a class=\"site-title\" href=\"/\">Nishi Family</a><nav><a href=\"/hub\">Hub</a><a href=\"/games\">Games</a><a href=\"/video/\">Video</a><a href=\"/pages/about\">About</a></nav></header><main><section class=\"hero\"><h1>Games</h1><p class=\"tagline\">Sovereign WebAssembly games. No install, no store account &mdash; just open and play.</p></section><p class=\"gnote\">Every game is compiled bits-up to WebAssembly and pinned to a version. Published games update automatically when a new version ships (watch the badge).</p><section class=\"apps\"><div class=\"app-grid\" id=\"gamegrid\">" as *u8) 117 118 let foff: *i64 = sys_mmap(64) as *i64 119 let flen: *i64 = sys_mmap(64) as *i64 120 121 var ls: i64 = 0 122 while ls < dn { 123 var le: i64 = 0 - 1 124 var k: i64 = ls 125 while k < dn { 126 if le < 0 { 127 if (data[k] as i64) == 0x0a { le = k } 128 } 129 k = k + 1 130 } 131 if le < 0 { le = dn } 132 133 var skip: i64 = 0 134 if le <= ls { skip = 1 } 135 if skip == 0 { 136 if (data[ls] as i64) == 0x23 { skip = 1 } 137 } 138 139 if skip == 0 { 140 let nf: i64 = gp_split(data, ls, le, foff, flen) 141 if nf >= 6 { 142 let is_soon: i64 = gp_eq(data, foff[0], flen[0], "soon" as *u8) 143 o = gp_puts(out, o, "<a class=\"app-card" as *u8) 144 if is_soon == 1 { o = gp_puts(out, o, " app-soon" as *u8) } 145 o = gp_puts(out, o, "\" href=\"" as *u8) 146 if is_soon == 1 { 147 o = gp_puts(out, o, "#" as *u8) 148 } else { 149 o = gp_putb(out, o, ((data as i64) + foff[3]) as *u8, flen[3]) 150 } 151 o = gp_puts(out, o, "\" data-kw=\"" as *u8) 152 o = gp_put_esc(out, o, ((data as i64) + foff[2]) as *u8, flen[2]) 153 o = gp_puts(out, o, " " as *u8) 154 o = gp_put_esc(out, o, ((data as i64) + foff[5]) as *u8, flen[5]) 155 o = gp_puts(out, o, "\"><span class=\"app-emoji\">" as *u8) 156 o = gp_putb(out, o, ((data as i64) + foff[1]) as *u8, flen[1]) 157 o = gp_puts(out, o, "</span><h3>" as *u8) 158 o = gp_put_esc(out, o, ((data as i64) + foff[2]) as *u8, flen[2]) 159 if is_soon == 0 { 160 o = gp_puts(out, o, " <span class=\"gver\">" as *u8) 161 o = gp_putb(out, o, ((data as i64) + foff[4]) as *u8, flen[4]) 162 o = gp_puts(out, o, "</span>" as *u8) 163 } 164 o = gp_puts(out, o, "</h3><p>" as *u8) 165 o = gp_put_esc(out, o, ((data as i64) + foff[5]) as *u8, flen[5]) 166 o = gp_puts(out, o, "</p>" as *u8) 167 if is_soon == 0 { 168 o = gp_puts(out, o, "<span class=\"gplay\">&#9654; Play</span>" as *u8) 169 } 170 o = gp_puts(out, o, "</a>" as *u8) 171 } 172 } 173 174 ls = le + 1 175 } 176 177 o = gp_puts(out, o, "</div></section></main><footer><p>&copy; 2026 Elder West. Served by NishiVM &mdash; sovereign substrate.</p></footer></body></html>" as *u8) 178 179 let fd: i64 = sys_openat_wr("dist/games/index.html" as *u8, 420) 180 if fd < 0 { return 6 } 181 sys_write(fd, out, o) 182 sys_close(fd) 183 184 let banner: *u8 = "nx_games_publish: wrote dist/games/index.html\n" as *u8 185 var bn: i64 = 0 186 while banner[bn] != 0 { bn = bn + 1 } 187 sys_write(1, banner, bn) 188 return 0 189}