nx_kkfacts.nx source
↩ module page · 95 lines · 4314 B
1// nx_kkfacts.nx -- CLI: Koikatsu card structural facts. nx_kkfacts probe <card.png>
2// Reads up to 64MB (cards are full files, payload census needs the whole artifact). Facts only:
3// structure + sizes + block census; slider values / names / creative content stay unread.
4// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
5import "nx_syscalls.nx"
6import "nx_kkfacts_lib.nx"
7
8const KKC_CAP: i64 = 67108864
9const KKD_MAX_VALS: i64 = 256 // slider arrays measure ~52 (face) / ~44 (body); 256 is the refuse-beyond bound, overflow visible via parsed vs declared
10
11func kc_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
12func kc_w(s: *u8) -> i64 { sys_write(1, s, kc_slen(s)); return 0 }
13func kc_e(s: *u8) -> i64 { sys_write(2, s, kc_slen(s)); return 0 }
14func kc_num(v: i64) -> i64 {
15 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 }
16 var m: i64 = v
17 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
18 let t: *u8 = sys_mmap(32)
19 var k: i64 = 0
20 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
21 let o: *u8 = sys_mmap(32)
22 var i: i64 = 0
23 while i < k { o[i] = t[k - 1 - i]; i = i + 1 }
24 sys_write(1, o, k)
25 return 0
26}
27
28func kc_eq(a: *u8, b: *u8) -> i64 { var i: i64 = 0; while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } if b[i] != (0 as u8) { return 0 } return 1 }
29
30func main(argc: i64, argv: *i64) -> i64 {
31 if argc < 3 { kc_e("usage: nx_kkfacts probe|decode <card.png>\n" as *u8); sys_exit(2); return 2 }
32 let buf: *u8 = sys_mmap(KKC_CAP)
33 let fd: i64 = sys_openat_rd(argv[2] as *u8)
34 if fd < 0 { kc_e("ERROR: cannot open file\n" as *u8); sys_exit(1); return 1 }
35 let size: i64 = sys_lseek(fd, 0, 2)
36 sys_lseek(fd, 0, 0)
37 var want: i64 = size
38 if want > KKC_CAP { want = KKC_CAP }
39 var got: i64 = 0
40 var stop: i64 = 0
41 while stop == 0 {
42 if got >= want { stop = 1 } else {
43 let k: i64 = sys_read(fd, ((buf as i64) + got) as *u8, want - got)
44 if k <= 0 { stop = 1 } else { got = got + k }
45 }
46 }
47 sys_close(fd)
48 let facts: *i64 = sys_mmap(KK_N_SLOTS * 8) as *i64
49 // DIALECT AUTO-DETECT (2026-08-18): KK first (the incumbent contract), then AI Shoujo/HS2.
50 // dialect= is APPENDED LAST so every existing consumer of the KK line shape reads unchanged.
51 kk_probe(buf, got, size, facts)
52 var dialect: *u8 = "KK" as *u8
53 if facts[0] == 0 {
54 kk_probe_aishoujo(buf, got, size, facts)
55 if facts[0] == 1 { dialect = "AIS" as *u8 }
56 }
57 kc_w("KKFACT is_card=" as *u8); kc_num(facts[0])
58 if facts[0] == 1 {
59 kc_w(" portrait_bytes=" as *u8); kc_num(facts[1])
60 kc_w(" portrait_px=" as *u8); kc_num(facts[2]); kc_w("x" as *u8); kc_num(facts[3])
61 kc_w(" payload_bytes=" as *u8); kc_num(facts[4])
62 if facts[5] >= 0 {
63 kc_w(" custom=" as *u8); kc_num(facts[5])
64 kc_w(" coordinate=" as *u8); kc_num(facts[6])
65 kc_w(" parameter=" as *u8); kc_num(facts[7])
66 kc_w(" status=" as *u8); kc_num(facts[8])
67 kc_w(" kkex=" as *u8); kc_num(facts[9])
68 }
69 kc_w(" dialect=" as *u8); kc_w(dialect)
70 }
71 kc_w("\n" as *u8)
72 if facts[0] == 0 { sys_exit(4); return 4 }
73 // decode verb: the kk_decode_sliders contract. Values go to the CALLER's stdout only --
74 // per-card creative content never enters a published plane (the k-wall in the lib header).
75 if kc_eq(argv[1] as *u8, "decode" as *u8) == 1 {
76 let vals: *i64 = sys_mmap(KKD_MAX_VALS * 8) as *i64
77 let meta: *i64 = sys_mmap(16) as *i64
78 var ki: i64 = 0
79 while ki < 2 {
80 var key: *u8 = "shapeValueFace" as *u8
81 if ki == 1 { key = "shapeValueBody" as *u8 }
82 let parsed: i64 = kk_decode_sliders(buf, facts[1], got, key, vals, KKD_MAX_VALS, meta)
83 kc_w("KKDECODE key=" as *u8); kc_w(key)
84 kc_w(" declared=" as *u8); kc_num(meta[0])
85 kc_w(" parsed=" as *u8); kc_num(parsed)
86 kc_w(" permil:" as *u8)
87 var vi: i64 = 0
88 while vi < parsed { if vi < KKD_MAX_VALS { kc_w(" " as *u8); kc_num(vals[vi]) } vi = vi + 1 }
89 kc_w("\n" as *u8)
90 ki = ki + 1
91 }
92 }
93 sys_exit(0)
94 return 0
95}