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             &params.bitcount, &params.compact,
450             &params.width, &params.height,
451             &params.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