1 /* SCCS Id: @(#)dlb.c 3.4 1997/07/29 */
2 /* Copyright (c) Kenneth Lorber, Bethesda, Maryland, 1993. */
3 /* NetHack may be freely redistributed. See license for details. */
16 * Data librarian. Present a STDIO-like interface to NetHack while
17 * multiplexing on one or more "data libraries". If a file is not found
18 * in a given library, look for it outside the libraries.
21 typedef struct dlb_procs {
22 boolean NDECL((*dlb_init_proc));
23 void NDECL((*dlb_cleanup_proc));
24 boolean FDECL((*dlb_fopen_proc), (DLB_P,const char *,const char *));
25 int FDECL((*dlb_fclose_proc), (DLB_P));
26 int FDECL((*dlb_fread_proc), (char *,int,int,DLB_P));
27 int FDECL((*dlb_fseek_proc), (DLB_P,long,int));
28 char *FDECL((*dlb_fgets_proc), (char *,int,DLB_P));
29 int FDECL((*dlb_fgetc_proc), (DLB_P));
30 long FDECL((*dlb_ftell_proc), (DLB_P));
33 /* without extern.h via hack.h, these haven't been declared for us */
34 extern FILE *FDECL(fopen_datafile, (const char *,const char *,int));
38 * Library Implementation:
40 * When initialized, we open all library files and read in their tables
41 * of contents. The library files stay open all the time. When
42 * a open is requested, the libraries' directories are searched. If
43 * successful, we return a descriptor that contains the library, file
44 * size, and current file mark. This descriptor is used for all
47 * The ability to open more than one library is supported but used
48 * only in the Amiga port (the second library holds the sound files).
49 * For Unix, the idea would be to split the NetHack library
50 * into text and binary parts, where the text version could be shared.
54 static library dlb_libs[MAX_LIBS];
56 static boolean FDECL(readlibdir,(library *lp));
57 static boolean FDECL(find_file,(const char *name, library **lib, long *startp,
59 static boolean NDECL(lib_dlb_init);
60 static void NDECL(lib_dlb_cleanup);
61 static boolean FDECL(lib_dlb_fopen,(dlb *, const char *, const char *));
62 static int FDECL(lib_dlb_fclose,(dlb *));
63 static int FDECL(lib_dlb_fread,(char *, int, int, dlb *));
64 static int FDECL(lib_dlb_fseek,(dlb *, long, int));
65 static char *FDECL(lib_dlb_fgets,(char *, int, dlb *));
66 static int FDECL(lib_dlb_fgetc,(dlb *));
67 static long FDECL(lib_dlb_ftell,(dlb *));
69 /* not static because shared with dlb_main.c */
70 boolean FDECL(open_library,(const char *lib_name, library *lp));
71 void FDECL(close_library,(library *lp));
73 /* without extern.h via hack.h, these haven't been declared for us */
74 extern char *FDECL(eos, (char *));
79 * Read the directory out of the library. Return 1 if successful,
82 * NOTE: An improvement of the file structure should be the file
83 * size as part of the directory entry or perhaps in place of the
84 * offset -- the offset can be calculated by a running tally of
87 * Library file structure:
90 * %3ld library FORMAT revision (currently rev 1)
92 * %8ld # of files in archive (includes 1 for directory)
94 * %8ld size of allocation for string space for directory names
96 * %8ld library offset - sanity check - lseek target for start of first file
98 * %8ld size - sanity check - byte size of complete archive file
100 * followed by one DIRECTORY entry for each file in the archive, including
101 * the directory itself:
102 * %1c handling information (compression, etc.) Always ' ' in rev 1.
105 * %8ld offset in archive file of start of this file
108 * followed by the contents of the files
110 #define DLB_MIN_VERS 1 /* min library version readable by this code */
111 #define DLB_MAX_VERS 1 /* max library version readable by this code */
114 * Read the directory from the library file. This will allocate and
115 * fill in our globals. The file pointer is reset back to position
116 * zero. If any part fails, leave nothing that needs to be deallocated.
118 * Return TRUE on success, FALSE on failure.
122 library *lp; /* library pointer to fill in */
126 long liboffset, totalsize;
128 if (fscanf(lp->fdata, "%ld %ld %ld %ld %ld\n",
129 &lp->rev,&lp->nentries,&lp->strsize,&liboffset,&totalsize) != 5)
131 if (lp->rev > DLB_MAX_VERS || lp->rev < DLB_MIN_VERS) return FALSE;
133 lp->dir = (libdir *) alloc(lp->nentries * sizeof(libdir));
134 lp->sspace = (char *) alloc(lp->strsize);
136 /* read in each directory entry */
137 for (i = 0, sp = lp->sspace; i < lp->nentries; i++) {
138 lp->dir[i].fname = sp;
139 if (fscanf(lp->fdata, "%c%s %ld\n",
140 &lp->dir[i].handling, sp, &lp->dir[i].foffset) != 3) {
141 free((genericptr_t) lp->dir);
142 free((genericptr_t) lp->sspace);
143 lp->dir = (libdir *) 0;
144 lp->sspace = (char *) 0;
150 /* calculate file sizes using offset information */
151 for (i = 0; i < lp->nentries; i++) {
152 if (i == lp->nentries - 1)
153 lp->dir[i].fsize = totalsize - lp->dir[i].foffset;
155 lp->dir[i].fsize = lp->dir[i+1].foffset - lp->dir[i].foffset;
158 (void) fseek(lp->fdata, 0L, SEEK_SET); /* reset back to zero */
165 * Look for the file in our directory structure. Return 1 if successful,
166 * 0 if not found. Fill in the size and starting position.
169 find_file(name, lib, startp, sizep)
172 long *startp, *sizep;
177 for (i = 0; i < MAX_LIBS && dlb_libs[i].fdata; i++) {
179 for (j = 0; j < lp->nentries; j++) {
180 if (FILENAME_CMP(name, lp->dir[j].fname) == 0) {
182 *startp = lp->dir[j].foffset;
183 *sizep = lp->dir[j].fsize;
188 *lib = (library *) 0;
189 *startp = *sizep = 0;
194 * Open the library of the given name and fill in the given library
195 * structure. Return TRUE if successful, FALSE otherwise.
198 open_library(lib_name, lp)
199 const char *lib_name;
202 boolean status = FALSE;
204 lp->fdata = fopen_datafile(lib_name, RDBMODE, DATAPREFIX);
206 if (readlibdir(lp)) {
209 (void) fclose(lp->fdata);
210 lp->fdata = (FILE *) 0;
220 (void) fclose(lp->fdata);
221 free((genericptr_t) lp->dir);
222 free((genericptr_t) lp->sspace);
224 (void) memset((char *)lp, 0, sizeof(library));
228 * Open the library file once using stdio. Keep it open, but
229 * keep track of the file position.
235 (void) memset((char *)&dlb_libs[0], 0, sizeof(dlb_libs));
237 /* To open more than one library, add open library calls here. */
238 if (!open_library(DLBFILE, &dlb_libs[0])) return FALSE;
240 if (!open_library(DLBFILE2, &dlb_libs[1])) {
241 close_library(&dlb_libs[0]);
253 /* close the data file(s) */
254 for (i = 0; i < MAX_LIBS && dlb_libs[i].fdata; i++)
255 close_library(&dlb_libs[i]);
259 lib_dlb_fopen(dp, name, mode)
261 const char *name, *mode;
266 /* look up file in directory */
267 if (find_file(name, &lp, &start, &size)) {
275 return FALSE; /* failed */
282 /* nothing needs to be done */
287 lib_dlb_fread(buf, size, quan, dp)
292 long pos, nread, nbytes;
294 /* make sure we don't read into the next file */
295 if ((dp->size - dp->mark) < (size * quan))
296 quan = (dp->size - dp->mark) / size;
297 if (quan == 0) return 0;
299 pos = dp->start + dp->mark;
300 if (dp->lib->fmark != pos) {
301 fseek(dp->lib->fdata, pos, SEEK_SET); /* check for error??? */
302 dp->lib->fmark = pos;
305 nread = fread(buf, size, quan, dp->lib->fdata);
306 nbytes = nread * size;
308 dp->lib->fmark += nbytes;
314 lib_dlb_fseek(dp, pos, whence)
322 case SEEK_CUR: curpos = dp->mark + pos; break;
323 case SEEK_END: curpos = dp->size - pos; break;
324 default: /* set */ curpos = pos; break;
326 if (curpos < 0) curpos = 0;
327 if (curpos > dp->size) curpos = dp->size;
334 lib_dlb_fgets(buf, len, dp)
342 if (len <= 0) return buf; /* sanity check */
344 /* return NULL on EOF */
345 if (dp->mark >= dp->size) return (char *) 0;
347 len--; /* save room for null */
348 for (i = 0, bp = buf;
349 i < len && dp->mark < dp->size && c != '\n'; i++, bp++) {
350 if (dlb_fread(bp, 1, 1, dp) <= 0) break; /* EOF or error */
355 #if defined(MSDOS) || defined(WIN32)
356 if ((bp = index(buf, '\r')) != 0) {
371 if (lib_dlb_fread(&c, 1, 1, dp) != 1) return EOF;
383 const dlb_procs_t lib_dlb_procs = {
398 const dlb_procs_t rsrc_dlb_procs = {
411 /* Global wrapper functions ------------------------------------------------ */
413 #define do_dlb_init (*dlb_procs->dlb_init_proc)
414 #define do_dlb_cleanup (*dlb_procs->dlb_cleanup_proc)
415 #define do_dlb_fopen (*dlb_procs->dlb_fopen_proc)
416 #define do_dlb_fclose (*dlb_procs->dlb_fclose_proc)
417 #define do_dlb_fread (*dlb_procs->dlb_fread_proc)
418 #define do_dlb_fseek (*dlb_procs->dlb_fseek_proc)
419 #define do_dlb_fgets (*dlb_procs->dlb_fgets_proc)
420 #define do_dlb_fgetc (*dlb_procs->dlb_fgetc_proc)
421 #define do_dlb_ftell (*dlb_procs->dlb_ftell_proc)
423 static const dlb_procs_t *dlb_procs;
424 static boolean dlb_initialized = FALSE;
429 if (!dlb_initialized) {
431 dlb_procs = &lib_dlb_procs;
434 dlb_procs = &rsrc_dlb_procs;
438 dlb_initialized = do_dlb_init();
441 return dlb_initialized;
447 if (dlb_initialized) {
449 dlb_initialized = FALSE;
454 dlb_fopen(name, mode)
455 const char *name, *mode;
460 if (!dlb_initialized) return (dlb *) 0;
462 dp = (dlb *) alloc(sizeof(dlb));
463 if (do_dlb_fopen(dp, name, mode))
465 else if ((fp = fopen_datafile(name, mode, DATAPREFIX)) != 0)
468 /* can't find anything */
469 free((genericptr_t) dp);
482 if (dlb_initialized) {
483 if (dp->fp) ret = fclose(dp->fp);
484 else ret = do_dlb_fclose(dp);
486 free((genericptr_t) dp);
492 dlb_fread(buf, size, quan, dp)
497 if (!dlb_initialized || size <= 0 || quan <= 0) return 0;
498 if (dp->fp) return (int) fread(buf, size, quan, dp->fp);
499 return do_dlb_fread(buf, size, quan, dp);
503 dlb_fseek(dp, pos, whence)
508 if (!dlb_initialized) return EOF;
509 if (dp->fp) return fseek(dp->fp, pos, whence);
510 return do_dlb_fseek(dp, pos, whence);
514 dlb_fgets(buf, len, dp)
519 if (!dlb_initialized) return (char *) 0;
520 if (dp->fp) return fgets(buf, len, dp->fp);
521 return do_dlb_fgets(buf, len, dp);
528 if (!dlb_initialized) return EOF;
529 if (dp->fp) return fgetc(dp->fp);
530 return do_dlb_fgetc(dp);
537 if (!dlb_initialized) return 0;
538 if (dp->fp) return ftell(dp->fp);
539 return do_dlb_ftell(dp);