1 /*
2 ** files.cpp
3 ** Implements classes for reading from files or memory blocks
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2008 Randy Heit
7 ** Copyright 2005-2008 Christoph Oelckers
8 ** All rights reserved.
9 **
10 ** Redistribution and use in source and binary forms, with or without
11 ** modification, are permitted provided that the following conditions
12 ** are met:
13 **
14 ** 1. Redistributions of source code must retain the above copyright
15 ** notice, this list of conditions and the following disclaimer.
16 ** 2. Redistributions in binary form must reproduce the above copyright
17 ** notice, this list of conditions and the following disclaimer in the
18 ** documentation and/or other materials provided with the distribution.
19 ** 3. The name of the author may not be used to endorse or promote products
20 ** derived from this software without specific prior written permission.
21 **
22 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 **---------------------------------------------------------------------------
33 **
34 */
35
36 #ifdef _WIN32
37 #define USE_WINDOWS_DWORD
38 #endif
39 #include "LzmaDec.h"
40
41 #include "files.h"
42 #include "i_system.h"
43 #include "templates.h"
44 #include "m_misc.h"
45
46
47 //==========================================================================
48 //
49 // FileReader
50 //
51 // reads data from an uncompressed file or part of it
52 //
53 //==========================================================================
54
FileReader()55 FileReader::FileReader ()
56 : File(NULL), Length(0), StartPos(0), FilePos(0), CloseOnDestruct(false)
57 {
58 }
59
FileReader(const FileReader & other,long length)60 FileReader::FileReader (const FileReader &other, long length)
61 : File(other.File), Length(length), CloseOnDestruct(false)
62 {
63 FilePos = StartPos = ftell (other.File);
64 }
65
FileReader(const char * filename)66 FileReader::FileReader (const char *filename)
67 : File(NULL), Length(0), StartPos(0), FilePos(0), CloseOnDestruct(false)
68 {
69 if (!Open(filename))
70 {
71 I_Error ("Could not open %s", filename);
72 }
73 }
74
FileReader(FILE * file)75 FileReader::FileReader (FILE *file)
76 : File(file), Length(0), StartPos(0), FilePos(0), CloseOnDestruct(false)
77 {
78 Length = CalcFileLen();
79 }
80
FileReader(FILE * file,long length)81 FileReader::FileReader (FILE *file, long length)
82 : File(file), Length(length), CloseOnDestruct(true)
83 {
84 FilePos = StartPos = ftell (file);
85 }
86
~FileReader()87 FileReader::~FileReader ()
88 {
89 if (CloseOnDestruct && File != NULL)
90 {
91 fclose (File);
92 File = NULL;
93 }
94 }
95
Open(const char * filename)96 bool FileReader::Open (const char *filename)
97 {
98 File = fopen (filename, "rb");
99 if (File == NULL) return false;
100 FilePos = 0;
101 StartPos = 0;
102 CloseOnDestruct = true;
103 Length = CalcFileLen();
104 return true;
105 }
106
107
ResetFilePtr()108 void FileReader::ResetFilePtr ()
109 {
110 FilePos = ftell (File);
111 }
112
Tell() const113 long FileReader::Tell () const
114 {
115 return FilePos - StartPos;
116 }
117
Seek(long offset,int origin)118 long FileReader::Seek (long offset, int origin)
119 {
120 if (origin == SEEK_SET)
121 {
122 offset += StartPos;
123 }
124 else if (origin == SEEK_CUR)
125 {
126 offset += FilePos;
127 }
128 else if (origin == SEEK_END)
129 {
130 offset += StartPos + Length;
131 }
132 if (0 == fseek (File, offset, SEEK_SET))
133 {
134 FilePos = offset;
135 return 0;
136 }
137 return -1;
138 }
139
Read(void * buffer,long len)140 long FileReader::Read (void *buffer, long len)
141 {
142 assert(len >= 0);
143 if (len <= 0) return 0;
144 if (FilePos + len > StartPos + Length)
145 {
146 len = Length - FilePos + StartPos;
147 }
148 len = (long)fread (buffer, 1, len, File);
149 FilePos += len;
150 return len;
151 }
152
Gets(char * strbuf,int len)153 char *FileReader::Gets(char *strbuf, int len)
154 {
155 if (len <= 0 || FilePos >= StartPos + Length) return NULL;
156 char *p = fgets(strbuf, len, File);
157 if (p != NULL)
158 {
159 int old = FilePos;
160 FilePos = ftell(File);
161 if (FilePos - StartPos > Length)
162 {
163 strbuf[Length - old + StartPos] = 0;
164 }
165 }
166 return p;
167 }
168
GetsFromBuffer(const char * bufptr,char * strbuf,int len)169 char *FileReader::GetsFromBuffer(const char * bufptr, char *strbuf, int len)
170 {
171 if (len>Length-FilePos) len=Length-FilePos;
172 if (len <= 0) return NULL;
173
174 char *p = strbuf;
175 while (len > 1)
176 {
177 if (bufptr[FilePos] == 0)
178 {
179 FilePos++;
180 break;
181 }
182 if (bufptr[FilePos] != '\r')
183 {
184 *p++ = bufptr[FilePos];
185 len--;
186 if (bufptr[FilePos] == '\n')
187 {
188 FilePos++;
189 break;
190 }
191 }
192 FilePos++;
193 }
194 if (p==strbuf) return NULL;
195 *p++=0;
196 return strbuf;
197 }
198
CalcFileLen() const199 long FileReader::CalcFileLen() const
200 {
201 long endpos;
202
203 fseek (File, 0, SEEK_END);
204 endpos = ftell (File);
205 fseek (File, 0, SEEK_SET);
206 return endpos;
207 }
208
209 //==========================================================================
210 //
211 // FileReaderZ
212 //
213 // The zlib wrapper
214 // reads data from a ZLib compressed stream
215 //
216 //==========================================================================
217
FileReaderZ(FileReader & file,bool zip)218 FileReaderZ::FileReaderZ (FileReader &file, bool zip)
219 : File(file), SawEOF(false)
220 {
221 int err;
222
223 FillBuffer ();
224
225 Stream.zalloc = Z_NULL;
226 Stream.zfree = Z_NULL;
227
228 if (!zip) err = inflateInit (&Stream);
229 else err = inflateInit2 (&Stream, -MAX_WBITS);
230
231 if (err != Z_OK)
232 {
233 I_Error ("FileReaderZ: inflateInit failed: %s\n", M_ZLibError(err).GetChars());
234 }
235 }
236
~FileReaderZ()237 FileReaderZ::~FileReaderZ ()
238 {
239 inflateEnd (&Stream);
240 }
241
Read(void * buffer,long len)242 long FileReaderZ::Read (void *buffer, long len)
243 {
244 int err;
245
246 Stream.next_out = (Bytef *)buffer;
247 Stream.avail_out = len;
248
249 do
250 {
251 err = inflate (&Stream, Z_SYNC_FLUSH);
252 if (Stream.avail_in == 0 && !SawEOF)
253 {
254 FillBuffer ();
255 }
256 } while (err == Z_OK && Stream.avail_out != 0);
257
258 if (err != Z_OK && err != Z_STREAM_END)
259 {
260 I_Error ("Corrupt zlib stream");
261 }
262
263 if (Stream.avail_out != 0)
264 {
265 I_Error ("Ran out of data in zlib stream");
266 }
267
268 return len - Stream.avail_out;
269 }
270
FillBuffer()271 void FileReaderZ::FillBuffer ()
272 {
273 long numread = File.Read (InBuff, BUFF_SIZE);
274
275 if (numread < BUFF_SIZE)
276 {
277 SawEOF = true;
278 }
279 Stream.next_in = InBuff;
280 Stream.avail_in = numread;
281 }
282
283 //==========================================================================
284 //
285 // FileReaderZ
286 //
287 // The bzip2 wrapper
288 // reads data from a libbzip2 compressed stream
289 //
290 //==========================================================================
291
FileReaderBZ2(FileReader & file)292 FileReaderBZ2::FileReaderBZ2 (FileReader &file)
293 : File(file), SawEOF(false)
294 {
295 int err;
296
297 FillBuffer ();
298
299 Stream.bzalloc = NULL;
300 Stream.bzfree = NULL;
301 Stream.opaque = NULL;
302
303 err = BZ2_bzDecompressInit(&Stream, 0, 0);
304
305 if (err != BZ_OK)
306 {
307 I_Error ("FileReaderBZ2: bzDecompressInit failed: %d\n", err);
308 }
309 }
310
~FileReaderBZ2()311 FileReaderBZ2::~FileReaderBZ2 ()
312 {
313 BZ2_bzDecompressEnd (&Stream);
314 }
315
Read(void * buffer,long len)316 long FileReaderBZ2::Read (void *buffer, long len)
317 {
318 int err;
319
320 Stream.next_out = (char *)buffer;
321 Stream.avail_out = len;
322
323 do
324 {
325 err = BZ2_bzDecompress(&Stream);
326 if (Stream.avail_in == 0 && !SawEOF)
327 {
328 FillBuffer ();
329 }
330 } while (err == BZ_OK && Stream.avail_out != 0);
331
332 if (err != BZ_OK && err != BZ_STREAM_END)
333 {
334 I_Error ("Corrupt bzip2 stream");
335 }
336
337 if (Stream.avail_out != 0)
338 {
339 I_Error ("Ran out of data in bzip2 stream");
340 }
341
342 return len - Stream.avail_out;
343 }
344
FillBuffer()345 void FileReaderBZ2::FillBuffer ()
346 {
347 long numread = File.Read(InBuff, BUFF_SIZE);
348
349 if (numread < BUFF_SIZE)
350 {
351 SawEOF = true;
352 }
353 Stream.next_in = (char *)InBuff;
354 Stream.avail_in = numread;
355 }
356
357 //==========================================================================
358 //
359 // bz_internal_error
360 //
361 // libbzip2 wants this, since we build it with BZ_NO_STDIO set.
362 //
363 //==========================================================================
364
bz_internal_error(int errcode)365 extern "C" void bz_internal_error (int errcode)
366 {
367 I_FatalError("libbzip2: internal error number %d\n", errcode);
368 }
369
370 //==========================================================================
371 //
372 // FileReaderLZMA
373 //
374 // The lzma wrapper
375 // reads data from a LZMA compressed stream
376 //
377 //==========================================================================
378
379 // This is retarded but necessary to work around the inclusion of windows.h in recent
380 // LZMA versions, meaning it's no longer possible to include the LZMA headers in files.h.
381 // As a result we cannot declare the CLzmaDec member in the header so we work around
382 // it my wrapping it into another struct that can be declared anonymously in the header.
383 struct FileReaderLZMA::StreamPointer
384 {
385 CLzmaDec Stream;
386 };
387
SzAlloc(void *,size_t size)388 static void *SzAlloc(void *, size_t size) { return malloc(size); }
SzFree(void *,void * address)389 static void SzFree(void *, void *address) { free(address); }
390 ISzAlloc g_Alloc = { SzAlloc, SzFree };
391
FileReaderLZMA(FileReader & file,size_t uncompressed_size,bool zip)392 FileReaderLZMA::FileReaderLZMA (FileReader &file, size_t uncompressed_size, bool zip)
393 : File(file), SawEOF(false)
394 {
395 BYTE header[4 + LZMA_PROPS_SIZE];
396 int err;
397
398 assert(zip == true);
399
400 Size = uncompressed_size;
401 OutProcessed = 0;
402
403 // Read zip LZMA properties header
404 if (File.Read(header, sizeof(header)) < (long)sizeof(header))
405 {
406 I_Error("FileReaderLZMA: File too shart\n");
407 }
408 if (header[2] + header[3] * 256 != LZMA_PROPS_SIZE)
409 {
410 I_Error("FileReaderLZMA: LZMA props size is %d (expected %d)\n",
411 header[2] + header[3] * 256, LZMA_PROPS_SIZE);
412 }
413
414 FillBuffer();
415
416 Streamp = new StreamPointer;
417 LzmaDec_Construct(&Streamp->Stream);
418 err = LzmaDec_Allocate(&Streamp->Stream, header + 4, LZMA_PROPS_SIZE, &g_Alloc);
419
420 if (err != SZ_OK)
421 {
422 I_Error("FileReaderLZMA: LzmaDec_Allocate failed: %d\n", err);
423 }
424
425 LzmaDec_Init(&Streamp->Stream);
426 }
427
~FileReaderLZMA()428 FileReaderLZMA::~FileReaderLZMA ()
429 {
430 LzmaDec_Free(&Streamp->Stream, &g_Alloc);
431 delete Streamp;
432 }
433
Read(void * buffer,long len)434 long FileReaderLZMA::Read (void *buffer, long len)
435 {
436 int err;
437 Byte *next_out = (Byte *)buffer;
438
439 do
440 {
441 ELzmaFinishMode finish_mode = LZMA_FINISH_ANY;
442 ELzmaStatus status;
443 size_t out_processed = len;
444 size_t in_processed = InSize;
445
446 err = LzmaDec_DecodeToBuf(&Streamp->Stream, next_out, &out_processed, InBuff + InPos, &in_processed, finish_mode, &status);
447 InPos += in_processed;
448 InSize -= in_processed;
449 next_out += out_processed;
450 len = (long)(len - out_processed);
451 if (err != SZ_OK)
452 {
453 I_Error ("Corrupt LZMA stream");
454 }
455 if (in_processed == 0 && out_processed == 0)
456 {
457 if (status != LZMA_STATUS_FINISHED_WITH_MARK)
458 {
459 I_Error ("Corrupt LZMA stream");
460 }
461 }
462 if (InSize == 0 && !SawEOF)
463 {
464 FillBuffer ();
465 }
466 } while (err == SZ_OK && len != 0);
467
468 if (err != Z_OK && err != Z_STREAM_END)
469 {
470 I_Error ("Corrupt LZMA stream");
471 }
472
473 if (len != 0)
474 {
475 I_Error ("Ran out of data in LZMA stream");
476 }
477
478 return (long)(next_out - (Byte *)buffer);
479 }
480
FillBuffer()481 void FileReaderLZMA::FillBuffer ()
482 {
483 long numread = File.Read(InBuff, BUFF_SIZE);
484
485 if (numread < BUFF_SIZE)
486 {
487 SawEOF = true;
488 }
489 InPos = 0;
490 InSize = numread;
491 }
492
493 //==========================================================================
494 //
495 // MemoryReader
496 //
497 // reads data from a block of memory
498 //
499 //==========================================================================
500
MemoryReader(const char * buffer,long length)501 MemoryReader::MemoryReader (const char *buffer, long length)
502 {
503 bufptr=buffer;
504 Length=length;
505 FilePos=0;
506 }
507
~MemoryReader()508 MemoryReader::~MemoryReader ()
509 {
510 }
511
Tell() const512 long MemoryReader::Tell () const
513 {
514 return FilePos;
515 }
516
Seek(long offset,int origin)517 long MemoryReader::Seek (long offset, int origin)
518 {
519 switch (origin)
520 {
521 case SEEK_CUR:
522 offset+=FilePos;
523 break;
524
525 case SEEK_END:
526 offset+=Length;
527 break;
528
529 }
530 FilePos=clamp<long>(offset,0,Length-1);
531 return 0;
532 }
533
Read(void * buffer,long len)534 long MemoryReader::Read (void *buffer, long len)
535 {
536 if (len>Length-FilePos) len=Length-FilePos;
537 if (len<0) len=0;
538 memcpy(buffer,bufptr+FilePos,len);
539 FilePos+=len;
540 return len;
541 }
542
Gets(char * strbuf,int len)543 char *MemoryReader::Gets(char *strbuf, int len)
544 {
545 return GetsFromBuffer(bufptr, strbuf, len);
546 }
547
548 //==========================================================================
549 //
550 // MemoryArrayReader
551 //
552 // reads data from an array of memory
553 //
554 //==========================================================================
555
MemoryArrayReader(const char * buffer,long length)556 MemoryArrayReader::MemoryArrayReader (const char *buffer, long length)
557 {
558 buf.Resize(length);
559 memcpy(&buf[0], buffer, length);
560 Length=length;
561 FilePos=0;
562 }
563
~MemoryArrayReader()564 MemoryArrayReader::~MemoryArrayReader ()
565 {
566 }
567
Tell() const568 long MemoryArrayReader::Tell () const
569 {
570 return FilePos;
571 }
572
Seek(long offset,int origin)573 long MemoryArrayReader::Seek (long offset, int origin)
574 {
575 switch (origin)
576 {
577 case SEEK_CUR:
578 offset+=FilePos;
579 break;
580
581 case SEEK_END:
582 offset+=Length;
583 break;
584
585 }
586 FilePos=clamp<long>(offset,0,Length-1);
587 return 0;
588 }
589
Read(void * buffer,long len)590 long MemoryArrayReader::Read (void *buffer, long len)
591 {
592 if (len>Length-FilePos) len=Length-FilePos;
593 if (len<0) len=0;
594 memcpy(buffer,&buf[FilePos],len);
595 FilePos+=len;
596 return len;
597 }
598
Gets(char * strbuf,int len)599 char *MemoryArrayReader::Gets(char *strbuf, int len)
600 {
601 return GetsFromBuffer((char*)&buf[0], strbuf, len);
602 }
603