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