1 /*
2 quakeio.c
3
4 (description)
5
6 Copyright (C) 1996-1997 Id Software, Inc.
7 Copyright (C) 1999,2000 contributors of the QuakeForge project
8 Please see the file "AUTHORS" for a list of contributors
9
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18
19 See the GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to:
23
24 Free Software Foundation, Inc.
25 59 Temple Place - Suite 330
26 Boston, MA 02111-1307, USA
27
28 */
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #ifdef HAVE_ZLIB
34 # include <zlib.h>
35 #endif
36 #ifdef HAVE_STRING_H
37 # include <string.h>
38 #endif
39 #ifdef HAVE_STRINGS_H
40 # include <strings.h>
41 #endif
42 #ifdef HAVE_UNISTD_H
43 # include <unistd.h>
44 #endif
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <fcntl.h>
48
49 #if defined(_WIN32) && defined(HAVE_MALLOC_H)
50 #include <malloc.h>
51 #endif
52
53 #ifdef HAVE_IO_H
54 #include <io.h>
55 #endif
56
57 #include <limits.h>
58 #include <stdarg.h>
59 #include <stdlib.h>
60 #include <errno.h>
61
62 #include "qfalloca.h"
63
64 #include "QF/dstring.h"
65 #include "QF/qendian.h"
66 #include "QF/quakefs.h"
67 #include "QF/quakeio.h"
68
69 #ifdef _WIN32
70 # ifndef __BORLANDC__
71 # define setmode _setmode
72 # define O_BINARY _O_BINARY
73 # endif
74 #endif
75
76 #define QF_ZIP 1
77 #define QF_READ 2
78
79 struct QFile_s {
80 FILE *file;
81 #ifdef HAVE_ZLIB
82 gzFile gzfile;
83 #endif
84 off_t size;
85 off_t start;
86 off_t pos;
87 int c;
88 int sub;
89 };
90
91
92 VISIBLE int
Qrename(const char * old_path,const char * new_path)93 Qrename (const char *old_path, const char *new_path)
94 {
95 return rename (old_path, new_path);
96 }
97
98 VISIBLE int
Qremove(const char * path)99 Qremove (const char *path)
100 {
101 return remove (path);
102 }
103
104 VISIBLE int
Qfilesize(QFile * file)105 Qfilesize (QFile *file)
106 {
107 return file->size;
108 }
109
110 static int
check_file(int fd,int offs,int len,int * zip)111 check_file (int fd, int offs, int len, int *zip)
112 {
113 unsigned char id[2], len_bytes[4];
114
115 if (offs < 0 || len < 0) {
116 // normal file
117 offs = 0;
118 len = lseek (fd, 0, SEEK_END);
119 lseek (fd, 0, SEEK_SET);
120 }
121 if (zip && *zip) {
122 int r;
123
124 lseek (fd, offs, SEEK_SET);
125 r = read (fd, id, 2);
126 if (r == 2 && id[0] == 0x1f && id[1] == 0x8b && len >= 6 &&
127 lseek (fd, offs + len - 4, SEEK_SET) >= 0 &&
128 read (fd, len_bytes, 4) == 4) {
129 len = ((len_bytes[3] << 24)
130 | (len_bytes[2] << 16)
131 | (len_bytes[1] << 8)
132 | (len_bytes[0]));
133 } else {
134 *zip = 0;
135 }
136 }
137 lseek (fd, offs, SEEK_SET);
138 return len;
139 }
140
141 static int
file_mode(const char * mode,char * out)142 file_mode (const char *mode, char *out)
143 {
144 int flags = 0;
145 char *p;
146
147 for (p = out; *mode; mode++) {
148 if (*mode == 'z') {
149 flags |= QF_ZIP;
150 continue;
151 }
152 if (*mode == 'r' || ((*mode == 'w' || *mode == 'a') && *mode == '+')) {
153 flags |= QF_READ;
154 }
155 #ifndef HAVE_ZLIB
156 if (strchr ("0123456789fh", *mode)) {
157 continue;
158 }
159 #endif
160 if (p)
161 *p++ = *mode;
162 }
163 if (p)
164 *p = 0;
165 return flags;
166 }
167
168 VISIBLE QFile *
Qopen(const char * path,const char * mode)169 Qopen (const char *path, const char *mode)
170 {
171 QFile *file;
172 char *m;
173 int flags, reading, zip;
174 int size = -1;
175
176 m = alloca (strlen (mode) + 1);
177 flags = file_mode (mode, m);
178
179 zip = flags & QF_ZIP;
180 reading = flags & QF_READ;
181
182 if (reading) {
183 int fd = open (path, O_RDONLY);
184 if (fd != -1) {
185 size = check_file (fd, -1, -1, &zip);
186 close (fd);
187 }
188 }
189
190 file = calloc (sizeof (*file), 1);
191 if (!file)
192 return 0;
193 file->size = size;
194 #ifdef HAVE_ZLIB
195 if (zip) {
196 file->gzfile = gzopen (path, m);
197 if (!file->gzfile) {
198 free (file);
199 return 0;
200 }
201 } else
202 #endif
203 {
204 file->file = fopen (path, m);
205 if (!file->file) {
206 free (file);
207 return 0;
208 }
209 }
210 file->c = -1;
211 return file;
212 }
213
214 VISIBLE QFile *
Qdopen(int fd,const char * mode)215 Qdopen (int fd, const char *mode)
216 {
217 QFile *file;
218 char *m, *p;
219 #ifdef HAVE_ZLIB
220 int zip = 0;
221 #endif
222 int len = strlen (mode);
223
224 m = alloca (len + 1);
225 #ifdef _WIN32
226 setmode (fd, O_BINARY);
227 #endif
228 for (p = m; *mode && p - m < len; mode++) {
229 if (*mode == 'z') {
230 #ifdef HAVE_ZLIB
231 zip = 1;
232 #endif
233 continue;
234 }
235 *p++ = *mode;
236 }
237
238 *p = 0;
239
240 file = calloc (sizeof (*file), 1);
241 if (!file)
242 return 0;
243 #ifdef HAVE_ZLIB
244 if (zip) {
245 file->gzfile = gzdopen (fd, m);
246 if (!file->gzfile) {
247 free (file);
248 return 0;
249 }
250 } else
251 #endif
252 {
253 file->file = fdopen (fd, m);
254 if (!file->file) {
255 free (file);
256 return 0;
257 }
258 }
259 file->c = -1;
260 return file;
261 }
262
263 VISIBLE QFile *
Qfopen(FILE * file,const char * mode)264 Qfopen (FILE *file, const char *mode)
265 {
266 QFile *qfile;
267 int flags = file_mode (mode, 0);
268
269 if (!file)
270 return 0;
271 qfile = calloc (sizeof (*qfile), 1);
272 if (!qfile)
273 return 0;
274 qfile->file = file;
275 if (flags & QF_READ)
276 qfile->size = check_file (fileno (file), -1, -1, 0);
277 qfile->c = -1;
278 return qfile;
279 }
280
281 VISIBLE QFile *
Qsubopen(const char * path,int offs,int len,int zip)282 Qsubopen (const char *path, int offs, int len, int zip)
283 {
284 int fd = open (path, O_RDONLY);
285 QFile *file;
286
287 if (fd == -1)
288 return 0;
289 #ifdef _WIN32
290 setmode (fd, O_BINARY);
291 #endif
292
293 len = check_file (fd, offs, len, &zip);
294 file = Qdopen (fd, zip ? "rbz" : "rb");
295 file->size = len;
296 file->start = offs;
297 file->sub = 1;
298 return file;
299 }
300
301 VISIBLE void
Qclose(QFile * file)302 Qclose (QFile *file)
303 {
304 if (file->file)
305 fclose (file->file);
306 #ifdef HAVE_ZLIB
307 else
308 gzclose (file->gzfile);
309 #endif
310 free (file);
311 }
312
313 VISIBLE int
Qread(QFile * file,void * buf,int count)314 Qread (QFile *file, void *buf, int count)
315 {
316 int offs = 0;
317 int ret;
318
319 if (file->c != -1) {
320 char *b = buf;
321 *b++ = file->c;
322 buf = b;
323 offs = 1;
324 file->c = -1;
325 count--;
326 if (!count)
327 return 1;
328 }
329 if (file->sub) {
330 // sub-files are always opened in binary mode, so we don't need to
331 // worry about character translation messing up count/pos. Normal
332 // files can be left to the operating system to take care of EOF.
333 if (file->pos + count > file->size)
334 count = file->size - file->pos;
335 if (count < 0)
336 return -1;
337 if (!count)
338 return 0;
339 }
340 if (file->file)
341 ret = fread (buf, 1, count, file->file);
342 else
343 #ifdef HAVE_ZLIB
344 ret = gzread (file->gzfile, buf, count);
345 #else
346 return -1;
347 #endif
348 if (file->sub)
349 file->pos += ret;
350 return ret == -1 ? ret : ret + offs;
351 }
352
353 VISIBLE int
Qwrite(QFile * file,const void * buf,int count)354 Qwrite (QFile *file, const void *buf, int count)
355 {
356 if (file->sub) // can't write to a sub-file
357 return -1;
358 if (file->file)
359 return fwrite (buf, 1, count, file->file);
360 #ifdef HAVE_ZLIB
361 else
362 return gzwrite (file->gzfile, (const voidp)buf, count);
363 #else
364 return -1;
365 #endif
366 }
367
368 VISIBLE int
Qprintf(QFile * file,const char * fmt,...)369 Qprintf (QFile *file, const char *fmt, ...)
370 {
371 va_list args;
372 int ret = -1;
373
374 if (file->sub) // can't write to a sub-file
375 return -1;
376 va_start (args, fmt);
377 if (file->file)
378 ret = vfprintf (file->file, fmt, args);
379 #ifdef HAVE_ZLIB
380 else {
381 static dstring_t *buf;
382
383 if (!buf)
384 buf = dstring_new ();
385
386 va_start (args, fmt);
387 dvsprintf (buf, fmt, args);
388 va_end (args);
389 ret = strlen (buf->str);
390 if (ret > 0)
391 ret = gzwrite (file->gzfile, buf, (unsigned) ret);
392 }
393 #endif
394 va_end (args);
395 return ret;
396 }
397
398 VISIBLE int
Qputs(QFile * file,const char * buf)399 Qputs (QFile *file, const char *buf)
400 {
401 if (file->sub) // can't write to a sub-file
402 return -1;
403 if (file->file)
404 return fputs (buf, file->file);
405 #ifdef HAVE_ZLIB
406 else
407 return gzputs (file->gzfile, buf);
408 #else
409 return 0;
410 #endif
411 }
412
413 VISIBLE char *
Qgets(QFile * file,char * buf,int count)414 Qgets (QFile *file, char *buf, int count)
415 {
416 char *ret = buf;
417 char c;
418
419 while (buf - ret < count - 1) {
420 c = Qgetc (file);
421 if (c < 0)
422 break;
423 *buf++ = c;
424 if (c == '\n')
425 break;
426 }
427 if (buf == ret)
428 return 0;
429
430 *buf++ = 0;
431 return ret;
432 }
433
434 VISIBLE int
Qgetc(QFile * file)435 Qgetc (QFile *file)
436 {
437 if (file->c != -1) {
438 int c = file->c;
439 file->c = -1;
440 return c;
441 }
442 if (file->sub) {
443 if (file->pos >= file->size)
444 return EOF;
445 file->pos++;
446 }
447 if (file->file)
448 return fgetc (file->file);
449 #ifdef HAVE_ZLIB
450 else
451 return gzgetc (file->gzfile);
452 #else
453 return -1;
454 #endif
455 }
456
457 VISIBLE int
Qputc(QFile * file,int c)458 Qputc (QFile *file, int c)
459 {
460 if (file->sub) // can't write to a sub-file
461 return -1;
462 if (file->file)
463 return fputc (c, file->file);
464 #ifdef HAVE_ZLIB
465 else
466 return gzputc (file->gzfile, c);
467 #else
468 return -1;
469 #endif
470 }
471
472 VISIBLE int
Qungetc(QFile * file,int c)473 Qungetc (QFile *file, int c)
474 {
475 if (file->c == -1)
476 file->c = (byte) c;
477 return c;
478 }
479
480 VISIBLE int
Qseek(QFile * file,long offset,int whence)481 Qseek (QFile *file, long offset, int whence)
482 {
483 int res;
484
485 file->c = -1;
486 if (file->file) {
487 switch (whence) {
488 case SEEK_SET:
489 res = fseek (file->file, file->start + offset, whence);
490 break;
491 case SEEK_CUR:
492 res = fseek (file->file, offset, whence);
493 break;
494 case SEEK_END:
495 if (file->size == -1) {
496 // we don't know the size (due to writing) so punt and
497 // pass on the request as-is
498 res = fseek (file->file, offset, SEEK_END);
499 } else {
500 res = fseek (file->file,
501 file->start + file->size - offset, SEEK_SET);
502 }
503 break;
504 default:
505 errno = EINVAL;
506 return -1;
507 }
508 if (res != -1)
509 res = ftell (file->file) - file->start;
510 if (file->sub)
511 file->pos = res;
512 return res;
513 }
514 #ifdef HAVE_ZLIB
515 else {
516 // libz seems to keep track of the true start position itself
517 // doesn't support SEEK_END, though
518 res = gzseek (file->gzfile, offset, whence);
519 if (file->sub)
520 file->pos = res;
521 return res;
522 }
523 #else
524 return -1;
525 #endif
526 }
527
528 VISIBLE long
Qtell(QFile * file)529 Qtell (QFile *file)
530 {
531 int offs;
532 int ret;
533
534 offs = (file->c != -1) ? 1 : 0;
535 if (file->file)
536 ret = ftell (file->file) - file->start;
537 else
538 #ifdef HAVE_ZLIB
539 ret = gztell (file->gzfile); //FIXME does gztell do the right thing?
540 #else
541 return -1;
542 #endif
543 if (file->sub)
544 file->pos = ret;
545 return ret == -1 ? ret : ret - offs;
546 }
547
548 VISIBLE int
Qflush(QFile * file)549 Qflush (QFile *file)
550 {
551 if (file->file)
552 return fflush (file->file);
553 #ifdef HAVE_ZLIB
554 else
555 return gzflush (file->gzfile, Z_SYNC_FLUSH);
556 #else
557 return -1;
558 #endif
559 }
560
561 VISIBLE int
Qeof(QFile * file)562 Qeof (QFile *file)
563 {
564 if (file->c != -1)
565 return 0;
566 if (file->sub)
567 return file->pos >= file->size;
568 if (file->file)
569 return feof (file->file);
570 #ifdef HAVE_ZLIB
571 else
572 return gzeof (file->gzfile);
573 #else
574 return -1;
575 #endif
576 }
577
578 /*
579 Qgetline
580
581 Dynamic length version of Qgets. Do NOT free the buffer.
582 */
583 VISIBLE const char *
Qgetline(QFile * file)584 Qgetline (QFile *file)
585 {
586 static int size = 256;
587 static char *buf = 0;
588 int len;
589
590 if (!buf) {
591 buf = malloc (size);
592 if (!buf)
593 return 0;
594 }
595
596 if (!Qgets (file, buf, size))
597 return 0;
598
599 len = strlen (buf);
600 while (len && buf[len - 1] != '\n') {
601 char *t = realloc (buf, size + 256);
602
603 if (!t)
604 return 0;
605 buf = t;
606 size += 256;
607 if (!Qgets (file, buf + len, size - len))
608 break;
609 len = strlen (buf);
610 }
611 return buf;
612 }
613