OSDN Git Service

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