1 // _________ __ __
2 // / _____// |_____________ _/ |______ ____ __ __ ______
3 // \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
4 // / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
5 // /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
6 // \/ \/ \//_____/ \/
7 // ______________________ ______________________
8 // T H E W A R B E G I N S
9 // Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name iolib.cpp - Compression-IO helper functions. */
12 //
13 // (c) Copyright 2000-2011 by Andreas Arens, Lutz Sammer Jimmy Salmon and
14 // Pali Rohár
15 //
16 // This program is free software; you can redistribute it and/or modify
17 // it under the terms of the GNU General Public License as published by
18 // the Free Software Foundation; only version 2 of the License.
19 //
20 // This program is distributed in the hope that it will be useful,
21 // but WITHOUT ANY WARRANTY; without even the implied warranty of
22 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 // GNU General Public License for more details.
24 //
25 // You should have received a copy of the GNU General Public License
26 // along with this program; if not, write to the Free Software
27 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28 // 02111-1307, USA.
29 //
30
31 //@{
32
33 /*----------------------------------------------------------------------------
34 -- Includes
35 ----------------------------------------------------------------------------*/
36
37 #include "stratagus.h"
38
39 #include "iolib.h"
40
41 #include "game.h"
42 #include "iocompat.h"
43 #include "map.h"
44 #include "parameters.h"
45 #include "util.h"
46
47 #include "SDL.h"
48
49 #include <stdarg.h>
50 #include <stdio.h>
51
52 #ifdef USE_ZLIB
53 #include <zlib.h>
54 #endif
55
56 #ifdef USE_BZ2LIB
57 #include <bzlib.h>
58 #endif
59
60 class CFile::PImpl
61 {
62 public:
63 PImpl();
64 ~PImpl();
65
66 int open(const char *name, long flags);
67 int close();
68 void flush();
69 int read(void *buf, size_t len);
70 int seek(long offset, int whence);
71 long tell();
72 int write(const void *buf, size_t len);
73
74 private:
75 PImpl(const PImpl &rhs); // No implementation
76 const PImpl &operator = (const PImpl &rhs); // No implementation
77
78 private:
79 int cl_type; /// type of CFile
80 FILE *cl_plain; /// standard file pointer
81 #ifdef USE_ZLIB
82 gzFile cl_gz; /// gzip file pointer
83 #endif // !USE_ZLIB
84 #ifdef USE_BZ2LIB
85 BZFILE *cl_bz; /// bzip2 file pointer
86 #endif // !USE_BZ2LIB
87 };
88
CFile()89 CFile::CFile() : pimpl(new CFile::PImpl)
90 {
91 }
92
~CFile()93 CFile::~CFile()
94 {
95 delete pimpl;
96 }
97
98
99 /**
100 ** CLopen Library file open
101 **
102 ** @param name File name.
103 ** @param openflags Open read, or write and compression options
104 **
105 ** @return File Pointer
106 */
open(const char * name,long flags)107 int CFile::open(const char *name, long flags)
108 {
109 return pimpl->open(name, flags);
110 }
111
112 /**
113 ** CLclose Library file close
114 */
close()115 int CFile::close()
116 {
117 return pimpl->close();
118 }
119
flush()120 void CFile::flush()
121 {
122 pimpl->flush();
123 }
124
125 /**
126 ** CLread Library file read
127 **
128 ** @param buf Pointer to read the data to.
129 ** @param len number of bytes to read.
130 */
read(void * buf,size_t len)131 int CFile::read(void *buf, size_t len)
132 {
133 return pimpl->read(buf, len);
134 }
135
136 /**
137 ** CLseek Library file seek
138 **
139 ** @param offset Seek position
140 ** @param whence How to seek
141 */
seek(long offset,int whence)142 int CFile::seek(long offset, int whence)
143 {
144 return pimpl->seek(offset, whence);
145 }
146
147 /**
148 ** CLtell Library file tell
149 */
tell()150 long CFile::tell()
151 {
152 return pimpl->tell();
153 }
154
155 /**
156 ** CLprintf Library file write
157 **
158 ** @param format String Format.
159 ** @param ... Parameter List.
160 */
printf(const char * format,...)161 int CFile::printf(const char *format, ...)
162 {
163 int size = 500;
164 char *p = new char[size];
165 if (p == NULL) {
166 return -1;
167 }
168 while (1) {
169 // Try to print in the allocated space.
170 va_list ap;
171 va_start(ap, format);
172 const int n = vsnprintf(p, size, format, ap);
173 va_end(ap);
174 // If that worked, string was processed.
175 if (n > -1 && n < size) {
176 break;
177 }
178 // Else try again with more space.
179 if (n > -1) { // glibc 2.1
180 size = n + 1; // precisely what is needed
181 } else { /* glibc 2.0, vc++ */
182 size *= 2; // twice the old size
183 }
184 delete[] p;
185 p = new char[size];
186 if (p == NULL) {
187 return -1;
188 }
189 }
190 size = strlen(p);
191 int ret = pimpl->write(p, size);
192 delete[] p;
193 return ret;
194 }
195
sdl_size(SDL_RWops * context)196 static Sint64 sdl_size(SDL_RWops * context) {
197 return -1;
198 }
199
sdl_seek(SDL_RWops * context,Sint64 offset,int whence)200 static Sint64 sdl_seek(SDL_RWops * context, Sint64 offset, int whence) {
201 CFile *self = reinterpret_cast<CFile*>(context->hidden.unknown.data1);
202 return self->seek(offset, whence);
203 }
204
sdl_read(SDL_RWops * context,void * ptr,size_t size,size_t maxnum)205 static size_t sdl_read(SDL_RWops * context, void *ptr, size_t size, size_t maxnum) {
206 CFile *self = reinterpret_cast<CFile*>(context->hidden.unknown.data1);
207 return self->read(ptr, size * maxnum) / size;
208 }
209
sdl_write(SDL_RWops * context,const void * ptr,size_t size,size_t num)210 static size_t sdl_write(SDL_RWops * context, const void *ptr, size_t size, size_t num) {
211 return 0;
212 }
213
sdl_close(SDL_RWops * context)214 static int sdl_close(SDL_RWops * context) {
215 CFile *self = reinterpret_cast<CFile*>(context->hidden.unknown.data1);
216 free(context);
217 return self->close();
218 }
219
as_SDL_RWops()220 SDL_RWops * CFile::as_SDL_RWops()
221 {
222 SDL_RWops *ops = (SDL_RWops *) calloc(1, sizeof(SDL_RWops));
223 ops->type = SDL_RWOPS_UNKNOWN;
224 ops->hidden.unknown.data1 = this;
225 ops->size = sdl_size;
226 ops->seek = sdl_seek;
227 ops->read = sdl_read;
228 ops->write = sdl_write;
229 ops->close = sdl_close;
230 return ops;
231 }
232
233 //
234 // Implementation.
235 //
236
PImpl()237 CFile::PImpl::PImpl()
238 {
239 cl_type = CLF_TYPE_INVALID;
240 }
241
~PImpl()242 CFile::PImpl::~PImpl()
243 {
244 if (cl_type != CLF_TYPE_INVALID) {
245 DebugPrint("File wasn't closed\n");
246 close();
247 }
248 }
249
250 #ifdef USE_ZLIB
251
252 #ifndef z_off_t // { ZLIB_VERSION<="1.0.4"
253
254 /**
255 ** Seek on compressed input. (Newer libs support it directly)
256 **
257 ** @param file File
258 ** @param offset Seek position
259 ** @param whence How to seek
260 */
gzseek(CFile * file,unsigned offset,int whence)261 static int gzseek(CFile *file, unsigned offset, int whence)
262 {
263 char buf[32];
264
265 while (offset > sizeof(buf)) {
266 gzread(file, buf, sizeof(buf));
267 offset -= sizeof(buf);
268 }
269 return gzread(file, buf, offset);
270 }
271
272 #endif // } ZLIB_VERSION<="1.0.4"
273
274 #endif // USE_ZLIB
275
276 #ifdef USE_BZ2LIB
277
278 /**
279 ** Seek on compressed input. (I hope newer libs support it directly)
280 **
281 ** @param file File handle
282 ** @param offset Seek position
283 ** @param whence How to seek
284 */
bzseek(BZFILE * file,unsigned offset,int)285 static void bzseek(BZFILE *file, unsigned offset, int)
286 {
287 char buf[32];
288
289 while (offset > sizeof(buf)) {
290 BZ2_bzread(file, buf, sizeof(buf));
291 offset -= sizeof(buf);
292 }
293 BZ2_bzread(file, buf, offset);
294 }
295
296 #endif // USE_BZ2LIB
297
open(const char * name,long openflags)298 int CFile::PImpl::open(const char *name, long openflags)
299 {
300 char buf[512];
301 const char *openstring;
302
303 if ((openflags & CL_OPEN_READ) && (openflags & CL_OPEN_WRITE)) {
304 openstring = "rwb";
305 } else if (openflags & CL_OPEN_READ) {
306 openstring = "rb";
307 } else if (openflags & CL_OPEN_WRITE) {
308 openstring = "wb";
309 } else {
310 DebugPrint("Bad CLopen flags");
311 Assert(0);
312 return -1;
313 }
314
315 cl_type = CLF_TYPE_INVALID;
316
317 if (openflags & CL_OPEN_WRITE) {
318 #ifdef USE_BZ2LIB
319 if ((openflags & CL_WRITE_BZ2)
320 && (cl_bz = BZ2_bzopen(strcat(strcpy(buf, name), ".bz2"), openstring))) {
321 cl_type = CLF_TYPE_BZIP2;
322 } else
323 #endif
324 #ifdef USE_ZLIB
325 if ((openflags & CL_WRITE_GZ)
326 && (cl_gz = gzopen(strcat(strcpy(buf, name), ".gz"), openstring))) {
327 cl_type = CLF_TYPE_GZIP;
328 } else
329 #endif
330 if ((cl_plain = fopen(name, openstring))) {
331 cl_type = CLF_TYPE_PLAIN;
332 }
333 } else {
334 if (!(cl_plain = fopen(name, openstring))) { // try plain first
335 #ifdef USE_ZLIB
336 if ((cl_gz = gzopen(strcat(strcpy(buf, name), ".gz"), "rb"))) {
337 cl_type = CLF_TYPE_GZIP;
338 } else
339 #endif
340 #ifdef USE_BZ2LIB
341 if ((cl_bz = BZ2_bzopen(strcat(strcpy(buf, name), ".bz2"), "rb"))) {
342 cl_type = CLF_TYPE_BZIP2;
343 } else
344 #endif
345 { }
346
347 } else {
348 cl_type = CLF_TYPE_PLAIN;
349 // Hmm, plain worked, but nevertheless the file may be compressed!
350 if (fread(buf, 2, 1, cl_plain) == 1) {
351 #ifdef USE_BZ2LIB
352 if (buf[0] == 'B' && buf[1] == 'Z') {
353 fclose(cl_plain);
354 if ((cl_bz = BZ2_bzopen(name, "rb"))) {
355 cl_type = CLF_TYPE_BZIP2;
356 } else {
357 if (!(cl_plain = fopen(name, "rb"))) {
358 cl_type = CLF_TYPE_INVALID;
359 }
360 }
361 }
362 #endif // USE_BZ2LIB
363 #ifdef USE_ZLIB
364 if (buf[0] == 0x1f) { // don't check for buf[1] == 0x8b, so that old compress also works!
365 fclose(cl_plain);
366 if ((cl_gz = gzopen(name, "rb"))) {
367 cl_type = CLF_TYPE_GZIP;
368 } else {
369 if (!(cl_plain = fopen(name, "rb"))) {
370 cl_type = CLF_TYPE_INVALID;
371 }
372 }
373 }
374 #endif // USE_ZLIB
375 }
376 if (cl_type == CLF_TYPE_PLAIN) { // ok, it is not compressed
377 rewind(cl_plain);
378 }
379 }
380 }
381
382 if (cl_type == CLF_TYPE_INVALID) {
383 //fprintf(stderr, "%s in ", buf);
384 return -1;
385 }
386 return 0;
387 }
388
close()389 int CFile::PImpl::close()
390 {
391 int ret = EOF;
392 int tp = cl_type;
393
394 if (tp != CLF_TYPE_INVALID) {
395 if (tp == CLF_TYPE_PLAIN) {
396 ret = fclose(cl_plain);
397 }
398 #ifdef USE_ZLIB
399 if (tp == CLF_TYPE_GZIP) {
400 ret = gzclose(cl_gz);
401 }
402 #endif // USE_ZLIB
403 #ifdef USE_BZ2LIB
404 if (tp == CLF_TYPE_BZIP2) {
405 BZ2_bzclose(cl_bz);
406 ret = 0;
407 }
408 #endif // USE_BZ2LIB
409 } else {
410 errno = EBADF;
411 }
412 cl_type = CLF_TYPE_INVALID;
413 return ret;
414 }
415
read(void * buf,size_t len)416 int CFile::PImpl::read(void *buf, size_t len)
417 {
418 int ret = 0;
419
420 if (cl_type != CLF_TYPE_INVALID) {
421 if (cl_type == CLF_TYPE_PLAIN) {
422 ret = fread(buf, 1, len, cl_plain);
423 }
424 #ifdef USE_ZLIB
425 if (cl_type == CLF_TYPE_GZIP) {
426 ret = gzread(cl_gz, buf, len);
427 }
428 #endif // USE_ZLIB
429 #ifdef USE_BZ2LIB
430 if (cl_type == CLF_TYPE_BZIP2) {
431 ret = BZ2_bzread(cl_bz, buf, len);
432 }
433 #endif // USE_BZ2LIB
434 } else {
435 errno = EBADF;
436 }
437 return ret;
438 }
439
flush()440 void CFile::PImpl::flush()
441 {
442 if (cl_type != CLF_TYPE_INVALID) {
443 if (cl_type == CLF_TYPE_PLAIN) {
444 fflush(cl_plain);
445 }
446 #ifdef USE_ZLIB
447 if (cl_type == CLF_TYPE_GZIP) {
448 gzflush(cl_gz, Z_SYNC_FLUSH);
449 }
450 #endif // USE_ZLIB
451 #ifdef USE_BZ2LIB
452 if (cl_type == CLF_TYPE_BZIP2) {
453 BZ2_bzflush(cl_bz);
454 }
455 #endif // USE_BZ2LIB
456 } else {
457 errno = EBADF;
458 }
459 }
460
write(const void * buf,size_t size)461 int CFile::PImpl::write(const void *buf, size_t size)
462 {
463 int tp = cl_type;
464 int ret = -1;
465
466 if (tp != CLF_TYPE_INVALID) {
467 if (tp == CLF_TYPE_PLAIN) {
468 ret = fwrite(buf, size, 1, cl_plain);
469 }
470 #ifdef USE_ZLIB
471 if (tp == CLF_TYPE_GZIP) {
472 ret = gzwrite(cl_gz, buf, size);
473 }
474 #endif // USE_ZLIB
475 #ifdef USE_BZ2LIB
476 if (tp == CLF_TYPE_BZIP2) {
477 ret = BZ2_bzwrite(cl_bz, const_cast<void *>(buf), size);
478 }
479 #endif // USE_BZ2LIB
480 } else {
481 errno = EBADF;
482 }
483 return ret;
484 }
485
seek(long offset,int whence)486 int CFile::PImpl::seek(long offset, int whence)
487 {
488 int ret = -1;
489 int tp = cl_type;
490
491 if (tp != CLF_TYPE_INVALID) {
492 if (tp == CLF_TYPE_PLAIN) {
493 ret = fseek(cl_plain, offset, whence);
494 }
495 #ifdef USE_ZLIB
496 if (tp == CLF_TYPE_GZIP) {
497 ret = gzseek(cl_gz, offset, whence);
498 }
499 #endif // USE_ZLIB
500 #ifdef USE_BZ2LIB
501 if (tp == CLF_TYPE_BZIP2) {
502 bzseek(cl_bz, offset, whence);
503 ret = 0;
504 }
505 #endif // USE_BZ2LIB
506 } else {
507 errno = EBADF;
508 }
509 return ret;
510 }
511
tell()512 long CFile::PImpl::tell()
513 {
514 int ret = -1;
515 int tp = cl_type;
516
517 if (tp != CLF_TYPE_INVALID) {
518 if (tp == CLF_TYPE_PLAIN) {
519 ret = ftell(cl_plain);
520 }
521 #ifdef USE_ZLIB
522 if (tp == CLF_TYPE_GZIP) {
523 ret = gztell(cl_gz);
524 }
525 #endif // USE_ZLIB
526 #ifdef USE_BZ2LIB
527 if (tp == CLF_TYPE_BZIP2) {
528 // FIXME: need to implement this
529 ret = -1;
530 }
531 #endif // USE_BZ2LIB
532 } else {
533 errno = EBADF;
534 }
535 return ret;
536 }
537
538
539 /**
540 ** Find a file with its correct extension ("", ".gz" or ".bz2")
541 **
542 ** @param file The string with the file path. Upon success, the string
543 ** is replaced by the full filename with the correct extension.
544 ** @param filesize Size of the file buffer
545 **
546 ** @return true if the file has been found.
547 */
FindFileWithExtension(char (& file)[PATH_MAX])548 static bool FindFileWithExtension(char(&file)[PATH_MAX])
549 {
550 if (!access(file, R_OK)) {
551 return true;
552 }
553 #if defined(USE_ZLIB) || defined(USE_BZ2LIB)
554 char buf[PATH_MAX + 4];
555 #endif
556 #ifdef USE_ZLIB // gzip or bzip2 in global shared directory
557 snprintf(buf, PATH_MAX, "%s.gz", file);
558 if (!access(buf, R_OK)) {
559 strcpy_s(file, PATH_MAX, buf);
560 return true;
561 }
562 #endif
563 #ifdef USE_BZ2LIB
564 snprintf(buf, PATH_MAX, "%s.bz2", file);
565 if (!access(buf, R_OK)) {
566 strcpy_s(file, PATH_MAX, buf);
567 return true;
568 }
569 #endif
570 return false;
571 }
572
573 /**
574 ** Generate a filename into library.
575 **
576 ** Try current directory, user home directory, global directory.
577 ** This supports .gz, .bz2 and .zip.
578 **
579 ** @param file Filename to open.
580 ** @param buffer Allocated buffer for generated filename.
581 */
LibraryFileName(const char * file,char (& buffer)[PATH_MAX])582 static void LibraryFileName(const char *file, char(&buffer)[PATH_MAX])
583 {
584 // Absolute path or in current directory.
585 strcpy_s(buffer, PATH_MAX, file);
586 if (*buffer == '/') {
587 return;
588 }
589 if (FindFileWithExtension(buffer)) {
590 return;
591 }
592
593 // Try in map directory
594 if (*CurrentMapPath) {
595 if (*CurrentMapPath == '.' || *CurrentMapPath == '/') {
596 strcpy_s(buffer, PATH_MAX, CurrentMapPath);
597 char *s = strrchr(buffer, '/');
598 if (s) {
599 s[1] = '\0';
600 }
601 strcat_s(buffer, PATH_MAX, file);
602 } else {
603 strcpy_s(buffer, PATH_MAX, StratagusLibPath.c_str());
604 if (*buffer) {
605 strcat_s(buffer, PATH_MAX, "/");
606 }
607 strcat_s(buffer, PATH_MAX, CurrentMapPath);
608 char *s = strrchr(buffer, '/');
609 if (s) {
610 s[1] = '\0';
611 }
612 strcat_s(buffer, PATH_MAX, file);
613 }
614 if (FindFileWithExtension(buffer)) {
615 return;
616 }
617 }
618
619 // In user home directory
620 if (!GameName.empty()) {
621 sprintf(buffer, "%s/%s/%s", Parameters::Instance.GetUserDirectory().c_str(), GameName.c_str(), file);
622 if (FindFileWithExtension(buffer)) {
623 return;
624 }
625 }
626
627 // In global shared directory
628 sprintf(buffer, "%s/%s", StratagusLibPath.c_str(), file);
629 if (FindFileWithExtension(buffer)) {
630 return;
631 }
632
633 // Support for graphics in default graphics dir.
634 // They could be anywhere now, but check if they haven't
635 // got full paths.
636 sprintf(buffer, "graphics/%s", file);
637 if (FindFileWithExtension(buffer)) {
638 return;
639 }
640 sprintf(buffer, "%s/graphics/%s", StratagusLibPath.c_str(), file);
641 if (FindFileWithExtension(buffer)) {
642 return;
643 }
644
645 // Support for sounds in default sounds dir.
646 // They could be anywhere now, but check if they haven't
647 // got full paths.
648 sprintf(buffer, "sounds/%s", file);
649 if (FindFileWithExtension(buffer)) {
650 return;
651 }
652 sprintf(buffer, "%s/sounds/%s", StratagusLibPath.c_str(), file);
653 if (FindFileWithExtension(buffer)) {
654 return;
655 }
656
657 // Support for scripts in default scripts dir.
658 sprintf(buffer, "scripts/%s", file);
659 if (FindFileWithExtension(buffer)) {
660 return;
661 }
662 sprintf(buffer, "%s/scripts/%s", StratagusLibPath.c_str(), file);
663 if (FindFileWithExtension(buffer)) {
664 return;
665 }
666
667 DebugPrint("File '%s' not found\n" _C_ file);
668 strcpy_s(buffer, PATH_MAX, file);
669 }
670
LibraryFileName(const char * file)671 extern std::string LibraryFileName(const char *file)
672 {
673 char buffer[PATH_MAX];
674 LibraryFileName(file, buffer);
675 return buffer;
676 }
677
CanAccessFile(const char * filename)678 bool CanAccessFile(const char *filename)
679 {
680 if (filename && filename[0] != '\0') {
681 char name[PATH_MAX];
682 name[0] = '\0';
683 LibraryFileName(filename, name);
684 return (name[0] != '\0' && 0 == access(name, R_OK));
685 }
686 return false;
687 }
688
689 /**
690 ** Generate a list of files within a specified directory
691 **
692 ** @param dirname Directory to read.
693 ** @param fl Filelist pointer.
694 **
695 ** @return the number of entries added to FileList.
696 */
ReadDataDirectory(const char * dirname,std::vector<FileList> & fl)697 int ReadDataDirectory(const char *dirname, std::vector<FileList> &fl)
698 {
699 struct stat st;
700 char buffer[PATH_MAX];
701 char *filename;
702
703 strcpy_s(buffer, sizeof(buffer), dirname);
704 int n = strlen(buffer);
705 if (!n || buffer[n - 1] != '/') {
706 buffer[n++] = '/';
707 buffer[n] = 0;
708 }
709 char *np = buffer + n;
710
711 #ifndef USE_WIN32
712 DIR *dirp = opendir(dirname);
713 struct dirent *dp;
714
715 if (dirp) {
716 while ((dp = readdir(dirp)) != NULL) {
717 filename = dp->d_name;
718 #else
719 strcat_s(buffer, sizeof(buffer), "*.*");
720 struct _finddata_t fileinfo;
721 intptr_t hFile = _findfirst(buffer, &fileinfo);
722 if (hFile != -1) {
723 do {
724 filename = fileinfo.name;
725 #endif
726 if (strcmp(filename, ".") == 0) {
727 continue;
728 }
729 if (strcmp(filename, "..") == 0) {
730 continue;
731 }
732 strcpy_s(np, sizeof(buffer) - (np - buffer), filename);
733 if (stat(buffer, &st) == 0) {
734 int isdir = S_ISDIR(st.st_mode);
735 if (isdir || S_ISREG(st.st_mode)) {
736 FileList nfl;
737
738 if (isdir) {
739 nfl.name = np;
740 } else {
741 nfl.name = np;
742 nfl.type = 1;
743 }
744 // sorted instertion
745 fl.insert(std::lower_bound(fl.begin(), fl.end(), nfl), nfl);
746 }
747 }
748 #ifndef USE_WIN32
749 }
750 closedir(dirp);
751 #else
752 } while (_findnext(hFile, &fileinfo) == 0);
753 _findclose(hFile);
754 #endif
755 }
756 return fl.size();
757 }
758
759 void FileWriter::printf(const char *format, ...)
760 {
761 // FIXME: hardcoded size
762 char buf[1024];
763
764 va_list ap;
765 va_start(ap, format);
766 buf[sizeof(buf) - 1] = '\0';
767 vsnprintf(buf, sizeof(buf) - 1, format, ap);
768 va_end(ap);
769 write(buf, strlen(buf));
770 }
771
772
773 class RawFileWriter : public FileWriter
774 {
775 FILE *file;
776
777 public:
778 RawFileWriter(const std::string &filename)
779 {
780 file = fopen(filename.c_str(), "wb");
781 if (!file) {
782 fprintf(stderr, "Can't open file '%s' for writing\n", filename.c_str());
783 throw FileException();
784 }
785 }
786
787 virtual ~RawFileWriter()
788 {
789 if (file) { fclose(file); }
790 }
791
792 virtual int write(const char *data, unsigned int size)
793 {
794 return fwrite(data, size, 1, file);
795 }
796 };
797
798 class GzFileWriter : public FileWriter
799 {
800 gzFile file;
801
802 public:
803 GzFileWriter(const std::string &filename)
804 {
805 file = gzopen(filename.c_str(), "wb9");
806 if (!file) {
807 fprintf(stderr, "Can't open file '%s' for writing\n", filename.c_str());
808 throw FileException();
809 }
810 }
811
812 virtual ~GzFileWriter()
813 {
814 if (file) { gzclose(file); }
815 }
816
817 virtual int write(const char *data, unsigned int size)
818 {
819 return gzwrite(file, data, size);
820 }
821 };
822
823 /**
824 ** Create FileWriter
825 */
826 FileWriter *CreateFileWriter(const std::string &filename)
827 {
828 if (strcasestr(filename.c_str(), ".gz")) {
829 return new GzFileWriter(filename);
830 } else {
831 return new RawFileWriter(filename);
832 }
833 }
834
835 //@}
836