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 21typedef 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 */ 34extern 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 54static library dlb_libs[MAX_LIBS]; 55 56static boolean FDECL(readlibdir,(library *lp)); 57static boolean FDECL(find_file,(const char *name, library **lib, long *startp, 58 long *sizep)); 59static boolean NDECL(lib_dlb_init); 60static void NDECL(lib_dlb_cleanup); 61static boolean FDECL(lib_dlb_fopen,(dlb *, const char *, const char *)); 62static int FDECL(lib_dlb_fclose,(dlb *)); 63static int FDECL(lib_dlb_fread,(char *, int, int, dlb *)); 64static int FDECL(lib_dlb_fseek,(dlb *, long, int)); 65static char *FDECL(lib_dlb_fgets,(char *, int, dlb *)); 66static int FDECL(lib_dlb_fgetc,(dlb *)); 67static long FDECL(lib_dlb_ftell,(dlb *)); 68 69/* not static because shared with dlb_main.c */ 70boolean FDECL(open_library,(const char *lib_name, library *lp)); 71void FDECL(close_library,(library *lp)); 72 73/* without extern.h via hack.h, these haven't been declared for us */ 74extern 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 */ 120static boolean 121readlibdir(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 */ 168static boolean 169find_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 */ 197boolean 198open_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 216void 217close_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 */ 231static boolean 232lib_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 248static void 249lib_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 258static boolean 259lib_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 278static int 279lib_dlb_fclose(dp) 280 dlb *dp; 281{ 282 /* nothing needs to be done */ 283 return 0; 284} 285 286static int 287lib_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 313static int 314lib_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 333static char * 334lib_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 365static int 366lib_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 376static long 377lib_dlb_ftell(dp) 378 dlb *dp; 379{ 380 return dp->mark; 381} 382 383const 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 398const 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 423static const dlb_procs_t *dlb_procs; 424static boolean dlb_initialized = FALSE; 425 426boolean 427dlb_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 444void 445dlb_cleanup() 446{ 447 if (dlb_initialized) { 448 do_dlb_cleanup(); 449 dlb_initialized = FALSE; 450 } 451} 452 453dlb * 454dlb_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 476int 477dlb_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 491int 492dlb_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 502int 503dlb_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 513char * 514dlb_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 524int 525dlb_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 533long 534dlb_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*/ 545