code wiki / _hdl_build / nx_vn_site.nx
nx_vn_site.nx source
↩ module page · 71 lines · 6065 B
1// nx_vn_site.nx -- builds the VN CATALOG as a clean-routed SITE: a /vn index that lists each visual novel,
2// and one numbered VN per work (/vn/1, /vn/2), so every piece of content EXISTS at its own canonical clean URL
3// (operator: "shouldnt it be /vn/1 and /vn/2 so both ... exist"). Reuses the sovereign VN engine (nx_vn) to emit
4// a 2nd real branching story, strips its external bg ref so each page is self-contained (no dangling asset),
5// and emits a zero-JS catalog index linking the clean URLs. This is routing as a property of PUBLISHING.
6// license_tier: ORIGINAL expect_exit: 0
7import "nx_vn.nx"
8import "nx_syscalls.nx"
9const K_MAGIC_65536: i64 = 65536
10const K_MAGIC_16384: i64 = 16384
11
12func sw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
13func sn(v: i64) -> i64 { var m: i64=v; if m==0{sys_write(1,"0" as *u8,1);return 0} let t:*u8=sys_mmap(24); var k:i64=0; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} let o:*u8=sys_mmap(24); var i:i64=0; while i<k{o[i]=t[k-1-i];i=i+1} sys_write(1,o,k); return 0 }
14func vp(buf: *u8, o: i64, s: *u8) -> i64 { var w: i64=o; var i: i64=0; while s[i]!=(0 as u8){buf[w]=s[i];w=w+1;i=i+1} return w }
15
16func wfile(path: *u8, buf: *u8, n: i64) -> i64 {
17 let fd: i64 = sys_openat_wr(path, 420)
18 if fd < 0 { sw(" cannot write " as *u8); sw(path); sw("\n" as *u8); return 1 }
19 sys_write(fd, buf, n); sys_close(fd); return 0
20}
21
22// vn_sample2 ("The Cartographer's Vow") now lives in the engine (nx_vn.nx) so the critic gates it too -- DRY.
23
24// make an engine-emitted VN self-contained: replace the external url('vn_bg.png') (16 bytes) with a harmless
25// same-length self-contained value (no dangling asset request). 16->16 in-place, no byte shift.
26func strip_bg(buf: *u8, n: i64) -> i64 {
27 let ndl: *u8 = "url('vn_bg.png')" as *u8
28 var i: i64 = 0
29 while i + 16 <= n {
30 var j: i64=0; var ok: i64=1
31 while j<16 { if buf[i+j]!=ndl[j]{ok=0;j=16} else {j=j+1} }
32 if ok==1 { let rep: *u8 = "none " as *u8; var k: i64=0; while k<16 { buf[i+k]=rep[k]; k=k+1 } return 1 }
33 i=i+1
34 }
35 return 0
36}
37
38// zero-JS catalog index: lists each VN, links its CLEAN canonical URL (/vn/1, /vn/2). No script, no external loads.
39func emit_catalog(buf: *u8) -> i64 {
40 var o: i64 = 0
41 o = vp(buf, o, "<!doctype html><html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width,initial-scale=1'><title>Nishi Visual Novels</title><style>body{margin:0;background:linear-gradient(rgba(8,10,18,.6),rgba(8,10,18,.86)),#0c0e16;color:#e7e9f0;font-family:Georgia,serif;min-height:100vh}.wrap{max-width:780px;margin:0 auto;padding:40px 20px}.head{text-align:center;padding:30px 0 26px;border-bottom:1px solid #232a3d}.head h1{font-size:2rem;margin:0 0 8px;font-family:system-ui,sans-serif;letter-spacing:.02em}.head p{color:#8aa0c6;font-family:system-ui,sans-serif;font-size:13px;margin:0}.grid{display:flex;flex-wrap:wrap;gap:18px;margin-top:26px}.card{flex:1 1 320px;background:#121726;border:1px solid #232a3d;border-radius:14px;padding:22px 24px}.card h2{margin:0 0 6px;font-size:1.3rem}.tag{display:inline-block;font-family:system-ui,sans-serif;font-size:11px;color:#9fb3d8;border:1px solid #34405c;border-radius:20px;padding:2px 10px;margin-bottom:10px}.card p{line-height:1.6;color:#c8cfde;margin:0}.play{display:inline-block;margin-top:14px;padding:10px 18px;background:#1b2334;color:#cfe2ff;border:1px solid #34405c;border-radius:9px;text-decoration:none;font-family:system-ui,sans-serif}.play:hover{background:#27324a}.foot{margin-top:40px;color:#5c667a;font-size:12px;text-align:center;font-family:system-ui,sans-serif}</style></head><body><div class='wrap'>")
42 o = vp(buf, o, "<div class='head'><h1>Nishi Visual Novels</h1><p>Sovereign · zero-JavaScript · branching stories you can actually play</p></div>")
43 o = vp(buf, o, "<div class='grid'>")
44 o = vp(buf, o, "<div class='card'><span class='tag'>Visual Novel · 2 endings</span><h2>The Lighthouse Keeper</h2><p>A letter in a strange looping hand draws you to a lighthouse on the grey northern coast — and to the secret its keeper has guarded alone. A living, branching story.</p><a class='play' href='/vn/1'>Play ▶</a></div>")
45 o = vp(buf, o, "<div class='card'><span class='tag'>Visual Novel · 2 endings</span><h2>The Cartographer's Vow</h2><p>The last cartographer in Hollowmere is hired to map a road that left no town for forty years. Walk it, or burn the commission — the map remembers either way.</p><a class='play' href='/vn/2'>Play ▶</a></div>")
46 o = vp(buf, o, "</div><div class='foot'>Nishi visual novels · pure sovereign HTML · zero JavaScript · clean canonical routing (/vn · /vn/1 · /vn/2)</div></div></body></html>")
47 buf[o] = 0 as u8
48 return o
49}
50
51func main(argc: i64, argv: *i64) -> i64 {
52 sw("=== nx_vn_site -- VN catalog as a clean-routed site (/vn, /vn/1, /vn/2) ===\n" as *u8)
53 sys_mkdir("knowledge/staging/game" as *u8, 0x1ed)
54 // ---- VN2 ----
55 let txt: *i64 = sys_mmap(8*VN_NS) as *i64; let nc: *i64 = sys_mmap(8*VN_NS) as *i64
56 let ct: *i64 = sys_mmap(8*VN_NS*VN_MC) as *i64; let cl: *i64 = sys_mmap(8*VN_NS*VN_MC) as *i64
57 let nbox: *i64 = sys_mmap(16) as *i64; nbox[0] = 0
58 vn_sample2(txt, nc, ct, cl, nbox)
59 let n: i64 = nbox[0]
60 let buf: *u8 = sys_mmap(K_MAGIC_65536)
61 let blen: i64 = vn_emit_html(txt, nc, ct, cl, n, "The Cartographer's Vow" as *u8, buf)
62 strip_bg(buf, blen)
63 wfile("knowledge/staging/game/vn2.html" as *u8, buf, blen)
64 sw(" VN2 'The Cartographer's Vow' -> vn2.html ("); sn(blen); sw(" B, "); sn(n); sw(" scenes, zero-JS, self-contained)\n" as *u8)
65 // ---- catalog index ----
66 let cbuf: *u8 = sys_mmap(K_MAGIC_16384)
67 let clen: i64 = emit_catalog(cbuf)
68 wfile("knowledge/staging/game/vn_catalog.html" as *u8, cbuf, clen)
69 sw(" catalog index -> vn_catalog.html ("); sn(clen); sw(" B, links /vn/1 + /vn/2)\n" as *u8)
70 sys_exit(0); return 0
71}