code wiki / _hdl_build / nx_roku_emit.nx
nx_roku_emit.nx source
↩ module page · 155 lines · 8674 B
1// nx_roku_emit.nx -- SOVEREIGN Roku channel-package emitter (R0 "hardware rung" of the Roku media-app arc).
2// Emits a sideload-ready Roku channel as a STORED ZIP via the shared nx_opc framer -- NO new ZIP code:
3// manifest (key=value text, named exactly `manifest`, NO extension, at zip root)
4// source/main.brs (SceneGraph entry point -- the BrightScript platform shim, EMITTED not hand-written)
5// components/MainScene.xml (a Scene with a background Rectangle + a centered "Nishi" Label)
6// images/icon.png (a fully sovereign solid-colour PNG: signature + IHDR + IDAT[zlib stored-DEFLATE]
7// + IEND; chunk CRCs via nx_crc32, IDAT zlib trailer via inline Adler-32)
8// Doctrine: BrightScript/SceneGraph can't run Nishi bytecode (no WASM on Roku) so it's a platform shim, the same
9// way HTML is in the browser -- we keep it sovereign by EMITTING it from an organ (cf. nx_wiki_shell emits HTML).
10// Sovereignty = our own packager + our own PNG; rigor (ZIP + PNG are standard formats) is proven in the harness
11// by a 3rd-party oracle (python zipfile.testzip + PNG IHDR unpack) per the non-novel-format doctrine.
12// nx_roku_emit [out.zip] (default: /mnt/c/Users/elder/nishi_roku.zip)
13// license_tier: ORIGINAL
14import "nx_syscalls.nx"
15import "nx_crc32.nx"
16import "nx_opc.nx"
17const K_MAGIC_65521: i64 = 65521
18const K_MAGIC_262144: i64 = 262144
19const K_MAGIC_65535: i64 = 65535
20const K_MAGIC_4096: i64 = 4096
21
22func rk_strlen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
23func rk_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){ dst[off+i]=s[i]; i=i+1 } return off+i }
24func rk_puts(s: *u8) -> i64 { sys_write(1, s, rk_strlen(s)); return 0 }
25func rk_num(v: i64) -> i64 {
26 let t: *u8=sys_mmap(28); var m: i64=v; var k: i64=0
27 if m==0 { t[0]=48 as u8; k=1 }
28 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
29 let bb: *u8=sys_mmap(28); var i: i64=0
30 while i<k { bb[i]=t[k-1-i]; i=i+1 }
31 sys_write(1, bb, k); return 0
32}
33
34// big-endian u32 (PNG chunk length / CRC / IHDR dims / zlib Adler trailer)
35func rk_u32be(buf: *u8, off: i64, v: i64) -> i64 {
36 buf[off]=((v>>24)&0xff) as u8; buf[off+1]=((v>>16)&0xff) as u8
37 buf[off+2]=((v>>8)&0xff) as u8; buf[off+3]=(v&0xff) as u8; return off+4
38}
39// little-endian u16 (DEFLATE stored-block LEN / NLEN)
40func rk_u16le(buf: *u8, off: i64, v: i64) -> i64 { buf[off]=(v&0xff) as u8; buf[off+1]=((v>>8)&0xff) as u8; return off+2 }
41
42// Adler-32 over data[0..n) (zlib IDAT trailer). a=1,b=0; mod 65521.
43func rk_adler32(data: *u8, n: i64) -> i64 {
44 var a: i64=1; var b: i64=0; var i: i64=0
45 while i<n { a=(a+(data[i] as i64))%K_MAGIC_65521; b=(b+a)%K_MAGIC_65521; i=i+1 }
46 return (b<<16)|a
47}
48
49// write one PNG chunk [len][type+data][crc32(type+data)] into out at off; return new off
50func rk_png_chunk(out: *u8, off: i64, typ: *u8, data: *u8, dlen: i64) -> i64 {
51 var o: i64=rk_u32be(out, off, dlen)
52 let crcstart: i64=o
53 out[o]=typ[0]; out[o+1]=typ[1]; out[o+2]=typ[2]; out[o+3]=typ[3]; o=o+4
54 var i: i64=0; while i<dlen { out[o]=data[i]; o=o+1; i=i+1 }
55 let crc: i64=nx_crc32(((out as i64)+crcstart) as *u8, o-crcstart)
56 o=rk_u32be(out, o, crc)
57 return o
58}
59
60// build a w x h solid (r,g,bl) RGB PNG into out; return byte length. Single uncompressed-DEFLATE block
61// (valid for our small icon where the raw scanline buffer < 65535 bytes).
62func rk_png_solid(out: *u8, w: i64, h: i64, r: i64, g: i64, bl: i64) -> i64 {
63 out[0]=137 as u8; out[1]=80 as u8; out[2]=78 as u8; out[3]=71 as u8
64 out[4]=13 as u8; out[5]=10 as u8; out[6]=26 as u8; out[7]=10 as u8
65 var o: i64=8
66 let ihdr: *u8=sys_mmap(32); var p: i64=0
67 p=rk_u32be(ihdr, p, w); p=rk_u32be(ihdr, p, h)
68 ihdr[p]=8 as u8; p=p+1 // bit depth
69 ihdr[p]=2 as u8; p=p+1 // colour type = 2 (RGB truecolour)
70 ihdr[p]=0 as u8; p=p+1 // compression = deflate
71 ihdr[p]=0 as u8; p=p+1 // filter = adaptive
72 ihdr[p]=0 as u8; p=p+1 // interlace = none
73 o=rk_png_chunk(out, o, "IHDR" as *u8, ihdr, p)
74 // raw image: per scanline a 0 filter byte then w RGB triples
75 let raw: *u8=sys_mmap(K_MAGIC_262144); var ro: i64=0; var y: i64=0
76 while y<h {
77 raw[ro]=0 as u8; ro=ro+1
78 var x: i64=0
79 while x<w { raw[ro]=r as u8; raw[ro+1]=g as u8; raw[ro+2]=bl as u8; ro=ro+3; x=x+1 }
80 y=y+1
81 }
82 let rawlen: i64=ro
83 // zlib stream = 0x78 0x01 header + one final stored block + Adler-32 trailer (big-endian)
84 let z: *u8=sys_mmap(K_MAGIC_262144); var zo: i64=0
85 z[0]=0x78 as u8; z[1]=0x01 as u8; zo=2
86 z[zo]=0x01 as u8; zo=zo+1 // BFINAL=1, BTYPE=00 (stored)
87 zo=rk_u16le(z, zo, rawlen) // LEN
88 zo=rk_u16le(z, zo, K_MAGIC_65535-rawlen) // NLEN = one's complement of LEN
89 var i: i64=0; while i<rawlen { z[zo]=raw[i]; zo=zo+1; i=i+1 }
90 zo=rk_u32be(z, zo, rk_adler32(raw, rawlen))
91 o=rk_png_chunk(out, o, "IDAT" as *u8, z, zo)
92 o=rk_png_chunk(out, o, "IEND" as *u8, 0 as *u8, 0)
93 return o
94}
95
96func main(argc: i64, argv: *i64) -> i64 {
97 var outpath: *u8="/mnt/c/Users/elder/nishi_roku.zip" as *u8
98 if argc>=2 { outpath=argv[1] as *u8 }
99
100 // -- manifest (named exactly `manifest`, no extension) --
101 let manifest: *u8=sys_mmap(K_MAGIC_4096); var mo: i64=0
102 mo=rk_cat(manifest, mo, "title=Nishi\n" as *u8)
103 mo=rk_cat(manifest, mo, "major_version=1\n" as *u8)
104 mo=rk_cat(manifest, mo, "minor_version=0\n" as *u8)
105 mo=rk_cat(manifest, mo, "build_version=00001\n" as *u8)
106 mo=rk_cat(manifest, mo, "mm_icon_focus_hd=pkg:/images/icon.png\n" as *u8)
107 mo=rk_cat(manifest, mo, "mm_icon_focus_sd=pkg:/images/icon.png\n" as *u8)
108 mo=rk_cat(manifest, mo, "splash_screen_hd=pkg:/images/icon.png\n" as *u8)
109 mo=rk_cat(manifest, mo, "ui_resolutions=hd\n" as *u8)
110
111 // -- source/main.brs (SceneGraph entry) --
112 let mbrs: *u8=sys_mmap(K_MAGIC_4096); var bo: i64=0
113 bo=rk_cat(mbrs, bo, "sub Main()\n" as *u8)
114 bo=rk_cat(mbrs, bo, " screen = CreateObject(\"roSGScreen\")\n" as *u8)
115 bo=rk_cat(mbrs, bo, " m.port = CreateObject(\"roMessagePort\")\n" as *u8)
116 bo=rk_cat(mbrs, bo, " screen.setMessagePort(m.port)\n" as *u8)
117 bo=rk_cat(mbrs, bo, " scene = screen.CreateScene(\"MainScene\")\n" as *u8)
118 bo=rk_cat(mbrs, bo, " screen.show()\n" as *u8)
119 bo=rk_cat(mbrs, bo, " while true\n" as *u8)
120 bo=rk_cat(mbrs, bo, " msg = wait(0, m.port)\n" as *u8)
121 bo=rk_cat(mbrs, bo, " if type(msg) = \"roSGScreenEvent\"\n" as *u8)
122 bo=rk_cat(mbrs, bo, " if msg.isScreenClosed() then return\n" as *u8)
123 bo=rk_cat(mbrs, bo, " end if\n" as *u8)
124 bo=rk_cat(mbrs, bo, " end while\n" as *u8)
125 bo=rk_cat(mbrs, bo, "end sub\n" as *u8)
126
127 // -- components/MainScene.xml --
128 let scn: *u8=sys_mmap(K_MAGIC_4096); var so: i64=0
129 so=rk_cat(scn, so, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" as *u8)
130 so=rk_cat(scn, so, "<component name=\"MainScene\" extends=\"Scene\">\n" as *u8)
131 so=rk_cat(scn, so, " <children>\n" as *u8)
132 so=rk_cat(scn, so, " <Rectangle width=\"1280\" height=\"720\" color=\"0x10243Cff\" />\n" as *u8)
133 so=rk_cat(scn, so, " <Label id=\"hello\" text=\"Nishi - sovereign, from the hardware rung up\" width=\"1180\" height=\"100\" translation=\"[50,310]\" horizAlign=\"center\" vertAlign=\"center\" color=\"0xFFFFFFff\" />\n" as *u8)
134 so=rk_cat(scn, so, " </children>\n" as *u8)
135 so=rk_cat(scn, so, "</component>\n" as *u8)
136
137 // -- images/icon.png (sovereign 128x128 solid Nishi-blue) --
138 let png: *u8=sys_mmap(K_MAGIC_262144)
139 let pnglen: i64=rk_png_solid(png, 128, 128, 20, 110, 180)
140
141 // -- frame all four parts as a STORED zip --
142 let names: *i64=sys_mmap(64) as *i64
143 let datap: *i64=sys_mmap(64) as *i64
144 let lens: *i64=sys_mmap(64) as *i64
145 names[0]="manifest" as *u8 as i64; datap[0]=manifest as i64; lens[0]=mo
146 names[1]="source/main.brs" as *u8 as i64; datap[1]=mbrs as i64; lens[1]=bo
147 names[2]="components/MainScene.xml" as *u8 as i64; datap[2]=scn as i64; lens[2]=so
148 names[3]="images/icon.png" as *u8 as i64; datap[3]=png as i64; lens[3]=pnglen
149 let nbytes: i64=opc_write(outpath, names, datap, lens, 4)
150 if nbytes<0 { rk_puts("ROKU-EMIT-FAIL code=" as *u8); rk_num(nbytes); rk_puts("\n" as *u8); sys_exit(1); return 1 }
151 rk_puts("ROKU-EMIT-OK path=" as *u8); rk_puts(outpath)
152 rk_puts(" zipbytes=" as *u8); rk_num(nbytes)
153 rk_puts(" entries=4 pngbytes=" as *u8); rk_num(pnglen); rk_puts("\n" as *u8)
154 sys_exit(0); return 0
155}