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}