summaryrefslogtreecommitdiff
path: root/appl/lib/readpng.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/lib/readpng.b')
-rw-r--r--appl/lib/readpng.b823
1 files changed, 823 insertions, 0 deletions
diff --git a/appl/lib/readpng.b b/appl/lib/readpng.b
new file mode 100644
index 00000000..9cff27ec
--- /dev/null
+++ b/appl/lib/readpng.b
@@ -0,0 +1,823 @@
+implement RImagefile;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+ draw: Draw;
+ Point: import Draw;
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "imagefile.m";
+
+include "crc.m";
+ crc: Crc;
+ CRCstate: import Crc;
+
+include "filter.m";
+ inflate: Filter;
+
+Chunk: adt {
+ size : int;
+ typ: string;
+ crc_state: ref CRCstate;
+};
+
+Png: adt {
+ depth: int;
+ filterbpp: int;
+ colortype: int;
+ compressionmethod: int;
+ filtermethod: int;
+ interlacemethod: int;
+ # tRNS
+ PLTEsize: int;
+ tRNS: array of byte;
+ # state for managing unpacking
+ alpha: int;
+ done: int;
+ error: string;
+ row, rowstep, colstart, colstep: int;
+ phase: int;
+ phasecols: int;
+ phaserows: int;
+ rowsize: int;
+ rowbytessofar: int;
+ thisrow: array of byte;
+ lastrow: array of byte;
+};
+
+init(iomod: Bufio)
+{
+ if(sys == nil)
+ sys = load Sys Sys->PATH;
+ if(crc == nil)
+ crc = load Crc Crc->PATH;
+ if(inflate == nil)
+ inflate = load Filter "/dis/lib/inflate.dis";
+ inflate->init();
+ bufio = iomod;
+}
+
+readmulti(fd: ref Iobuf): (array of ref Rawimage, string)
+{
+ (i, err) := read(fd);
+ if(i != nil){
+ a := array[1] of { i };
+ return (a, err);
+ }
+ return (nil, err);
+}
+
+read(fd: ref Iobuf): (ref Rawimage, string)
+{
+ chunk := ref Chunk;
+ png := ref Png;
+ raw := ref Rawimage;
+
+ chunk.crc_state = crc->init(0, int 16rffffffff);
+# Check it's a PNG
+ if (!get_signature(fd))
+ return (nil, "not a PNG");
+# Get the IHDR
+ if (!get_chunk_header(fd, chunk))
+ return (nil, "duff header");
+ if (chunk.typ != "IHDR")
+ return (nil, "IHDR must come first");
+ if (chunk.size != 13)
+ return (nil, "IHDR wrong size");
+ raw.r.max.x = get_int(fd, chunk.crc_state);
+ if (raw.r.max.x <= 0)
+ return (nil, "invalid width");
+ raw.r.max.y = get_int(fd, chunk.crc_state);
+ if (raw.r.max.y <= 0)
+ return (nil, "invalid height");
+ png.depth = get_byte(fd, chunk.crc_state);
+ case png.depth {
+ 1 or 2 or 4 or 8 or 16 =>
+ ;
+ * =>
+ return (nil, "invalid depth");
+ }
+ png.colortype = get_byte(fd, chunk.crc_state);
+
+ okcombo : int;
+
+ case png.colortype {
+ 0 =>
+ okcombo = 1;
+ raw.nchans = 1;
+ raw.chandesc = RImagefile->CY;
+ png.alpha = 0;
+ 2 =>
+ okcombo = (png.depth == 8 || png.depth == 16);
+ raw.nchans = 3;
+ raw.chandesc = RImagefile->CRGB;
+ png.alpha = 0;
+ 3 =>
+ okcombo = (png.depth != 16);
+ raw.nchans = 1;
+ raw.chandesc = RImagefile->CRGB1;
+ png.alpha = 0;
+ 4 =>
+ okcombo = (png.depth == 8 || png.depth == 16);
+ raw.nchans = 1;
+ raw.chandesc = RImagefile->CY;
+ png.alpha = 1;
+ 6 =>
+ okcombo = (png.depth == 8 || png.depth == 16);
+ raw.nchans = 3;
+ raw.chandesc = RImagefile->CRGB;
+ png.alpha = 1;
+ * =>
+ return (nil, "invalid colortype");
+ }
+ if (!okcombo)
+ return (nil, "invalid depth/colortype combination");
+ png.compressionmethod = get_byte(fd, chunk.crc_state);
+ if (png.compressionmethod != 0)
+ return (nil, "invalid compression method " + string png.compressionmethod);
+ png.filtermethod = get_byte(fd, chunk.crc_state);
+ if (png.filtermethod != 0)
+ return (nil, "invalid filter method");
+ png.interlacemethod = get_byte(fd, chunk.crc_state);
+ if (png.interlacemethod != 0 && png.interlacemethod != 1)
+ return (nil, "invalid interlace method");
+ if(0)
+ sys->print("width %d height %d depth %d colortype %d interlace %d\n",
+ raw.r.max.x, raw.r.max.y, png.depth, png.colortype, png.interlacemethod);
+ if (!get_crc_and_check(fd, chunk))
+ return (nil, "invalid CRC");
+# Stash some detail in raw
+ raw.r.min = Point(0, 0);
+ raw.transp = 0;
+ raw.chans = array[raw.nchans] of array of byte;
+ {
+ for (r:= 0; r < raw.nchans; r++)
+ raw.chans[r] = array[raw.r.max.x * raw.r.max.y] of byte;
+ }
+# Get the next chunk
+ seenPLTE := 0;
+ seenIDAT := 0;
+ seenLastIDAT := 0;
+ inflateFinished := 0;
+ seenIEND := 0;
+ seentRNS := 0;
+ rq: chan of ref Filter->Rq;
+
+ png.error = nil;
+ rq = nil;
+ while (png.error == nil) {
+ if (!get_chunk_header(fd, chunk)) {
+ if (!seenIEND)
+ png.error = "duff header";
+ break;
+ }
+ if (seenIEND) {
+ png.error = "rubbish at eof";
+ break;
+ }
+ case (chunk.typ) {
+ "IEND" =>
+ seenIEND = 1;
+ "PLTE" =>
+ if (seenPLTE) {
+ png.error = "too many PLTEs";
+ break;
+ }
+ if (seentRNS) {
+ png.error = "tRNS before PLTE";
+ break;
+ }
+ if (seenIDAT) {
+ png.error = "PLTE too late";
+ break;
+ }
+ if (chunk.size % 3 || chunk.size < 1 * 3 || chunk.size > 256 * 3) {
+ png.error = "PLTE strange size";
+ break;
+ }
+ if (png.colortype == 0 || png.colortype == 4) {
+ png.error = "superfluous PLTE";
+ break;
+ }
+ raw.cmap = array[256 * 3] of byte;
+ png.PLTEsize = chunk.size / 3;
+ if (!get_bytes(fd, chunk.crc_state, raw.cmap, chunk.size)) {
+ png.error = "eof in PLTE";
+ break;
+ }
+# {
+# x: int;
+# sys->print("Palette:\n");
+# for (x = 0; x < chunk.size; x += 3)
+# sys->print("%3d: (%3d, %3d, %3d)\n",
+# x / 3, int raw.cmap[x], int raw.cmap[x + 1], int raw.cmap[x + 2]);
+# }
+ seenPLTE = 1;
+ "tRNS" =>
+ if (seenIDAT) {
+ png.error = "tRNS too late";
+ break;
+ }
+ case png.colortype {
+ 0 =>
+ if (chunk.size != 2) {
+ png.error = "tRNS wrong size";
+ break;
+ }
+ level := get_ushort(fd, chunk.crc_state);
+ if (level < 0) {
+ png.error = "eof in tRNS";
+ break;
+ }
+ if (png.depth != 16) {
+ raw.transp = 1;
+ raw.trindex = byte level;
+ }
+ 2 =>
+ # a legitimate coding, but we can't use the information
+ if (!skip_bytes(fd, chunk.crc_state, chunk.size))
+ png.error = "eof in skipped tRNS chunk";
+ break;
+ 3 =>
+ if (!seenPLTE) {
+ png.error = "tRNS too early";
+ break;
+ }
+ if (chunk.size > png.PLTEsize) {
+ png.error = "tRNS too big";
+ break;
+ }
+ png.tRNS = array[png.PLTEsize] of byte;
+ for (x := chunk.size; x < png.PLTEsize; x++)
+ png.tRNS[x] = byte 255;
+ if (!get_bytes(fd, chunk.crc_state, png.tRNS, chunk.size)) {
+ png.error = "eof in tRNS";
+ break;
+ }
+# {
+# sys->print("tRNS:\n");
+# for (x = 0; x < chunk.size; x++)
+# sys->print("%3d: (%3d)\n", x, int png.tRNS[x]);
+# }
+ if (png.error == nil) {
+ # analyse the tRNS chunk to see if it contains a single transparent index
+ # translucent entries are treated as opaque
+ for (x = 0; x < chunk.size; x++)
+ if (png.tRNS[x] == byte 0) {
+ raw.trindex = byte x;
+ if (raw.transp) {
+ raw.transp = 0;
+ break;
+ }
+ raw.transp = 1;
+ }
+# if (raw.transp)
+# sys->print("selected index %d\n", int raw.trindex);
+ }
+ 4 or 6 =>
+ png.error = "tRNS invalid when alpha present";
+ }
+ seentRNS = 1;
+ "IDAT" =>
+ if (seenLastIDAT) {
+ png.error = "non contiguous IDATs";
+ break;
+ }
+ if (inflateFinished) {
+ png.error = "too many IDATs";
+ break;
+ }
+ remaining := 0;
+ if (!seenIDAT) {
+ # open channel to inflate filter
+ if (!processdatainit(png, raw))
+ break;
+ rq = inflate->start(nil);
+ skip_bytes(fd, chunk.crc_state, 2);
+ remaining = chunk.size - 2;
+ }
+ else
+ remaining = chunk.size;
+ while (remaining && png.error == nil) {
+ pick m := <- rq {
+ Fill =>
+# sys->print("Fill(%d) remaining %d\n", len m.buf, remaining);
+ toget := len m.buf;
+ if (toget > remaining)
+ toget = remaining;
+ if (!get_bytes(fd, chunk.crc_state, m.buf, toget)) {
+ m.reply <-= -1;
+ png.error = "eof during IDAT";
+ break;
+ }
+ m.reply <-= toget;
+ remaining -= toget;
+ Result =>
+# sys->print("Result(%d)\n", len m.buf);
+ m.reply <-= 0;
+ processdata(png, raw, m.buf);
+ Info =>
+# sys->print("Info(%s)\n", m.msg);
+ Finished =>
+ inflateFinished = 1;
+# sys->print("Finished\n");
+ Error =>
+ return (nil, "inflate error\n");
+ }
+ }
+ seenIDAT = 1;
+ * =>
+ # skip the blighter
+ if (!skip_bytes(fd, chunk.crc_state, chunk.size))
+ png.error = "eof in skipped chunk";
+ }
+ if (png.error != nil)
+ break;
+ if (!get_crc_and_check(fd, chunk))
+ return (nil, "invalid CRC");
+ if (chunk.typ != "IDAT" && seenIDAT)
+ seenLastIDAT = 1;
+ }
+ # can only get here if IEND was last chunk, or png.error set
+
+ if (png.error == nil && !seenIDAT) {
+ png.error = "no IDAT!";
+ inflateFinished = 1;
+ }
+ while (rq != nil && !inflateFinished) {
+ pick m := <-rq {
+ Fill =>
+# sys->print("Fill(%d)\n", len m.buf);
+ png.error = "eof in zlib stream";
+ m.reply <-= -1;
+ inflateFinished = 1;
+ Result =>
+# sys->print("Result(%d)\n", len m.buf);
+ if (png.error != nil) {
+ m.reply <-= -1;
+ inflateFinished = 1;
+ }
+ else {
+ m.reply <-= 0;
+ processdata(png, raw, m.buf);
+ }
+ Info =>
+# sys->print("Info(%s)\n", m.msg);
+ Finished =>
+# sys->print("Finished\n");
+ inflateFinished = 1;
+ break;
+ Error =>
+ png.error = "inflate error\n";
+ inflateFinished = 1;
+ }
+
+ }
+ if (png.error == nil && !png.done)
+ png.error = "insufficient data";
+ return (raw, png.error);
+}
+
+phase2stepping(phase: int): (int, int, int, int)
+{
+ case phase {
+ 0 =>
+ return (0, 1, 0, 1);
+ 1 =>
+ return (0, 8, 0, 8);
+ 2 =>
+ return (0, 8, 4, 8);
+ 3 =>
+ return (4, 8, 0, 4);
+ 4 =>
+ return (0, 4, 2, 4);
+ 5 =>
+ return (2, 4, 0, 2);
+ 6 =>
+ return (0, 2, 1, 2);
+ 7 =>
+ return (1, 2, 0, 1);
+ * =>
+ return (-1, -1, -1, -1);
+ }
+}
+
+processdatainitphase(png: ref Png, raw: ref Rawimage)
+{
+ (png.row, png.rowstep, png.colstart, png.colstep) = phase2stepping(png.phase);
+ if (raw.r.max.x > png.colstart)
+ png.phasecols = (raw.r.max.x - png.colstart + png.colstep - 1) / png.colstep;
+ else
+ png.phasecols = 0;
+ if (raw.r.max.y > png.row)
+ png.phaserows = (raw.r.max.y - png.row + png.rowstep - 1) / png.rowstep;
+ else
+ png.phaserows = 0;
+ png.rowsize = png.phasecols * (raw.nchans + png.alpha) * png.depth;
+ png.rowsize = (png.rowsize + 7) / 8;
+ png.rowsize++; # for the filter byte
+ png.rowbytessofar = 0;
+ png.thisrow = array[png.rowsize] of byte;
+ png.lastrow = array[png.rowsize] of byte;
+# sys->print("init phase %d: r (%d, %d, %d) c (%d, %d, %d) (%d)\n",
+# png.phase, png.row, png.rowstep, png.phaserows,
+# png.colstart, png.colstep, png.phasecols, png.rowsize);
+}
+
+processdatainit(png: ref Png, raw: ref Rawimage): int
+{
+ if (raw.nchans != 1&& raw.nchans != 3) {
+ png.error = "only 1 or 3 channels supported";
+ return 0;
+ }
+# if (png.interlacemethod != 0) {
+# png.error = "only progressive supported";
+# return 0;
+# }
+ if (png.colortype == 3 && raw.cmap == nil) {
+ png.error = "PLTE chunk missing";
+ return 0;
+ }
+ png.done = 0;
+ png.filterbpp = (png.depth * (raw.nchans + png.alpha) + 7) / 8;
+ png.phase = png.interlacemethod;
+
+ processdatainitphase(png, raw);
+
+ return 1;
+}
+
+upconvert(out: array of byte, outstride: int, in: array of byte, pixels: int, bpp: int)
+{
+ b: byte;
+ bits := pixels * bpp;
+ lim := bits / 8;
+ mask := byte ((1 << bpp) - 1);
+ outx := 0;
+ inx := 0;
+ for (x := 0; x < lim; x++) {
+ b = in[inx];
+ for (s := 8 - bpp; s >= 0; s -= bpp) {
+ pixel := (b >> s) & mask;
+ ucp := pixel;
+ for (y := bpp; y < 8; y += bpp)
+ ucp |= pixel << y;
+ out[outx] = ucp;
+ outx += outstride;
+ }
+ inx++;
+ }
+ residue := (bits % 8) / bpp;
+ if (residue) {
+ b = in[inx];
+ for (s := 8 - bpp; s >= 0; s -= bpp) {
+ pixel := (b >> s) & mask;
+ ucp := pixel;
+ for (y := bpp; y < 8; y += bpp)
+ ucp |= pixel << y;
+ out[outx] = ucp;
+ outx += outstride;
+ if (--residue <= 0)
+ break;
+ }
+ }
+}
+
+# expand (1 or 2 or 4) bit to 8 bit without scaling (for palletized stuff)
+
+expand(out: array of byte, outstride: int, in: array of byte, pixels: int, bpp: int)
+{
+ b: byte;
+ bits := pixels * bpp;
+ lim := bits / 8;
+ mask := byte ((1 << bpp) - 1);
+ outx := 0;
+ inx := 0;
+ for (x := 0; x < lim; x++) {
+ b = in[inx];
+ for (s := 8 - bpp; s >= 0; s -= bpp) {
+ out[outx] = (b >> s) & mask;
+ outx += outstride;
+ }
+ inx++;
+ }
+ residue := (bits % 8) / bpp;
+ if (residue) {
+ b = in[inx];
+ for (s := 8 - bpp; s >= 0; s -= bpp) {
+ out[outx] = (b >> s) & mask;
+ outx += outstride;
+ if (--residue <= 0)
+ break;
+ }
+ }
+}
+
+copybytes(out: array of byte, outstride: int, in: array of byte, instride: int, pixels: int)
+{
+ inx := 0;
+ outx := 0;
+ for (x := 0; x < pixels; x++) {
+ out[outx] = in[inx];
+ inx += instride;
+ outx += outstride;
+ }
+}
+
+outputrow(png: ref Png, raw: ref Rawimage, row: array of byte)
+{
+ offset := png.row * raw.r.max.x;
+ case raw.nchans {
+ 1 =>
+ case (png.depth) {
+ * =>
+ png.error = "depth not supported";
+ return;
+ 1 or 2 or 4 =>
+ if (raw.chandesc == RImagefile->CRGB1)
+ expand(raw.chans[0][offset + png.colstart:], png.colstep, row, png.phasecols, png.depth);
+ else
+ upconvert(raw.chans[0][offset + png.colstart:], png.colstep, row, png.phasecols, png.depth);
+ 8 or 16 =>
+ # might have an Alpha channel to ignore!
+ stride := (png.alpha + 1) * png.depth / 8;
+ copybytes(raw.chans[0][offset + png.colstart:], png.colstep, row, stride, png.phasecols);
+ }
+ 3 =>
+ case (png.depth) {
+ * =>
+ png.error = "depth not supported (2)";
+ return;
+ 8 or 16 =>
+ # split rgb into three channels
+ bytespc := png.depth / 8;
+ stride := (3 + png.alpha) * bytespc;
+ copybytes(raw.chans[0][offset + png.colstart:], png.colstep, row, stride, png.phasecols);
+ copybytes(raw.chans[1][offset + png.colstart:], png.colstep, row[bytespc:], stride, png.phasecols);
+ copybytes(raw.chans[2][offset + png.colstart:], png.colstep, row[bytespc * 2:], stride, png.phasecols);
+ }
+ }
+}
+
+filtersub(png: ref Png)
+{
+ subx := 1;
+ for (x := int png.filterbpp + 1; x < png.rowsize; x++) {
+ png.thisrow[x] += png.thisrow[subx];
+ subx++;
+ }
+}
+
+filterup(png: ref Png)
+{
+ if (png.row == 0)
+ return;
+ for (x := 1; x < png.rowsize; x++)
+ png.thisrow[x] += png.lastrow[x];
+}
+
+filteraverage(png: ref Png)
+{
+ for (x := 1; x < png.rowsize; x++) {
+ a: int;
+ if (x > png.filterbpp)
+ a = int png.thisrow[x - png.filterbpp];
+ else
+ a = 0;
+ if (png.row != 0)
+ a += int png.lastrow[x];
+ png.thisrow[x] += byte (a / 2);
+ }
+}
+
+filterpaeth(png: ref Png)
+{
+ a, b, c: byte;
+ p, pa, pb, pc: int;
+ for (x := 1; x < png.rowsize; x++) {
+ if (x > png.filterbpp)
+ a = png.thisrow[x - png.filterbpp];
+ else
+ a = byte 0;
+ if (png.row == 0) {
+ b = byte 0;
+ c = byte 0;
+ } else {
+ b = png.lastrow[x];
+ if (x > png.filterbpp)
+ c = png.lastrow[x - png.filterbpp];
+ else
+ c = byte 0;
+ }
+ p = int a + int b - int c;
+ pa = p - int a;
+ if (pa < 0)
+ pa = -pa;
+ pb = p - int b;
+ if (pb < 0)
+ pb = -pb;
+ pc = p - int c;
+ if (pc < 0)
+ pc = -pc;
+ if (pa <= pb && pa <= pc)
+ png.thisrow[x] += a;
+ else if (pb <= pc)
+ png.thisrow[x] += b;
+ else
+ png.thisrow[x] += c;
+ }
+}
+
+phaseendcheck(png: ref Png, raw: ref Rawimage): int
+{
+ if (png.row >= raw.r.max.y || png.rowsize <= 1) {
+ # this phase is over
+ if (png.phase == 0) {
+ png.done = 1;
+ }
+ else {
+ png.phase++;
+ if (png.phase > 7)
+ png.done = 1;
+ else
+ processdatainitphase(png, raw);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+processdata(png: ref Png, raw: ref Rawimage, buf: array of byte)
+{
+#sys->print("processdata(%d)\n", len buf);
+ if (png.error != nil)
+ return;
+ i := 0;
+ while (i < len buf) {
+ if (png.done) {
+ png.error = "too much data";
+ return;
+ }
+ if (phaseendcheck(png, raw))
+ continue;
+ tocopy := (png.rowsize - png.rowbytessofar);
+ if (tocopy > (len buf - i))
+ tocopy = len buf - i;
+ png.thisrow[png.rowbytessofar :] = buf[i : i + tocopy];
+ i += tocopy;
+ png.rowbytessofar += tocopy;
+ if (png.rowbytessofar >= png.rowsize) {
+ # a new row has arrived
+ # apply filter here
+#sys->print("phase %d row %d\n", png.phase, png.row);
+ case int png.thisrow[0] {
+ 0 =>
+ ;
+ 1 =>
+ filtersub(png);
+ 2 =>
+ filterup(png);
+ 3 =>
+ filteraverage(png);
+ 4 =>
+ filterpaeth(png);
+ * =>
+# sys->print("implement filter method %d\n", int png.thisrow[0]);
+ png.error = "filter method unsupported";
+ return;
+ }
+ # output row
+ if (png.row >= raw.r.max.y) {
+ png.error = "too much data";
+ return;
+ }
+ outputrow(png, raw, png.thisrow[1 :]);
+ png.row += png.rowstep;
+ save := png.lastrow;
+ png.lastrow = png.thisrow;
+ png.thisrow = save;
+ png.rowbytessofar = 0;
+ }
+ }
+ phaseendcheck(png, raw);
+}
+
+get_signature(fd: ref Iobuf): int
+{
+ sig := array[8] of { byte 137, byte 80, byte 78, byte 71, byte 13, byte 10, byte 26, byte 10 };
+ x: int;
+ for (x = 0; x < 8; x++)
+ if (fd.getb() != int sig[x])
+ return 0;
+ return 1;
+}
+
+get_bytes(fd: ref Iobuf, crc_state: ref CRCstate, buf: array of byte, n: int): int
+{
+ if (buf == nil) {
+ fd.seek(big n, bufio->SEEKRELA);
+ return 1;
+ }
+ if (fd.read(buf, n) != n)
+ return 0;
+ if (crc_state != nil)
+ crc->crc(crc_state, buf, n);
+ return 1;
+}
+
+skip_bytes(fd: ref Iobuf, crc_state: ref CRCstate, n: int): int
+{
+ buf := array[1024] of byte;
+ while (n) {
+ thistime: int = 1024;
+ if (thistime > n)
+ thistime = n;
+ if (!get_bytes(fd, crc_state, buf, thistime))
+ return 0;
+ n -= thistime;
+ }
+ return 1;
+}
+
+get_4(fd: ref Iobuf, crc_state: ref CRCstate, signed: int): (int, int)
+{
+ buf := array[4] of byte;
+ if (!get_bytes(fd, crc_state, buf, 4))
+ return (0, 0);
+ if (signed && int buf[0] & 16r80)
+ return (0, 0);
+ r:int = (int buf[0] << 24) | (int buf[1] << 16) | (int buf[2] << 8) | (int buf[3]);
+# sys->print("got int %d\n", r);
+ return (1, r);
+}
+
+get_int(fd: ref Iobuf, crc_state: ref CRCstate): int
+{
+ ok, r: int;
+ (ok, r) = get_4(fd, crc_state, 1);
+ if (ok)
+ return r;
+ return -1;
+}
+
+get_ushort(fd: ref Iobuf, crc_state: ref CRCstate): int
+{
+ buf := array[2] of byte;
+ if (!get_bytes(fd, crc_state, buf, 2))
+ return -1;
+ return (int buf[0] << 8) | int buf[1];
+}
+
+get_crc_and_check(fd: ref Iobuf, chunk: ref Chunk): int
+{
+ crc, ok: int;
+ (ok, crc) = get_4(fd, nil, 0);
+ if (!ok)
+ return 0;
+# sys->print("crc: computed %.8ux expected %.8ux\n", chunk.crc_state.crc, crc);
+ if (chunk.crc_state.crc != crc)
+ return 1;
+ return 1;
+}
+
+get_byte(fd: ref Iobuf, crc_state: ref CRCstate): int
+{
+ buf := array[1] of byte;
+ if (!get_bytes(fd, crc_state, buf, 1))
+ return -1;
+# sys->print("got byte %d\n", int buf[0]);
+ return int buf[0];
+}
+
+get_type(fd: ref Iobuf, crc_state: ref CRCstate): string
+{
+ x: int;
+ buf := array[4] of byte;
+ if (!get_bytes(fd, crc_state, buf, 4))
+ return nil;
+ for (x = 0; x < 4; x++) {
+ c: int;
+ c = int buf[x];
+ if (c == bufio->EOF || (c < 65 || c > 90 && c < 97) || c > 122)
+ return nil;
+ }
+ return string buf;
+}
+
+get_chunk_header(fd: ref Iobuf, chunk: ref Chunk): int
+{
+ chunk.size = get_int(fd, nil);
+ if (chunk.size < 0)
+ return 0;
+ crc->reset(chunk.crc_state);
+ chunk.typ = get_type(fd, chunk.crc_state);
+ if (chunk.typ == nil)
+ return 0;
+# sys->print("%s(%d)\n", chunk.typ, chunk.size);
+ return 1;
+}