code wiki / _hdl_build / nx_manga_sites.nx

nx_manga_sites.nx source

↩ module page · 400 lines · 21013 B

1// nx_manga_sites.nx -- R2b PER-SITE ADAPTERS (operator 2026-07-03: "make sure nhentai and mangadex and the others 2// each work as endpoints that we had working before along with all the new ones"). nhentai + MangaDex ship their 3// PAGE LIST in a JSON API (not the chapter HTML), so the generic harvester can't reach them -- each needs a real 4// adapter: detect the site from the URL, build its API URL, and parse the API JSON into the exact ORDERED page-URL 5// list. Everything else (batoto/manhwaread/new sites) falls back to nx_img_harvest (the generic multi-strategy). 6// Patterns GROUNDED from the working Python mm (verified, not assumed): 7// nhentai: /g/<id>/ -> GET https://nhentai.net/api/gallery/<id> 8// JSON media_id + images.pages[].t (j/p/g/w) -> https://i1.nhentai.net/galleries/<media_id>/<i>.<ext> 9// mangadex: /chapter/<uuid> -> GET https://api.mangadex.org/at-home/server/<uuid> 10// JSON baseUrl + chapter.hash + chapter.data[] -> <baseUrl>/data/<hash>/<filename> 11// PURE parsers (na_*_build) are gated deterministically on FIXTURE JSON (legal posture: build the general 12// capability, gate on fixtures, the operator points it where they like). Referer: the shared fetch already sends a 13// full browser UA which both CDNs accept; a referer-capable fetch is the fallback if a CDN 403s (R2d). license_tier: ORIGINAL 14import "nx_syscalls.nx" 15 16func na_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 17func na_apps(dst: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[o + i] = s[i]; i = i + 1 } return o + i } 18func na_lc(c: i64) -> i64 { if c >= 65 { if c <= 90 { return c + 32 } } return c } 19// case-insensitive substring index of needle in hay[0..hn), or -1 20func na_find_ci(hay: *u8, hn: i64, needle: *u8) -> i64 { 21 var nl: i64 = 0; while needle[nl] != (0 as u8) { nl = nl + 1 } 22 if nl == 0 { return 0 - 1 } 23 var i: i64 = 0 24 while i + nl <= hn { 25 var m: i64 = 1; var j: i64 = 0 26 while j < nl { if na_lc(hay[i+j] as i64) != na_lc(needle[j] as i64) { m = 0; j = nl } else { j = j + 1 } } 27 if m == 1 { return i } 28 i = i + 1 29 } 30 return 0 - 1 31} 32// exact substring (case-sensitive) from `from` 33func na_find_from(hay: *u8, hn: i64, needle: *u8, from: i64) -> i64 { 34 var nl: i64 = 0; while needle[nl] != (0 as u8) { nl = nl + 1 } 35 if nl == 0 { return 0 - 1 } 36 if from < 0 { return 0 - 1 } 37 var i: i64 = from 38 while i + nl <= hn { 39 var m: i64 = 1; var j: i64 = 0 40 while j < nl { if hay[i+j] != needle[j] { m = 0; j = nl } else { j = j + 1 } } 41 if m == 1 { return i } 42 i = i + 1 43 } 44 return 0 - 1 45} 46 47// site id: 1=nhentai, 2=mangadex, 0=generic 48func na_detect(url: *u8, n: i64) -> i64 { 49 if na_find_ci(url, n, "nhentai.net" as *u8) >= 0 { if na_find_ci(url, n, "/g/" as *u8) >= 0 { return 1 } } 50 if na_find_ci(url, n, "mangadex.org" as *u8) >= 0 { if na_find_ci(url, n, "/chapter/" as *u8) >= 0 { return 2 } } 51 return 0 52} 53 54// build the nhentai gallery API url from a /g/<id>/ page url. returns len, 0 if no id. 55func na_nhentai_api(url: *u8, n: i64, out: *u8) -> i64 { 56 let at: i64 = na_find_ci(url, n, "/g/" as *u8) 57 if at < 0 { return 0 } 58 var i: i64 = at + 3 59 var o: i64 = na_apps(out, 0, "https://nhentai.net/api/gallery/" as *u8) 60 var any: i64 = 0 61 while i < n { let c: i64 = url[i] as i64; if c >= 48 { if c <= 57 { out[o] = url[i]; o = o + 1; any = 1; i = i + 1 } else { i = n } } else { i = n } } 62 out[o] = 0 as u8 63 if any == 0 { return 0 } 64 return o 65} 66// build the mangadex at-home API url from a /chapter/<uuid> page url. returns len, 0 if no id. 67func na_mangadex_api(url: *u8, n: i64, out: *u8) -> i64 { 68 let at: i64 = na_find_ci(url, n, "/chapter/" as *u8) 69 if at < 0 { return 0 } 70 var i: i64 = at + 9 71 var o: i64 = na_apps(out, 0, "https://api.mangadex.org/at-home/server/" as *u8) 72 var any: i64 = 0 73 while i < n { let c: i64 = url[i] as i64 74 var ok: i64 = 0 75 if c >= 48 { if c <= 57 { ok = 1 } } 76 if c >= 97 { if c <= 122 { ok = 1 } } 77 if c >= 65 { if c <= 90 { ok = 1 } } 78 if c == 45 { ok = 1 } 79 if ok == 1 { out[o] = url[i]; o = o + 1; any = 1; i = i + 1 } else { i = n } 80 } 81 out[o] = 0 as u8 82 if any == 0 { return 0 } 83 return o 84} 85 86// build the mangadex chapter-metadata API url (series title + chapter number) from a /chapter/<uuid> page url. 87func na_mangadex_meta_api(url: *u8, n: i64, out: *u8) -> i64 { 88 let at: i64 = na_find_ci(url, n, "/chapter/" as *u8) 89 if at < 0 { return 0 } 90 var i: i64 = at + 9 91 var o: i64 = na_apps(out, 0, "https://api.mangadex.org/chapter/" as *u8) 92 var any: i64 = 0 93 while i < n { let c: i64 = url[i] as i64 94 var ok: i64 = 0 95 if c >= 48 { if c <= 57 { ok = 1 } } 96 if c >= 97 { if c <= 122 { ok = 1 } } 97 if c >= 65 { if c <= 90 { ok = 1 } } 98 if c == 45 { ok = 1 } 99 if ok == 1 { out[o] = url[i]; o = o + 1; any = 1; i = i + 1 } else { i = n } 100 } 101 o = na_apps(out, o, "?includes[]=manga" as *u8) 102 out[o] = 0 as u8 103 if any == 0 { return 0 } 104 return o 105} 106 107// parse "<Series> - Ch.<N>" from a /chapter?includes[]=manga response. The MANGA relationship's title is the 108// object form `title":{"en":"..."` (the chapter's own title is a plain string), so that marker finds the series. 109// returns len (0 if no series found -> caller keeps its default title). Also the SERIES-RECOGNITION key. 110func na_mangadex_title(json: *u8, jn: i64, out: *u8, cap: i64) -> i64 { 111 // the MANGA title is the OBJECT form `title":{"<lang>":"..."` (chapter title is a plain string). First lang wins. 112 let m: i64 = na_find_from(json, jn, "title\":{" as *u8, 0) 113 if m < 0 { return 0 } 114 var a: i64 = m + 8 // just past `title":{` 115 var r1: i64 = 1 // skip the lang key up to its ':' separator 116 while r1 == 1 { if a >= jn { r1 = 0 } else { if (json[a] as i64) == 58 { a = a + 1; r1 = 0 } else { a = a + 1 } } } 117 var r2: i64 = 1 // advance to the opening '"' of the value 118 while r2 == 1 { if a >= jn { r2 = 0 } else { if (json[a] as i64) == 34 { a = a + 1; r2 = 0 } else { a = a + 1 } } } 119 var o: i64 = 0 120 while a < jn { let c: i64 = json[a] as i64; if c == 34 { a = jn } else { if c == 92 { a = a + 1 } else { if o < cap - 24 { out[o] = json[a] as u8; o = o + 1 } a = a + 1 } } } 121 let ch: i64 = na_find_from(json, jn, "\"chapter\":\"" as *u8, 0) 122 if ch >= 0 { 123 var b: i64 = ch + 11 // just past `"chapter":"` 124 o = na_apps(out, o, " - Ch." as *u8) 125 while b < jn { let c2: i64 = json[b] as i64; if c2 == 34 { b = jn } else { if o < cap - 2 { out[o] = json[b] as u8; o = o + 1 } b = b + 1 } } 126 } 127 out[o] = 0 as u8 128 return o 129} 130 131// offset of the char just after the closing quote of `"key"` in buf, or -1 132func na_key_end(buf: *u8, n: i64, key: *u8) -> i64 { 133 let kq: *u8 = sys_mmap(128) 134 var ko: i64 = 0; kq[ko]=34 as u8; ko=ko+1 135 var ki: i64 = 0; while key[ki] != (0 as u8) { kq[ko]=key[ki]; ko=ko+1; ki=ki+1 } 136 kq[ko]=34 as u8; ko=ko+1; kq[ko]=0 as u8 // kq content = "key" (quote+key+quote); its length = ko 137 let at: i64 = na_find_from(buf, n, kq, 0) 138 if at < 0 { return 0 - 1 } 139 return at + ko // just past the closing quote 140} 141// 1 if c is JSON whitespace 142func na_ws(c: i64) -> i64 { if c==32 { return 1 } if c==9 { return 1 } if c==10 { return 1 } if c==13 { return 1 } return 0 } 143// index of the first byte == ch in buf[from..limit), or -1 144func na_char_from(buf: *u8, limit: i64, ch: i64, from: i64) -> i64 { 145 var i: i64 = from 146 while i < limit { if (buf[i] as i64) == ch { return i } i = i + 1 } 147 return 0 - 1 148} 149// find "key": "value" -> copy value (JSON \/-> /) into out. returns len, -1 absent. 150func na_json_str(buf: *u8, n: i64, key: *u8, out: *u8, cap: i64) -> i64 { 151 let ke: i64 = na_key_end(buf, n, key) 152 if ke < 0 { return 0 - 1 } 153 // clean scan: skip ws, require ':', skip ws, require '"', then read the value 154 var i: i64 = ke 155 var st: i64 = 0 // 0=want colon, 1=want quote, 2=reading 156 var o: i64 = 0 157 while i < n { 158 let c: i64 = buf[i] as i64 159 if st == 0 { 160 if c == 58 { st = 1 } else { if na_ws(c)==0 { return 0 - 1 } } 161 i = i + 1 162 } else { if st == 1 { 163 if c == 34 { st = 2 } else { if na_ws(c)==0 { return 0 - 1 } } 164 i = i + 1 165 } else { 166 if c == 34 { out[o] = 0 as u8; return o } 167 if c == 92 { if i + 1 < n { if buf[i+1]==(47 as u8) { if o<cap-1 { out[o]=47 as u8; o=o+1 } i=i+2 } else { if o<cap-1 { out[o]=buf[i] as u8; o=o+1 } i=i+1 } } else { i = i + 1 } } 168 else { if o<cap-1 { out[o]=buf[i]; o=o+1 } i=i+1 } 169 } } 170 } 171 return 0 - 1 172} 173func na_num(dst: *u8, o: i64, v: i64) -> i64 { let t: *u8=sys_mmap(24); var m: i64=v; var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var w: i64=o; var i: i64=0; while i<k{dst[w]=t[k-1-i]; w=w+1; i=i+1} return w } 174func na_ext_for(t: i64) -> *u8 { 175 if t == 112 { return "png" as *u8 } // p 176 if t == 103 { return "gif" as *u8 } // g 177 if t == 119 { return "webp" as *u8 } // w 178 return "jpg" as *u8 // j (default) 179} 180 181// ---- TITLE (general capability: "grab the title so I can just focus on reading") ---- 182// extract a human title from a page: prefer og:title, else <title> (strip a trailing " » site" / " :: site"). 183// unescapes &amp; &#39; &quot; &lt; &gt;. returns length, 0 if none. 184func na_html_title(html: *u8, hlen: i64, out: *u8, cap: i64) -> i64 { 185 var s: i64 = 0 - 1; var e: i64 = 0 - 1 186 // og:title content="..." 187 let og: i64 = na_find_ci(html, hlen, "og:title" as *u8) 188 if og >= 0 { 189 let ct: i64 = na_find_from(html, hlen, "content=" as *u8, og) 190 if ct >= 0 { let q: i64 = na_char_from(html, hlen, 34, ct); if q >= 0 { s = q + 1; e = na_char_from(html, hlen, 34, s) } } 191 } 192 if s < 0 { 193 let tt: i64 = na_find_ci(html, hlen, "<title>" as *u8) 194 if tt >= 0 { s = tt + 7; e = na_find_from(html, hlen, "<" as *u8, s) } 195 } 196 if s < 0 { return 0 } 197 if e < 0 { e = s } 198 // copy s..e with entity decode 199 var o: i64 = 0 200 var i: i64 = s 201 while i < e { 202 let c: i64 = html[i] as i64 203 if c == 38 { // '&' -> maybe an entity 204 let cl: i64 = na_ent_len(html, e, i) // input bytes consumed (1 if not an entity) 205 let dec: i64 = na_ent_byte(html, e, i) // decoded byte, or -1 if not an entity 206 if dec >= 0 { if o < cap-1 { out[o]=dec as u8; o=o+1 } i = i + cl } 207 else { if o < cap-1 { out[o]=html[i]; o=o+1 } i = i + 1 } 208 } else { if o < cap-1 { out[o]=html[i]; o=o+1 } i = i + 1 } 209 } 210 out[o] = 0 as u8 211 // strip a trailing " » site" separator (0xc2 0xbb = '»' in UTF-8) 212 var j: i64 = 0 213 while j + 2 < o { if out[j]==(32 as u8) { if out[j+1]==(0xc2 as u8) { if out[j+2]==(0xbb as u8) { out[j]=0 as u8; return j } } } j = j + 1 } 214 return o 215} 216func na_ent_is(html: *u8, e: i64, i: i64, ent: *u8) -> i64 { var k: i64=0; while ent[k]!=(0 as u8) { if i+k>=e { return 0 } if html[i+k]!=ent[k] { return 0 } k=k+1 } return 1 } 217// decoded byte for a known entity at i, or -1 if none 218func na_ent_byte(html: *u8, e: i64, i: i64) -> i64 { 219 if na_ent_is(html, e, i, "&amp;" as *u8)==1 { return 38 } 220 if na_ent_is(html, e, i, "&#39;" as *u8)==1 { return 39 } 221 if na_ent_is(html, e, i, "&quot;" as *u8)==1 { return 34 } 222 if na_ent_is(html, e, i, "&lt;" as *u8)==1 { return 60 } 223 if na_ent_is(html, e, i, "&gt;" as *u8)==1 { return 62 } 224 return 0 - 1 225} 226// input bytes consumed by a known entity at i (1 if not an entity) 227func na_ent_len(html: *u8, e: i64, i: i64) -> i64 { 228 if na_ent_is(html, e, i, "&amp;" as *u8)==1 { return 5 } 229 if na_ent_is(html, e, i, "&#39;" as *u8)==1 { return 5 } 230 if na_ent_is(html, e, i, "&quot;" as *u8)==1 { return 6 } 231 if na_ent_is(html, e, i, "&lt;" as *u8)==1 { return 4 } 232 if na_ent_is(html, e, i, "&gt;" as *u8)==1 { return 4 } 233 return 1 234} 235 236// ---- nhentai PAGE-BASED extraction (the v1 API is deprecated -> 403; parse the gallery PAGE instead) ---- 237// The page embeds thumbnails t{d}.nhentai.net/galleries/<media_id>/<n>t.<ext> for the current gallery (before the 238// related-galleries section). Full-res = i1.nhentai.net/galleries/<media_id>/<n>.<ext>. Returns count, -1 fail. 239func na_isdig(c: i64) -> i64 { if c>=48 { if c<=57 { return 1 } } return 0 } 240func na_islet(c: i64) -> i64 { let l: i64 = na_lc(c); if l>=97 { if l<=122 { return 1 } } return 0 } 241// index of first NON-digit at/after `from` (bounded by limit) -- clean, no sentinel 242func na_end_digits(buf: *u8, limit: i64, from: i64) -> i64 { var i: i64 = from; while i < limit { if na_isdig(buf[i] as i64)==1 { i = i + 1 } else { return i } } return i } 243func na_end_letters(buf: *u8, limit: i64, from: i64) -> i64 { var i: i64 = from; while i < limit { if na_islet(buf[i] as i64)==1 { i = i + 1 } else { return i } } return i } 244func na_nhentai_from_page(html: *u8, hlen: i64, url_buf: *u8, cap: i64, offs: *i64, lens: *i64, max: i64) -> i64 { 245 // 1. current gallery media_id = the first /galleries/<digits>/<digits>t.<ext> 246 let mid: *u8 = sys_mmap(64); var midlen: i64 = 0 247 var i: i64 = 0; var found: i64 = 0 248 while found == 0 { 249 let at: i64 = na_find_from(html, hlen, "/galleries/" as *u8, i) 250 if at < 0 { found = 0 - 1 } else { 251 let ms: i64 = at + 11 252 let p: i64 = na_end_digits(html, hlen, ms) // end of media_id digits 253 if p > ms { if p < hlen { if html[p]==(47 as u8) { // '/' 254 let qs: i64 = p + 1 255 let q: i64 = na_end_digits(html, hlen, qs) // end of page-number digits 256 if q > qs { if q < hlen { if html[q]==(116 as u8) { // 't' -> page thumbnail 257 var m: i64 = 0; while m < p - ms { mid[m]=html[ms+m]; m=m+1 } mid[p-ms]=0 as u8; midlen = p - ms; found = 1 258 } } } 259 } } } 260 i = at + 1 261 } 262 } 263 if found != 1 { return 0 - 1 } 264 // 2. collect all /galleries/<mid>/<n>t.<ext> for this mid, deduped by full URL, in order. 265 let key: *u8 = sys_mmap(128); var ko: i64 = na_apps(key, 0, "/galleries/" as *u8) 266 var mm: i64 = 0; while mm < midlen { key[ko]=mid[mm]; ko=ko+1; mm=mm+1 } key[ko]=47 as u8; ko=ko+1; key[ko]=0 as u8 267 var count: i64 = 0; var buf_pos: i64 = 0 268 var scan: i64 = 0; var go: i64 = 1 269 while go == 1 { 270 let at2: i64 = na_find_from(html, hlen, key, scan) 271 if at2 < 0 { go = 0 } else { 272 let ns: i64 = at2 + ko // just after "/galleries/<mid>/" 273 let p2: i64 = na_end_digits(html, hlen, ns) 274 var is_page: i64 = 0 275 if p2 > ns { if p2 < hlen { if html[p2]==(116 as u8) { if p2+1 < hlen { if html[p2+1]==(46 as u8) { is_page = 1 } } } } } // <digits> 't' '.' 276 if is_page == 1 { 277 let es: i64 = p2 + 2 278 let ee: i64 = na_end_letters(html, hlen, es) 279 if count < max { 280 // build https://i1.nhentai.net/galleries/<mid>/<n>.<ext> 281 let start: i64 = buf_pos 282 buf_pos = na_apps(url_buf, buf_pos, "https://i1.nhentai.net/galleries/" as *u8) 283 var mi: i64 = 0; while mi < midlen { url_buf[buf_pos]=mid[mi]; buf_pos=buf_pos+1; mi=mi+1 } 284 url_buf[buf_pos]=47 as u8; buf_pos=buf_pos+1 285 var di: i64 = ns; while di < p2 { url_buf[buf_pos]=html[di]; buf_pos=buf_pos+1; di=di+1 } // page number 286 url_buf[buf_pos]=46 as u8; buf_pos=buf_pos+1 287 var xi: i64 = es; while xi < ee { url_buf[buf_pos]=(na_lc(html[xi] as i64)) as u8; buf_pos=buf_pos+1; xi=xi+1 } 288 // dedup by full URL (thumbnails may repeat) 289 let cn: i64 = buf_pos - start 290 if na_seen(url_buf, offs, lens, count, start, cn) == 1 { buf_pos = start } else { offs[count]=start; lens[count]=cn; count=count+1 } 291 } 292 } 293 scan = at2 + ko 294 } 295 } 296 if count <= 0 { return 0 - 1 } 297 return count 298} 299// seen? compare url_buf[start..start+cn) against each collected offs/lens 300func na_seen(url_buf: *u8, offs: *i64, lens: *i64, count: i64, start: i64, cn: i64) -> i64 { 301 var i: i64 = 0 302 while i < count { 303 if lens[i] == cn { 304 var eq: i64 = 1; var k: i64 = 0 305 while k < cn { if url_buf[offs[i]+k] != url_buf[start+k] { eq = 0; k = cn } else { k = k + 1 } } 306 if eq == 1 { return 1 } 307 } 308 i = i + 1 309 } 310 return 0 311} 312 313// nhentai: parse media_id + count of pages[].t -> build i1.nhentai.net page URLs. returns count, -1 on parse fail. 314func na_nhentai_build(json: *u8, jn: i64, url_buf: *u8, cap: i64, offs: *i64, lens: *i64, max: i64) -> i64 { 315 let media: *u8 = sys_mmap(64) 316 let ml: i64 = na_json_str(json, jn, "media_id" as *u8, media, 64) 317 if ml <= 0 { return 0 - 1 } 318 // find the pages array: "pages":[ 319 let pat: i64 = na_find_from(json, jn, "\"pages\"" as *u8, 0) 320 if pat < 0 { return 0 - 1 } 321 let lb: i64 = na_find_from(json, jn, "[" as *u8, pat) 322 if lb < 0 { return 0 - 1 } 323 let rb: i64 = na_find_from(json, jn, "]" as *u8, lb) 324 if rb < 0 { return 0 - 1 } 325 var count: i64 = 0 326 var buf_pos: i64 = 0 327 var i: i64 = lb 328 var page: i64 = 1 329 // each page object contains exactly one "t":"X" 330 var go: i64 = 1 331 while go == 1 { 332 let ti: i64 = na_find_from(json, rb, "\"t\"" as *u8, i) 333 if ti < 0 { go = 0 } else { 334 let q1: i64 = na_char_from(json, rb, 58, ti) // ':' after "t" 335 if q1 < 0 { go = 0 } else { 336 let vq: i64 = na_char_from(json, rb, 34, q1 + 1) // opening value quote 337 if vq < 0 { go = 0 } else { 338 let tchar: i64 = json[vq + 1] as i64 339 let ext: *u8 = na_ext_for(na_lc(tchar)) 340 if count < max { 341 let start: i64 = buf_pos 342 buf_pos = na_apps(url_buf, buf_pos, "https://i1.nhentai.net/galleries/" as *u8) 343 var mi: i64 = 0; while mi < ml { url_buf[buf_pos]=media[mi]; buf_pos=buf_pos+1; mi=mi+1 } 344 url_buf[buf_pos]=47 as u8; buf_pos=buf_pos+1 345 buf_pos = na_num(url_buf, buf_pos, page) 346 url_buf[buf_pos]=46 as u8; buf_pos=buf_pos+1 347 buf_pos = na_apps(url_buf, buf_pos, ext) 348 offs[count]=start; lens[count]=buf_pos-start; count=count+1 349 } 350 page = page + 1 351 i = vq + 2 352 } 353 } 354 } 355 } 356 if count <= 0 { return 0 - 1 } 357 return count 358} 359 360// mangadex: parse baseUrl + chapter.hash + data[] -> build <baseUrl>/data/<hash>/<file>. returns count, -1 fail. 361func na_mangadex_build(json: *u8, jn: i64, url_buf: *u8, cap: i64, offs: *i64, lens: *i64, max: i64) -> i64 { 362 let base: *u8 = sys_mmap(512) 363 let bl: i64 = na_json_str(json, jn, "baseUrl" as *u8, base, 512) 364 if bl <= 0 { return 0 - 1 } 365 let hash: *u8 = sys_mmap(128) 366 let hl: i64 = na_json_str(json, jn, "hash" as *u8, hash, 128) 367 if hl <= 0 { return 0 - 1 } 368 // "data":[ (the full-quality array; dataSaver is separate) 369 let dat: i64 = na_find_from(json, jn, "\"data\"" as *u8, 0) 370 if dat < 0 { return 0 - 1 } 371 let lb: i64 = na_find_from(json, jn, "[" as *u8, dat) 372 if lb < 0 { return 0 - 1 } 373 let rb: i64 = na_find_from(json, jn, "]" as *u8, lb) 374 if rb < 0 { return 0 - 1 } 375 var count: i64 = 0 376 var buf_pos: i64 = 0 377 var i: i64 = lb + 1 378 var go: i64 = 1 379 while go == 1 { 380 let sq: i64 = na_char_from(json, rb, 34, i) // opening quote of the next filename string 381 if sq < 0 { go = 0 } else { 382 let e: i64 = na_char_from(json, rb, 34, sq + 1) // closing quote 383 if e < 0 { go = 0 } else { 384 let fl: i64 = e - (sq + 1) 385 if fl > 0 { if count < max { 386 let start: i64 = buf_pos 387 var q: i64 = 0; while q < bl { url_buf[buf_pos]=base[q]; buf_pos=buf_pos+1; q=q+1 } 388 buf_pos = na_apps(url_buf, buf_pos, "/data/" as *u8) 389 q = 0; while q < hl { url_buf[buf_pos]=hash[q]; buf_pos=buf_pos+1; q=q+1 } 390 url_buf[buf_pos]=47 as u8; buf_pos=buf_pos+1 391 q = 0; while q < fl { url_buf[buf_pos]=json[sq+1+q]; buf_pos=buf_pos+1; q=q+1 } 392 offs[count]=start; lens[count]=buf_pos-start; count=count+1 393 } } 394 i = e + 1 395 } 396 } 397 } 398 if count <= 0 { return 0 - 1 } 399 return count 400}