OSDN Git Service

fix #36659
[jnethack/source.git] / src / dlb.c
1 /* NetHack 3.6  dlb.c   $NHDT-Date: 1446975464 2015/11/08 09:37:44 $  $NHDT-Branch: master $:$NHDT-Revision: 1.15 $ */
2 /* Copyright (c) Kenneth Lorber, Bethesda, Maryland, 1993. */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 #include "config.h"
6 #include "dlb.h"
7
8 #ifdef __DJGPP__
9 #include <string.h>
10 #endif
11
12 #define DATAPREFIX 4
13
14 #if defined(OVERLAY)
15 #define STATIC_DCL extern
16 #define STATIC_OVL
17 #else /* !OVERLAY */
18 #define STATIC_DCL static
19 #define STATIC_OVL static
20 #endif /* OVERLAY */
21
22 #ifdef DLB
23 /*
24  * Data librarian.  Present a STDIO-like interface to NetHack while
25  * multiplexing on one or more "data libraries".  If a file is not found
26  * in a given library, look for it outside the libraries.
27  */
28
29 typedef struct dlb_procs {
30     boolean NDECL((*dlb_init_proc));
31     void NDECL((*dlb_cleanup_proc));
32     boolean FDECL((*dlb_fopen_proc), (DLB_P, const char *, const char *));
33     int FDECL((*dlb_fclose_proc), (DLB_P));
34     int FDECL((*dlb_fread_proc), (char *, int, int, DLB_P));
35     int FDECL((*dlb_fseek_proc), (DLB_P, long, int));
36     char *FDECL((*dlb_fgets_proc), (char *, int, DLB_P));
37     int FDECL((*dlb_fgetc_proc), (DLB_P));
38     long FDECL((*dlb_ftell_proc), (DLB_P));
39 } dlb_procs_t;
40
41 /* without extern.h via hack.h, these haven't been declared for us */
42 extern FILE *FDECL(fopen_datafile, (const char *, const char *, int));
43
44 #ifdef DLBLIB
45 /*
46  * Library Implementation:
47  *
48  * When initialized, we open all library files and read in their tables
49  * of contents.  The library files stay open all the time.  When
50  * a open is requested, the libraries' directories are searched.  If
51  * successful, we return a descriptor that contains the library, file
52  * size, and current file mark.  This descriptor is used for all
53  * successive calls.
54  *
55  * The ability to open more than one library is supported but used
56  * only in the Amiga port (the second library holds the sound files).
57  * For Unix, the idea would be to split the NetHack library
58  * into text and binary parts, where the text version could be shared.
59  */
60
61 #define MAX_LIBS 4
62 static library dlb_libs[MAX_LIBS];
63
64 STATIC_DCL boolean FDECL(readlibdir, (library * lp));
65 STATIC_DCL boolean FDECL(find_file, (const char *name, library **lib,
66                                      long *startp, long *sizep));
67 STATIC_DCL boolean NDECL(lib_dlb_init);
68 STATIC_DCL void NDECL(lib_dlb_cleanup);
69 STATIC_DCL boolean FDECL(lib_dlb_fopen, (dlb *, const char *, const char *));
70 STATIC_DCL int FDECL(lib_dlb_fclose, (dlb *));
71 STATIC_DCL int FDECL(lib_dlb_fread, (char *, int, int, dlb *));
72 STATIC_DCL int FDECL(lib_dlb_fseek, (dlb *, long, int));
73 STATIC_DCL char *FDECL(lib_dlb_fgets, (char *, int, dlb *));
74 STATIC_DCL int FDECL(lib_dlb_fgetc, (dlb *));
75 STATIC_DCL long FDECL(lib_dlb_ftell, (dlb *));
76
77 /* not static because shared with dlb_main.c */
78 boolean FDECL(open_library, (const char *lib_name, library *lp));
79 void FDECL(close_library, (library * lp));
80
81 /* without extern.h via hack.h, these haven't been declared for us */
82 extern char *FDECL(eos, (char *));
83
84 /*
85  * Read the directory out of the library.  Return 1 if successful,
86  * 0 if it failed.
87  *
88  * NOTE: An improvement of the file structure should be the file
89  * size as part of the directory entry or perhaps in place of the
90  * offset -- the offset can be calculated by a running tally of
91  * the sizes.
92  *
93  * Library file structure:
94  *
95  * HEADER:
96  * %3ld library FORMAT revision (currently rev 1)
97  * %1c  space
98  * %8ld # of files in archive (includes 1 for directory)
99  * %1c  space
100  * %8ld size of allocation for string space for directory names
101  * %1c  space
102  * %8ld library offset - sanity check - lseek target for start of first file
103  * %1c  space
104  * %8ld size - sanity check - byte size of complete archive file
105  *
106  * followed by one DIRECTORY entry for each file in the archive, including
107  *  the directory itself:
108  * %1c  handling information (compression, etc.)  Always ' ' in rev 1.
109  * %s   file name
110  * %1c  space
111  * %8ld offset in archive file of start of this file
112  * %c   newline
113  *
114  * followed by the contents of the files
115  */
116 #define DLB_MIN_VERS 1 /* min library version readable by this code */
117 #define DLB_MAX_VERS 1 /* max library version readable by this code */
118
119 /*
120  * Read the directory from the library file.   This will allocate and
121  * fill in our globals.  The file pointer is reset back to position
122  * zero.  If any part fails, leave nothing that needs to be deallocated.
123  *
124  * Return TRUE on success, FALSE on failure.
125  */
126 STATIC_OVL boolean
127 readlibdir(lp)
128 library *lp; /* library pointer to fill in */
129 {
130     int i;
131     char *sp;
132     long liboffset, totalsize;
133
134     if (fscanf(lp->fdata, "%ld %ld %ld %ld %ld\n", &lp->rev, &lp->nentries,
135                &lp->strsize, &liboffset, &totalsize) != 5)
136         return FALSE;
137     if (lp->rev > DLB_MAX_VERS || lp->rev < DLB_MIN_VERS)
138         return FALSE;
139
140     lp->dir = (libdir *) alloc(lp->nentries * sizeof(libdir));
141     lp->sspace = (char *) alloc(lp->strsize);
142
143     /* read in each directory entry */
144     for (i = 0, sp = lp->sspace; i < lp->nentries; i++) {
145         lp->dir[i].fname = sp;
146         if (fscanf(lp->fdata, "%c%s %ld\n", &lp->dir[i].handling, sp,
147                    &lp->dir[i].foffset) != 3) {
148             free((genericptr_t) lp->dir);
149             free((genericptr_t) lp->sspace);
150             lp->dir = (libdir *) 0;
151             lp->sspace = (char *) 0;
152             return FALSE;
153         }
154         sp = eos(sp) + 1;
155     }
156
157     /* calculate file sizes using offset information */
158     for (i = 0; i < lp->nentries; i++) {
159         if (i == lp->nentries - 1)
160             lp->dir[i].fsize = totalsize - lp->dir[i].foffset;
161         else
162             lp->dir[i].fsize = lp->dir[i + 1].foffset - lp->dir[i].foffset;
163     }
164
165     (void) fseek(lp->fdata, 0L, SEEK_SET); /* reset back to zero */
166     lp->fmark = 0;
167
168     return TRUE;
169 }
170
171 /*
172  * Look for the file in our directory structure.  Return 1 if successful,
173  * 0 if not found.  Fill in the size and starting position.
174  */
175 STATIC_OVL boolean
176 find_file(name, lib, startp, sizep)
177 const char *name;
178 library **lib;
179 long *startp, *sizep;
180 {
181     int i, j;
182     library *lp;
183
184     for (i = 0; i < MAX_LIBS && dlb_libs[i].fdata; i++) {
185         lp = &dlb_libs[i];
186         for (j = 0; j < lp->nentries; j++) {
187             if (FILENAME_CMP(name, lp->dir[j].fname) == 0) {
188                 *lib = lp;
189                 *startp = lp->dir[j].foffset;
190                 *sizep = lp->dir[j].fsize;
191                 return TRUE;
192             }
193         }
194     }
195     *lib = (library *) 0;
196     *startp = *sizep = 0;
197     return FALSE;
198 }
199
200 /*
201  * Open the library of the given name and fill in the given library
202  * structure.  Return TRUE if successful, FALSE otherwise.
203  */
204 boolean
205 open_library(lib_name, lp)
206 const char *lib_name;
207 library *lp;
208 {
209     boolean status = FALSE;
210
211     lp->fdata = fopen_datafile(lib_name, RDBMODE, DATAPREFIX);
212     if (lp->fdata) {
213         if (readlibdir(lp)) {
214             status = TRUE;
215         } else {
216             (void) fclose(lp->fdata);
217             lp->fdata = (FILE *) 0;
218         }
219     }
220     return status;
221 }
222
223 void
224 close_library(lp)
225 library *lp;
226 {
227     (void) fclose(lp->fdata);
228     free((genericptr_t) lp->dir);
229     free((genericptr_t) lp->sspace);
230
231     (void) memset((char *) lp, 0, sizeof(library));
232 }
233
234 /*
235  * Open the library file once using stdio.  Keep it open, but
236  * keep track of the file position.
237  */
238 STATIC_OVL boolean
239 lib_dlb_init(VOID_ARGS)
240 {
241     /* zero out array */
242     (void) memset((char *) &dlb_libs[0], 0, sizeof(dlb_libs));
243
244     /* To open more than one library, add open library calls here. */
245     if (!open_library(DLBFILE, &dlb_libs[0]))
246         return FALSE;
247 #ifdef DLBFILE2
248     if (!open_library(DLBFILE2, &dlb_libs[1])) {
249         close_library(&dlb_libs[0]);
250         return FALSE;
251     }
252 #endif
253     return TRUE;
254 }
255
256 STATIC_OVL void
257 lib_dlb_cleanup(VOID_ARGS)
258 {
259     int i;
260
261     /* close the data file(s) */
262     for (i = 0; i < MAX_LIBS && dlb_libs[i].fdata; i++)
263         close_library(&dlb_libs[i]);
264 }
265
266 /*ARGSUSED*/
267 STATIC_OVL boolean
268 lib_dlb_fopen(dp, name, mode)
269 dlb *dp;
270 const char *name;
271 const char *mode UNUSED;
272 {
273     long start, size;
274     library *lp;
275
276     /* look up file in directory */
277     if (find_file(name, &lp, &start, &size)) {
278         dp->lib = lp;
279         dp->start = start;
280         dp->size = size;
281         dp->mark = 0;
282         return TRUE;
283     }
284
285     return FALSE; /* failed */
286 }
287
288 /*ARGUSED*/
289 STATIC_OVL int
290 lib_dlb_fclose(dp)
291 dlb *dp UNUSED;
292 {
293     /* nothing needs to be done */
294     return 0;
295 }
296
297 STATIC_OVL int
298 lib_dlb_fread(buf, size, quan, dp)
299 char *buf;
300 int size, quan;
301 dlb *dp;
302 {
303     long pos, nread, nbytes;
304
305     /* make sure we don't read into the next file */
306     if ((dp->size - dp->mark) < (size * quan))
307         quan = (dp->size - dp->mark) / size;
308     if (quan == 0)
309         return 0;
310
311     pos = dp->start + dp->mark;
312     if (dp->lib->fmark != pos) {
313         fseek(dp->lib->fdata, pos, SEEK_SET); /* check for error??? */
314         dp->lib->fmark = pos;
315     }
316
317     nread = fread(buf, size, quan, dp->lib->fdata);
318     nbytes = nread * size;
319     dp->mark += nbytes;
320     dp->lib->fmark += nbytes;
321
322     return nread;
323 }
324
325 STATIC_OVL int
326 lib_dlb_fseek(dp, pos, whence)
327 dlb *dp;
328 long pos;
329 int whence;
330 {
331     long curpos;
332
333     switch (whence) {
334     case SEEK_CUR:
335         curpos = dp->mark + pos;
336         break;
337     case SEEK_END:
338         curpos = dp->size - pos;
339         break;
340     default: /* set */
341         curpos = pos;
342         break;
343     }
344     if (curpos < 0)
345         curpos = 0;
346     if (curpos > dp->size)
347         curpos = dp->size;
348
349     dp->mark = curpos;
350     return 0;
351 }
352
353 STATIC_OVL char *
354 lib_dlb_fgets(buf, len, dp)
355 char *buf;
356 int len;
357 dlb *dp;
358 {
359     int i;
360     char *bp, c = 0;
361
362     if (len <= 0)
363         return buf; /* sanity check */
364
365     /* return NULL on EOF */
366     if (dp->mark >= dp->size)
367         return (char *) 0;
368
369     len--; /* save room for null */
370     for (i = 0, bp = buf; i < len && dp->mark < dp->size && c != '\n';
371          i++, bp++) {
372         if (dlb_fread(bp, 1, 1, dp) <= 0)
373             break; /* EOF or error */
374         c = *bp;
375     }
376     *bp = '\0';
377
378 #if defined(MSDOS) || defined(WIN32)
379     if ((bp = index(buf, '\r')) != 0) {
380         *bp++ = '\n';
381         *bp = '\0';
382     }
383 #endif
384
385     return buf;
386 }
387
388 STATIC_OVL int
389 lib_dlb_fgetc(dp)
390 dlb *dp;
391 {
392     char c;
393
394     if (lib_dlb_fread(&c, 1, 1, dp) != 1)
395         return EOF;
396     return (int) c;
397 }
398
399 STATIC_OVL long
400 lib_dlb_ftell(dp)
401 dlb *dp;
402 {
403     return dp->mark;
404 }
405
406 const dlb_procs_t lib_dlb_procs = { lib_dlb_init,  lib_dlb_cleanup,
407                                     lib_dlb_fopen, lib_dlb_fclose,
408                                     lib_dlb_fread, lib_dlb_fseek,
409                                     lib_dlb_fgets, lib_dlb_fgetc,
410                                     lib_dlb_ftell };
411
412 #endif /* DLBLIB */
413
414 #ifdef DLBRSRC
415 const dlb_procs_t rsrc_dlb_procs = { rsrc_dlb_init,  rsrc_dlb_cleanup,
416                                      rsrc_dlb_fopen, rsrc_dlb_fclose,
417                                      rsrc_dlb_fread, rsrc_dlb_fseek,
418                                      rsrc_dlb_fgets, rsrc_dlb_fgetc,
419                                      rsrc_dlb_ftell };
420 #endif
421
422 /* Global wrapper functions ------------------------------------------------
423  */
424
425 #define do_dlb_init (*dlb_procs->dlb_init_proc)
426 #define do_dlb_cleanup (*dlb_procs->dlb_cleanup_proc)
427 #define do_dlb_fopen (*dlb_procs->dlb_fopen_proc)
428 #define do_dlb_fclose (*dlb_procs->dlb_fclose_proc)
429 #define do_dlb_fread (*dlb_procs->dlb_fread_proc)
430 #define do_dlb_fseek (*dlb_procs->dlb_fseek_proc)
431 #define do_dlb_fgets (*dlb_procs->dlb_fgets_proc)
432 #define do_dlb_fgetc (*dlb_procs->dlb_fgetc_proc)
433 #define do_dlb_ftell (*dlb_procs->dlb_ftell_proc)
434
435 static const dlb_procs_t *dlb_procs;
436 static boolean dlb_initialized = FALSE;
437
438 boolean
439 dlb_init()
440 {
441     if (!dlb_initialized) {
442 #ifdef DLBLIB
443         dlb_procs = &lib_dlb_procs;
444 #endif
445 #ifdef DLBRSRC
446         dlb_procs = &rsrc_dlb_procs;
447 #endif
448
449         if (dlb_procs)
450             dlb_initialized = do_dlb_init();
451     }
452
453     return dlb_initialized;
454 }
455
456 void
457 dlb_cleanup()
458 {
459     if (dlb_initialized) {
460         do_dlb_cleanup();
461         dlb_initialized = FALSE;
462     }
463 }
464
465 dlb *
466 dlb_fopen(name, mode)
467 const char *name, *mode;
468 {
469     FILE *fp;
470     dlb *dp;
471
472     if (!dlb_initialized)
473         return (dlb *) 0;
474
475     /* only support reading; ignore possible binary flag */
476     if (!mode || mode[0] != 'r')
477         return (dlb *) 0;
478
479     dp = (dlb *) alloc(sizeof(dlb));
480     if (do_dlb_fopen(dp, name, mode))
481         dp->fp = (FILE *) 0;
482     else if ((fp = fopen_datafile(name, mode, DATAPREFIX)) != 0)
483         dp->fp = fp;
484     else {
485         /* can't find anything */
486         free((genericptr_t) dp);
487         dp = (dlb *) 0;
488     }
489
490     return dp;
491 }
492
493 int
494 dlb_fclose(dp)
495 dlb *dp;
496 {
497     int ret = 0;
498
499     if (dlb_initialized) {
500         if (dp->fp)
501             ret = fclose(dp->fp);
502         else
503             ret = do_dlb_fclose(dp);
504
505         free((genericptr_t) dp);
506     }
507     return ret;
508 }
509
510 int
511 dlb_fread(buf, size, quan, dp)
512 char *buf;
513 int size, quan;
514 dlb *dp;
515 {
516     if (!dlb_initialized || size <= 0 || quan <= 0)
517         return 0;
518     if (dp->fp)
519         return (int) fread(buf, size, quan, dp->fp);
520     return do_dlb_fread(buf, size, quan, dp);
521 }
522
523 int
524 dlb_fseek(dp, pos, whence)
525 dlb *dp;
526 long pos;
527 int whence;
528 {
529     if (!dlb_initialized)
530         return EOF;
531     if (dp->fp)
532         return fseek(dp->fp, pos, whence);
533     return do_dlb_fseek(dp, pos, whence);
534 }
535
536 char *
537 dlb_fgets(buf, len, dp)
538 char *buf;
539 int len;
540 dlb *dp;
541 {
542     if (!dlb_initialized)
543         return (char *) 0;
544     if (dp->fp)
545         return fgets(buf, len, dp->fp);
546     return do_dlb_fgets(buf, len, dp);
547 }
548
549 int
550 dlb_fgetc(dp)
551 dlb *dp;
552 {
553     if (!dlb_initialized)
554         return EOF;
555     if (dp->fp)
556         return fgetc(dp->fp);
557     return do_dlb_fgetc(dp);
558 }
559
560 long
561 dlb_ftell(dp)
562 dlb *dp;
563 {
564     if (!dlb_initialized)
565         return 0;
566     if (dp->fp)
567         return ftell(dp->fp);
568     return do_dlb_ftell(dp);
569 }
570
571 #endif /* DLB */
572
573 /*dlb.c*/