1 //C- -*- C++ -*-
2 //C- -------------------------------------------------------------------
3 //C- DjVuLibre-3.5
4 //C- Copyright (c) 2002 Leon Bottou and Yann Le Cun.
5 //C- Copyright (c) 2001 AT&T
6 //C-
7 //C- This software is subject to, and may be distributed under, the
8 //C- GNU General Public License, either Version 2 of the license,
9 //C- or (at your option) any later version. The license should have
10 //C- accompanied the software or you may obtain a copy of the license
11 //C- from the Free Software Foundation at http://www.fsf.org .
12 //C-
13 //C- This program is distributed in the hope that it will be useful,
14 //C- but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 //C- GNU General Public License for more details.
17 //C-
18 //C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library from
19 //C- Lizardtech Software. Lizardtech Software has authorized us to
20 //C- replace the original DjVu(r) Reference Library notice by the following
21 //C- text (see doc/lizard2002.djvu and doc/lizardtech2007.djvu):
22 //C-
23 //C- ------------------------------------------------------------------
24 //C- | DjVu (r) Reference Library (v. 3.5)
25 //C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
26 //C- | The DjVu Reference Library is protected by U.S. Pat. No.
27 //C- | 6,058,214 and patents pending.
28 //C- |
29 //C- | This software is subject to, and may be distributed under, the
30 //C- | GNU General Public License, either Version 2 of the license,
31 //C- | or (at your option) any later version. The license should have
32 //C- | accompanied the software or you may obtain a copy of the license
33 //C- | from the Free Software Foundation at http://www.fsf.org .
34 //C- |
35 //C- | The computer code originally released by LizardTech under this
36 //C- | license and unmodified by other parties is deemed "the LIZARDTECH
37 //C- | ORIGINAL CODE." Subject to any third party intellectual property
38 //C- | claims, LizardTech grants recipient a worldwide, royalty-free,
39 //C- | non-exclusive license to make, use, sell, or otherwise dispose of
40 //C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the
41 //C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
42 //C- | General Public License. This grant only confers the right to
43 //C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
44 //C- | the extent such infringement is reasonably necessary to enable
45 //C- | recipient to make, have made, practice, sell, or otherwise dispose
46 //C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
47 //C- | any greater extent that may be necessary to utilize further
48 //C- | modifications or combinations.
49 //C- |
50 //C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
51 //C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
52 //C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
53 //C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
54 //C- +------------------------------------------------------------------
55
56 #ifdef HAVE_CONFIG_H
57 # include "config.h"
58 #endif
59 #if NEED_GNUG_PRAGMAS
60 # pragma implementation
61 #endif
62
63 // -- Implementation of IFFByteStream
64 // - Author: Leon Bottou, 06/1998
65
66 // From: Leon Bottou, 1/31/2002
67 // This has been changed by Lizardtech to fit better
68 // with their re-implementation of ByteStreams.
69
70 #include <assert.h>
71 #include "IFFByteStream.h"
72
73
74 #ifdef HAVE_NAMESPACES
75 namespace DJVU {
76 # ifdef NOT_DEFINED // Just to fool emacs c++ mode
77 }
78 #endif
79 #endif
80
81
82 // Constructor
IFFByteStream(const GP<ByteStream> & xbs,const int xpos)83 IFFByteStream::IFFByteStream(const GP<ByteStream> &xbs,const int xpos)
84 : ByteStream::Wrapper(xbs), ctx(0), dir(0)
85 {
86 offset = seekto = xpos;
87 has_magic_att = false;
88 has_magic_sdjv = false;
89 }
90
91 // Destructor
~IFFByteStream()92 IFFByteStream::~IFFByteStream()
93 {
94 while (ctx)
95 close_chunk();
96 }
97
98 GP<IFFByteStream>
create(const GP<ByteStream> & bs)99 IFFByteStream::create(const GP<ByteStream> &bs)
100 {
101 const int pos=bs->tell();
102 return new IFFByteStream(bs,pos);
103 }
104
105
106 // IFFByteStream::ready
107 // -- indicates if bytestream is ready for reading
108 // returns number of bytes available
109
110 int
ready()111 IFFByteStream::ready()
112 {
113 if (ctx && dir < 0)
114 return ctx->offEnd - offset;
115 else if (ctx)
116 return 1;
117 else
118 return 0;
119 }
120
121
122 // IFFByteStream::composite
123 // -- indicates if bytestream is ready for putting or getting chunks
124
125 int
composite()126 IFFByteStream::composite()
127 {
128 if (ctx && !ctx->bComposite)
129 return 0;
130 else
131 return 1;
132 }
133
134
135
136
137 // IFFByteStream::check_id
138 // -- checks if an id is legal
139
140 int
check_id(const char * id)141 IFFByteStream::check_id(const char *id)
142 {
143 int i;
144 // check absence of null bytes
145 for (i=0; i<4; i++)
146 if (id[i]<0x20 || id[i]>0x7e)
147 return -1;
148 // check composite chunks
149 static const char *szComposite[] = { "FORM", "LIST", "PROP", "CAT ", 0 };
150 for (i=0; szComposite[i]; i++)
151 if (!memcmp(id, szComposite[i], 4))
152 return 1;
153 // check reserved chunks
154 static const char *szReserved[] = { "FOR", "LIS", "CAT", 0 };
155 for (i=0; szReserved[i]; i++)
156 if (!memcmp(id, szReserved[i], 3) && id[3]>='1' && id[3]<='9')
157 return -1;
158 // regular chunk
159 return 0;
160 }
161
162
163
164 // IFFByteStream::get_chunk
165 // -- get next chunk header
166
167 int
get_chunk(GUTF8String & chkid,int * rawoffsetptr,int * rawsizeptr)168 IFFByteStream::get_chunk(GUTF8String &chkid, int *rawoffsetptr, int *rawsizeptr)
169 {
170 int bytes;
171 char buffer[8];
172
173 // Check that we are allowed to read a chunk
174 if (dir > 0)
175 G_THROW( ERR_MSG("IFFByteStream.read_write") );
176 if (ctx && !ctx->bComposite)
177 G_THROW( ERR_MSG("IFFByteStream.not_ready") );
178 dir = -1;
179
180 // Seek to end of previous chunk if necessary
181 if (seekto > offset)
182 {
183 bs->seek(seekto);
184 offset = seekto;
185 }
186
187 // Skip padding byte
188 if (ctx && offset == ctx->offEnd)
189 return 0;
190 if (offset & 1)
191 {
192 bytes = bs->read( (void*)buffer, 1);
193 if (bytes==0 && !ctx)
194 return 0;
195 offset += bytes;
196 }
197
198 // Record raw offset
199 int rawoffset = offset;
200
201 // Read chunk id (skipping magic sequences inserted here to make
202 // DjVu files recognizable.)
203 for(;;)
204 {
205 if (ctx && offset == ctx->offEnd)
206 return 0;
207 if (ctx && offset+4 > ctx->offEnd)
208 G_THROW( ERR_MSG("IFFByteStream.corrupt_end") );
209 bytes = bs->readall( (void*)&buffer[0], 4);
210 offset = seekto = offset + bytes;
211 if (bytes==0 && !ctx)
212 return 0;
213 if (bytes != 4)
214 G_THROW(ByteStream::EndOfFile);
215 if (buffer[0] == 'S' && buffer[1] == 'D' &&
216 buffer[2] == 'J' && buffer[3] == 'V' )
217 {
218 has_magic_sdjv = true;
219 continue;
220 }
221 else if (buffer[0] == 'A' && buffer[1] == 'T' &&
222 buffer[2] == '&' && buffer[3] == 'T' )
223 {
224 has_magic_att=true;
225 continue;
226 }
227 else
228 break;
229 }
230
231 // Read chunk size
232 if (ctx && offset+4 > ctx->offEnd)
233 G_THROW( ERR_MSG("IFFByteStream.corrupt_end2") );
234 bytes = bs->readall( (void*)&buffer[4], 4);
235 offset = seekto = offset + bytes;
236 if (bytes != 4)
237 G_THROW( ByteStream::EndOfFile );
238 long size = ((unsigned char)buffer[4]<<24) |
239 ((unsigned char)buffer[5]<<16) |
240 ((unsigned char)buffer[6]<<8) |
241 ((unsigned char)buffer[7]);
242 if (ctx && offset+size > ctx->offEnd)
243 G_THROW( ERR_MSG("IFFByteStream.corrupt_mangled") );
244
245 // Check if composite
246 int composite = check_id(buffer);
247 if (composite < 0)
248 G_THROW( ERR_MSG("IFFByteStream.corrupt_id") );
249
250 // Read secondary id of composite chunk
251 if (composite)
252 {
253 if (ctx && ctx->offEnd<offset+4)
254 G_THROW( ERR_MSG("IFFByteStream.corrupt_header") );
255 bytes = bs->readall( (void*)&buffer[4], 4);
256 offset += bytes;
257 if (bytes != 4)
258 G_THROW( ByteStream::EndOfFile );
259 if (check_id(&buffer[4]))
260 G_THROW( ERR_MSG("IFFByteStream.corrupt_2nd_id") );
261 }
262
263 // Create context record
264 IFFContext *nctx = new IFFContext;
265 G_TRY
266 {
267 nctx->next = ctx;
268 nctx->offStart = seekto;
269 nctx->offEnd = seekto + size;
270 if (composite)
271 {
272 memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4);
273 memcpy( (void*)(nctx->idTwo), (void*)&buffer[4], 4);
274 nctx->bComposite = 1;
275 }
276 else
277 {
278 memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4);
279 memset( (void*)(nctx->idTwo), 0, 4);
280 nctx->bComposite = 0;
281 }
282 }
283 G_CATCH_ALL
284 {
285 delete nctx;
286 G_RETHROW;
287 }
288 G_ENDCATCH;
289
290 // Install context record
291 ctx = nctx;
292 chkid = GUTF8String(ctx->idOne, 4);
293 if (composite)
294 chkid = chkid + ":" + GUTF8String(ctx->idTwo, 4);
295
296 // Return
297 if (rawoffsetptr)
298 *rawoffsetptr = rawoffset;
299 if (rawsizeptr)
300 *rawsizeptr = ( ctx->offEnd - rawoffset + 1) & ~0x1;
301 return size;
302 }
303
304
305
306 // IFFByteStream::put_chunk
307 // -- write new chunk header
308
309 void
put_chunk(const char * chkid,int insert_magic)310 IFFByteStream::put_chunk(const char *chkid, int insert_magic)
311 {
312 int bytes;
313 char buffer[8];
314
315 // Check that we are allowed to write a chunk
316 if (dir < 0)
317 G_THROW( ERR_MSG("IFFByteStream.read_write") );
318 if (ctx && !ctx->bComposite)
319 G_THROW( ERR_MSG("IFFByteStream.not_ready2") );
320 dir = +1;
321
322 // Check primary id
323 int composite = check_id(chkid);
324 if ((composite<0) || (composite==0 && chkid[4])
325 || (composite && (chkid[4]!=':' || check_id(&chkid[5]) || chkid[9])) )
326 G_THROW( ERR_MSG("IFFByteStream.bad_chunk") );
327
328 // Write padding byte
329 assert(seekto <= offset);
330 memset((void*)buffer, 0, 8);
331 if (offset & 1)
332 offset += bs->write((void*)&buffer[4], 1);
333
334 // Insert magic to make this file recognizable as DjVu
335 if (insert_magic)
336 {
337 // Don't change the way you do the file magic!
338 // I rely on these bytes letters in some places
339 // (like DjVmFile.cpp and djvm.cpp) -- eaf
340 buffer[0]=0x41;
341 buffer[1]=0x54;
342 buffer[2]=0x26;
343 buffer[3]=0x54;
344 offset += bs->writall((void*)&buffer[0], 4);
345 }
346
347 // Write chunk header
348 memcpy((void*)&buffer[0], (void*)&chkid[0], 4);
349 bytes = bs->writall((void*)&buffer[0], 8);
350 offset = seekto = offset + bytes;
351 if (composite)
352 {
353 memcpy((void*)&buffer[4], (void*)&chkid[5], 4);
354 bytes = bs->writall((void*)&buffer[4], 4);
355 offset = offset + bytes;
356 }
357
358 // Create new context record
359 IFFContext *nctx = new IFFContext;
360 G_TRY
361 {
362 nctx->next = ctx;
363 nctx->offStart = seekto;
364 nctx->offEnd = 0;
365 if (composite)
366 {
367 memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4);
368 memcpy( (void*)(nctx->idTwo), (void*)&buffer[4], 4);
369 nctx->bComposite = 1;
370 }
371 else
372 {
373 memcpy( (void*)(nctx->idOne), (void*)&buffer[0], 4);
374 memset( (void*)(nctx->idTwo), 0, 4);
375 nctx->bComposite = 0;
376 }
377 }
378 G_CATCH_ALL
379 {
380 delete nctx;
381 G_RETHROW;
382 }
383 G_ENDCATCH;
384 // Install context record and leave
385 ctx = nctx;
386 }
387
388
389
390 void
close_chunk()391 IFFByteStream::close_chunk()
392 {
393 // Check that this is ok
394 if (!ctx)
395 G_THROW( ERR_MSG("IFFByteStream.cant_close") );
396 // Patch size field in new chunk
397 if (dir > 0)
398 {
399 ctx->offEnd = offset;
400 long size = ctx->offEnd - ctx->offStart;
401 char buffer[4];
402 buffer[0] = (unsigned char)(size>>24);
403 buffer[1] = (unsigned char)(size>>16);
404 buffer[2] = (unsigned char)(size>>8);
405 buffer[3] = (unsigned char)(size);
406 bs->seek(ctx->offStart - 4);
407 bs->writall((void*)buffer, 4);
408 bs->seek(offset);
409 }
410 // Arrange for reader to seek at next chunk
411 seekto = ctx->offEnd;
412 // Remove ctx record
413 IFFContext *octx = ctx;
414 ctx = octx->next;
415 assert(ctx==0 || ctx->bComposite);
416 delete octx;
417 }
418
419 // This is the same as above, but adds a seek to the close
420 // Otherwise an EOF in this chunk won't be reported until we
421 // try to open the next chunk, which makes error recovery
422 // very difficult.
423 void
seek_close_chunk(void)424 IFFByteStream::seek_close_chunk(void)
425 {
426 close_chunk();
427 if ((dir <= 0)&&((!ctx)||(ctx->bComposite))&&(seekto > offset))
428 {
429 bs->seek(seekto);
430 offset = seekto;
431 }
432 }
433
434 // IFFByteStream::short_id
435 // Returns the id of the current chunk
436
437 void
short_id(GUTF8String & chkid)438 IFFByteStream::short_id(GUTF8String &chkid)
439 {
440 if (!ctx)
441 G_THROW( ERR_MSG("IFFByteStream.no_chunk_id") );
442 if (ctx->bComposite)
443 chkid = GUTF8String(ctx->idOne, 4) + ":" + GUTF8String(ctx->idTwo, 4);
444 else
445 chkid = GUTF8String(ctx->idOne, 4);
446 }
447
448
449 // IFFByteStream::full_id
450 // Returns the full chunk id of the current chunk
451
452 void
full_id(GUTF8String & chkid)453 IFFByteStream::full_id(GUTF8String &chkid)
454 {
455 short_id(chkid);
456 if (ctx->bComposite)
457 return;
458 // Search parent FORM or PROP chunk.
459 for (IFFContext *ct = ctx->next; ct; ct=ct->next)
460 if (memcmp(ct->idOne, "FOR", 3)==0 ||
461 memcmp(ct->idOne, "PRO", 3)==0 )
462 {
463 chkid = GUTF8String(ct->idTwo, 4) + "." + chkid;
464 break;
465 }
466 }
467
468
469
470 // IFFByteStream::read
471 // -- read bytes from IFF file chunk
472
473 size_t
read(void * buffer,size_t size)474 IFFByteStream::read(void *buffer, size_t size)
475 {
476 if (! (ctx && dir < 0))
477 G_THROW( ERR_MSG("IFFByteStream.not_ready3") );
478 // Seek if necessary
479 if (seekto > offset) {
480 bs->seek(seekto);
481 offset = seekto;
482 }
483 // Ensure that read does not extend beyond chunk
484 if (offset > ctx->offEnd)
485 G_THROW( ERR_MSG("IFFByteStream.bad_offset") );
486 if (offset + (long)size > ctx->offEnd)
487 size = (size_t) (ctx->offEnd - offset);
488 // Read bytes
489 size_t bytes = bs->read(buffer, size);
490 offset += bytes;
491 return bytes;
492 }
493
494
495 // IFFByteStream::write
496 // -- write bytes to IFF file chunk
497
498 size_t
write(const void * buffer,size_t size)499 IFFByteStream::write(const void *buffer, size_t size)
500 {
501 if (! (ctx && dir > 0))
502 G_THROW( ERR_MSG("IFFByteStream.not_ready4") );
503 if (seekto > offset)
504 G_THROW( ERR_MSG("IFFByteStream.cant_write") );
505 size_t bytes = bs->write(buffer, size);
506 offset += bytes;
507 return bytes;
508 }
509
510 // IFFByteStream::tell
511 // -- tell position
512
513 long
tell() const514 IFFByteStream::tell() const
515 {
516 return (seekto>offset)?seekto:offset;
517 }
518
519 bool
compare(IFFByteStream & iff)520 IFFByteStream::compare(IFFByteStream &iff)
521 {
522 bool retval=(&iff == this);
523 if(!retval)
524 {
525 GUTF8String chkid1, chkid2;
526 int size;
527 while((size=get_chunk(chkid1)) == iff.get_chunk(chkid2))
528 {
529 if(chkid1 != chkid2)
530 {
531 break;
532 }
533 if(!size)
534 {
535 retval=true;
536 break;
537 }
538 char buf[4096];
539 int len;
540 while((len=read(buf,sizeof(buf))))
541 {
542 int s=0;
543 char buf2[sizeof(buf)];
544 while(s<len)
545 {
546 const int i=iff.read(buf2+s,len-s);
547 if(!i)
548 break;
549 s+=i;
550 }
551 if((s != len)||memcmp(buf,buf2,len))
552 break;
553 }
554 if(len)
555 break;
556 iff.close_chunk();
557 close_chunk();
558 }
559 }
560 return retval;
561 }
562
563
564 #ifdef HAVE_NAMESPACES
565 }
566 # ifndef NOT_USING_DJVU_NAMESPACE
567 using namespace DJVU;
568 # endif
569 #endif
570