prefer files from d2 hog over d1 hog
[btb/d2x.git] / cfile / cfile.c
1 /* $Id: cfile.c,v 1.11 2003-04-14 18:34:40 btb Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14
15 /*
16  *
17  * Functions for accessing compressed files.
18  *
19  * Old Log:
20  * Revision 1.7  1995/10/27  15:18:20  allender
21  * get back to descent directory before trying to read a hog file
22  *
23  * Revision 1.6  1995/10/21  23:48:24  allender
24  * hogfile(s) are now in :Data: folder
25  *
26  * Revision 1.5  1995/08/14  09:27:31  allender
27  * added byteswap header
28  *
29  * Revision 1.4  1995/05/12  11:54:33  allender
30  * changed memory stuff again
31  *
32  * Revision 1.3  1995/05/04  20:03:38  allender
33  * added code that was missing...use NewPtr instead of malloc
34  *
35  * Revision 1.2  1995/04/03  09:59:49  allender
36  * *** empty log message ***
37  *
38  * Revision 1.1  1995/03/30  10:25:02  allender
39  * Initial revision
40  *
41  *
42  * --- PC RCS Information ---
43  * Revision 1.24  1995/03/15  14:20:27  john
44  * Added critical error checker.
45  *
46  * Revision 1.23  1995/03/13  15:16:53  john
47  * Added alternate directory stuff.
48  *
49  * Revision 1.22  1995/02/09  23:08:47  matt
50  * Increased the max number of files in hogfile to 250
51  *
52  * Revision 1.21  1995/02/01  20:56:47  john
53  * Added cfexist function
54  *
55  * Revision 1.20  1995/01/21  17:53:48  john
56  * Added alternate pig file thing.
57  *
58  * Revision 1.19  1994/12/29  15:10:02  john
59  * Increased hogfile max files to 200.
60  *
61  * Revision 1.18  1994/12/12  13:20:57  john
62  * Made cfile work with fiellentth.
63  *
64  * Revision 1.17  1994/12/12  13:14:25  john
65  * Made cfiles prefer non-hog files.
66  *
67  * Revision 1.16  1994/12/09  18:53:26  john
68  * *** empty log message ***
69  *
70  * Revision 1.15  1994/12/09  18:52:56  john
71  * Took out mem, error checking.
72  *
73  * Revision 1.14  1994/12/09  18:10:31  john
74  * Speed up cfgets, which was slowing down the reading of
75  * bitmaps.tbl, which was making POF loading look slow.
76  *
77  * Revision 1.13  1994/12/09  17:53:51  john
78  * Added error checking to number of hogfiles..
79  *
80  * Revision 1.12  1994/12/08  19:02:55  john
81  * Added cfgets.
82  *
83  * Revision 1.11  1994/12/07  21:57:48  john
84  * Took out data dir.
85  *
86  * Revision 1.10  1994/12/07  21:38:02  john
87  * Made cfile not return error..
88  *
89  * Revision 1.9  1994/12/07  21:35:34  john
90  * Made it read from data directory.
91  *
92  * Revision 1.8  1994/12/07  21:33:55  john
93  * Stripped out compression stuff...
94  *
95  * Revision 1.7  1994/04/13  23:44:59  matt
96  * When file cannot be opened, free up the buffer for that file.
97  *
98  * Revision 1.6  1994/02/18  12:38:20  john
99  * Optimized a bit
100  *
101  * Revision 1.5  1994/02/15  18:13:20  john
102  * Fixed more bugs.
103  *
104  * Revision 1.4  1994/02/15  13:27:58  john
105  * Works ok...
106  *
107  * Revision 1.3  1994/02/15  12:51:57  john
108  * Crappy inbetween version
109  *
110  * Revision 1.2  1994/02/14  20:12:29  john
111  * First version working with new cfile stuff.
112  *
113  * Revision 1.1  1994/02/14  15:51:33  john
114  * Initial revision
115  *
116  * Revision 1.1  1994/02/10  15:45:12  john
117  * Initial revision
118  *
119  *
120  */
121
122 #ifdef HAVE_CONFIG_H
123 #include <conf.h>
124 #endif
125
126 #include <stdio.h>
127 #include <string.h>
128 #include <sys/stat.h>
129
130 #include "pstypes.h"
131 #include "u_mem.h"
132 #include "strutil.h"
133 #include "d_io.h"
134 #include "error.h"
135 #include "cfile.h"
136 #include "byteswap.h"
137
138 typedef struct hogfile {
139         char    name[13];
140         int     offset;
141         int     length;
142 } hogfile;
143
144 #define MAX_HOGFILES 300
145
146 hogfile HogFiles[MAX_HOGFILES];
147 char Hogfile_initialized = 0;
148 int Num_hogfiles = 0;
149 char HogFilename[64];
150
151 hogfile D1HogFiles[MAX_HOGFILES];
152 char D1Hogfile_initialized = 0;
153 int D1Num_hogfiles = 0;
154 char D1HogFilename[64];
155
156 hogfile AltHogFiles[MAX_HOGFILES];
157 char AltHogfile_initialized = 0;
158 int AltNum_hogfiles = 0;
159 char AltHogFilename[64];
160
161 char AltHogDir[64];
162 char AltHogdir_initialized = 0;
163
164 // routine to take a DOS path and turn it into a macintosh
165 // pathname.  This routine is based on the fact that we should
166 // see a \ character in the dos path.  The sequence .\ a tthe
167 // beginning of a path is turned into a :
168
169 #ifdef MACINTOSH
170 void macify_dospath(char *dos_path, char *mac_path)
171 {
172         char *p;
173
174         if (!strncmp(dos_path, ".\\", 2)) {
175                 strcpy(mac_path, ":");
176                 strcat(mac_path, &(dos_path[2]) );
177         } else
178                 strcpy(mac_path, dos_path);
179
180         while ( (p = strchr(mac_path, '\\')) != NULL)
181                 *p = ':';
182
183 }
184 #endif
185
186 void cfile_use_alternate_hogdir( char * path )
187 {
188         if ( path )     {
189                 strcpy( AltHogDir, path );
190                 AltHogdir_initialized = 1;
191         } else {
192                 AltHogdir_initialized = 0;
193         }
194 }
195
196 //in case no one installs one
197 int default_error_counter=0;
198
199 //ptr to counter of how many critical errors
200 int *critical_error_counter_ptr=&default_error_counter;
201
202 //tell cfile about your critical error counter
203 void cfile_set_critical_error_counter_ptr(int *ptr)
204 {
205         critical_error_counter_ptr = ptr;
206
207 }
208
209
210 FILE * cfile_get_filehandle( char * filename, char * mode )
211 {
212         FILE * fp;
213         char temp[128];
214
215         *critical_error_counter_ptr = 0;
216         fp = fopen( filename, mode );
217         if ( fp && *critical_error_counter_ptr )        {
218                 fclose(fp);
219                 fp = NULL;
220         }
221         if ( (fp==NULL) && (AltHogdir_initialized) )    {
222                 strcpy( temp, AltHogDir );
223                 strcat( temp, "/");
224                 strcat( temp, filename );
225                 *critical_error_counter_ptr = 0;
226                 fp = fopen( temp, mode );
227                 if ( fp && *critical_error_counter_ptr )        {
228                         fclose(fp);
229                         fp = NULL;
230                 }
231         }
232         return fp;
233 }
234
235 //returns 1 if file loaded with no errors
236 int cfile_init_hogfile(char *fname, hogfile * hog_files, int * nfiles )
237 {
238         char id[4];
239         FILE * fp;
240         int i, len;
241
242         *nfiles = 0;
243
244         fp = cfile_get_filehandle( fname, "rb" );
245         if ( fp == NULL ) return 0;
246
247         fread( id, 3, 1, fp );
248         if ( strncmp( id, "DHF", 3 ) )  {
249                 fclose(fp);
250                 return 0;
251         }
252
253         while( 1 )
254         {
255                 if ( *nfiles >= MAX_HOGFILES ) {
256                         fclose(fp);
257                         Error( "HOGFILE is limited to %d files.\n",  MAX_HOGFILES );
258                 }
259                 i = fread( hog_files[*nfiles].name, 13, 1, fp );
260                 if ( i != 1 )   {               //eof here is ok
261                         fclose(fp);
262                         return 1;
263                 }
264                 i = fread( &len, 4, 1, fp );
265                 if ( i != 1 )   {
266                         fclose(fp);
267                         return 0;
268                 }
269                 hog_files[*nfiles].length = INTEL_INT(len);
270                 hog_files[*nfiles].offset = ftell( fp );
271                 *nfiles = (*nfiles) + 1;
272                 // Skip over
273                 i = fseek( fp, INTEL_INT(len), SEEK_CUR );
274         }
275 }
276
277 //Specify the name of the hogfile.  Returns 1 if hogfile found & had files
278 int cfile_init(char *hogname)
279 {
280         #ifdef MACINTOSH
281         char mac_path[255];
282
283         macify_dospath(hogname, mac_path);
284         #endif
285
286         Assert(Hogfile_initialized == 0);
287
288         #ifndef MACINTOSH
289         if (cfile_init_hogfile(hogname, HogFiles, &Num_hogfiles )) {
290                 strcpy( HogFilename, hogname );
291         #else
292         if (cfile_init_hogfile(mac_path, HogFiles, &Num_hogfiles )) {
293                 strcpy( HogFilename, mac_path );
294         #endif
295                 Hogfile_initialized = 1;
296                 return 1;
297         }
298         else
299                 return 0;       //not loaded!
300 }
301
302
303 int cfile_size(char *hogname)
304 {
305         CFILE *fp;
306         struct stat statbuf;
307
308         fp = cfopen(hogname, "rb");
309         if (fp == NULL)
310                 return -1;
311         fstat(fileno(fp->file), &statbuf);
312         cfclose(fp);
313         return statbuf.st_size;
314 }
315
316 /*
317  * return handle for file called "name", embedded in one of the hogfiles
318  */
319 FILE * cfile_find_libfile(char * name, int * length)
320 {
321         FILE * fp;
322         int i;
323
324         if ( AltHogfile_initialized )   {
325                 for (i=0; i<AltNum_hogfiles; i++ )      {
326                         if ( !stricmp( AltHogFiles[i].name, name ))     {
327                                 fp = cfile_get_filehandle( AltHogFilename, "rb" );
328                                 if ( fp == NULL ) return NULL;
329                                 fseek( fp,  AltHogFiles[i].offset, SEEK_SET );
330                                 *length = AltHogFiles[i].length;
331                                 return fp;
332                         }
333                 }
334         }
335
336         if ( !Hogfile_initialized )     {
337                 //@@cfile_init_hogfile( "DESCENT2.HOG", HogFiles, &Num_hogfiles );
338                 //@@Hogfile_initialized = 1;
339
340                 //Int3();       //hogfile ought to be initialized
341         }
342
343         for (i=0; i<Num_hogfiles; i++ ) {
344                 if ( !stricmp( HogFiles[i].name, name ))        {
345                         fp = cfile_get_filehandle( HogFilename, "rb" );
346                         if ( fp == NULL ) return NULL;
347                         fseek( fp,  HogFiles[i].offset, SEEK_SET );
348                         *length = HogFiles[i].length;
349                         return fp;
350                 }
351         }
352
353         if (D1Hogfile_initialized)      {
354                 for (i = 0; i < D1Num_hogfiles; i++) {
355                         if (!stricmp(D1HogFiles[i].name, name)) {
356                                 fp = cfile_get_filehandle(D1HogFilename, "rb");
357                                 if (fp == NULL) return NULL;
358                                 fseek(fp,  D1HogFiles[i].offset, SEEK_SET);
359                                 *length = D1HogFiles[i].length;
360                                 return fp;
361                         }
362                 }
363         }
364
365         return NULL;
366 }
367
368 int cfile_use_alternate_hogfile( char * name )
369 {
370         if ( name )     {
371                 #ifdef MACINTOSH
372                 char mac_path[255];
373
374                 macify_dospath(name, mac_path);
375                 strcpy( AltHogFilename, mac_path);
376                 #else
377                 strcpy( AltHogFilename, name );
378                 #endif
379                 cfile_init_hogfile( AltHogFilename, AltHogFiles, &AltNum_hogfiles );
380                 AltHogfile_initialized = 1;
381                 return (AltNum_hogfiles > 0);
382         } else {
383                 AltHogfile_initialized = 0;
384                 return 1;
385         }
386 }
387
388 int cfile_use_descent1_hogfile( char * name )
389 {
390         if (name)       {
391 #ifdef MACINTOSH
392                 char mac_path[255];
393
394                 macify_dospath(name, mac_path);
395                 strcpy(D1HogFilename, mac_path);
396 #else
397                 strcpy(D1HogFilename, name);
398 #endif
399                 cfile_init_hogfile(D1HogFilename, D1HogFiles, &D1Num_hogfiles);
400                 D1Hogfile_initialized = 1;
401                 return (D1Num_hogfiles > 0);
402         } else {
403                 D1Hogfile_initialized = 0;
404                 return 1;
405         }
406 }
407
408 int cfexist( char * filename )
409 {
410         int length;
411         FILE *fp;
412
413
414         if (filename[0] != '\x01')
415                 fp = cfile_get_filehandle( filename, "rb" );            // Check for non-hog file first...
416         else {
417                 fp = NULL;              //don't look in dir, only in hogfile
418                 filename++;
419         }
420
421         if ( fp )       {
422                 fclose(fp);
423                 return 1;
424         }
425
426         fp = cfile_find_libfile(filename, &length );
427         if ( fp )       {
428                 fclose(fp);
429                 return 2;               // file found in hog
430         }
431
432         return 0;               // Couldn't find it.
433 }
434
435
436 CFILE * cfopen(char * filename, char * mode )
437 {
438         int length;
439         FILE * fp;
440         CFILE *cfile;
441
442         if (stricmp( mode, "rb"))       {
443                 Error( "cfiles can only be opened with mode==rb\n" );
444         }
445
446         if (filename[0] != '\x01') {
447                 #ifdef MACINTOSH
448                 char mac_path[255];
449
450                 macify_dospath(filename, mac_path);
451                 fp = cfile_get_filehandle( mac_path, mode);
452                 #else
453                 fp = cfile_get_filehandle( filename, mode );            // Check for non-hog file first...
454                 #endif
455         } else {
456                 fp = NULL;              //don't look in dir, only in hogfile
457                 filename++;
458         }
459
460         if ( !fp ) {
461                 fp = cfile_find_libfile(filename, &length );
462                 if ( !fp )
463                         return NULL;            // No file found
464                 cfile = d_malloc ( sizeof(CFILE) );
465                 if ( cfile == NULL ) {
466                         fclose(fp);
467                         return NULL;
468                 }
469                 cfile->file = fp;
470                 cfile->size = length;
471                 cfile->lib_offset = ftell( fp );
472                 cfile->raw_position = 0;
473                 return cfile;
474         } else {
475                 cfile = d_malloc ( sizeof(CFILE) );
476                 if ( cfile == NULL ) {
477                         fclose(fp);
478                         return NULL;
479                 }
480                 cfile->file = fp;
481                 cfile->size = filelength( fileno(fp) );
482                 cfile->lib_offset = 0;
483                 cfile->raw_position = 0;
484                 return cfile;
485         }
486 }
487
488 int cfilelength( CFILE *fp )
489 {
490         return fp->size;
491 }
492
493 int cfgetc( CFILE * fp )
494 {
495         int c;
496
497         if (fp->raw_position >= fp->size ) return EOF;
498
499         c = getc( fp->file );
500         if (c!=EOF)
501                 fp->raw_position++;
502
503 //      Assert( fp->raw_position==(ftell(fp->file)-fp->lib_offset) );
504
505         return c;
506 }
507
508 char * cfgets( char * buf, size_t n, CFILE * fp )
509 {
510         char * t = buf;
511         int i;
512         int c;
513
514         for (i=0; i<n-1; i++ ) {
515                 do {
516                         if (fp->raw_position >= fp->size ) {
517                                 *buf = 0;
518                                 return NULL;
519                         }
520                         c = fgetc( fp->file );
521                         fp->raw_position++;
522                         if (c == 0 || c == 10)        // Unix line ending
523                                 break;
524                         if (c == 13) {      // Mac or DOS line ending
525                                 int c1;
526
527                                 c1 = fgetc( fp->file );
528                                 fseek( fp->file, -1, SEEK_CUR);
529                                 if ( c1 == 10 ) // DOS line ending
530                                         continue;
531                                 else            // Mac line ending
532                                         break;
533                         }
534                 } while ( c == 13 );
535                 if ( c == 13 )  // because cr-lf is a bad thing on the mac
536                         c = '\n';   // and anyway -- 0xod is CR on mac, not 0x0a
537                 *buf++ = c;
538                 if ( c=='\n' ) break;
539         }
540         *buf++ = 0;
541         return  t;
542 }
543
544 size_t cfread( void * buf, size_t elsize, size_t nelem, CFILE * fp ) 
545 {
546         unsigned int i, size;
547
548         size = elsize * nelem;
549         if ( size < 1 ) return 0;
550
551         i = fread ( buf, 1, size, fp->file );
552         fp->raw_position += i;
553         return i/elsize;
554 }
555
556
557 int cftell( CFILE *fp ) 
558 {
559         return fp->raw_position;
560 }
561
562 int cfseek( CFILE *fp, long int offset, int where )
563 {
564         int c, goal_position;
565
566         switch( where ) {
567         case SEEK_SET:
568                 goal_position = offset;
569                 break;
570         case SEEK_CUR:
571                 goal_position = fp->raw_position+offset;
572                 break;
573         case SEEK_END:
574                 goal_position = fp->size+offset;
575                 break;
576         default:
577                 return 1;
578         }       
579         c = fseek( fp->file, fp->lib_offset + goal_position, SEEK_SET );
580         fp->raw_position = ftell(fp->file)-fp->lib_offset;
581         return c;
582 }
583
584 void cfclose( CFILE * fp ) 
585 {       
586         fclose(fp->file);
587         d_free(fp);
588         return;
589 }
590
591 // routines to read basic data types from CFILE's.  Put here to
592 // simplify mac/pc reading from cfiles.
593
594 int cfile_read_int(CFILE *file)
595 {
596         int32_t i;
597
598         if (cfread( &i, sizeof(i), 1, file) != 1)
599                 Error( "Error reading int in cfile_read_int()" );
600
601         i = INTEL_INT(i);
602         return i;
603 }
604
605 short cfile_read_short(CFILE *file)
606 {
607         int16_t s;
608
609         if (cfread( &s, sizeof(s), 1, file) != 1)
610                 Error( "Error reading short in cfile_read_short()" );
611
612         s = INTEL_SHORT(s);
613         return s;
614 }
615
616 byte cfile_read_byte(CFILE *file)
617 {
618         byte b;
619
620         if (cfread( &b, sizeof(b), 1, file) != 1)
621                 Error( "Error reading byte in cfile_read_byte()" );
622
623         return b;
624 }
625
626 fix cfile_read_fix(CFILE *file)
627 {
628         fix f;
629
630         if (cfread( &f, sizeof(f), 1, file) != 1)
631                 Error( "Error reading fix in cfile_read_fix()" );
632
633         f = (fix)INTEL_INT((int)f);
634         return f;
635 }
636
637 fixang cfile_read_fixang(CFILE *file)
638 {
639         fixang f;
640
641         if (cfread(&f, 2, 1, file) != 1)
642                 Error("Error reading fixang in cfile_read_fixang()");
643
644         f = (fixang) INTEL_SHORT((int) f);
645         return f;
646 }
647
648 void cfile_read_vector(vms_vector *v, CFILE *file)
649 {
650         v->x = cfile_read_fix(file);
651         v->y = cfile_read_fix(file);
652         v->z = cfile_read_fix(file);
653 }
654
655 void cfile_read_angvec(vms_angvec *v, CFILE *file)
656 {
657         v->p = cfile_read_fixang(file);
658         v->b = cfile_read_fixang(file);
659         v->h = cfile_read_fixang(file);
660 }
661
662 void cfile_read_matrix(vms_matrix *m,CFILE *file)
663 {
664         cfile_read_vector(&m->rvec,file);
665         cfile_read_vector(&m->uvec,file);
666         cfile_read_vector(&m->fvec,file);
667 }
668