1implement RImagefile; 2 3include "sys.m"; 4 sys: Sys; 5 6include "draw.m"; 7 draw: Draw; 8 Point: import Draw; 9 10include "bufio.m"; 11 bufio: Bufio; 12 Iobuf: import bufio; 13 14include "imagefile.m"; 15 16include "crc.m"; 17 crc: Crc; 18 CRCstate: import Crc; 19 20include "filter.m"; 21 inflate: Filter; 22 23Chunk: adt { 24 size : int; 25 typ: string; 26 crc_state: ref CRCstate; 27}; 28 29Png: adt { 30 depth: int; 31 filterbpp: int; 32 colortype: int; 33 compressionmethod: int; 34 filtermethod: int; 35 interlacemethod: int; 36 # tRNS 37 PLTEsize: int; 38 tRNS: array of byte; 39 # state for managing unpacking 40 alpha: int; 41 done: int; 42 error: string; 43 row, rowstep, colstart, colstep: int; 44 phase: int; 45 phasecols: int; 46 phaserows: int; 47 rowsize: int; 48 rowbytessofar: int; 49 thisrow: array of byte; 50 lastrow: array of byte; 51}; 52 53init(iomod: Bufio) 54{ 55 if(sys == nil) 56 sys = load Sys Sys->PATH; 57 if(crc == nil) 58 crc = load Crc Crc->PATH; 59 if(inflate == nil) 60 inflate = load Filter "/dis/lib/inflate.dis"; 61 inflate->init(); 62 bufio = iomod; 63} 64 65readmulti(fd: ref Iobuf): (array of ref Rawimage, string) 66{ 67 (i, err) := read(fd); 68 if(i != nil){ 69 a := array[1] of { i }; 70 return (a, err); 71 } 72 return (nil, err); 73} 74 75read(fd: ref Iobuf): (ref Rawimage, string) 76{ 77 chunk := ref Chunk; 78 png := ref Png; 79 raw := ref Rawimage; 80 81 chunk.crc_state = crc->init(0, int 16rffffffff); 82# Check it's a PNG 83 if (!get_signature(fd)) 84 return (nil, "not a PNG"); 85# Get the IHDR 86 if (!get_chunk_header(fd, chunk)) 87 return (nil, "duff header"); 88 if (chunk.typ != "IHDR") 89 return (nil, "IHDR must come first"); 90 if (chunk.size != 13) 91 return (nil, "IHDR wrong size"); 92 raw.r.max.x = get_int(fd, chunk.crc_state); 93 if (raw.r.max.x <= 0) 94 return (nil, "invalid width"); 95 raw.r.max.y = get_int(fd, chunk.crc_state); 96 if (raw.r.max.y <= 0) 97 return (nil, "invalid height"); 98 png.depth = get_byte(fd, chunk.crc_state); 99 case png.depth { 100 1 or 2 or 4 or 8 or 16 => 101 ; 102 * => 103 return (nil, "invalid depth"); 104 } 105 png.colortype = get_byte(fd, chunk.crc_state); 106 107 okcombo : int; 108 109 case png.colortype { 110 0 => 111 okcombo = 1; 112 raw.nchans = 1; 113 raw.chandesc = RImagefile->CY; 114 png.alpha = 0; 115 2 => 116 okcombo = (png.depth == 8 || png.depth == 16); 117 raw.nchans = 3; 118 raw.chandesc = RImagefile->CRGB; 119 png.alpha = 0; 120 3 => 121 okcombo = (png.depth != 16); 122 raw.nchans = 1; 123 raw.chandesc = RImagefile->CRGB1; 124 png.alpha = 0; 125 4 => 126 okcombo = (png.depth == 8 || png.depth == 16); 127 raw.nchans = 1; 128 raw.chandesc = RImagefile->CY; 129 png.alpha = 1; 130 6 => 131 okcombo = (png.depth == 8 || png.depth == 16); 132 raw.nchans = 3; 133 raw.chandesc = RImagefile->CRGB; 134 png.alpha = 1; 135 * => 136 return (nil, "invalid colortype"); 137 } 138 if (!okcombo) 139 return (nil, "invalid depth/colortype combination"); 140 png.compressionmethod = get_byte(fd, chunk.crc_state); 141 if (png.compressionmethod != 0) 142 return (nil, "invalid compression method " + string png.compressionmethod); 143 png.filtermethod = get_byte(fd, chunk.crc_state); 144 if (png.filtermethod != 0) 145 return (nil, "invalid filter method"); 146 png.interlacemethod = get_byte(fd, chunk.crc_state); 147 if (png.interlacemethod != 0 && png.interlacemethod != 1) 148 return (nil, "invalid interlace method"); 149 if(0) 150 sys->print("width %d height %d depth %d colortype %d interlace %d\n", 151 raw.r.max.x, raw.r.max.y, png.depth, png.colortype, png.interlacemethod); 152 if (!get_crc_and_check(fd, chunk)) 153 return (nil, "invalid CRC"); 154# Stash some detail in raw 155 raw.r.min = Point(0, 0); 156 raw.transp = 0; 157 raw.chans = array[raw.nchans] of array of byte; 158 { 159 for (r:= 0; r < raw.nchans; r++) 160 raw.chans[r] = array[raw.r.max.x * raw.r.max.y] of byte; 161 } 162# Get the next chunk 163 seenPLTE := 0; 164 seenIDAT := 0; 165 seenLastIDAT := 0; 166 inflateFinished := 0; 167 seenIEND := 0; 168 seentRNS := 0; 169 rq: chan of ref Filter->Rq; 170 171 png.error = nil; 172 rq = nil; 173 while (png.error == nil) { 174 if (!get_chunk_header(fd, chunk)) { 175 if (!seenIEND) 176 png.error = "duff header"; 177 break; 178 } 179 if (seenIEND) { 180 png.error = "rubbish at eof"; 181 break; 182 } 183 case (chunk.typ) { 184 "IEND" => 185 seenIEND = 1; 186 "PLTE" => 187 if (seenPLTE) { 188 png.error = "too many PLTEs"; 189 break; 190 } 191 if (seentRNS) { 192 png.error = "tRNS before PLTE"; 193 break; 194 } 195 if (seenIDAT) { 196 png.error = "PLTE too late"; 197 break; 198 } 199 if (chunk.size % 3 || chunk.size < 1 * 3 || chunk.size > 256 * 3) { 200 png.error = "PLTE strange size"; 201 break; 202 } 203 if (png.colortype == 0 || png.colortype == 4) { 204 png.error = "superfluous PLTE"; 205 break; 206 } 207 raw.cmap = array[256 * 3] of byte; 208 png.PLTEsize = chunk.size / 3; 209 if (!get_bytes(fd, chunk.crc_state, raw.cmap, chunk.size)) { 210 png.error = "eof in PLTE"; 211 break; 212 } 213# { 214# x: int; 215# sys->print("Palette:\n"); 216# for (x = 0; x < chunk.size; x += 3) 217# sys->print("%3d: (%3d, %3d, %3d)\n", 218# x / 3, int raw.cmap[x], int raw.cmap[x + 1], int raw.cmap[x + 2]); 219# } 220 seenPLTE = 1; 221 "tRNS" => 222 if (seenIDAT) { 223 png.error = "tRNS too late"; 224 break; 225 } 226 case png.colortype { 227 0 => 228 if (chunk.size != 2) { 229 png.error = "tRNS wrong size"; 230 break; 231 } 232 level := get_ushort(fd, chunk.crc_state); 233 if (level < 0) { 234 png.error = "eof in tRNS"; 235 break; 236 } 237 if (png.depth != 16) { 238 raw.transp = 1; 239 raw.trindex = byte level; 240 } 241 2 => 242 # a legitimate coding, but we can't use the information 243 if (!skip_bytes(fd, chunk.crc_state, chunk.size)) 244 png.error = "eof in skipped tRNS chunk"; 245 break; 246 3 => 247 if (!seenPLTE) { 248 png.error = "tRNS too early"; 249 break; 250 } 251 if (chunk.size > png.PLTEsize) { 252 png.error = "tRNS too big"; 253 break; 254 } 255 png.tRNS = array[png.PLTEsize] of byte; 256 for (x := chunk.size; x < png.PLTEsize; x++) 257 png.tRNS[x] = byte 255; 258 if (!get_bytes(fd, chunk.crc_state, png.tRNS, chunk.size)) { 259 png.error = "eof in tRNS"; 260 break; 261 } 262# { 263# sys->print("tRNS:\n"); 264# for (x = 0; x < chunk.size; x++) 265# sys->print("%3d: (%3d)\n", x, int png.tRNS[x]); 266# } 267 if (png.error == nil) { 268 # analyse the tRNS chunk to see if it contains a single transparent index 269 # translucent entries are treated as opaque 270 for (x = 0; x < chunk.size; x++) 271 if (png.tRNS[x] == byte 0) { 272 raw.trindex = byte x; 273 if (raw.transp) { 274 raw.transp = 0; 275 break; 276 } 277 raw.transp = 1; 278 } 279# if (raw.transp) 280# sys->print("selected index %d\n", int raw.trindex); 281 } 282 4 or 6 => 283 png.error = "tRNS invalid when alpha present"; 284 } 285 seentRNS = 1; 286 "IDAT" => 287 if (seenLastIDAT) { 288 png.error = "non contiguous IDATs"; 289 break; 290 } 291 if (inflateFinished) { 292 png.error = "too many IDATs"; 293 break; 294 } 295 remaining := 0; 296 if (!seenIDAT) { 297 # open channel to inflate filter 298 if (!processdatainit(png, raw)) 299 break; 300 rq = inflate->start(nil); 301 skip_bytes(fd, chunk.crc_state, 2); 302 remaining = chunk.size - 2; 303 } 304 else 305 remaining = chunk.size; 306 while (remaining && png.error == nil) { 307 pick m := <- rq { 308 Fill => 309# sys->print("Fill(%d) remaining %d\n", len m.buf, remaining); 310 toget := len m.buf; 311 if (toget > remaining) 312 toget = remaining; 313 if (!get_bytes(fd, chunk.crc_state, m.buf, toget)) { 314 m.reply <-= -1; 315 png.error = "eof during IDAT"; 316 break; 317 } 318 m.reply <-= toget; 319 remaining -= toget; 320 Result => 321# sys->print("Result(%d)\n", len m.buf); 322 m.reply <-= 0; 323 processdata(png, raw, m.buf); 324 Info => 325# sys->print("Info(%s)\n", m.msg); 326 Finished => 327 inflateFinished = 1; 328# sys->print("Finished\n"); 329 Error => 330 return (nil, "inflate error\n"); 331 } 332 } 333 seenIDAT = 1; 334 * => 335 # skip the blighter 336 if (!skip_bytes(fd, chunk.crc_state, chunk.size)) 337 png.error = "eof in skipped chunk"; 338 } 339 if (png.error != nil) 340 break; 341 if (!get_crc_and_check(fd, chunk)) 342 return (nil, "invalid CRC"); 343 if (chunk.typ != "IDAT" && seenIDAT) 344 seenLastIDAT = 1; 345 } 346 # can only get here if IEND was last chunk, or png.error set 347 348 if (png.error == nil && !seenIDAT) { 349 png.error = "no IDAT!"; 350 inflateFinished = 1; 351 } 352 while (rq != nil && !inflateFinished) { 353 pick m := <-rq { 354 Fill => 355# sys->print("Fill(%d)\n", len m.buf); 356 png.error = "eof in zlib stream"; 357 m.reply <-= -1; 358 inflateFinished = 1; 359 Result => 360# sys->print("Result(%d)\n", len m.buf); 361 if (png.error != nil) { 362 m.reply <-= -1; 363 inflateFinished = 1; 364 } 365 else { 366 m.reply <-= 0; 367 processdata(png, raw, m.buf); 368 } 369 Info => 370# sys->print("Info(%s)\n", m.msg); 371 Finished => 372# sys->print("Finished\n"); 373 inflateFinished = 1; 374 break; 375 Error => 376 png.error = "inflate error\n"; 377 inflateFinished = 1; 378 } 379 380 } 381 if (png.error == nil && !png.done) 382 png.error = "insufficient data"; 383 return (raw, png.error); 384} 385 386phase2stepping(phase: int): (int, int, int, int) 387{ 388 case phase { 389 0 => 390 return (0, 1, 0, 1); 391 1 => 392 return (0, 8, 0, 8); 393 2 => 394 return (0, 8, 4, 8); 395 3 => 396 return (4, 8, 0, 4); 397 4 => 398 return (0, 4, 2, 4); 399 5 => 400 return (2, 4, 0, 2); 401 6 => 402 return (0, 2, 1, 2); 403 7 => 404 return (1, 2, 0, 1); 405 * => 406 return (-1, -1, -1, -1); 407 } 408} 409 410processdatainitphase(png: ref Png, raw: ref Rawimage) 411{ 412 (png.row, png.rowstep, png.colstart, png.colstep) = phase2stepping(png.phase); 413 if (raw.r.max.x > png.colstart) 414 png.phasecols = (raw.r.max.x - png.colstart + png.colstep - 1) / png.colstep; 415 else 416 png.phasecols = 0; 417 if (raw.r.max.y > png.row) 418 png.phaserows = (raw.r.max.y - png.row + png.rowstep - 1) / png.rowstep; 419 else 420 png.phaserows = 0; 421 png.rowsize = png.phasecols * (raw.nchans + png.alpha) * png.depth; 422 png.rowsize = (png.rowsize + 7) / 8; 423 png.rowsize++; # for the filter byte 424 png.rowbytessofar = 0; 425 png.thisrow = array[png.rowsize] of byte; 426 png.lastrow = array[png.rowsize] of byte; 427# sys->print("init phase %d: r (%d, %d, %d) c (%d, %d, %d) (%d)\n", 428# png.phase, png.row, png.rowstep, png.phaserows, 429# png.colstart, png.colstep, png.phasecols, png.rowsize); 430} 431 432processdatainit(png: ref Png, raw: ref Rawimage): int 433{ 434 if (raw.nchans != 1&& raw.nchans != 3) { 435 png.error = "only 1 or 3 channels supported"; 436 return 0; 437 } 438# if (png.interlacemethod != 0) { 439# png.error = "only progressive supported"; 440# return 0; 441# } 442 if (png.colortype == 3 && raw.cmap == nil) { 443 png.error = "PLTE chunk missing"; 444 return 0; 445 } 446 png.done = 0; 447 png.filterbpp = (png.depth * (raw.nchans + png.alpha) + 7) / 8; 448 png.phase = png.interlacemethod; 449 450 processdatainitphase(png, raw); 451 452 return 1; 453} 454 455upconvert(out: array of byte, outstride: int, in: array of byte, pixels: int, bpp: int) 456{ 457 b: byte; 458 bits := pixels * bpp; 459 lim := bits / 8; 460 mask := byte ((1 << bpp) - 1); 461 outx := 0; 462 inx := 0; 463 for (x := 0; x < lim; x++) { 464 b = in[inx]; 465 for (s := 8 - bpp; s >= 0; s -= bpp) { 466 pixel := (b >> s) & mask; 467 ucp := pixel; 468 for (y := bpp; y < 8; y += bpp) 469 ucp |= pixel << y; 470 out[outx] = ucp; 471 outx += outstride; 472 } 473 inx++; 474 } 475 residue := (bits % 8) / bpp; 476 if (residue) { 477 b = in[inx]; 478 for (s := 8 - bpp; s >= 0; s -= bpp) { 479 pixel := (b >> s) & mask; 480 ucp := pixel; 481 for (y := bpp; y < 8; y += bpp) 482 ucp |= pixel << y; 483 out[outx] = ucp; 484 outx += outstride; 485 if (--residue <= 0) 486 break; 487 } 488 } 489} 490 491# expand (1 or 2 or 4) bit to 8 bit without scaling (for palletized stuff) 492 493expand(out: array of byte, outstride: int, in: array of byte, pixels: int, bpp: int) 494{ 495 b: byte; 496 bits := pixels * bpp; 497 lim := bits / 8; 498 mask := byte ((1 << bpp) - 1); 499 outx := 0; 500 inx := 0; 501 for (x := 0; x < lim; x++) { 502 b = in[inx]; 503 for (s := 8 - bpp; s >= 0; s -= bpp) { 504 out[outx] = (b >> s) & mask; 505 outx += outstride; 506 } 507 inx++; 508 } 509 residue := (bits % 8) / bpp; 510 if (residue) { 511 b = in[inx]; 512 for (s := 8 - bpp; s >= 0; s -= bpp) { 513 out[outx] = (b >> s) & mask; 514 outx += outstride; 515 if (--residue <= 0) 516 break; 517 } 518 } 519} 520 521copybytes(out: array of byte, outstride: int, in: array of byte, instride: int, pixels: int) 522{ 523 inx := 0; 524 outx := 0; 525 for (x := 0; x < pixels; x++) { 526 out[outx] = in[inx]; 527 inx += instride; 528 outx += outstride; 529 } 530} 531 532outputrow(png: ref Png, raw: ref Rawimage, row: array of byte) 533{ 534 offset := png.row * raw.r.max.x; 535 case raw.nchans { 536 1 => 537 case (png.depth) { 538 * => 539 png.error = "depth not supported"; 540 return; 541 1 or 2 or 4 => 542 if (raw.chandesc == RImagefile->CRGB1) 543 expand(raw.chans[0][offset + png.colstart:], png.colstep, row, png.phasecols, png.depth); 544 else 545 upconvert(raw.chans[0][offset + png.colstart:], png.colstep, row, png.phasecols, png.depth); 546 8 or 16 => 547 # might have an Alpha channel to ignore! 548 stride := (png.alpha + 1) * png.depth / 8; 549 copybytes(raw.chans[0][offset + png.colstart:], png.colstep, row, stride, png.phasecols); 550 } 551 3 => 552 case (png.depth) { 553 * => 554 png.error = "depth not supported (2)"; 555 return; 556 8 or 16 => 557 # split rgb into three channels 558 bytespc := png.depth / 8; 559 stride := (3 + png.alpha) * bytespc; 560 copybytes(raw.chans[0][offset + png.colstart:], png.colstep, row, stride, png.phasecols); 561 copybytes(raw.chans[1][offset + png.colstart:], png.colstep, row[bytespc:], stride, png.phasecols); 562 copybytes(raw.chans[2][offset + png.colstart:], png.colstep, row[bytespc * 2:], stride, png.phasecols); 563 } 564 } 565} 566 567filtersub(png: ref Png) 568{ 569 subx := 1; 570 for (x := int png.filterbpp + 1; x < png.rowsize; x++) { 571 png.thisrow[x] += png.thisrow[subx]; 572 subx++; 573 } 574} 575 576filterup(png: ref Png) 577{ 578 if (png.row == 0) 579 return; 580 for (x := 1; x < png.rowsize; x++) 581 png.thisrow[x] += png.lastrow[x]; 582} 583 584filteraverage(png: ref Png) 585{ 586 for (x := 1; x < png.rowsize; x++) { 587 a: int; 588 if (x > png.filterbpp) 589 a = int png.thisrow[x - png.filterbpp]; 590 else 591 a = 0; 592 if (png.row != 0) 593 a += int png.lastrow[x]; 594 png.thisrow[x] += byte (a / 2); 595 } 596} 597 598filterpaeth(png: ref Png) 599{ 600 a, b, c: byte; 601 p, pa, pb, pc: int; 602 for (x := 1; x < png.rowsize; x++) { 603 if (x > png.filterbpp) 604 a = png.thisrow[x - png.filterbpp]; 605 else 606 a = byte 0; 607 if (png.row == 0) { 608 b = byte 0; 609 c = byte 0; 610 } else { 611 b = png.lastrow[x]; 612 if (x > png.filterbpp) 613 c = png.lastrow[x - png.filterbpp]; 614 else 615 c = byte 0; 616 } 617 p = int a + int b - int c; 618 pa = p - int a; 619 if (pa < 0) 620 pa = -pa; 621 pb = p - int b; 622 if (pb < 0) 623 pb = -pb; 624 pc = p - int c; 625 if (pc < 0) 626 pc = -pc; 627 if (pa <= pb && pa <= pc) 628 png.thisrow[x] += a; 629 else if (pb <= pc) 630 png.thisrow[x] += b; 631 else 632 png.thisrow[x] += c; 633 } 634} 635 636phaseendcheck(png: ref Png, raw: ref Rawimage): int 637{ 638 if (png.row >= raw.r.max.y || png.rowsize <= 1) { 639 # this phase is over 640 if (png.phase == 0) { 641 png.done = 1; 642 } 643 else { 644 png.phase++; 645 if (png.phase > 7) 646 png.done = 1; 647 else 648 processdatainitphase(png, raw); 649 } 650 return 1; 651 } 652 return 0; 653} 654 655processdata(png: ref Png, raw: ref Rawimage, buf: array of byte) 656{ 657#sys->print("processdata(%d)\n", len buf); 658 if (png.error != nil) 659 return; 660 i := 0; 661 while (i < len buf) { 662 if (png.done) { 663 png.error = "too much data"; 664 return; 665 } 666 if (phaseendcheck(png, raw)) 667 continue; 668 tocopy := (png.rowsize - png.rowbytessofar); 669 if (tocopy > (len buf - i)) 670 tocopy = len buf - i; 671 png.thisrow[png.rowbytessofar :] = buf[i : i + tocopy]; 672 i += tocopy; 673 png.rowbytessofar += tocopy; 674 if (png.rowbytessofar >= png.rowsize) { 675 # a new row has arrived 676 # apply filter here 677#sys->print("phase %d row %d\n", png.phase, png.row); 678 case int png.thisrow[0] { 679 0 => 680 ; 681 1 => 682 filtersub(png); 683 2 => 684 filterup(png); 685 3 => 686 filteraverage(png); 687 4 => 688 filterpaeth(png); 689 * => 690# sys->print("implement filter method %d\n", int png.thisrow[0]); 691 png.error = "filter method unsupported"; 692 return; 693 } 694 # output row 695 if (png.row >= raw.r.max.y) { 696 png.error = "too much data"; 697 return; 698 } 699 outputrow(png, raw, png.thisrow[1 :]); 700 png.row += png.rowstep; 701 save := png.lastrow; 702 png.lastrow = png.thisrow; 703 png.thisrow = save; 704 png.rowbytessofar = 0; 705 } 706 } 707 phaseendcheck(png, raw); 708} 709 710get_signature(fd: ref Iobuf): int 711{ 712 sig := array[8] of { byte 137, byte 80, byte 78, byte 71, byte 13, byte 10, byte 26, byte 10 }; 713 x: int; 714 for (x = 0; x < 8; x++) 715 if (fd.getb() != int sig[x]) 716 return 0; 717 return 1; 718} 719 720get_bytes(fd: ref Iobuf, crc_state: ref CRCstate, buf: array of byte, n: int): int 721{ 722 if (buf == nil) { 723 fd.seek(big n, bufio->SEEKRELA); 724 return 1; 725 } 726 if (fd.read(buf, n) != n) 727 return 0; 728 if (crc_state != nil) 729 crc->crc(crc_state, buf, n); 730 return 1; 731} 732 733skip_bytes(fd: ref Iobuf, crc_state: ref CRCstate, n: int): int 734{ 735 buf := array[1024] of byte; 736 while (n) { 737 thistime: int = 1024; 738 if (thistime > n) 739 thistime = n; 740 if (!get_bytes(fd, crc_state, buf, thistime)) 741 return 0; 742 n -= thistime; 743 } 744 return 1; 745} 746 747get_4(fd: ref Iobuf, crc_state: ref CRCstate, signed: int): (int, int) 748{ 749 buf := array[4] of byte; 750 if (!get_bytes(fd, crc_state, buf, 4)) 751 return (0, 0); 752 if (signed && int buf[0] & 16r80) 753 return (0, 0); 754 r:int = (int buf[0] << 24) | (int buf[1] << 16) | (int buf[2] << 8) | (int buf[3]); 755# sys->print("got int %d\n", r); 756 return (1, r); 757} 758 759get_int(fd: ref Iobuf, crc_state: ref CRCstate): int 760{ 761 ok, r: int; 762 (ok, r) = get_4(fd, crc_state, 1); 763 if (ok) 764 return r; 765 return -1; 766} 767 768get_ushort(fd: ref Iobuf, crc_state: ref CRCstate): int 769{ 770 buf := array[2] of byte; 771 if (!get_bytes(fd, crc_state, buf, 2)) 772 return -1; 773 return (int buf[0] << 8) | int buf[1]; 774} 775 776get_crc_and_check(fd: ref Iobuf, chunk: ref Chunk): int 777{ 778 crc, ok: int; 779 (ok, crc) = get_4(fd, nil, 0); 780 if (!ok) 781 return 0; 782# sys->print("crc: computed %.8ux expected %.8ux\n", chunk.crc_state.crc, crc); 783 if (chunk.crc_state.crc != crc) 784 return 1; 785 return 1; 786} 787 788get_byte(fd: ref Iobuf, crc_state: ref CRCstate): int 789{ 790 buf := array[1] of byte; 791 if (!get_bytes(fd, crc_state, buf, 1)) 792 return -1; 793# sys->print("got byte %d\n", int buf[0]); 794 return int buf[0]; 795} 796 797get_type(fd: ref Iobuf, crc_state: ref CRCstate): string 798{ 799 x: int; 800 buf := array[4] of byte; 801 if (!get_bytes(fd, crc_state, buf, 4)) 802 return nil; 803 for (x = 0; x < 4; x++) { 804 c: int; 805 c = int buf[x]; 806 if (c == bufio->EOF || (c < 65 || c > 90 && c < 97) || c > 122) 807 return nil; 808 } 809 return string buf; 810} 811 812get_chunk_header(fd: ref Iobuf, chunk: ref Chunk): int 813{ 814 chunk.size = get_int(fd, nil); 815 if (chunk.size < 0) 816 return 0; 817 crc->reset(chunk.crc_state); 818 chunk.typ = get_type(fd, chunk.crc_state); 819 if (chunk.typ == nil) 820 return 0; 821# sys->print("%s(%d)\n", chunk.typ, chunk.size); 822 return 1; 823} 824