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