comments, formatting, etc. courtesy of Martin Schaffner <maschaffner@gmx.ch>
[btb/d2x.git] / cfile / cfile.c
1 /* $Id: cfile.c,v 1.10 2002-10-04 07:19:59 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 #ifdef HAVE_CONFIG_H
16 #include <conf.h>
17 #endif
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <sys/stat.h>
22
23 #include "pstypes.h"
24 #include "u_mem.h"
25 #include "strutil.h"
26 #include "d_io.h"
27 #include "error.h"
28 #include "cfile.h"
29 #include "byteswap.h"
30
31 typedef struct hogfile {
32         char    name[13];
33         int     offset;
34         int     length;
35 } hogfile;
36
37 #define MAX_HOGFILES 300
38
39 hogfile HogFiles[MAX_HOGFILES];
40 char Hogfile_initialized = 0;
41 int Num_hogfiles = 0;
42 char HogFilename[64];
43
44 hogfile D1HogFiles[MAX_HOGFILES];
45 char D1Hogfile_initialized = 0;
46 int D1Num_hogfiles = 0;
47 char D1HogFilename[64];
48
49 hogfile AltHogFiles[MAX_HOGFILES];
50 char AltHogfile_initialized = 0;
51 int AltNum_hogfiles = 0;
52 char AltHogFilename[64];
53
54 char AltHogDir[64];
55 char AltHogdir_initialized = 0;
56
57 // routine to take a DOS path and turn it into a macintosh
58 // pathname.  This routine is based on the fact that we should
59 // see a \ character in the dos path.  The sequence .\ a tthe
60 // beginning of a path is turned into a :
61
62 #ifdef MACINTOSH
63 void macify_dospath(char *dos_path, char *mac_path)
64 {
65         char *p;
66
67         if (!strncmp(dos_path, ".\\", 2)) {
68                 strcpy(mac_path, ":");
69                 strcat(mac_path, &(dos_path[2]) );
70         } else
71                 strcpy(mac_path, dos_path);
72
73         while ( (p = strchr(mac_path, '\\')) != NULL)
74                 *p = ':';
75
76 }
77 #endif
78
79 void cfile_use_alternate_hogdir( char * path )
80 {
81         if ( path )     {
82                 strcpy( AltHogDir, path );
83                 AltHogdir_initialized = 1;
84         } else {
85                 AltHogdir_initialized = 0;
86         }
87 }
88
89 //in case no one installs one
90 int default_error_counter=0;
91
92 //ptr to counter of how many critical errors
93 int *critical_error_counter_ptr=&default_error_counter;
94
95 //tell cfile about your critical error counter
96 void cfile_set_critical_error_counter_ptr(int *ptr)
97 {
98         critical_error_counter_ptr = ptr;
99
100 }
101
102
103 FILE * cfile_get_filehandle( char * filename, char * mode )
104 {
105         FILE * fp;
106         char temp[128];
107
108         *critical_error_counter_ptr = 0;
109         fp = fopen( filename, mode );
110         if ( fp && *critical_error_counter_ptr )        {
111                 fclose(fp);
112                 fp = NULL;
113         }
114         if ( (fp==NULL) && (AltHogdir_initialized) )    {
115                 strcpy( temp, AltHogDir );
116                 strcat( temp, "/");
117                 strcat( temp, filename );
118                 *critical_error_counter_ptr = 0;
119                 fp = fopen( temp, mode );
120                 if ( fp && *critical_error_counter_ptr )        {
121                         fclose(fp);
122                         fp = NULL;
123                 }
124         }
125         return fp;
126 }
127
128 //returns 1 if file loaded with no errors
129 int cfile_init_hogfile(char *fname, hogfile * hog_files, int * nfiles )
130 {
131         char id[4];
132         FILE * fp;
133         int i, len;
134
135         *nfiles = 0;
136
137         fp = cfile_get_filehandle( fname, "rb" );
138         if ( fp == NULL ) return 0;
139
140         fread( id, 3, 1, fp );
141         if ( strncmp( id, "DHF", 3 ) )  {
142                 fclose(fp);
143                 return 0;
144         }
145
146         while( 1 )
147         {
148                 if ( *nfiles >= MAX_HOGFILES ) {
149                         fclose(fp);
150                         Error( "HOGFILE is limited to %d files.\n",  MAX_HOGFILES );
151                 }
152                 i = fread( hog_files[*nfiles].name, 13, 1, fp );
153                 if ( i != 1 )   {               //eof here is ok
154                         fclose(fp);
155                         return 1;
156                 }
157                 i = fread( &len, 4, 1, fp );
158                 if ( i != 1 )   {
159                         fclose(fp);
160                         return 0;
161                 }
162                 hog_files[*nfiles].length = INTEL_INT(len);
163                 hog_files[*nfiles].offset = ftell( fp );
164                 *nfiles = (*nfiles) + 1;
165                 // Skip over
166                 i = fseek( fp, INTEL_INT(len), SEEK_CUR );
167         }
168 }
169
170 //Specify the name of the hogfile.  Returns 1 if hogfile found & had files
171 int cfile_init(char *hogname)
172 {
173         #ifdef MACINTOSH
174         char mac_path[255];
175
176         macify_dospath(hogname, mac_path);
177         #endif
178
179         Assert(Hogfile_initialized == 0);
180
181         #ifndef MACINTOSH
182         if (cfile_init_hogfile(hogname, HogFiles, &Num_hogfiles )) {
183                 strcpy( HogFilename, hogname );
184         #else
185         if (cfile_init_hogfile(mac_path, HogFiles, &Num_hogfiles )) {
186                 strcpy( HogFilename, mac_path );
187         #endif
188                 Hogfile_initialized = 1;
189                 return 1;
190         }
191         else
192                 return 0;       //not loaded!
193 }
194
195
196 int cfile_size(char *hogname)
197 {
198         CFILE *fp;
199         struct stat statbuf;
200
201         fp = cfopen(hogname, "rb");
202         if (fp == NULL)
203                 return -1;
204         fstat(fileno(fp->file), &statbuf);
205         cfclose(fp);
206         return statbuf.st_size;
207 }
208
209 /*
210  * return handle for file called "name", embedded in one of the hogfiles
211  */
212 FILE * cfile_find_libfile(char * name, int * length)
213 {
214         FILE * fp;
215         int i;
216
217         if ( AltHogfile_initialized )   {
218                 for (i=0; i<AltNum_hogfiles; i++ )      {
219                         if ( !stricmp( AltHogFiles[i].name, name ))     {
220                                 fp = cfile_get_filehandle( AltHogFilename, "rb" );
221                                 if ( fp == NULL ) return NULL;
222                                 fseek( fp,  AltHogFiles[i].offset, SEEK_SET );
223                                 *length = AltHogFiles[i].length;
224                                 return fp;
225                         }
226                 }
227         }
228
229         if (D1Hogfile_initialized)      {
230                 for (i = 0; i < D1Num_hogfiles; i++) {
231                         if (!stricmp(D1HogFiles[i].name, name)) {
232                                 fp = cfile_get_filehandle(D1HogFilename, "rb");
233                                 if (fp == NULL) return NULL;
234                                 fseek(fp,  D1HogFiles[i].offset, SEEK_SET);
235                                 *length = D1HogFiles[i].length;
236                                 return fp;
237                         }
238                 }
239         }
240
241         if ( !Hogfile_initialized )     {
242                 //@@cfile_init_hogfile( "DESCENT2.HOG", HogFiles, &Num_hogfiles );
243                 //@@Hogfile_initialized = 1;
244
245                 //Int3();       //hogfile ought to be initialized
246         }
247
248         for (i=0; i<Num_hogfiles; i++ ) {
249                 if ( !stricmp( HogFiles[i].name, name ))        {
250                         fp = cfile_get_filehandle( HogFilename, "rb" );
251                         if ( fp == NULL ) return NULL;
252                         fseek( fp,  HogFiles[i].offset, SEEK_SET );
253                         *length = HogFiles[i].length;
254                         return fp;
255                 }
256         }
257         return NULL;
258 }
259
260 int cfile_use_alternate_hogfile( char * name )
261 {
262         if ( name )     {
263                 #ifdef MACINTOSH
264                 char mac_path[255];
265
266                 macify_dospath(name, mac_path);
267                 strcpy( AltHogFilename, mac_path);
268                 #else
269                 strcpy( AltHogFilename, name );
270                 #endif
271                 cfile_init_hogfile( AltHogFilename, AltHogFiles, &AltNum_hogfiles );
272                 AltHogfile_initialized = 1;
273                 return (AltNum_hogfiles > 0);
274         } else {
275                 AltHogfile_initialized = 0;
276                 return 1;
277         }
278 }
279
280 int cfile_use_descent1_hogfile( char * name )
281 {
282         if (name)       {
283 #ifdef MACINTOSH
284                 char mac_path[255];
285
286                 macify_dospath(name, mac_path);
287                 strcpy(D1HogFilename, mac_path);
288 #else
289                 strcpy(D1HogFilename, name);
290 #endif
291                 cfile_init_hogfile(D1HogFilename, D1HogFiles, &D1Num_hogfiles);
292                 D1Hogfile_initialized = 1;
293                 return (D1Num_hogfiles > 0);
294         } else {
295                 D1Hogfile_initialized = 0;
296                 return 1;
297         }
298 }
299
300 int cfexist( char * filename )
301 {
302         int length;
303         FILE *fp;
304
305
306         if (filename[0] != '\x01')
307                 fp = cfile_get_filehandle( filename, "rb" );            // Check for non-hog file first...
308         else {
309                 fp = NULL;              //don't look in dir, only in hogfile
310                 filename++;
311         }
312
313         if ( fp )       {
314                 fclose(fp);
315                 return 1;
316         }
317
318         fp = cfile_find_libfile(filename, &length );
319         if ( fp )       {
320                 fclose(fp);
321                 return 2;               // file found in hog
322         }
323
324         return 0;               // Couldn't find it.
325 }
326
327
328 CFILE * cfopen(char * filename, char * mode )
329 {
330         int length;
331         FILE * fp;
332         CFILE *cfile;
333
334         if (stricmp( mode, "rb"))       {
335                 Error( "cfiles can only be opened with mode==rb\n" );
336         }
337
338         if (filename[0] != '\x01') {
339                 #ifdef MACINTOSH
340                 char mac_path[255];
341
342                 macify_dospath(filename, mac_path);
343                 fp = cfile_get_filehandle( mac_path, mode);
344                 #else
345                 fp = cfile_get_filehandle( filename, mode );            // Check for non-hog file first...
346                 #endif
347         } else {
348                 fp = NULL;              //don't look in dir, only in hogfile
349                 filename++;
350         }
351
352         if ( !fp ) {
353                 fp = cfile_find_libfile(filename, &length );
354                 if ( !fp )
355                         return NULL;            // No file found
356                 cfile = d_malloc ( sizeof(CFILE) );
357                 if ( cfile == NULL ) {
358                         fclose(fp);
359                         return NULL;
360                 }
361                 cfile->file = fp;
362                 cfile->size = length;
363                 cfile->lib_offset = ftell( fp );
364                 cfile->raw_position = 0;
365                 return cfile;
366         } else {
367                 cfile = d_malloc ( sizeof(CFILE) );
368                 if ( cfile == NULL ) {
369                         fclose(fp);
370                         return NULL;
371                 }
372                 cfile->file = fp;
373                 cfile->size = filelength( fileno(fp) );
374                 cfile->lib_offset = 0;
375                 cfile->raw_position = 0;
376                 return cfile;
377         }
378 }
379
380 int cfilelength( CFILE *fp )
381 {
382         return fp->size;
383 }
384
385 int cfgetc( CFILE * fp )
386 {
387         int c;
388
389         if (fp->raw_position >= fp->size ) return EOF;
390
391         c = getc( fp->file );
392         if (c!=EOF)
393                 fp->raw_position++;
394
395 //      Assert( fp->raw_position==(ftell(fp->file)-fp->lib_offset) );
396
397         return c;
398 }
399
400 char * cfgets( char * buf, size_t n, CFILE * fp )
401 {
402         char * t = buf;
403         int i;
404         int c;
405
406         for (i=0; i<n-1; i++ ) {
407                 do {
408                         if (fp->raw_position >= fp->size ) {
409                                 *buf = 0;
410                                 return NULL;
411                         }
412                         c = fgetc( fp->file );
413                         fp->raw_position++;
414                         if (c == 0 || c == 10)        // Unix line ending
415                                 break;
416                         if (c == 13) {      // Mac or DOS line ending
417                                 int c1;
418
419                                 c1 = fgetc( fp->file );
420                                 fseek( fp->file, -1, SEEK_CUR);
421                                 if ( c1 == 10 ) // DOS line ending
422                                         continue;
423                                 else            // Mac line ending
424                                         break;
425                         }
426                 } while ( c == 13 );
427                 if ( c == 13 )  // because cr-lf is a bad thing on the mac
428                         c = '\n';   // and anyway -- 0xod is CR on mac, not 0x0a
429                 *buf++ = c;
430                 if ( c=='\n' ) break;
431         }
432         *buf++ = 0;
433         return  t;
434 }
435
436 size_t cfread( void * buf, size_t elsize, size_t nelem, CFILE * fp ) 
437 {
438         unsigned int i, size;
439
440         size = elsize * nelem;
441         if ( size < 1 ) return 0;
442
443         i = fread ( buf, 1, size, fp->file );
444         fp->raw_position += i;
445         return i/elsize;
446 }
447
448
449 int cftell( CFILE *fp ) 
450 {
451         return fp->raw_position;
452 }
453
454 int cfseek( CFILE *fp, long int offset, int where )
455 {
456         int c, goal_position;
457
458         switch( where ) {
459         case SEEK_SET:
460                 goal_position = offset;
461                 break;
462         case SEEK_CUR:
463                 goal_position = fp->raw_position+offset;
464                 break;
465         case SEEK_END:
466                 goal_position = fp->size+offset;
467                 break;
468         default:
469                 return 1;
470         }       
471         c = fseek( fp->file, fp->lib_offset + goal_position, SEEK_SET );
472         fp->raw_position = ftell(fp->file)-fp->lib_offset;
473         return c;
474 }
475
476 void cfclose( CFILE * fp ) 
477 {       
478         fclose(fp->file);
479         d_free(fp);
480         return;
481 }
482
483 // routines to read basic data types from CFILE's.  Put here to
484 // simplify mac/pc reading from cfiles.
485
486 int cfile_read_int(CFILE *file)
487 {
488         int32_t i;
489
490         if (cfread( &i, sizeof(i), 1, file) != 1)
491                 Error( "Error reading int in cfile_read_int()" );
492
493         i = INTEL_INT(i);
494         return i;
495 }
496
497 short cfile_read_short(CFILE *file)
498 {
499         int16_t s;
500
501         if (cfread( &s, sizeof(s), 1, file) != 1)
502                 Error( "Error reading short in cfile_read_short()" );
503
504         s = INTEL_SHORT(s);
505         return s;
506 }
507
508 byte cfile_read_byte(CFILE *file)
509 {
510         byte b;
511
512         if (cfread( &b, sizeof(b), 1, file) != 1)
513                 Error( "Error reading byte in cfile_read_byte()" );
514
515         return b;
516 }
517
518 fix cfile_read_fix(CFILE *file)
519 {
520         fix f;
521
522         if (cfread( &f, sizeof(f), 1, file) != 1)
523                 Error( "Error reading fix in cfile_read_fix()" );
524
525         f = (fix)INTEL_INT((int)f);
526         return f;
527 }
528
529 fixang cfile_read_fixang(CFILE *file)
530 {
531         fixang f;
532
533         if (cfread(&f, 2, 1, file) != 1)
534                 Error("Error reading fixang in cfile_read_fixang()");
535
536         f = (fixang) INTEL_SHORT((int) f);
537         return f;
538 }
539
540 void cfile_read_vector(vms_vector *v, CFILE *file)
541 {
542         v->x = cfile_read_fix(file);
543         v->y = cfile_read_fix(file);
544         v->z = cfile_read_fix(file);
545 }
546
547 void cfile_read_angvec(vms_angvec *v, CFILE *file)
548 {
549         v->p = cfile_read_fixang(file);
550         v->b = cfile_read_fixang(file);
551         v->h = cfile_read_fixang(file);
552 }
553
554 void cfile_read_matrix(vms_matrix *m,CFILE *file)
555 {
556         cfile_read_vector(&m->rvec,file);
557         cfile_read_vector(&m->uvec,file);
558         cfile_read_vector(&m->fvec,file);
559 }
560