nx_manga.nx source
↩ module page · 135 lines · 4791 B
1// nx_manga.nx -- WRITING arc, rung W-MANGA-1: the PAGE/PANEL SCRIPT model.
2// Operator intent: "make mangas". A manga script is pages -> panels, each panel a
3// small key:value block (shot / char / say / sfx / caption). This organ counts
4// pages+panels, locates a panel's byte range, validates each panel has the camera
5// "shot" needed to draw it, and EMITS the per-panel image-gen prompt -- feeding the
6// SAME image-gen + companion ingestion the rest of nishi-write already uses.
7//
8// DRY: the per-panel fields are parsed by REUSING nx_scenario's field parser on the
9// panel's byte sub-range (sc_find_field / sc_has / sc_app_field) -- not reimplemented.
10// Pure integer, NO syscalls. The script TEXT is operator/lane DATA (the seam).
11//
12// Script format (PANEL / PAGE are bare marker lines; fields are key:value):
13// PAGE
14// PANEL
15// shot: wide establishing
16// char: Mira, Dev
17// say: We made it.
18// sfx: WHOOSH
19// caption: Dawn over the ruined city.
20//
21// license_tier: ORIGINAL
22// module: nishi-core.write.manga
23// depends: nishi-core.write.scenario
24// capability: WRITE_MANGA_SCRIPT
25import "nx_scenario.nx"
26
27// 1 if the line at i (after optional leading spaces) is exactly the marker tok,
28// i.e. tok followed by a space, newline, or end-of-buffer.
29func mg_line_is(buf: *u8, n: i64, i: i64, tok: *u8) -> i64 {
30 var j: i64 = i
31 var sg: i64 = 1
32 while sg == 1 {
33 if j < n { let c: i64 = buf[j] as i64; if c == 32 { j = j + 1 } else { sg = 0 } } else { sg = 0 }
34 }
35 var m: i64 = 0
36 var okk: i64 = 1
37 var done: i64 = 0
38 while done == 0 {
39 let tc: i64 = tok[m] as i64
40 if tc == 0 { done = 1 }
41 else {
42 if j + m >= n { okk = 0; done = 1 }
43 else { let bc: i64 = buf[j + m] as i64; if bc != tc { okk = 0; done = 1 } else { m = m + 1 } }
44 }
45 }
46 if okk == 1 {
47 if j + m >= n { return 1 }
48 let nc: i64 = buf[j + m] as i64
49 if nc == 32 { return 1 }
50 if nc == 10 { return 1 }
51 return 0
52 }
53 return 0
54}
55
56// count marker lines equal to tok
57func mg_count(buf: *u8, n: i64, tok: *u8) -> i64 {
58 var i: i64 = 0
59 var c: i64 = 0
60 while i < n {
61 if mg_line_is(buf, n, i, tok) == 1 { c = c + 1 }
62 i = sc_next_line(buf, n, i)
63 }
64 return c
65}
66func mg_count_pages(buf: *u8, n: i64) -> i64 { return mg_count(buf, n, "PAGE\x00" as *u8) }
67func mg_count_panels(buf: *u8, n: i64) -> i64 { return mg_count(buf, n, "PANEL\x00" as *u8) }
68
69// byte offset of the idx-th PANEL marker line (0-based), or -1
70func mg_panel_start(buf: *u8, n: i64, idx: i64) -> i64 {
71 var i: i64 = 0
72 var seen: i64 = 0
73 var res: i64 = 0 - 1
74 var go: i64 = 1
75 while go == 1 {
76 if i >= n { go = 0 }
77 else {
78 if mg_line_is(buf, n, i, "PANEL\x00" as *u8) == 1 {
79 if seen == idx { res = i; go = 0 }
80 else { seen = seen + 1; i = sc_next_line(buf, n, i) }
81 } else { i = sc_next_line(buf, n, i) }
82 }
83 }
84 return res
85}
86
87// end of the panel that starts at `start` = next PANEL/PAGE marker, or n
88func mg_panel_end(buf: *u8, n: i64, start: i64) -> i64 {
89 var i: i64 = sc_next_line(buf, n, start)
90 var e: i64 = n
91 var go: i64 = 1
92 while go == 1 {
93 if i >= n { e = n; go = 0 }
94 else {
95 if mg_line_is(buf, n, i, "PANEL\x00" as *u8) == 1 { e = i; go = 0 }
96 else {
97 if mg_line_is(buf, n, i, "PAGE\x00" as *u8) == 1 { e = i; go = 0 }
98 else { i = sc_next_line(buf, n, i) }
99 }
100 }
101 }
102 return e
103}
104
105// completeness: count of panels missing the required "shot" field (0 = drawable)
106func mg_validate(buf: *u8, n: i64) -> i64 {
107 var miss: i64 = 0
108 let np: i64 = mg_count_panels(buf, n)
109 var idx: i64 = 0
110 while idx < np {
111 let s: i64 = mg_panel_start(buf, n, idx)
112 let e: i64 = mg_panel_end(buf, n, s)
113 let sub: *u8 = (buf as i64 + s) as *u8
114 if sc_has(sub, e - s, "shot\x00" as *u8) == 0 { miss = miss + 1 }
115 idx = idx + 1
116 }
117 return miss
118}
119
120// emit the image-gen prompt for panel idx: "<shot>, <char>, <caption>"
121func mg_emit_panel_prompt(buf: *u8, n: i64, idx: i64, out: *u8, cap: i64) -> i64 {
122 let s: i64 = mg_panel_start(buf, n, idx)
123 if s < 0 { out[0] = 0 as u8; return 0 }
124 let e: i64 = mg_panel_end(buf, n, s)
125 let sub: *u8 = (buf as i64 + s) as *u8
126 let sn: i64 = e - s
127 var p: i64 = 0
128 p = sc_app_field(sub, sn, "shot\x00" as *u8, out, p, cap)
129 p = sc_app_lit(out, p, cap, ", \x00" as *u8)
130 p = sc_app_field(sub, sn, "char\x00" as *u8, out, p, cap)
131 p = sc_app_lit(out, p, cap, ", \x00" as *u8)
132 p = sc_app_field(sub, sn, "caption\x00" as *u8, out, p, cap)
133 out[p] = 0 as u8
134 return p
135}