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