OSDN Git Service

add gitignore
[nethackexpress/trunk.git] / src / dlb.c
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. */
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 #ifdef DLB
15 /*
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.
19  */
20
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));
31 } dlb_procs_t;
32
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));
35
36 #ifdef DLBLIB
37 /*
38  * Library Implementation:
39  *
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
45  * successive calls.
46  *
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.
51  */
52
53 #define MAX_LIBS 4
54 static library dlb_libs[MAX_LIBS];
55
56 static boolean FDECL(readlibdir,(library *lp));
57 static boolean FDECL(find_file,(const char *name, library **lib, long *startp,
58                                                                 long *sizep));
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 *));
68
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));
72
73 /* without extern.h via hack.h, these haven't been declared for us */
74 extern char *FDECL(eos, (char *));
75
76
77
78 /*
79  * Read the directory out of the library.  Return 1 if successful,
80  * 0 if it failed.
81  *
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
85  * the sizes.
86  *
87  * Library file structure:
88  *
89  * HEADER:
90  * %3ld library FORMAT revision (currently rev 1)
91  * %1c  space
92  * %8ld # of files in archive (includes 1 for directory)
93  * %1c  space
94  * %8ld size of allocation for string space for directory names
95  * %1c  space
96  * %8ld library offset - sanity check - lseek target for start of first file
97  * %1c  space
98  * %8ld size - sanity check - byte size of complete archive file
99  *
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.
103  * %s   file name
104  * %1c  space
105  * %8ld offset in archive file of start of this file
106  * %c   newline
107  *
108  * followed by the contents of the files
109  */
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 */
112
113 /*
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.
117  *
118  * Return TRUE on success, FALSE on failure.
119  */
120 static boolean
121 readlibdir(lp)
122     library *lp;        /* library pointer to fill in */
123 {
124     int i;
125     char *sp;
126     long liboffset, totalsize;
127
128     if (fscanf(lp->fdata, "%ld %ld %ld %ld %ld\n",
129             &lp->rev,&lp->nentries,&lp->strsize,&liboffset,&totalsize) != 5)
130         return FALSE;
131     if (lp->rev > DLB_MAX_VERS || lp->rev < DLB_MIN_VERS) return FALSE;
132
133     lp->dir = (libdir *) alloc(lp->nentries * sizeof(libdir));
134     lp->sspace = (char *) alloc(lp->strsize);
135
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;
145             return FALSE;
146         }
147         sp = eos(sp) + 1;
148     }
149
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;
154         else
155             lp->dir[i].fsize = lp->dir[i+1].foffset - lp->dir[i].foffset;
156     }
157
158     (void) fseek(lp->fdata, 0L, SEEK_SET);      /* reset back to zero */
159     lp->fmark = 0;
160
161     return TRUE;
162 }
163
164 /*
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.
167  */
168 static boolean
169 find_file(name, lib, startp, sizep)
170     const char *name;
171     library **lib;
172     long *startp, *sizep;
173 {
174     int i, j;
175     library *lp;
176
177     for (i = 0; i < MAX_LIBS && dlb_libs[i].fdata; i++) {
178         lp = &dlb_libs[i];
179         for (j = 0; j < lp->nentries; j++) {
180             if (FILENAME_CMP(name, lp->dir[j].fname) == 0) {
181                 *lib = lp;
182                 *startp = lp->dir[j].foffset;
183                 *sizep = lp->dir[j].fsize;
184                 return TRUE;
185             }
186         }
187     }
188     *lib = (library *) 0;
189     *startp = *sizep = 0;
190     return FALSE;
191 }
192
193 /*
194  * Open the library of the given name and fill in the given library
195  * structure.  Return TRUE if successful, FALSE otherwise.
196  */
197 boolean
198 open_library(lib_name, lp)
199     const char *lib_name;
200     library *lp;
201 {
202     boolean status = FALSE;
203
204     lp->fdata = fopen_datafile(lib_name, RDBMODE, DATAPREFIX);
205     if (lp->fdata) {
206         if (readlibdir(lp)) {
207             status = TRUE;
208         } else {
209             (void) fclose(lp->fdata);
210             lp->fdata = (FILE *) 0;
211         }
212     }
213     return status;
214 }
215
216 void
217 close_library(lp)
218     library *lp;
219 {
220     (void) fclose(lp->fdata);
221     free((genericptr_t) lp->dir);
222     free((genericptr_t) lp->sspace);
223
224     (void) memset((char *)lp, 0, sizeof(library));
225 }
226
227 /*
228  * Open the library file once using stdio.  Keep it open, but
229  * keep track of the file position.
230  */
231 static boolean
232 lib_dlb_init()
233 {
234     /* zero out array */
235     (void) memset((char *)&dlb_libs[0], 0, sizeof(dlb_libs));
236
237     /* To open more than one library, add open library calls here. */
238     if (!open_library(DLBFILE, &dlb_libs[0])) return FALSE;
239 #ifdef DLBFILE2
240     if (!open_library(DLBFILE2, &dlb_libs[1]))  {
241         close_library(&dlb_libs[0]);
242         return FALSE;
243     }
244 #endif
245     return TRUE;
246 }
247
248 static void
249 lib_dlb_cleanup()
250 {
251     int i;
252
253     /* close the data file(s) */
254     for (i = 0; i < MAX_LIBS && dlb_libs[i].fdata; i++)
255         close_library(&dlb_libs[i]);
256 }
257
258 static boolean
259 lib_dlb_fopen(dp, name, mode)
260     dlb *dp;
261     const char *name, *mode;
262 {
263     long start, size;
264     library *lp;
265
266     /* look up file in directory */
267     if (find_file(name, &lp, &start, &size)) {
268         dp->lib = lp;
269         dp->start = start;
270         dp->size = size;
271         dp->mark = 0;
272         return TRUE;
273         }
274
275     return FALSE;       /* failed */
276 }
277
278 static int
279 lib_dlb_fclose(dp)
280     dlb *dp;
281 {
282     /* nothing needs to be done */
283     return 0;
284 }
285
286 static int
287 lib_dlb_fread(buf, size, quan, dp)
288     char *buf;
289     int size, quan;
290     dlb *dp;
291 {
292     long pos, nread, nbytes;
293
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;
298
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;
303     }
304
305     nread = fread(buf, size, quan, dp->lib->fdata);
306     nbytes = nread * size;
307     dp->mark += nbytes;
308     dp->lib->fmark += nbytes;
309
310     return nread;
311 }
312
313 static int
314 lib_dlb_fseek(dp, pos, whence)
315     dlb *dp;
316     long pos;
317     int whence;
318 {
319     long curpos;
320
321     switch (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;
325     }
326     if (curpos < 0) curpos = 0;
327     if (curpos > dp->size) curpos = dp->size;
328
329     dp->mark = curpos;
330     return 0;
331 }
332
333 static char *
334 lib_dlb_fgets(buf, len, dp)
335     char *buf;
336     int len;
337     dlb *dp;
338 {
339     int i;
340     char *bp, c = 0;
341
342     if (len <= 0) return buf;   /* sanity check */
343
344     /* return NULL on EOF */
345     if (dp->mark >= dp->size) return (char *) 0;
346
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 */
351         c = *bp;
352     }
353     *bp = '\0';
354
355 #if defined(MSDOS) || defined(WIN32)
356     if ((bp = index(buf, '\r')) != 0) {
357         *bp++ = '\n';
358         *bp = '\0';
359     }
360 #endif
361
362     return buf;
363 }
364
365 static int
366 lib_dlb_fgetc(dp)
367     dlb *dp;
368 {
369     char c;
370
371     if (lib_dlb_fread(&c, 1, 1, dp) != 1) return EOF;
372     return (int) c;
373 }
374
375
376 static long
377 lib_dlb_ftell(dp)
378     dlb *dp;
379 {
380     return dp->mark;
381 }
382
383 const dlb_procs_t lib_dlb_procs = {
384     lib_dlb_init,
385     lib_dlb_cleanup,
386     lib_dlb_fopen,
387     lib_dlb_fclose,
388     lib_dlb_fread,
389     lib_dlb_fseek,
390     lib_dlb_fgets,
391     lib_dlb_fgetc,
392     lib_dlb_ftell
393 };
394
395 #endif /* DLBLIB */
396
397 #ifdef DLBRSRC
398 const dlb_procs_t rsrc_dlb_procs = {
399     rsrc_dlb_init,
400     rsrc_dlb_cleanup,
401     rsrc_dlb_fopen,
402     rsrc_dlb_fclose,
403     rsrc_dlb_fread,
404     rsrc_dlb_fseek,
405     rsrc_dlb_fgets,
406     rsrc_dlb_fgetc,
407     rsrc_dlb_ftell
408 };
409 #endif
410
411 /* Global wrapper functions ------------------------------------------------ */
412
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)
422
423 static const dlb_procs_t *dlb_procs;
424 static boolean dlb_initialized = FALSE;
425
426 boolean
427 dlb_init()
428 {
429     if (!dlb_initialized) {
430 #ifdef DLBLIB
431         dlb_procs = &lib_dlb_procs;
432 #endif
433 #ifdef DLBRSRC
434         dlb_procs = &rsrc_dlb_procs;
435 #endif
436
437         if (dlb_procs) 
438             dlb_initialized = do_dlb_init();
439     }
440
441     return dlb_initialized;
442 }
443
444 void
445 dlb_cleanup()
446 {
447     if (dlb_initialized) {
448         do_dlb_cleanup();
449         dlb_initialized = FALSE;
450     }
451 }
452
453 dlb *
454 dlb_fopen(name, mode)
455     const char *name, *mode;
456 {
457     FILE *fp;
458     dlb *dp;
459
460     if (!dlb_initialized) return (dlb *) 0;
461
462     dp = (dlb *) alloc(sizeof(dlb));
463     if (do_dlb_fopen(dp, name, mode))
464         dp->fp = (FILE *) 0;
465     else if ((fp = fopen_datafile(name, mode, DATAPREFIX)) != 0)
466         dp->fp = fp;
467     else {
468         /* can't find anything */
469         free((genericptr_t) dp);
470         dp = (dlb *) 0;
471         }
472
473     return dp;
474 }
475
476 int
477 dlb_fclose(dp)
478     dlb *dp;
479 {
480         int ret = 0;
481
482     if (dlb_initialized) {
483         if (dp->fp) ret = fclose(dp->fp);
484         else ret = do_dlb_fclose(dp);
485
486         free((genericptr_t) dp);
487     }
488     return ret;
489 }
490
491 int
492 dlb_fread(buf, size, quan, dp)
493     char *buf;
494     int size, quan;
495     dlb *dp;
496 {
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);
500 }
501
502 int
503 dlb_fseek(dp, pos, whence)
504     dlb *dp;
505     long pos;
506     int whence;
507 {
508     if (!dlb_initialized) return EOF;
509     if (dp->fp) return fseek(dp->fp, pos, whence);
510     return do_dlb_fseek(dp, pos, whence);
511 }
512
513 char *
514 dlb_fgets(buf, len, dp)
515     char *buf;
516     int len;
517     dlb *dp;
518 {
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);
522 }
523
524 int
525 dlb_fgetc(dp)
526     dlb *dp;
527 {
528     if (!dlb_initialized) return EOF;
529     if (dp->fp) return fgetc(dp->fp);
530     return do_dlb_fgetc(dp);
531 }
532
533 long
534 dlb_ftell(dp)
535     dlb *dp;
536 {
537     if (!dlb_initialized) return 0;
538     if (dp->fp) return ftell(dp->fp);
539     return do_dlb_ftell(dp);
540 }
541
542 #endif /* DLB */
543
544 /*dlb.c*/