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 (*dlb_init_proc
)(void);
23 void (*dlb_cleanup_proc
)(void);
24 boolean (*dlb_fopen_proc
)(DLB_P
,const char *,const char *);
25 int (*dlb_fclose_proc
)(DLB_P
);
26 int (*dlb_fread_proc
)(char *,int,int,DLB_P
);
27 int (*dlb_fseek_proc
)(DLB_P
,long,int);
28 char *(*dlb_fgets_proc
)(char *,int,DLB_P
);
29 int (*dlb_fgetc_proc
)(DLB_P
);
30 long (*dlb_ftell_proc
)(DLB_P
);
33 /* without extern.h via hack.h, these haven't been declared for us */
35 extern FILE *fopen_datafile_area(const char *,const char *,
39 * If FILE_AREAS is not defined, then fopen_datafile_area
40 * is a macro defined in terms of fopen_datafile.
42 extern FILE *fopen_datafile(const char *,const char *,int);
47 * Library Implementation:
49 * When initialized, we open all library files and read in their tables
50 * of contents. The library files stay open all the time. When
51 * a open is requested, the libraries' directories are searched. If
52 * successful, we return a descriptor that contains the library, file
53 * size, and current file mark. This descriptor is used for all
56 * The ability to open more than one library is supported but used
57 * only in the Amiga port (the second library holds the sound files).
58 * For Unix, the idea would be to split the NetHack library
59 * into text and binary parts, where the text version could be shared.
63 static library dlb_libs
[MAX_LIBS
];
65 static boolean
readlibdir(library
*lp
);
66 static boolean
find_file(const char *name
, library
**lib
, long *startp
,
68 static boolean
lib_dlb_init(void);
69 static void lib_dlb_cleanup(void);
70 static boolean
lib_dlb_fopen(dlb
*, const char *, const char *);
71 static int lib_dlb_fclose(dlb
*);
72 static int lib_dlb_fread(char *, int, int, dlb
*);
73 static int lib_dlb_fseek(dlb
*, long, int);
74 static char *lib_dlb_fgets(char *, int, dlb
*);
75 static int lib_dlb_fgetc(dlb
*);
76 static long lib_dlb_ftell(dlb
*);
78 /* not static because shared with dlb_main.c */
79 boolean
open_library(const char *lib_area
, const char *lib_name
,
81 void close_library(library
*lp
);
83 /* without extern.h via hack.h, these haven't been declared for us */
84 extern char *eos(char *);
89 * Read the directory out of the library. Return 1 if successful,
92 * NOTE: An improvement of the file structure should be the file
93 * size as part of the directory entry or perhaps in place of the
94 * offset -- the offset can be calculated by a running tally of
97 * Library file structure:
100 * %3ld library FORMAT revision (currently rev 1)
102 * %8ld # of files in archive (includes 1 for directory)
104 * %8ld size of allocation for string space for directory names
106 * %8ld library offset - sanity check - lseek target for start of first file
108 * %8ld size - sanity check - byte size of complete archive file
110 * followed by one DIRECTORY entry for each file in the archive, including
111 * the directory itself:
112 * %1c handling information (compression, etc.) Always ' ' in rev 1.
115 * %8ld offset in archive file of start of this file
118 * followed by the contents of the files
120 #define DLB_MIN_VERS 1 /* min library version readable by this code */
121 #define DLB_MAX_VERS 1 /* max library version readable by this code */
124 * Read the directory from the library file. This will allocate and
125 * fill in our globals. The file pointer is reset back to position
126 * zero. If any part fails, leave nothing that needs to be deallocated.
128 * Return TRUE on success, FALSE on failure.
132 library
*lp
; /* library pointer to fill in */
136 long liboffset
, totalsize
;
138 if (fscanf(lp
->fdata
, "%ld %ld %ld %ld %ld\n",
139 &lp
->rev
,&lp
->nentries
,&lp
->strsize
,&liboffset
,&totalsize
) != 5)
141 if (lp
->rev
> DLB_MAX_VERS
|| lp
->rev
< DLB_MIN_VERS
) return FALSE
;
143 lp
->dir
= (libdir
*) alloc(lp
->nentries
* sizeof(libdir
));
144 lp
->sspace
= (char *) alloc(lp
->strsize
);
146 /* read in each directory entry */
147 for (i
= 0, sp
= lp
->sspace
; i
< lp
->nentries
; i
++) {
148 lp
->dir
[i
].fname
= sp
;
149 if (fscanf(lp
->fdata
, "%c%s %ld\n",
150 &lp
->dir
[i
].handling
, sp
, &lp
->dir
[i
].foffset
) != 3) {
151 free((void *) lp
->dir
);
152 free((void *) lp
->sspace
);
153 lp
->dir
= (libdir
*) 0;
154 lp
->sspace
= (char *) 0;
160 /* calculate file sizes using offset information */
161 for (i
= 0; i
< lp
->nentries
; i
++) {
162 if (i
== lp
->nentries
- 1)
163 lp
->dir
[i
].fsize
= totalsize
- lp
->dir
[i
].foffset
;
165 lp
->dir
[i
].fsize
= lp
->dir
[i
+1].foffset
- lp
->dir
[i
].foffset
;
168 (void) fseek(lp
->fdata
, 0L, SEEK_SET
); /* reset back to zero */
175 * Look for the file in our directory structure. Return 1 if successful,
176 * 0 if not found. Fill in the size and starting position.
179 find_file(name
, lib
, startp
, sizep
)
182 long *startp
, *sizep
;
187 for (i
= 0; i
< MAX_LIBS
&& dlb_libs
[i
].fdata
; i
++) {
189 for (j
= 0; j
< lp
->nentries
; j
++) {
190 if (FILENAME_CMP(name
, lp
->dir
[j
].fname
) == 0) {
192 *startp
= lp
->dir
[j
].foffset
;
193 *sizep
= lp
->dir
[j
].fsize
;
198 *lib
= (library
*) 0;
199 *startp
= *sizep
= 0;
204 * Open the library of the given name and fill in the given library
205 * structure. Return TRUE if successful, FALSE otherwise.
208 open_library(lib_area
, lib_name
, lp
)
209 const char *lib_area
, *lib_name
;
212 boolean status
= FALSE
;
214 lp
->fdata
= fopen_datafile_area(lib_area
, lib_name
, RDBMODE
, DATAPREFIX
);
216 if (readlibdir(lp
)) {
219 (void) fclose(lp
->fdata
);
220 lp
->fdata
= (FILE *) 0;
230 (void) fclose(lp
->fdata
);
231 free((void *) lp
->dir
);
232 free((void *) lp
->sspace
);
234 (void) memset((char *)lp
, 0, sizeof(library
));
238 * Open the library file once using stdio. Keep it open, but
239 * keep track of the file position.
245 (void) memset((char *)&dlb_libs
[0], 0, sizeof(dlb_libs
));
247 /* To open more than one library, add open library calls here. */
248 if (!open_library(DLBAREA
, DLBFILE
, &dlb_libs
[0])) return FALSE
;
250 if (!open_library(DLBAREA2
, DLBFILE2
, &dlb_libs
[1])) {
251 close_library(&dlb_libs
[0]);
263 /* close the data file(s) */
264 for (i
= 0; i
< MAX_LIBS
&& dlb_libs
[i
].fdata
; i
++)
265 close_library(&dlb_libs
[i
]);
269 lib_dlb_fopen(dp
, name
, mode
)
271 const char *name
, *mode
;
276 /* look up file in directory */
277 if (find_file(name
, &lp
, &start
, &size
)) {
285 return FALSE
; /* failed */
292 /* nothing needs to be done */
297 lib_dlb_fread(buf
, size
, quan
, dp
)
302 long pos
, nread
, nbytes
;
304 /* make sure we don't read into the next file */
305 if ((dp
->size
- dp
->mark
) < (size
* quan
))
306 quan
= (dp
->size
- dp
->mark
) / size
;
307 if (quan
== 0) return 0;
309 pos
= dp
->start
+ dp
->mark
;
310 if (dp
->lib
->fmark
!= pos
) {
311 fseek(dp
->lib
->fdata
, pos
, SEEK_SET
); /* check for error??? */
312 dp
->lib
->fmark
= pos
;
315 nread
= fread(buf
, size
, quan
, dp
->lib
->fdata
);
316 nbytes
= nread
* size
;
318 dp
->lib
->fmark
+= nbytes
;
324 lib_dlb_fseek(dp
, pos
, whence
)
332 case SEEK_CUR
: curpos
= dp
->mark
+ pos
; break;
333 case SEEK_END
: curpos
= dp
->size
- pos
; break;
334 default: /* set */ curpos
= pos
; break;
336 if (curpos
< 0) curpos
= 0;
337 if (curpos
> dp
->size
) curpos
= dp
->size
;
344 lib_dlb_fgets(buf
, len
, dp
)
352 if (len
<= 0) return buf
; /* sanity check */
354 /* return NULL on EOF */
355 if (dp
->mark
>= dp
->size
) return (char *) 0;
357 len
--; /* save room for null */
358 for (i
= 0, bp
= buf
;
359 i
< len
&& dp
->mark
< dp
->size
&& c
!= '\n'; i
++, bp
++) {
360 if (dlb_fread(bp
, 1, 1, dp
) <= 0) break; /* EOF or error */
365 #if defined(MSDOS) || defined(WIN32)
366 if ((bp
= index(buf
, '\r')) != 0) {
381 if (lib_dlb_fread(&c
, 1, 1, dp
) != 1) return EOF
;
393 const dlb_procs_t lib_dlb_procs
= {
408 const dlb_procs_t rsrc_dlb_procs
= {
421 /* Global wrapper functions ------------------------------------------------ */
423 #define do_dlb_init (*dlb_procs->dlb_init_proc)
424 #define do_dlb_cleanup (*dlb_procs->dlb_cleanup_proc)
425 #define do_dlb_fopen (*dlb_procs->dlb_fopen_proc)
426 #define do_dlb_fclose (*dlb_procs->dlb_fclose_proc)
427 #define do_dlb_fread (*dlb_procs->dlb_fread_proc)
428 #define do_dlb_fseek (*dlb_procs->dlb_fseek_proc)
429 #define do_dlb_fgets (*dlb_procs->dlb_fgets_proc)
430 #define do_dlb_fgetc (*dlb_procs->dlb_fgetc_proc)
431 #define do_dlb_ftell (*dlb_procs->dlb_ftell_proc)
433 static const dlb_procs_t
*dlb_procs
;
434 static boolean dlb_initialized
= FALSE
;
439 if (!dlb_initialized
) {
441 dlb_procs
= &lib_dlb_procs
;
444 dlb_procs
= &rsrc_dlb_procs
;
448 dlb_initialized
= do_dlb_init();
451 return dlb_initialized
;
457 if (dlb_initialized
) {
459 dlb_initialized
= FALSE
;
466 dlb_fopen(name
, mode
)
467 const char *name
, *mode
;
469 dlb_fopen_area(area
, name
, mode
)
470 const char *area
, *name
, *mode
;
476 if (!dlb_initialized
) return (dlb
*) 0;
478 dp
= (dlb
*) alloc(sizeof(dlb
));
479 if (do_dlb_fopen(dp
, name
, mode
))
482 else if ((fp
= fopen_datafile(name
, mode
, DATAPREFIX
)) != 0)
484 else if ((fp
= fopen_datafile_area(area
, name
, mode
, DATAPREFIX
)) != 0)
488 /* can't find anything */
502 if (dlb_initialized
) {
503 if (dp
->fp
) ret
= fclose(dp
->fp
);
504 else ret
= do_dlb_fclose(dp
);
512 dlb_fread(buf
, size
, quan
, dp
)
517 if (!dlb_initialized
|| size
<= 0 || quan
<= 0) return 0;
518 if (dp
->fp
) return (int) fread(buf
, size
, quan
, dp
->fp
);
519 return do_dlb_fread(buf
, size
, quan
, dp
);
523 dlb_fseek(dp
, pos
, whence
)
528 if (!dlb_initialized
) return EOF
;
529 if (dp
->fp
) return fseek(dp
->fp
, pos
, whence
);
530 return do_dlb_fseek(dp
, pos
, whence
);
534 dlb_fgets(buf
, len
, dp
)
539 if (!dlb_initialized
) return (char *) 0;
540 if (dp
->fp
) return fgets(buf
, len
, dp
->fp
);
541 return do_dlb_fgets(buf
, len
, dp
);
548 if (!dlb_initialized
) return EOF
;
549 if (dp
->fp
) return fgetc(dp
->fp
);
550 return do_dlb_fgetc(dp
);
557 if (!dlb_initialized
) return 0;
558 if (dp
->fp
) return ftell(dp
->fp
);
559 return do_dlb_ftell(dp
);