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