summaryrefslogtreecommitdiff
path: root/noncore/apps/opie-reader/iSilo.cpp
Side-by-side diff
Diffstat (limited to 'noncore/apps/opie-reader/iSilo.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/apps/opie-reader/iSilo.cpp638
1 files changed, 638 insertions, 0 deletions
diff --git a/noncore/apps/opie-reader/iSilo.cpp b/noncore/apps/opie-reader/iSilo.cpp
new file mode 100644
index 0000000..5f14b96
--- a/dev/null
+++ b/noncore/apps/opie-reader/iSilo.cpp
@@ -0,0 +1,638 @@
+#include "iSilo.h"
+#ifdef _WINDOWS
+#include <winsock.h>
+#endif
+u_int8_t *rodata = (u_int8_t *)
+ "\x10\x11\x12\x00\x08\x07\x09\x06\x0a\x05\x0b\x04\x0c\x03\x0d\x02\x0e\x01\x0f";
+
+u_int16_t *rsize_min = (u_int16_t *)
+ "\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0d"
+"\x00\x0f\x00\x11\x00\x13\x00\x17\x00\x1b\x00\x1f\x00\x23\x00\x2b\x00\x33\x00"
+"\x3b\x00\x43\x00\x53\x00\x63\x00\x73\x00\x83\x00\xa3\x00\xc3\x00\xe3\x00\x02"
+"\x01";
+
+u_int8_t *rsize_delta = (u_int8_t *)
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x02\x02\x02\x02\x03\x03\x03"
+"\x03\x04\x04\x04\x04\x05\x05\x05\x05\x00";
+
+u_int16_t *rpos_min = (u_int16_t *)
+ "\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x07\x00\x09\x00\x0d\x00\x11\x00\x19"
+"\x00\x21\x00\x31\x00\x41\x00\x61\x00\x81\x00\xc1\x00\x01\x01\x81\x01\x01\x02"
+"\x01\x03\x01\x04\x01\x06\x01\x08\x01\x0c\x01\x10\x01\x18\x01\x20\x01\x30\x01"
+"\x40\x01\x60";
+
+u_int8_t *rpos_delta = (u_int8_t *)
+ "\x00\x00\x00\x00\x01\x01\x02\x02\x03\x03\x04\x04\x05\x05\x06\x06\x07\x07"
+"\x08\x08\x09\x09\x0a\x0a\x0b\x0b\x0c\x0c\x0d\x0d";
+
+void iSilo::init_tables(void)
+{
+ int i;
+ u_int16_t j;
+ return;
+ for (i = 0; i < 3; i++)
+ rodata[i] = 0x10 + i;
+ rodata[3] = 0;
+ for (i = 4; i < 19; i = i + 2)
+ rodata[i] = 8 + (i-4)/2;
+ for (i = 5; i < 19; i = i + 2)
+ rodata[i] = 7 - (i-5)/2;
+
+ memset(rsize_delta, 0, 29);
+ for (i = 4; i < 29; i++) {
+ rsize_delta[i] = (i - 4) >> 2;
+ }
+ memset(rpos_delta, 0, 30);
+ for (i = 2; i < 30; i++) {
+ rpos_delta[i] = (i - 2) >> 1;
+ }
+ j = 3;
+ for (i = 0; i < 29; i++) {
+ rsize_min[i] = j;
+ j += 1 << rsize_delta[i];
+ }
+ j = 1;
+ for (i = 0; i < 30; i++) {
+ rpos_min[i] = j;
+ j += 1 << rpos_delta[i];
+ }
+}
+
+/* read num bits from the file descriptor */
+
+int iSilo::code2tree(struct s_huffman *h) {
+ struct s_tree *t, *tmp;
+ u_int32_t i;
+ int j, b;
+
+ t = new s_tree();
+ h->tree = t;
+ if (t == NULL) return -1;
+ memset(t, 0, sizeof(struct s_tree));
+ t->value = (u_int32_t)-1;
+ for (i = 0; i < h->num; i++) {
+ tmp = t;
+ for (j = 0; j < h->size[i]; j++) {
+ b = (h->code[i] >> j) & 1;
+ if (tmp->branch[b] == NULL) {
+ tmp->branch[b] =new s_tree();
+ if (tmp->branch[b] == NULL) return(-1);
+ memset(tmp->branch[b], 0, sizeof(struct s_tree));
+ tmp->value = (u_int32_t)-1;
+ }
+ tmp = tmp->branch[b];
+ }
+ if (h->size[i] > 0) tmp->value = i;
+ }
+ return (0);
+}
+
+u_int32_t iSilo::swap_bits(u_int32_t n, int num) {
+ n = ((n >> 1) & 0x55555555) | ((n << 1) & 0xaaaaaaaa);
+ n = ((n >> 2) & 0x33333333) | ((n << 2) & 0xcccccccc);
+ n = ((n >> 4) & 0x0f0f0f0f) | ((n << 4) & 0xf0f0f0f0);
+ n = ((n >> 8) & 0x00ff00ff) | ((n << 8) & 0xff00ff00);
+ n = ((n >> 16) & 0x0000ffff) | ((n << 16) & 0xffff0000);
+ n >>= (32 - num);
+ return(n);
+}
+
+
+u_int32_t *iSilo::huffman_get(struct s_huffman *h)
+{
+ int b;
+ struct s_tree *t = h->tree;
+
+ while (t->value == -1) {
+ b = get_bits(1);
+ if (t->branch[b] == NULL) {
+ return(NULL);
+ }
+ t = t->branch[b];
+ }
+ return (&t->value);
+}
+
+int iSilo::size2code(struct s_huffman *h) {
+ u_int8_t i, sk, max = 0;
+ u_int16_t sc, c;
+ u_int32_t j, k, l;
+ int skip;
+
+ for (l = 0; l < h->num; l++) {
+ if (h->size[l] > max) max = h->size[l];
+ }
+
+ for (i = 1; i <= max; i++) {
+ sc = 0;
+ c = 0;
+ for (j = 0; j < h->num; j++) {
+ if (h->size[j] == i) {
+ do {
+ skip = 0;
+ for (k = 0; k < h->num; k++) {
+ sk = h->size[k];
+
+ if ((sk < i) && (sk != 0) && !((h->code[k] ^ c) & ((1 << sk)-1))) {
+ if ((c + 1) == (1 << i)) {
+ return -1;
+ }
+ sc += 1 << (i-sk);
+ c = swap_bits(sc, i);
+ skip = 1;
+ break;
+ }
+ }
+ } while (skip);
+ h->code[j] = c;
+ sc++;
+ c = swap_bits(sc, i);
+ }
+ }
+ }
+ return(0);
+}
+
+
+struct s_huffman *iSilo::huffman_create(u_int32_t num) {
+ struct s_huffman *h;
+ h = new s_huffman();
+ h->tree = new s_tree();
+ memset(h->tree, 0, sizeof(struct s_tree));
+ h->tree->value = (u_int32_t)-1;
+ h->num = num;
+ h->code = new u_int16_t[num];
+ h->size = new u_int8_t[num];
+ memset(h->size, 0, num);
+ memset(h->code, 0, num*2);
+ return (h);
+}
+
+void iSilo::kill_tree(struct s_tree *tree) {
+ if (tree == NULL) {
+ return;
+ }
+ kill_tree(tree->branch[0]);
+ kill_tree(tree->branch[1]);
+ delete tree;
+}
+
+void iSilo::kill_huffman(struct s_huffman *h) {
+ if (h == NULL) {
+ return;
+ }
+ kill_tree(h->tree);
+ delete h->code;
+ delete h->size;
+ delete h;
+}
+
+int iSilo::read_size(struct s_huffman *prev, struct s_huffman *h) {
+ u_int32_t *j;
+ u_int32_t i, n;
+ int s_ok = 0, ls = 0;
+
+ for (i = 0;i < h->num;) {
+ j = huffman_get(prev);
+ if (j == NULL)
+ return(-1);
+ switch(*j) {
+ case HM_MEDIUM:
+ n = get_swapped(3) + 3; /* n bytes of 0 */
+ memset(h->size + i, 0, n);
+ i += n;
+ break;
+ case HM_LONG:
+ n = get_swapped(7) + 11; /* n bytes of 0 */
+ memset(h->size + i, 0, n);
+ i += n;
+ break;
+ case HM_SHORT:
+ if (!s_ok) {
+ return(-1);
+ }
+ n = get_swapped(2) + 3; /* n+1 bytes of ls */
+ memset(h->size + i, ls, n);
+ i += n;
+ break;
+ default:
+ h->size[i] = *j;
+ ls = *j;
+ i++;
+ break;
+ }
+ if ((*j == HM_LONG) || (*j == HM_MEDIUM)) {
+ s_ok = 0;
+ } else {
+ s_ok = 1;
+ }
+ }
+ return(0);
+}
+
+void iSilo::mymemcpy(u_int8_t *dst, u_int8_t *src, u_int32_t num) {
+ u_int32_t i;
+ for (i = 0; i < num; i++) {
+ dst[i] = src[i];
+ }
+}
+/* return size or -1 for error */
+
+int iSilo::read_tree(struct s_huffman *prev, struct s_huffman *curr) {
+ if (read_size(prev, curr) == -1)
+ return(-1);
+ if (size2code(curr) == -1)
+ return(-1);
+ if (code2tree(curr) == -1)
+ return(-1);
+ return(0);
+}
+bool iSilo::reset_trees()
+{
+ get_bits(0); /* flush buffer */
+ /* This is a Table record so we reload the tables */
+ kill_huffman(lz);
+ kill_huffman(text);
+ kill_huffman(master);
+
+ master = huffman_create(19);
+ text = huffman_create(get_swapped(5) + 257);
+ lz = huffman_create(get_swapped(5) + 1);
+ int rdmax = get_swapped(4) + 4;
+
+ for (int i = 0; i < rdmax; i++)
+ {
+ master->size[rodata[i]] = get_swapped(3);
+ }
+
+ if (size2code(master) == -1) {
+ qDebug("size2code(master) error: size-table is incompatible");
+ return false;
+ }
+
+ code2tree(master);
+
+ if (read_tree(master, text) == -1) {
+ qDebug("read_tree() failed (format incorrect?)");
+ return false;
+ }
+
+ if (read_tree(master, lz) == -1) {
+ qDebug("read_tree() failed (format incorrect?)");
+ return false;;
+ }
+ return true;
+}
+u_int32_t iSilo::get_bits(int num) {
+ int i, r;
+ u_int32_t result = 0;
+
+ if (num == 0) {
+ pos = 0;
+ return(0);
+ }
+
+ for (i = 0; i < num; i++) {
+ if (pos == 0) {
+ r = fread(buf, sizeof(u_int32_t), 256, fin);
+ if (r <= 0) {
+ qDebug("ERROR: Unexpected end of file");
+ exit(-1); /* FIXME */
+ }
+ pos = 32*256;
+ }
+
+ pos--;
+ result <<= 1;
+ result |= (ntohl(buf[255 - (pos/32)]) >> (31-(pos % 32))) & 1;
+ }
+ return(result);
+}
+u_int32_t iSilo::get_swapped(int num) {
+ return(swap_bits(get_bits(num),num));
+}
+int iSilo::read_text() {
+ u_int32_t *j;
+ u_int32_t k, l, bp, idx;
+ for (bp = 0; bp < buffer_size;) {
+ j = huffman_get(text);
+ if (j == NULL)
+ return(-1);
+ if (*j == 256) {
+ break;
+ }
+ if (*j >= 257) {
+ idx = *j - 257;
+ k = rsize_min[idx];
+ if (rsize_delta[idx] != 0) {
+ k += get_swapped(rsize_delta[idx]);
+ }
+ j = huffman_get(lz);
+ if (j == NULL)
+ return(-1);
+ l = rpos_min[*j];
+ if (rpos_delta[*j] != 0) {
+ l += get_swapped(rpos_delta[*j]);
+ }
+ if (k <= l) {
+ memcpy(buffer + bp, buffer + bp - l, k);
+ } else {
+ mymemcpy(buffer + bp, buffer + bp - l, k);
+ }
+ bp += k;
+ } else {
+ buffer[bp] = *j;
+ bp++;
+ }
+ }
+ return(bp);
+}
+u_int32_t iSilo::getreccode()
+{
+ u_int32_t offset, rec_code;
+ gotorecordnumber(cur_rec);
+ if (fread(&rec_code, sizeof(rec_code), 1, fin) != 1)
+ {
+ qDebug("short read");
+ return 0xff;
+ }
+ return rec_code;
+}
+bool iSilo::process_record()
+{
+ u_int32_t rec_code = getreccode();
+ if (((rec_code & 0xFF) == 0) && (cur_rec != 0))
+ {
+ return false;
+ }
+ else
+ {
+ /* process text */
+ if ((cur_rec % 9) == 1)
+ {
+ bsize = 0;
+ if (!reset_trees()) return false;
+ cur_rec++;
+ rec_code = getreccode();
+ if (((rec_code & 0xFF) == 0) && (cur_rec != 0))
+ {
+ return false;
+ }
+ }
+ if (cur_rec != 0)
+ {
+ /* This is a Data record so we decode text using the table */
+ get_bits(0);
+ bsize = read_text();
+ } /* end of text processing */
+ }
+ return true;
+}
+iSilo::~iSilo()
+{
+ kill_huffman(master);
+ kill_huffman(lz);
+ kill_huffman(text);
+ if (attr != NULL)
+ {
+ delete [] attr;
+ attr = NULL;
+ }
+}
+int iSilo::getch()
+{
+ if (filepos >= textsize)
+ {
+ // qDebug("File end at record %u", cur_rec);
+ return EOF;
+ }
+ if (current_pos >= bsize)
+ {
+ cur_rec++;
+ if (!process_record()) return EOF;
+ current_pos = 0;
+ }
+ filepos++;
+ while (attr != NULL && filepos > attr[current_attr].offset)
+ {
+ mystyle.unset();
+ u_int16_t a = attr[current_attr].value;
+ // qDebug("Handling attr:%x at pos:%u", a, filepos);
+ if ((a & 0x1) != 0)
+ {
+ mystyle.setBold();
+ }
+ if ((a & 0xe) != 0)
+ {
+ qDebug("Unknown attribute:%x (%x)", a & 0xe, a);
+ }
+ if ((a & 0x10) != 0)
+ {
+ mystyle.setCentreJustify();
+ }
+ else if ((a & 0x20) != 0)
+ {
+ mystyle.setRightJustify();
+ }
+ else
+ {
+ mystyle.setLeftJustify();
+ }
+ if ((a & 0x40) != 0)
+ {
+ qDebug("Unknown attribute:%x (%x)", a & 0x40, a);
+ }
+ if ((a & 0x80) != 0)
+ {
+ mystyle.setLeftMargin(10);
+ mystyle.setRightMargin(10);
+ }
+ switch (a & 0xf00)
+ {
+ case 0x300:
+ mystyle.setFontSize(0);
+ break;
+ case 0xd00:
+ mystyle.setFontSize(1);
+ break;
+ case 0xe00:
+ mystyle.setFontSize(2);
+ break;
+ case 0xf00:
+ mystyle.setFontSize(3);
+ break;
+ default:
+ mystyle.setFontSize(0);
+ qDebug("Not sure of font size:%x (%x)", a & 0xf00, a);
+ break;
+ }
+ if ((a & 0x1000) != 0)
+ {
+ mystyle.setMono();
+ }
+ if ((a & 0x2000) != 0)
+ {
+ mystyle.setItalic();
+ }
+ if ((a & 0x4000) != 0)
+ {
+ qDebug("Unknown attribute:%x (%x)", a & 0x4000, a);
+ }
+ if ((a & 0x8000) != 0)
+ {
+ mystyle.setUnderline();
+ }
+ current_attr++;
+ if (current_attr >= attr_num)
+ {
+ attr_rec++;
+ read_attr();
+ }
+ }
+ return buffer[current_pos++];
+}
+
+void iSilo::getch(tchar& ch, CStyle& sty, unsigned long& pos)
+{
+ pos = filepos;
+ ch = getch();
+ sty = mystyle;
+}
+
+int iSilo::OpenFile(const char* src)
+{
+ struct stat _stat;
+ stat(src,&_stat);
+ filesize = _stat.st_size;
+ pos = 0;
+ cur_rec = 0;
+ buffer_size = 4096;
+ current_pos = 0;
+ bsize = 0;
+ filepos = 0;
+
+ if (!Cpdb::openpdbfile(src))
+ {
+ return -1;
+ }
+ if (head.creator != 0x6F476F54 // 'ToGo'
+ || head.type != 0x6F476F54) // 'ToGo')
+ {
+ return -1;
+ }
+ qDebug("There is %u records in this PDB file", ntohs(head.recordList.numRecords));
+ init_tables();
+ gotorecordnumber(0);
+ fread(buffer,1,12,fin);
+ fread(&textsize, sizeof(textsize), 1, fin);
+ textsize = ntohl(textsize);
+ fread(buffer,1,4,fin);
+ fread(&attr_start,sizeof(attr_start),1,fin);
+ attr_start = ntohs(attr_start);
+ fread(buffer,1,2,fin);
+ fread(&attr_end,sizeof(attr_end),1,fin);
+ attr_end = ntohs(attr_end);
+ attr_rec = attr_start;
+ attr = NULL;
+ pos_hi = 0xffff;
+ last_pos = 0xffff;
+ last_value = 0xffff;
+ read_attr();
+ return 0;
+}
+
+void iSilo::read_attr()
+{
+ // qDebug("read_attr:<%u, %u, %u>", attr_rec, attr_start, attr_end);
+ current_attr = 0;
+ if (attr != NULL)
+ {
+ delete [] attr;
+ attr = NULL;
+ }
+ if (attr_rec >= attr_start && attr_rec < attr_end)
+ {
+ gotorecordnumber(attr_rec);
+ fread(buffer, 1, 4, fin);
+ fread(&attr_num, sizeof(attr_num), 1, fin);
+ attr_num = ntohs(attr_num)+1;
+ attr = new s_attrib[attr_num];
+ for (int j = 0; j < attr_num; j++)
+ {
+ fread(&attr[j].offset, 2, 1, fin);
+ attr[j].offset = htons(attr[j].offset);
+ }
+#ifdef _WINDOWS
+ for (j = 0; j < attr_num; j++)
+#else
+ for (int j = 0; j < attr_num; j++)
+#endif
+ {
+ fread(&attr[j].value, 2, 1, fin);
+ if (attr[j].offset < last_pos)
+ {
+ pos_hi++;
+ }
+
+ if ((attr[j].offset == last_pos) && (attr[j].value == last_value))
+ {
+ pos_hi++;
+ }
+
+ last_pos = attr[j].offset;
+ attr[j].offset |= ((u_int32_t)pos_hi) << 16;
+ last_value = attr[j].value;
+ }
+ current_attr = 0;
+ // last_value = attr[attr_num-1].value;
+ // qDebug("Next attr:%u (%u)", attr[current_attr].offset, filepos);
+ // qDebug("Next attr:%x (%x)", attr[current_attr].offset, filepos);
+ }
+}
+
+void iSilo::locate(unsigned int n)
+{
+ // qDebug("Locating %u", n);
+ if (n >= textsize) n = 0;
+ pos = 0;
+ buffer_size = 4096;
+ current_pos = 0;
+ bsize = 0;
+ /* Brute force
+ cur_rec = 0;
+ filepos = 0;
+ */ // Fast
+ filepos = n - n % (8*buffer_size);
+ cur_rec = 9*(n/(8*buffer_size));
+ // End of fast
+ pos_hi = 0xffff;
+ last_pos = 0xffff;
+ last_value = 0xffff;
+ attr_rec = attr_start;
+ read_attr();
+ // While loop added for fast locate
+ while (attr != NULL && attr[attr_num-1].offset < filepos)
+ {
+ attr_rec++;
+ read_attr();
+ }
+
+ while (filepos < n)
+ {
+ getch();
+ }
+}
+/*
+ if (!process_record()) return EOF;
+ current_pos = 0;
+*/
+
+QString iSilo::about()
+{
+ return QString("iSilo codec (c) Tim Wentford, based on code supplied by an anonymous donor");
+}
+
+extern "C"
+{
+ CExpander* newcodec() { return new iSilo; }
+}