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*/