- Online Community Forums » Allegro Development » Bug with Allegro packfile compression

This thread is locked; no one can reply to it. rss feed Print
Bug with Allegro packfile compression
Member #1,624
November 2001

I'm not entirely sure if it's the compression itself or the getc/putc functions, but using a packfile to store and read data can occasionally cause our data to become corrupt. What happens is that the unpacked data gets written to a packed packfile. However, when the packfile is read via Allegro's built-in functions, the output ends up being one byte short of the original's length, which screws up our loading functions. Note that this only occurs extremely rarely.

Packed into the file below is a test program I wrote to demonstrate this problem. It contains the program, the source, the dll, and the uncompressed data from our program that's exhibiting this problem. Any help would e greatly appreciated.

EDIT - forgot to mention that the program takes tst.tst, packs it via pack_putc, then unpacks itvia pack_getc, and puts the result into output.tst

Peter Wang
Member #23
April 2000

I can confirm the bug. I'd thought there was a bug remaining in the packfile code. Thanks for the test case.

In case anyone wants to have a go at this, I've found that:

(1) the bug doesn't show up with a random file of the same length as test.tst;

(2) appending a single byte to test.tst causes the bug not to show;

(3) removing the last byte of test.tst causes the bug not to show;

(4) removing the first byte of test.tst causes the bug not to show.

Milan Mimica
Member #3,877
September 2003

The bug also shows if you "double" test.tst by:
$ cat test.tst test.tst >tmp.tst; mv tmp.tst test.tst
Then output.tst lacks two bytes.

Peter Wang
Member #23
April 2000

Going to sleep now. I'm pretty certain the compressed file is okay. I checked the length using the following program. It seems the bug is an off by one error in the read buffering.

1#include <assert.h>
2#include <stdio.h>
3#include <stdlib.h>
5int main(void)
7 FILE *fp;
8 int length;
10 fp = fopen("tmp.tmp", "rb");
11 if (!fp) {
12 exit(EXIT_FAILURE);
13 }
15 /* skip signature */
16 fgetc(fp);
17 fgetc(fp);
18 fgetc(fp);
19 fgetc(fp);
21 length = 0;
22 while (1) {
23 int flags;
24 int bit;
26 flags = fgetc(fp);
27 if (flags == EOF) {
28 printf("Reached EOF\n");
29 break;
30 }
32 for (bit = 0; bit < 8; bit++) {
33 if (flags & (1 << bit)) {
34 if (fgetc(fp) == EOF) {
35 printf("Reached EOF while reading raw byte\n");
36 goto Exit;
37 }
38 length++;
39 }
40 else {
41 int byte1;
42 int byte2;
43 int index;
44 int len;
46 byte1 = fgetc(fp);
47 if (byte1 == EOF) {
48 printf("Reached EOF while reading byte1\n");
49 goto Exit;
50 }
52 byte2 = fgetc(fp);
53 if (byte2 == EOF) {
54 printf("Reached EOF while reading byte2\n");
55 goto Exit;
56 }
58 index = byte1 | ((byte2 & 0xF0) << 4);
59 len = (byte2 & 0x0F) + 3;
61 assert(index >= 0 && index < 4096);
62 length += len;
63 }
64 }
65 }
67 Exit:
69 fclose(fp);
70 printf("Length = %d bytes\n", length);
71 return 0;

EDIT: For anyone not on [AD], I posted this:


I think I have a fix. I would appreciate help looking it over (see the
comments for detailed explanation of the problem) and stress testing it,
since Evert is planning to release 4.2.1 this weekend. In addition to
the test case in the thread, I've attached a shell script which produces
files using /dev/zero and /dev/urandom, compresses and decompresses them
with the `pack' tool and checks they are the same.

I've tried to minimise the number of changes and maintaining PACKFILE
semantics, in particular that pack_feof() returns TRUE immediately after
the last byte has been read (unlike feof() which returns true once you
try to read past the last byte).

The patch and shell script are attached.

Member #1,624
November 2001

The fix seems to have done the trick. I'll report if anything else goes wrong. Thanks a lot Peter :)

Go to: