revert cfgets() and load_endlevel_data() to expect newline-terminated strings, fix...
[btb/d2x.git] / cfile / cfile.c
1 /* $Id: cfile.c,v 1.9 2002-09-14 00:20:44 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 FILE * cfile_find_libfile(char * name, int * length)
210 {
211         FILE * fp;
212         int i;
213
214         if ( AltHogfile_initialized )   {
215                 for (i=0; i<AltNum_hogfiles; i++ )      {
216                         if ( !stricmp( AltHogFiles[i].name, name ))     {
217                                 fp = cfile_get_filehandle( AltHogFilename, "rb" );
218                                 if ( fp == NULL ) return NULL;
219                                 fseek( fp,  AltHogFiles[i].offset, SEEK_SET );
220                                 *length = AltHogFiles[i].length;
221                                 return fp;
222                         }
223                 }
224         }
225
226         if (D1Hogfile_initialized)      {
227                 for (i = 0; i < D1Num_hogfiles; i++) {
228                         if (!stricmp(D1HogFiles[i].name, name)) {
229                                 fp = cfile_get_filehandle(D1HogFilename, "rb");
230                                 if (fp == NULL) return NULL;
231                                 fseek(fp,  D1HogFiles[i].offset, SEEK_SET);
232                                 *length = D1HogFiles[i].length;
233                                 return fp;
234                         }
235                 }
236         }
237
238         if ( !Hogfile_initialized )     {
239                 //@@cfile_init_hogfile( "DESCENT2.HOG", HogFiles, &Num_hogfiles );
240                 //@@Hogfile_initialized = 1;
241
242                 //Int3();       //hogfile ought to be initialized
243         }
244
245         for (i=0; i<Num_hogfiles; i++ ) {
246                 if ( !stricmp( HogFiles[i].name, name ))        {
247                         fp = cfile_get_filehandle( HogFilename, "rb" );
248                         if ( fp == NULL ) return NULL;
249                         fseek( fp,  HogFiles[i].offset, SEEK_SET );
250                         *length = HogFiles[i].length;
251                         return fp;
252                 }
253         }
254         return NULL;
255 }
256
257 int cfile_use_alternate_hogfile( char * name )
258 {
259         if ( name )     {
260                 #ifdef MACINTOSH
261                 char mac_path[255];
262
263                 macify_dospath(name, mac_path);
264                 strcpy( AltHogFilename, mac_path);
265                 #else
266                 strcpy( AltHogFilename, name );
267                 #endif
268                 cfile_init_hogfile( AltHogFilename, AltHogFiles, &AltNum_hogfiles );
269                 AltHogfile_initialized = 1;
270                 return (AltNum_hogfiles > 0);
271         } else {
272                 AltHogfile_initialized = 0;
273                 return 1;
274         }
275 }
276
277 int cfile_use_descent1_hogfile( char * name )
278 {
279         if (name)       {
280 #ifdef MACINTOSH
281                 char mac_path[255];
282
283                 macify_dospath(name, mac_path);
284                 strcpy(D1HogFilename, mac_path);
285 #else
286                 strcpy(D1HogFilename, name);
287 #endif
288                 cfile_init_hogfile(D1HogFilename, D1HogFiles, &D1Num_hogfiles);
289                 D1Hogfile_initialized = 1;
290                 return (D1Num_hogfiles > 0);
291         } else {
292                 D1Hogfile_initialized = 0;
293                 return 1;
294         }
295 }
296
297 int cfexist( char * filename )
298 {
299         int length;
300         FILE *fp;
301
302
303         if (filename[0] != '\x01')
304                 fp = cfile_get_filehandle( filename, "rb" );            // Check for non-hog file first...
305         else {
306                 fp = NULL;              //don't look in dir, only in hogfile
307                 filename++;
308         }
309
310         if ( fp )       {
311                 fclose(fp);
312                 return 1;
313         }
314
315         fp = cfile_find_libfile(filename, &length );
316         if ( fp )       {
317                 fclose(fp);
318                 return 2;               // file found in hog
319         }
320
321         return 0;               // Couldn't find it.
322 }
323
324
325 CFILE * cfopen(char * filename, char * mode )
326 {
327         int length;
328         FILE * fp;
329         CFILE *cfile;
330
331         if (stricmp( mode, "rb"))       {
332                 Error( "cfiles can only be opened with mode==rb\n" );
333         }
334
335         if (filename[0] != '\x01') {
336                 #ifdef MACINTOSH
337                 char mac_path[255];
338
339                 macify_dospath(filename, mac_path);
340                 fp = cfile_get_filehandle( mac_path, mode);
341                 #else
342                 fp = cfile_get_filehandle( filename, mode );            // Check for non-hog file first...
343                 #endif
344         } else {
345                 fp = NULL;              //don't look in dir, only in hogfile
346                 filename++;
347         }
348
349         if ( !fp ) {
350                 fp = cfile_find_libfile(filename, &length );
351                 if ( !fp )
352                         return NULL;            // No file found
353                 cfile = d_malloc ( sizeof(CFILE) );
354                 if ( cfile == NULL ) {
355                         fclose(fp);
356                         return NULL;
357                 }
358                 cfile->file = fp;
359                 cfile->size = length;
360                 cfile->lib_offset = ftell( fp );
361                 cfile->raw_position = 0;
362                 return cfile;
363         } else {
364                 cfile = d_malloc ( sizeof(CFILE) );
365                 if ( cfile == NULL ) {
366                         fclose(fp);
367                         return NULL;
368                 }
369                 cfile->file = fp;
370                 cfile->size = filelength( fileno(fp) );
371                 cfile->lib_offset = 0;
372                 cfile->raw_position = 0;
373                 return cfile;
374         }
375 }
376
377 int cfilelength( CFILE *fp )
378 {
379         return fp->size;
380 }
381
382 int cfgetc( CFILE * fp )
383 {
384         int c;
385
386         if (fp->raw_position >= fp->size ) return EOF;
387
388         c = getc( fp->file );
389         if (c!=EOF)
390                 fp->raw_position++;
391
392 //      Assert( fp->raw_position==(ftell(fp->file)-fp->lib_offset) );
393
394         return c;
395 }
396
397 char * cfgets( char * buf, size_t n, CFILE * fp )
398 {
399         char * t = buf;
400         int i;
401         int c;
402
403         for (i=0; i<n-1; i++ ) {
404                 do {
405                         if (fp->raw_position >= fp->size ) {
406                                 *buf = 0;
407                                 return NULL;
408                         }
409                         c = fgetc( fp->file );
410                         fp->raw_position++;
411                         if (c == 0 || c == 10)        // Unix line ending
412                                 break;
413                         if (c == 13) {      // Mac or DOS line ending
414                                 int c1;
415
416                                 c1 = fgetc( fp->file );
417                                 fseek( fp->file, -1, SEEK_CUR);
418                                 if ( c1 == 10 ) // DOS line ending
419                                         continue;
420                                 else            // Mac line ending
421                                         break;
422                         }
423                 } while ( c == 13 );
424                 if ( c == 13 )  // because cr-lf is a bad thing on the mac
425                         c = '\n';   // and anyway -- 0xod is CR on mac, not 0x0a
426                 *buf++ = c;
427                 if ( c=='\n' ) break;
428         }
429         *buf++ = 0;
430         return  t;
431 }
432
433 size_t cfread( void * buf, size_t elsize, size_t nelem, CFILE * fp ) 
434 {
435         unsigned int i, size;
436
437         size = elsize * nelem;
438         if ( size < 1 ) return 0;
439
440         i = fread ( buf, 1, size, fp->file );
441         fp->raw_position += i;
442         return i/elsize;
443 }
444
445
446 int cftell( CFILE *fp ) 
447 {
448         return fp->raw_position;
449 }
450
451 int cfseek( CFILE *fp, long int offset, int where )
452 {
453         int c, goal_position;
454
455         switch( where ) {
456         case SEEK_SET:
457                 goal_position = offset;
458                 break;
459         case SEEK_CUR:
460                 goal_position = fp->raw_position+offset;
461                 break;
462         case SEEK_END:
463                 goal_position = fp->size+offset;
464                 break;
465         default:
466                 return 1;
467         }       
468         c = fseek( fp->file, fp->lib_offset + goal_position, SEEK_SET );
469         fp->raw_position = ftell(fp->file)-fp->lib_offset;
470         return c;
471 }
472
473 void cfclose( CFILE * fp ) 
474 {       
475         fclose(fp->file);
476         d_free(fp);
477         return;
478 }
479
480 // routines to read basic data types from CFILE's.  Put here to
481 // simplify mac/pc reading from cfiles.
482
483 int cfile_read_int(CFILE *file)
484 {
485         int32_t i;
486
487         if (cfread( &i, sizeof(i), 1, file) != 1)
488                 Error( "Error reading int in cfile_read_int()" );
489
490         i = INTEL_INT(i);
491         return i;
492 }
493
494 short cfile_read_short(CFILE *file)
495 {
496         int16_t s;
497
498         if (cfread( &s, sizeof(s), 1, file) != 1)
499                 Error( "Error reading short in cfile_read_short()" );
500
501         s = INTEL_SHORT(s);
502         return s;
503 }
504
505 byte cfile_read_byte(CFILE *file)
506 {
507         byte b;
508
509         if (cfread( &b, sizeof(b), 1, file) != 1)
510                 Error( "Error reading byte in cfile_read_byte()" );
511
512         return b;
513 }
514
515 fix cfile_read_fix(CFILE *file)
516 {
517         fix f;
518
519         if (cfread( &f, sizeof(f), 1, file) != 1)
520                 Error( "Error reading fix in cfile_read_fix()" );
521
522         f = (fix)INTEL_INT((int)f);
523         return f;
524 }
525
526 fixang cfile_read_fixang(CFILE *file)
527 {
528         fixang f;
529
530         if (cfread(&f, 2, 1, file) != 1)
531                 Error("Error reading fixang in cfile_read_fixang()");
532
533         f = (fixang) INTEL_SHORT((int) f);
534         return f;
535 }
536
537 void cfile_read_vector(vms_vector *v, CFILE *file)
538 {
539         v->x = cfile_read_fix(file);
540         v->y = cfile_read_fix(file);
541         v->z = cfile_read_fix(file);
542 }
543
544 void cfile_read_angvec(vms_angvec *v, CFILE *file)
545 {
546         v->p = cfile_read_fixang(file);
547         v->b = cfile_read_fixang(file);
548         v->h = cfile_read_fixang(file);
549 }
550
551 void cfile_read_matrix(vms_matrix *m,CFILE *file)
552 {
553         cfile_read_vector(&m->rvec,file);
554         cfile_read_vector(&m->uvec,file);
555         cfile_read_vector(&m->fvec,file);
556 }
557