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. */
15 #define STATIC_DCL extern
18 #define STATIC_DCL static
19 #define STATIC_OVL static
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.
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));
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));
46 * Library Implementation:
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
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.
62 static library dlb_libs[MAX_LIBS];
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 *));
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));
81 /* without extern.h via hack.h, these haven't been declared for us */
82 extern char *FDECL(eos, (char *));
85 * Read the directory out of the library. Return 1 if successful,
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
93 * Library file structure:
96 * %3ld library FORMAT revision (currently rev 1)
98 * %8ld # of files in archive (includes 1 for directory)
100 * %8ld size of allocation for string space for directory names
102 * %8ld library offset - sanity check - lseek target for start of first file
104 * %8ld size - sanity check - byte size of complete archive file
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.
111 * %8ld offset in archive file of start of this file
114 * followed by the contents of the files
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 */
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.
124 * Return TRUE on success, FALSE on failure.
128 library *lp; /* library pointer to fill in */
132 long liboffset, totalsize;
134 if (fscanf(lp->fdata, "%ld %ld %ld %ld %ld\n", &lp->rev, &lp->nentries,
135 &lp->strsize, &liboffset, &totalsize) != 5)
137 if (lp->rev > DLB_MAX_VERS || lp->rev < DLB_MIN_VERS)
140 lp->dir = (libdir *) alloc(lp->nentries * sizeof(libdir));
141 lp->sspace = (char *) alloc(lp->strsize);
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;
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;
162 lp->dir[i].fsize = lp->dir[i + 1].foffset - lp->dir[i].foffset;
165 (void) fseek(lp->fdata, 0L, SEEK_SET); /* reset back to zero */
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.
176 find_file(name, lib, startp, sizep)
179 long *startp, *sizep;
184 for (i = 0; i < MAX_LIBS && dlb_libs[i].fdata; i++) {
186 for (j = 0; j < lp->nentries; j++) {
187 if (FILENAME_CMP(name, lp->dir[j].fname) == 0) {
189 *startp = lp->dir[j].foffset;
190 *sizep = lp->dir[j].fsize;
195 *lib = (library *) 0;
196 *startp = *sizep = 0;
201 * Open the library of the given name and fill in the given library
202 * structure. Return TRUE if successful, FALSE otherwise.
205 open_library(lib_name, lp)
206 const char *lib_name;
209 boolean status = FALSE;
211 lp->fdata = fopen_datafile(lib_name, RDBMODE, DATAPREFIX);
213 if (readlibdir(lp)) {
216 (void) fclose(lp->fdata);
217 lp->fdata = (FILE *) 0;
227 (void) fclose(lp->fdata);
228 free((genericptr_t) lp->dir);
229 free((genericptr_t) lp->sspace);
231 (void) memset((char *) lp, 0, sizeof(library));
235 * Open the library file once using stdio. Keep it open, but
236 * keep track of the file position.
239 lib_dlb_init(VOID_ARGS)
242 (void) memset((char *) &dlb_libs[0], 0, sizeof(dlb_libs));
244 /* To open more than one library, add open library calls here. */
245 if (!open_library(DLBFILE, &dlb_libs[0]))
248 if (!open_library(DLBFILE2, &dlb_libs[1])) {
249 close_library(&dlb_libs[0]);
257 lib_dlb_cleanup(VOID_ARGS)
261 /* close the data file(s) */
262 for (i = 0; i < MAX_LIBS && dlb_libs[i].fdata; i++)
263 close_library(&dlb_libs[i]);
268 lib_dlb_fopen(dp, name, mode)
271 const char *mode UNUSED;
276 /* look up file in directory */
277 if (find_file(name, &lp, &start, &size)) {
285 return FALSE; /* failed */
293 /* nothing needs to be done */
298 lib_dlb_fread(buf, size, quan, dp)
303 long pos, nread, nbytes;
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;
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;
317 nread = fread(buf, size, quan, dp->lib->fdata);
318 nbytes = nread * size;
320 dp->lib->fmark += nbytes;
326 lib_dlb_fseek(dp, pos, whence)
335 curpos = dp->mark + pos;
338 curpos = dp->size - pos;
346 if (curpos > dp->size)
354 lib_dlb_fgets(buf, len, dp)
363 return buf; /* sanity check */
365 /* return NULL on EOF */
366 if (dp->mark >= dp->size)
369 len--; /* save room for null */
370 for (i = 0, bp = buf; i < len && dp->mark < dp->size && c != '\n';
372 if (dlb_fread(bp, 1, 1, dp) <= 0)
373 break; /* EOF or error */
378 #if defined(MSDOS) || defined(WIN32)
379 if ((bp = index(buf, '\r')) != 0) {
394 if (lib_dlb_fread(&c, 1, 1, dp) != 1)
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,
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,
422 /* Global wrapper functions ------------------------------------------------
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)
435 static const dlb_procs_t *dlb_procs;
436 static boolean dlb_initialized = FALSE;
441 if (!dlb_initialized) {
443 dlb_procs = &lib_dlb_procs;
446 dlb_procs = &rsrc_dlb_procs;
450 dlb_initialized = do_dlb_init();
453 return dlb_initialized;
459 if (dlb_initialized) {
461 dlb_initialized = FALSE;
466 dlb_fopen(name, mode)
467 const char *name, *mode;
472 if (!dlb_initialized)
475 /* only support reading; ignore possible binary flag */
476 if (!mode || mode[0] != 'r')
479 dp = (dlb *) alloc(sizeof(dlb));
480 if (do_dlb_fopen(dp, name, mode))
482 else if ((fp = fopen_datafile(name, mode, DATAPREFIX)) != 0)
485 /* can't find anything */
486 free((genericptr_t) dp);
499 if (dlb_initialized) {
501 ret = fclose(dp->fp);
503 ret = do_dlb_fclose(dp);
505 free((genericptr_t) dp);
511 dlb_fread(buf, size, quan, dp)
516 if (!dlb_initialized || size <= 0 || quan <= 0)
519 return (int) fread(buf, size, quan, dp->fp);
520 return do_dlb_fread(buf, size, quan, dp);
524 dlb_fseek(dp, pos, whence)
529 if (!dlb_initialized)
532 return fseek(dp->fp, pos, whence);
533 return do_dlb_fseek(dp, pos, whence);
537 dlb_fgets(buf, len, dp)
542 if (!dlb_initialized)
545 return fgets(buf, len, dp->fp);
546 return do_dlb_fgets(buf, len, dp);
553 if (!dlb_initialized)
556 return fgetc(dp->fp);
557 return do_dlb_fgetc(dp);
564 if (!dlb_initialized)
567 return ftell(dp->fp);
568 return do_dlb_ftell(dp);