1 /*******************************************************
2
3 CoolReader Engine
4
5 wolutil.cpp: WOL file format support
6
7 Based on code by SeNS
8
9 *******************************************************/
10
11 #include <string.h>
12 #include "../include/wolutil.h"
13 #include "private/dumpfile.h"
14
15 #define N 4096
16 #define F 18
17 #define THRESHOLD 2
18 #define NIL N
19
20 //#define _DEBUG_LOG
21
22 #ifdef _DEBUG_LOG
23 DumpFile log( "wol.log" );
24 #endif
25
26 static lvByteOrderConv cnv;
27
28 /*
29 * Based on LZSS.C by Haruhiko Okumura 4/6/1989
30 */
31 class LZSSUtil {
32 lUInt16 textsize; /* text size counter */
33 lUInt16 codesize; /* code size counter */
34 lUInt16 printcount; /* counter for reporting progress every 1K bytes */
35 lUInt8 text_buf[N + F - 1]; /* ring buffer of size N,
36 with extra F-1 bytes to facilitate string comparison */
37 lUInt16 match_position, match_length; /* of longest match. These are
38 set by the InsertNode() procedure. */
39 lUInt16 lson[N + 1], rson[N + 257], dad[N + 1]; /* left & right children &
40 parents -- These constitute binary search trees. */
41
42 // private methods
43 void InsertNode(int r);
44 void DeleteNode(int p);
45
46 public:
47 /// init tables
48 LZSSUtil();
49 /// encode buffer
50 bool Encode(
51 const lUInt8 * in_buf,
52 int in_length,
53 lUInt8 * out_buf,
54 int & out_length
55 );
56 /// decode buffer
57 bool Decode(
58 const lUInt8 * in_buf,
59 int in_length,
60 lUInt8 * out_buf,
61 int & out_length
62 );
63 };
64
65
66
67 class InBuf {
68 const lUInt8 * _buf;
69 int _size;
70 int _pos;
71 public:
InBuf(const lUInt8 * buf,int size)72 InBuf( const lUInt8 * buf, int size )
73 : _buf(buf), _size(size), _pos(0) {}
get(lUInt32 & ch)74 bool get( lUInt32 & ch )
75 {
76 if (_pos>=_size)
77 return false;
78 ch = _buf[ _pos++ ];
79 return true;
80 }
81 };
82
83 class OutBuf {
84 lUInt8 * _buf;
85 int _size;
86 int _pos;
87 bool _overflow;
88 public:
OutBuf(lUInt8 * buf,int size)89 OutBuf( lUInt8 * buf, int size )
90 : _buf(buf), _size(size), _pos(0), _overflow(false) {}
getOverflow()91 bool getOverflow() { return _overflow; }
put(lUInt32 ch)92 bool put( lUInt32 ch )
93 {
94 if (_pos>=_size) {
95 _overflow = true;
96 return false;
97 }
98 _buf[ _pos++ ] = (lUInt8)ch;
99 return true;
100 }
getPos()101 int getPos() { return _pos; }
102 };
103
LZSSUtil()104 LZSSUtil::LZSSUtil()
105 {
106 /* initialize trees */
107 int i;
108 /* For i = 0 to N - 1, rson[i] and lson[i] will be the right and
109 left children of node i. These nodes need not be initialized.
110 Also, dad[i] is the parent of node i. These are initialized to
111 NIL (= N), which stands for 'not used.'
112 For i = 0 to 255, rson[N + i + 1] is the root of the tree
113 for strings that begin with character i. These are initialized
114 to NIL. Note there are 256 trees. */
115 for (i = N + 1; i <= N + 256; i++)
116 rson[i] = NIL;
117 for (i = 0; i < N; i++)
118 dad[i] = NIL;
119 }
120
InsertNode(int r)121 void LZSSUtil::InsertNode(int r)
122 /* Inserts string of length F, text_buf[r..r+F-1], into one of the
123 trees (text_buf[r]'th tree) and returns the longest-match position
124 and length via the global variables match_position and match_length.
125 If match_length = F, then removes the old node in favor of the new
126 one, because the old one will be deleted sooner.
127 Note r plays double role, as tree node and position in buffer. */
128 {
129 int i, p, cmp;
130 unsigned char *key;
131
132 cmp = 1; key = &text_buf[r]; p = N + 1 + key[0];
133 rson[r] = lson[r] = NIL; match_length = 0;
134 for ( ; ; ) {
135 if (cmp >= 0) {
136 if (rson[p] != NIL) p = rson[p];
137 else { rson[p] = r; dad[r] = p; return; }
138 } else {
139 if (lson[p] != NIL)
140 p = lson[p];
141 else {
142 lson[p] = r;
143 dad[r] = p;
144 return;
145 }
146 }
147 for (i = 1; i < F; i++)
148 if ((cmp = key[i] - text_buf[p + i]) != 0)
149 break;
150 if (i > match_length) {
151 match_position = p;
152 if ((match_length = i) >= F)
153 break;
154 }
155 }
156 dad[r] = dad[p];
157 lson[r] = lson[p];
158 rson[r] = rson[p];
159 dad[lson[p]] = r;
160 dad[rson[p]] = r;
161 if (rson[dad[p]] == p)
162 rson[dad[p]] = r;
163 else
164 lson[dad[p]] = r;
165 dad[p] = NIL; /* remove p */
166 }
167
DeleteNode(int p)168 void LZSSUtil::DeleteNode(int p) /* deletes node p from tree */
169 {
170 int q;
171
172 if (dad[p] == NIL) return; /* not in tree */
173 if (rson[p] == NIL) q = lson[p];
174 else if (lson[p] == NIL) q = rson[p];
175 else {
176 q = lson[p];
177 if (rson[q] != NIL) {
178 do { q = rson[q]; } while (rson[q] != NIL);
179 rson[dad[q]] = lson[q]; dad[lson[q]] = dad[q];
180 lson[q] = lson[p]; dad[lson[p]] = q;
181 }
182 rson[q] = rson[p]; dad[rson[p]] = q;
183 }
184 dad[q] = dad[p];
185 if (rson[dad[p]] == p) rson[dad[p]] = q; else lson[dad[p]] = q;
186 dad[p] = NIL;
187 }
188
Encode(const lUInt8 * in_buf,int in_length,lUInt8 * out_buf,int & out_length)189 bool LZSSUtil::Encode(
190 const lUInt8 * in_buf,
191 int in_length,
192 lUInt8 * out_buf,
193 int & out_length
194 )
195 {
196 InBuf in( in_buf, in_length );
197 OutBuf out( out_buf, out_length );
198 int i, len, r, s, last_match_length, code_buf_ptr;
199 lUInt32 c;
200 lUInt8 code_buf[17], mask;
201 code_buf[0] = 0; /* code_buf[1..16] saves eight units of code, and
202 code_buf[0] works as eight flags, "1" representing that the unit
203 is an unencoded letter (1 byte), "0" a position-and-length pair
204 (2 bytes). Thus, eight units require at most 16 bytes of code. */
205 code_buf_ptr = mask = 1;
206 s = 0; r = N - F;
207 for (i = s; i < r; i++)
208 text_buf[i] = ' '; /* Clear the buffer with
209 any character that will appear often. */
210 for (len = 0; len < F && in.get(c); len++)
211 text_buf[r + len] = (lUInt8)c; /* Read F bytes into the last F bytes of
212 the buffer */
213 if ((textsize = len) == 0)
214 return false; /* text of size zero */
215 //for (i = 1; i <= F; i++) InsertNode(r - i);
216 /* Insert the F strings,
217 each of which begins with one or more 'space' characters. Note
218 the order in which these strings are inserted. This way,
219 degenerate trees will be less likely to occur. */
220 InsertNode(r); /* Finally, insert the whole string just read. The
221 global variables match_length and match_position are set. */
222 do {
223 if (match_length > len) match_length = len; /* match_length
224 may be spuriously long near the end of text. */
225 if (match_length <= THRESHOLD) {
226 match_length = 1; /* Not long enough match. Send one byte. */
227 code_buf[0] |= mask; /* 'send one byte' flag */
228 code_buf[code_buf_ptr++] = text_buf[r]; /* Send uncoded. */
229 } else {
230 code_buf[code_buf_ptr++] = (lUInt8) match_position;
231 code_buf[code_buf_ptr++] = (lUInt8)
232 (((match_position >> 4) & 0xf0)
233 | (match_length - (THRESHOLD + 1))); /* Send position and
234 length pair. Note match_length > THRESHOLD. */
235 }
236 if ((mask <<= 1) == 0) { /* Shift mask left one bit. */
237 for (i = 0; i < code_buf_ptr; i++) /* Send at most 8 units of */
238 out.put(code_buf[i]); /* code together */
239 codesize += code_buf_ptr;
240 code_buf[0] = 0; code_buf_ptr = mask = 1;
241 }
242 last_match_length = match_length;
243 for (i = 0; i < last_match_length && in.get(c); i++) {
244 DeleteNode(s); /* Delete old strings and */
245 text_buf[s] = (lUInt8)c; /* read new bytes */
246 if (s < F - 1)
247 text_buf[s + N] = (lUInt8)c; /* If the position is
248 near the end of buffer, extend the buffer to make
249 string comparison easier. */
250 s = (s + 1) & (N - 1); r = (r + 1) & (N - 1);
251 /* Since this is a ring buffer, increment the position
252 modulo N. */
253 InsertNode(r); /* Register the string in text_buf[r..r+F-1] */
254 }
255 while (i++ < last_match_length) { /* After the end of text, */
256 DeleteNode(s); /* no need to read, but */
257 s = (s + 1) & (N - 1); r = (r + 1) & (N - 1);
258 if (--len) InsertNode(r); /* buffer may not be empty. */
259 }
260 } while (len > 0); /* until length of string to be processed is zero */
261 if (code_buf_ptr > 1) { /* Send remaining code. */
262 for (i = 0; i < code_buf_ptr; i++)
263 out.put(code_buf[i]);
264 codesize += code_buf_ptr;
265 }
266 out_length=out.getPos();
267 return true;
268 }
269
270 /* Just the reverse of Encode(). */
271
272 #if 0
273
274 bool LZSSUtil::Decode(
275 const lUInt8 * in_buf,
276 int in_length,
277 lUInt8 * out_buf,
278 int & out_length
279 )
280 {
281 InBitStream in( in_buf, in_length );
282 OutBuf out( out_buf, out_length );
283 lUInt32 i, k, r;
284 lUInt32 c, ci, cj;
285 unsigned int flags;
286
287 for (i = 0; i < N - F; i++)
288 text_buf[i] = 0; //' '
289 r = N - F; flags = 0;
290 for ( ; ; ) {
291 if (!in.read(c, 1))
292 break;
293 if (c)
294 {
295 if (!in.read(c, 8))
296 break;
297 if (!out.put(c))
298 break;
299 text_buf[r++] = c;
300 r &= (N - 1);
301 }
302 else
303 {
304 if (!in.read(cj, 4))
305 break;
306 cj += THRESHOLD;
307 if (!in.read(ci, 12))
308 break;
309 for (k = 0; k <= cj; k++) {
310 c = text_buf[(ci + k) & (N - 1)];
311 if (!out.put(c))
312 break;
313 text_buf[r++] = c;
314 r &= (N - 1);
315 }
316 }
317 }
318 out_length=out.getPos();
319 return !out.getOverflow();
320 }
321 #endif
322
Decode(const lUInt8 * in_buf,int in_length,lUInt8 * out_buf,int & out_length)323 bool LZSSUtil::Decode(
324 const lUInt8 * in_buf,
325 int in_length,
326 lUInt8 * out_buf,
327 int & out_length
328 )
329 {
330 InBuf in( in_buf, in_length );
331 OutBuf out( out_buf, out_length );
332 lUInt32 i, k, r;
333 lUInt32 c, ci, cj;
334 unsigned int flags;
335
336 for (i = 0; i < N - F; i++)
337 text_buf[i] = 'a';
338 r = N - F; flags = 0;
339 for ( ; ; ) {
340 if (((flags >>= 1) & 256) == 0) {
341 if (!in.get(c))
342 break;
343 flags = c | 0xff00; /* uses higher byte cleverly */
344 } /* to count eight */
345 if (flags & 1) {
346 if (!in.get(c))
347 break;
348 if (!out.put(c))
349 break;
350 text_buf[r++] = (lUInt8)c;
351 r &= (N - 1);
352 } else {
353 if (!in.get(ci))
354 break;
355 if (!in.get(cj))
356 break;
357 ci |= ((cj & 0xf0) << 4);
358 cj = (cj & 0x0f) + THRESHOLD;
359 for (k = 0; k <= cj; k++) {
360 c = text_buf[(ci + k) & (N - 1)];
361 if (!out.put(c))
362 break;
363 text_buf[r++] = (lUInt8)c;
364 r &= (N - 1);
365 }
366 }
367 }
368 out_length=out.getPos();
369 return !out.getOverflow();
370 }
371
372
373
374
375 ///////////////////////////////////////////////////////////////////////////
376 //
377 // WOLBase class
378 //
379 ///////////////////////////////////////////////////////////////////////////
380
381
382
383
WOLBase(LVStream * stream)384 WOLBase::WOLBase( LVStream * stream )
385 : _stream(stream)
386 , _book_title_size(0)
387 , _cover_image_size(0)
388 , _page_data_size(0)
389 , _catalog_size(0)
390 , _wolf_start_pos(0)
391 , _subcatalog_level23_items(0)
392 , _subcatalog_offset(0)
393 , _catalog_level1_items(0)
394 , _catalog_subcatalog_size(0)
395 {
396 }
397
WOLReader(LVStream * stream)398 WOLReader::WOLReader( LVStream * stream )
399 : WOLBase(stream)
400 {
401 }
402
readMem(const lUInt8 * buf,int offset,lUInt16 & dest)403 static void readMem( const lUInt8 * buf, int offset, lUInt16 & dest )
404 {
405 dest = ((lUInt16)buf[offset+1]<<8) | buf[offset];
406 }
407
readMem(const lUInt8 * buf,int offset,lUInt32 & dest)408 static void readMem( const lUInt8 * buf, int offset, lUInt32 & dest )
409 {
410 dest = ((lUInt32)buf[offset+3]<<24)
411 | ((lUInt32)buf[offset+2]<<16)
412 | ((lUInt32)buf[offset+1]<<8)
413 | buf[offset];
414 }
415
readHeader()416 bool WOLReader::readHeader()
417 {
418 lUInt8 header[0x80];
419 if (_stream->Read( header, 0x80, NULL )!=LVERR_OK)
420 return false;;
421 if (memcmp(header, "WolfEbook1.11", 13))
422 return false;
423 readMem(header, 0x17, _book_title_size);
424 readMem(header, 0x19, _cover_image_size); // 0x19
425 readMem(header, 0x5F, _subcatalog_level23_items); // 0x5F
426 readMem(header, 0x61, _subcatalog_offset); // 0x61
427 readMem(header, 0x22, _catalog_level1_items); // 0x1E
428 readMem(header, 0x1E, _catalog_subcatalog_size); // 0x1E
429 readMem(header, 0x26, _page_data_size); // 0x26
430 readMem(header, 0x3C, _catalog_size); // 0x3C
431 _book_title = readString(0x80, _book_title_size);
432 _stream->SetPos( 0x80 + _cover_image_size + _book_title_size );
433 lString8 str = readTag();
434 if (str!="wolf")
435 return false;
436 str = readTag();
437 if (str!="catalog")
438 return false;
439 for (;;)
440 {
441 str = readTag();
442 if (str.empty())
443 return false;
444 if (str=="/catalog")
445 break;
446 wolf_img_params params;
447 //img bitcount=1 compact=1 width=794 height=1123 length=34020
448 if (sscanf(str.c_str(), "img bitcount=%d compact=%d width=%d height=%d length=%d",
449 ¶ms.bitcount, ¶ms.compact,
450 ¶ms.width, ¶ms.height,
451 ¶ms.length)!=5)
452 return false;
453 params.offset = (lUInt32)_stream->GetPos();
454 _stream->SetPos(params.offset+params.length);
455 str = readTag();
456 const char * s = str.c_str();
457 lString8 tst(s);
458 tst += s;
459 if (str!="/img")
460 return false;
461 _images.add( params );
462 }
463 //_wolf_start_pos;
464 return true;
465 }
466
getImage(int index)467 LVGrayDrawBuf * WOLReader::getImage( int index )
468 {
469 if (index<0 || index>=_images.length())
470 return NULL;
471 const wolf_img_params * img = &_images[index];
472 LVArray<lUInt8> buf(img->length, 0);
473 _stream->SetPos( img->offset );
474 _stream->Read(buf.ptr(), img->length, NULL);
475 int img_size = img->height*((img->width*img->bitcount+7)/8);
476 int uncomp_len = img_size + 18;
477 LVArray<lUInt8> uncomp(uncomp_len, 0);
478
479 LZSSUtil unpacker;
480 if (unpacker.Decode(buf.ptr(), buf.length(), uncomp.ptr(), uncomp_len))
481 {
482 LVStreamRef dump = LVOpenFileStream( "test.dat", LVOM_WRITE );
483 if (!dump.isNull()) {
484 dump->Write(uncomp.ptr(), uncomp_len, NULL);
485 }
486
487 // inverse 1-bit images
488 if (img->bitcount==1)
489 {
490 for (int i=0; i<img_size; i++)
491 {
492 uncomp[i]= ~uncomp[i];
493 }
494 }
495
496 LVGrayDrawBuf * image = new LVGrayDrawBuf(img->width, img->height, img->bitcount);
497 memcpy(image->GetScanLine(0), uncomp.ptr(), img_size);
498 return image;
499 }
500
501 //delete image;
502 return NULL;
503 }
504
readString(int offset,int size)505 lString8 WOLReader::readString(int offset, int size)
506 {
507 _stream->SetPos( offset );
508 lString8 buf;
509 buf.append(size, ' ');
510 _stream->Read( buf.modify(), size, NULL);
511 return buf;
512 }
513
readTag()514 lString8 WOLReader::readTag()
515 {
516 lString8 buf;
517 lChar8 ch = 0;
518 for (;;)
519 {
520 if (_stream->Read(&ch, 1, NULL)!=LVERR_OK)
521 return lString8::empty_str;
522 if (ch==' '||ch=='\r'||ch=='\n')
523 continue;
524 if (ch!='<')
525 return lString8::empty_str;
526 break;
527 }
528 for (;;)
529 {
530 if (_stream->Read(&ch, 1, NULL)!=LVERR_OK)
531 return lString8::empty_str;
532 if (ch==0 || buf.length()>100)
533 return lString8::empty_str;
534 if (ch=='>')
535 return buf;
536 buf.append(1, ch);
537 }
538 }
539
getBookCover()540 LVArray<lUInt8> * WOLReader::getBookCover()
541 {
542 LVArray<lUInt8> * cover = new LVArray<lUInt8>(_cover_image_size, 0);
543 _stream->SetPos( 0x80 + _book_title_size );
544 _stream->Read( cover->ptr(), _cover_image_size, NULL);
545 return cover;
546 }
547
WOLWriter(LVStream * stream)548 WOLWriter::WOLWriter( LVStream * stream )
549 : WOLBase(stream)
550 , _catalog_opened(false)
551 {
552 lUInt8 header[0x80] = { 0 };
553 memcpy(header, "WolfEbook1.11", 13);
554 header[0x11]=1;
555 header[0x12]=2;
556 header[0x22]=1;
557 header[0x1D]=1;
558 header[0x40]=1;
559 stream->Write( header, 0x80, NULL );
560 }
561
startCatalog()562 void WOLWriter::startCatalog()
563 {
564 if (!_catalog_opened)
565 {
566 _catalog_start = (lUInt32)_stream->GetPos();
567 *_stream << "<catalog>";
568 _catalog_opened=true;
569 }
570 }
571
endCatalog()572 void WOLWriter::endCatalog()
573 {
574 if (_catalog_opened)
575 {
576 *_stream << "</catalog>";
577 _catalog_opened=false;
578 }
579 }
580
581 typedef struct
582 {
583 lUInt32 PageOffs; //[0x00] PageOffs: offset from beginning of section "WolfPages" to description of page (<img...>).
584 lUInt32 NameOffs; //[0x04] NameOffs: offset from beginning of file to beginnig of name (in "Names" area).
585 lUInt16 NameSize; //[0x08] NameSize: length (in chars) of name (in "Names" area).
586 lUInt16 ChildsCount; //[0x0A] ChildsCount: count of subitems for this element from TOC.
587 lUInt32 PrevPeerOffs;//[0x0C] PrevPeerOffs: offset from beginning of file to the description of previous peer element in SubCatalog table. 0, if current item is the first child of parent.
588 lUInt32 NextPeerOffs;//[0x10] NextPeerOffs: offset from beginning of file to the description of next peer element in SubCatalog table. 0, if current item is the last child of parent.
589 lUInt32 ChildOffs; //[0x14] ChildOffs: offset from beginning of file to the description of first child in SubCatalog table. 0, if there is no subitems (in TOC) for current item.
590 lUInt32 ParentOffs; //[0x18] ParentOffs: offset from beginning of file to the description of parent element in SubCatalog table. 0, if element is on level 1.
591 lUInt8 Level3Idx; //[0x1C] Level3Idx: level 3 index of element; 0 if element is on level 1 or level 2 in TOC.
592 lUInt8 Level2Idx; //[0x1D] Level2Idx: level 2 index of element; 0 if element is on level 1 in TOC.
593 lUInt8 Level1Idx; //[0x1E] Level1Idx: level 1 index of element.
594 lUInt8 AlignByte; //[0x1F] AlignByte: constant = 0x00.
595 lChar8 ItemName[48];//[0x20] ItemName: name of item in TOC.
596 } wol_toc_subcatalog_item;
597
writeToc()598 void WOLWriter::writeToc()
599 {
600 // enumerate ordered by level
601 int n = 0;
602 _subcatalog_level23_items = 0;
603 _subcatalog_offset = 0;
604 _catalog_level1_items = 0; // 0x22
605 _catalog_subcatalog_size = 0; // 0x1E
606 lUInt32 cat_start = (lUInt32) _stream->GetPos();
607 int len = _tocItems.length();
608 if ( len==0 ) {
609 // fake catalog = book name
610 *_stream << "<catalog><item>" << _book_name
611 << "</item>";
612 *_stream << cnv.lsf( getPageOffset(0) ) << "</catalog>";
613 _catalog_level1_items = 1;
614 } else {
615 //============================================================
616 // catalog
617 *_stream << "<catalog>";
618 for ( int ti=0; ti<len; ti++ ) {
619 TocItemInfo * src = _tocItems[ti];
620 if ( src->getLevel()==1 ) {
621 *_stream << "<item>" << src->name
622 << "</item>" << cnv.lsf( getPageOffset(src->page) );
623 _catalog_level1_items++;
624 }
625 }
626 *_stream << "</catalog>";
627
628 //============================================================
629 // subcatalog
630
631 // allocate TOC
632 _subcatalog_offset = (lUInt32) _stream->GetPos();
633 wol_toc_subcatalog_item * toc = new wol_toc_subcatalog_item[len]();
634 int catsize = sizeof(wol_toc_subcatalog_item) * len;
635 lString8 names;
636 for ( int lvl=1; lvl<=3; lvl++ ) {
637 for ( int i=0; i<_tocItems.length(); i++ ) {
638 if ( _tocItems[i]->getLevel() == lvl ) {
639 _tocItems[i]->catindex = n++;
640 if ( lvl>1 )
641 _subcatalog_level23_items++;
642 }
643 }
644 }
645 int names_start = _subcatalog_offset + 12 + catsize;
646 int i;
647 for ( i=0; i<len; i++ ) {
648 TocItemInfo * src = _tocItems[i];
649 int n = src->catindex;
650 wol_toc_subcatalog_item * item = &toc[n];
651 item->Level1Idx = src->l1index;
652 item->Level2Idx = src->l2index;
653 item->Level3Idx = src->l3index;
654 item->ChildOffs = cnv.lsf( src->firstChild ? src->firstChild->catindex * 80 + _subcatalog_offset + 12 : 0 );
655 item->ParentOffs = cnv.lsf( src->parent ? src->parent->catindex * 80 + _subcatalog_offset + 12 : 0 );
656 item->NextPeerOffs = cnv.lsf( src->nextSibling ? src->nextSibling->catindex * 80 + _subcatalog_offset + 12 : 0 );
657 item->PrevPeerOffs = cnv.lsf( src->prevSibling ? src->prevSibling->catindex * 80 + _subcatalog_offset + 12 : 0 );
658 lString8 name = src->name;
659 item->NameOffs = cnv.lsf( (lUInt32)(names_start + names.length()) );
660 name << ' ';
661 item->NameSize = cnv.lsf( (lUInt16)(name.length()) );
662 lStr_ncpy( item->ItemName, name.c_str(), 47 );
663 item->PageOffs = cnv.lsf( getPageOffset(src->page) ); // ???
664 names += name;
665 }
666 #ifdef _DEBUG_LOG
667 for ( i=0; i<len; i++ ) {
668 wol_toc_subcatalog_item * item = &toc[i];
669 lUInt32 currOffset = i * 80 + _subcatalog_offset + 12;
670 fprintf( log, "%2d %07d : (%d,%d,%d) \t parent=%07d child=%07d prev=%07d next=%07d\t %s\n",
671 i, currOffset, item->Level1Idx, item->Level2Idx, item->Level3Idx, item->ParentOffs, item->ChildOffs, item->PrevPeerOffs, item->NextPeerOffs, item->ItemName );
672 }
673 #endif
674 *_stream << "<subcatalog>";
675 _stream->Write( toc, sizeof(wol_toc_subcatalog_item) * len, NULL );
676 *_stream << names;
677 // SubcatalogSize = 26 + CountOfAllTOCItems * 80 + Length(NamesInAllTOCLevels)
678 *_stream << "\x08</subcatalog>";
679 delete[] ( toc );
680 }
681 // finalize
682 lUInt32 cat_end = (lUInt32) _stream->GetPos();
683 _catalog_subcatalog_size = cat_end - cat_start;
684 }
685
~WOLWriter()686 WOLWriter::~WOLWriter()
687 {
688 writePageIndex();
689 updateHeader();
690 }
691
updateHeader()692 void WOLWriter::updateHeader()
693 {
694 _stream->SetPos(0x17);
695 *_stream << cnv.lsf( _book_title_size );
696 _stream->SetPos(0x19);
697 *_stream << cnv.lsf( _cover_image_size );
698 _stream->SetPos(0x26);
699 *_stream << cnv.lsf( _page_data_size );
700 _stream->SetPos(0x1e);
701 *_stream << cnv.lsf( _catalog_subcatalog_size );
702 _stream->SetPos(0x22);
703 *_stream << cnv.lsf( _catalog_level1_items );
704 _stream->SetPos(0x3c);
705 *_stream << cnv.lsf( _catalog_size );
706 _stream->SetPos(0x42);
707 *_stream << cnv.lsf( (lUInt32)_page_starts.length() );
708 _stream->SetPos(0x4b);
709 *_stream << cnv.lsf( (lUInt32)_page_starts.length() );
710 _stream->SetPos(0x5F);
711 *_stream << cnv.lsf( _subcatalog_level23_items ); // 0x5F
712 _stream->SetPos(0x61);
713 *_stream << cnv.lsf( _subcatalog_offset ); // 0x61
714 }
715
addTitle(const lString8 & title,const lString8 & subject,const lString8 & author,const lString8 & adapter,const lString8 & translator,const lString8 & publisher,const lString8 & time_publish,const lString8 & introduction,const lString8 & isbn)716 void WOLWriter::addTitle(
717 const lString8 & title,
718 const lString8 & subject,
719 const lString8 & author,
720 const lString8 & adapter,
721 const lString8 & translator,
722 const lString8 & publisher,
723 const lString8 & time_publish,
724 const lString8 & introduction,
725 const lString8 & isbn)
726 {
727 //
728 _book_name = title;
729 lString8 buf;
730 buf.reserve(128);
731 buf << "<title>" << title << "\r\n"
732 << "<subject>" << subject << "\r\n"
733 << "<author>" << author << "\r\n"
734 << "<adpter>" << adapter << "\r\n"
735 << "<translator>" << translator << "\r\n"
736 << "<publisher>" << publisher << "\r\n"
737 << "<time_publish>" << time_publish << "\r\n"
738 << "<introduction>" << introduction << "\r\n"
739 << "<ISBN>" << isbn << "\r\n";
740 _book_title_size = buf.length();
741 *_stream << buf;
742 }
743
addCoverImage(const lUInt8 * buf,int size)744 void WOLWriter::addCoverImage( const lUInt8 * buf, int size )
745 {
746 static const lUInt8 cover_hdr[10] = {
747 0xFF, 0xFF, 0x58, 0x02, 0x01, 0x00, 0x4B, 0x00, 0x20, 0x03
748 };
749 _stream->Write(cover_hdr, 10, NULL);
750 _cover_image_size = size + 10;
751 _stream->Write(buf, size, NULL);
752 _wolf_start_pos = (lUInt32) _stream->GetPos();
753 *_stream << "<wolf>\r\n";
754 }
755
addImage(int width,int height,const lUInt8 * bitmap,int num_bits)756 void WOLWriter::addImage(
757 int width,
758 int height,
759 const lUInt8 * bitmap, // [width*height/4]
760 int num_bits
761 )
762 {
763 int bmp_sz = (width * height * num_bits)>>3;
764 startCatalog();
765 #if 0
766 lUInt8 * inversed = NULL;
767 if (num_bits==1)
768 {
769 inversed = new lUInt8 [bmp_sz];
770 for (int i=0; i<bmp_sz; i++)
771 {
772 inversed[i]= ~bitmap[i];
773 }
774 bitmap = inversed;
775 }
776 #endif
777 /*
778 #if 0
779 compressed.reserve(bitmap.size()*9/8);
780 for(int i=0; i<bitmap.size(); ) {
781 compressed.push_back((unsigned char)0xff);
782 for(int j=0; j<8 && i<bitmap.size(); j++) {
783 compressed.push_back(bitmap[i++]);
784 }
785 }
786 compressed.push_back((unsigned char)0x0); // extra last dummy char
787 #else
788 compressed.resize(bitmap.size()*2);
789 int compressed_len;
790 #if 1
791 Encode((const unsigned char*)&(bitmap[0]), bitmap.size(),
792 (unsigned char*)&(compressed[0]), &compressed_len);
793 #else
794 EncodeLZSS(&(bitmap[0]), bitmap.size(), &(compressed[0]),
795 compressed_len);
796 #endif
797 #endif
798 */
799
800
801 int compressed_len = bmp_sz * 9/8 + 18;
802 lUInt8 * compressed = new lUInt8 [compressed_len];
803
804 LZSSUtil packer;
805 packer.Encode(bitmap, bmp_sz, compressed, compressed_len);
806
807 compressed[ compressed_len++ ] = 0; // extra last dummy char
808
809 #if 0 //def _DEBUG_LOG
810 LZSSUtil unpacker;
811 lUInt8 * decomp = new lUInt8 [bmp_sz*2];
812 int decomp_len = 0;
813 unpacker.Decode(compressed, compressed_len-1, decomp, decomp_len);
814 assert(compressed_len==decomp_len);
815 for(int i=0; i<decomp_len; i++) {
816 assert(bitmap[i]==decomp[i]);
817 }
818 delete[] decomp;
819 #endif
820
821 _page_starts.add( (lUInt32)_stream->GetPos() );
822
823 lString8 buf;
824 buf.reserve(128);
825 buf << "<img bitcount="
826 << fmt::decimal(num_bits)
827 << " compact=1 width="
828 << fmt::decimal(width)
829 << " height="
830 << fmt::decimal(height)
831 << " length="
832 << fmt::decimal((int)compressed_len)
833 << ">";
834 *_stream << buf;
835
836 //_page_starts.add( (lUInt32)_stream->GetPos() );
837
838 _stream->Write( compressed, compressed_len, NULL );
839 endPage();
840 *_stream << cs8("</img>");
841
842 // cleanup
843 delete[] compressed;
844 //if (inversed)
845 // delete inversed;
846 }
847
endPage()848 void WOLWriter::endPage()
849 {
850 //m_page_starts.push_back(ftell(m_fp));
851 }
852
853 #define USE_001_FORMAT 0
854
addTocItem(int level1index,int level2index,int level3index,int pageNumber,lString8 title)855 void WOLWriter::addTocItem( int level1index, int level2index, int level3index, int pageNumber, lString8 title )
856 {
857 #ifdef _DEBUG_LOG
858 fprintf(log, "addTocItem(lvl=%d,%d,%d, page=%d, text=%s\n", level1index, level2index, level3index, pageNumber, title.c_str());
859 #endif
860 TocItemInfo * item = new TocItemInfo( _tocItems.length(), level1index, level2index, level3index, pageNumber, title );
861 _tocItems.add( item );
862 for ( int k=_tocItems.length()-2; k>=0; k-- ) {
863 TocItemInfo * last = item;
864 TocItemInfo * ki = _tocItems[k];
865 if ( last->isPrevSibling( *ki ) ) {
866 last->prevSibling = ki;
867 ki->nextSibling = last;
868 } else if ( last->isParent( *ki ) ) {
869 last->parent = ki;
870 if ( ki->firstChild==NULL )
871 ki->firstChild = last;
872 break;
873 }
874 }
875 }
876
writePageIndex()877 void WOLWriter::writePageIndex()
878 {
879 endCatalog();
880 *_stream << "</wolf>";
881 int pos0 = (int)_stream->GetPos();
882 _page_data_size = pos0 - _wolf_start_pos;
883
884 writeToc();
885
886 //*_stream << "<catalog><item>" << _book_name
887 // << "</item>";
888 //*_stream << (lUInt32)0x11 << "</catalog>";
889
890 int pos1 = (int)_stream->GetPos();
891
892 //_page_index_size = pos1-pos0; //0x1E
893
894 #if (USE_001_FORMAT==1)
895 *_stream << "<pagetable ver=\"001\">";
896 #else
897 *_stream << "<pagetable ver=\"021211 \">";
898 #endif
899
900 int start_of_catalog_table = (int)_stream->GetPos();
901
902 LVArray<lUInt32> pagegroup_1;
903 LVArray<lUInt32> pagegroup_2;
904 LVArray<lUInt32> pagegroup_delim;
905
906 pagegroup_delim.add( 0xFFFFFFFF );
907 pagegroup_1.add( cnv.lsf( _catalog_start ) );
908 #if (USE_001_FORMAT!=1)
909 pagegroup_1.add( cnv.lsf( _page_starts[0] ) );
910 #endif
911 pagegroup_2.add( cnv.lsf( _catalog_start ) );
912 for (int i=1; i<_page_starts.length(); i++)
913 {
914 pagegroup_1.add( cnv.lsf( _page_starts[i] ) );
915 #if (USE_001_FORMAT!=1)
916 pagegroup_1.add( cnv.lsf( _page_starts[i] ) );
917 #endif
918 pagegroup_2.add( cnv.lsf( _page_starts[i] ) );
919 }
920 int pagegroups_start = start_of_catalog_table + 13*4 + 12;
921 lUInt32 p = pagegroups_start;
922 LVArray<lUInt32> catalog;
923 catalog.add( cnv.lsf( p ) ); p += pagegroup_1.length()*4;
924 catalog.add( cnv.lsf( p ) ); p += pagegroup_1.length()*4;
925 catalog.add( cnv.lsf( p ) ); p += pagegroup_delim.length()*4;
926 catalog.add( cnv.lsf( p ) ); p += pagegroup_1.length()*4;
927 catalog.add( cnv.lsf( p ) ); p += pagegroup_1.length()*4;
928 catalog.add( cnv.lsf( p ) ); p += pagegroup_delim.length()*4;
929 catalog.add( cnv.lsf( p ) ); p += pagegroup_2.length()*4;
930 catalog.add( cnv.lsf( p ) ); p += pagegroup_2.length()*4;
931 catalog.add( cnv.lsf( p ) ); p += pagegroup_delim.length()*4;
932 catalog.add( cnv.lsf( p ) ); p += pagegroup_1.length()*4;
933 catalog.add( cnv.lsf( p ) ); p += pagegroup_1.length()*4;
934 catalog.add( cnv.lsf( p ) ); p += pagegroup_delim.length()*4;
935 catalog.add( cnv.lsf( p ) );
936 *_stream << catalog << "</pagetable>";
937
938
939 *_stream << pagegroup_1;
940 *_stream << pagegroup_1;
941 *_stream << pagegroup_delim;
942 *_stream << pagegroup_1;
943 *_stream << pagegroup_1;
944 *_stream << pagegroup_delim;
945 *_stream << pagegroup_2;
946 *_stream << pagegroup_2;
947 *_stream << pagegroup_delim;
948 *_stream << pagegroup_1;
949 *_stream << pagegroup_1;
950 *_stream << pagegroup_delim;
951
952 int pos2 = (lUInt32)_stream->GetPos();
953 _catalog_size = pos2 - pos1;
954 }
955
addImage(LVGrayDrawBuf & image)956 void WOLWriter::addImage( LVGrayDrawBuf & image )
957 {
958 addImage( image.GetWidth(), image.GetHeight(),
959 image.GetScanLine(0), image.GetBitsPerPixel() );
960 }
961
962 typedef struct {
963 lUInt16 compression; // Compression: 0xFFFF for raw data (no compression); 0x0001 for LZSS compression.
964 lUInt16 width; // ImageWidth: width of image (in pixels)
965 lUInt16 bpp; // BitsPerPixel: 1 for monochrome images; 2 for 4-level gray images.
966 lUInt16 bytesPerLine;// BytesPerLine: count of bytes in one row of image. (= ImageWidth*BitsPerPixel/8)
967 lUInt16 height; // ImageHeight: height of image (in pixels)
968 } CoverPageHeader_t;
969
addCoverImage(LVGrayDrawBuf & image)970 void WOLWriter::addCoverImage( LVGrayDrawBuf & image )
971 {
972 // convert 2bpp to 1bpp
973 #if 1
974
975
976 CoverPageHeader_t hdr;
977 hdr.compression = cnv.lsf( (lUInt16)1 );
978 lUInt16 width = image.GetWidth();
979 lUInt16 height = image.GetHeight();
980 lUInt16 bpp = image.GetBitsPerPixel();
981 lUInt16 bpl = (lUInt16)((width * bpp + 7)>>3);
982 hdr.width = cnv.lsf( width );
983 hdr.height = cnv.lsf( height );
984 hdr.bpp = cnv.lsf( bpp );
985 hdr.bytesPerLine = cnv.lsf( bpl );
986
987 lUInt32 cover_start_pos = (lUInt32) _stream->GetPos();
988 _stream->Write(&hdr, sizeof(hdr), NULL);
989
990 int bmp_sz = bpl * height;
991
992 lUInt8 * data = new lUInt8[ bmp_sz ];
993 memcpy( data, image.GetScanLine(0), bmp_sz );
994 if ( hdr.bpp == 2 )
995 {
996 for (int i=0; i<bmp_sz; i++)
997 data[i] = ~data[i];
998 }
999
1000 int compressed_len = bmp_sz * 9/8 + 18;
1001 lUInt8 * compressed = new lUInt8 [compressed_len];
1002
1003 LZSSUtil packer;
1004 packer.Encode(data, bmp_sz, compressed, compressed_len);
1005
1006 compressed[ compressed_len++ ] = 0; // extra last dummy char
1007
1008 delete[] data;
1009
1010 _stream->Write( compressed, compressed_len, NULL );
1011
1012 _wolf_start_pos = (lUInt32) _stream->GetPos();
1013 _cover_image_size = _wolf_start_pos - cover_start_pos;
1014 *_stream << "<wolf>\r\n";
1015
1016 #else
1017 image.ConvertToBitmap(true);
1018 int sz = (image.GetWidth()+7)/8 * image.GetHeight();
1019 addCoverImage( image.GetScanLine(0), sz );
1020 #endif
1021 }
1022
1023