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