]> icculus.org git repositories - btb/d2x.git/blob - cfile/cfile.c
move old per-file change logs into new file ChangeLog-old
[btb/d2x.git] / cfile / cfile.c
1 /* $Id: cfile.c,v 1.27 2004-08-28 23:17:45 schaffner 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  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <conf.h>
23 #endif
24
25 #include <stdio.h>
26 #include <string.h>
27
28 #include "pstypes.h"
29 #include "u_mem.h"
30 #include "strutil.h"
31 #include "d_io.h"
32 #include "error.h"
33 #include "cfile.h"
34 #include "byteswap.h"
35
36 struct CFILE {
37         FILE    *file;
38         int     size;
39         int     lib_offset;
40         int     raw_position;
41 };
42
43 typedef struct hogfile {
44         char    name[13];
45         int     offset;
46         int     length;
47 } hogfile;
48
49 #define MAX_HOGFILES 300
50
51 hogfile HogFiles[MAX_HOGFILES];
52 char Hogfile_initialized = 0;
53 int Num_hogfiles = 0;
54 char HogFilename[64];
55
56 hogfile D1HogFiles[MAX_HOGFILES];
57 char D1Hogfile_initialized = 0;
58 int D1Num_hogfiles = 0;
59 char D1HogFilename[64];
60
61 hogfile AltHogFiles[MAX_HOGFILES];
62 char AltHogfile_initialized = 0;
63 int AltNum_hogfiles = 0;
64 char AltHogFilename[64];
65
66 char AltHogDir[64];
67 char AltHogdir_initialized = 0;
68
69 // routine to take a DOS path and turn it into a macintosh
70 // pathname.  This routine is based on the fact that we should
71 // see a \ character in the dos path.  The sequence .\ a tthe
72 // beginning of a path is turned into a :
73
74 #ifdef MACINTOSH
75 void macify_dospath(char *dos_path, char *mac_path)
76 {
77         char *p;
78
79         if (!strncmp(dos_path, ".\\", 2)) {
80                 strcpy(mac_path, ":");
81                 strcat(mac_path, &(dos_path[2]) );
82         } else
83                 strcpy(mac_path, dos_path);
84
85         while ( (p = strchr(mac_path, '\\')) != NULL)
86                 *p = ':';
87
88 }
89 #endif
90
91 void cfile_use_alternate_hogdir( char * path )
92 {
93         if ( path )     {
94                 strcpy( AltHogDir, path );
95                 AltHogdir_initialized = 1;
96         } else {
97                 AltHogdir_initialized = 0;
98         }
99 }
100
101 //in case no one installs one
102 int default_error_counter=0;
103
104 //ptr to counter of how many critical errors
105 int *critical_error_counter_ptr=&default_error_counter;
106
107 //tell cfile about your critical error counter
108 void cfile_set_critical_error_counter_ptr(int *ptr)
109 {
110         critical_error_counter_ptr = ptr;
111
112 }
113
114
115 FILE * cfile_get_filehandle( char * filename, char * mode )
116 {
117         FILE * fp;
118         char temp[128];
119
120         *critical_error_counter_ptr = 0;
121         fp = fopen( filename, mode );
122         if ( fp && *critical_error_counter_ptr )        {
123                 fclose(fp);
124                 fp = NULL;
125         }
126         if ( (fp==NULL) && (AltHogdir_initialized) )    {
127                 strcpy( temp, AltHogDir );
128                 strcat( temp, "/");
129                 strcat( temp, filename );
130                 *critical_error_counter_ptr = 0;
131                 fp = fopen( temp, mode );
132                 if ( fp && *critical_error_counter_ptr )        {
133                         fclose(fp);
134                         fp = NULL;
135                 }
136         }
137         return fp;
138 }
139
140 //returns 1 if file loaded with no errors
141 int cfile_init_hogfile(char *fname, hogfile * hog_files, int * nfiles )
142 {
143         char id[4];
144         FILE * fp;
145         int i, len;
146
147         *nfiles = 0;
148
149         fp = cfile_get_filehandle( fname, "rb" );
150         if ( fp == NULL ) return 0;
151
152         fread( id, 3, 1, fp );
153         if ( strncmp( id, "DHF", 3 ) )  {
154                 fclose(fp);
155                 return 0;
156         }
157
158         while( 1 )
159         {
160                 if ( *nfiles >= MAX_HOGFILES ) {
161                         fclose(fp);
162                         Error( "HOGFILE is limited to %d files.\n",  MAX_HOGFILES );
163                 }
164                 i = fread( hog_files[*nfiles].name, 13, 1, fp );
165                 if ( i != 1 )   {               //eof here is ok
166                         fclose(fp);
167                         return 1;
168                 }
169                 i = fread( &len, 4, 1, fp );
170                 if ( i != 1 )   {
171                         fclose(fp);
172                         return 0;
173                 }
174                 hog_files[*nfiles].length = INTEL_INT(len);
175                 hog_files[*nfiles].offset = ftell( fp );
176                 *nfiles = (*nfiles) + 1;
177                 // Skip over
178                 i = fseek( fp, INTEL_INT(len), SEEK_CUR );
179         }
180 }
181
182 //Specify the name of the hogfile.  Returns 1 if hogfile found & had files
183 int cfile_init(char *hogname)
184 {
185         #ifdef MACINTOSH
186         char mac_path[255];
187
188         macify_dospath(hogname, mac_path);
189         #endif
190
191         Assert(Hogfile_initialized == 0);
192
193         #ifndef MACINTOSH
194         if (cfile_init_hogfile(hogname, HogFiles, &Num_hogfiles )) {
195                 strcpy( HogFilename, hogname );
196         #else
197         if (cfile_init_hogfile(mac_path, HogFiles, &Num_hogfiles )) {
198                 strcpy( HogFilename, mac_path );
199         #endif
200                 Hogfile_initialized = 1;
201                 return 1;
202         }
203         else
204                 return 0;       //not loaded!
205 }
206
207
208 int cfile_size(char *hogname)
209 {
210         CFILE *fp;
211         int size;
212
213         fp = cfopen(hogname, "rb");
214         if (fp == NULL)
215                 return -1;
216         size = ffilelength(fp->file);
217         cfclose(fp);
218         return size;
219 }
220
221 /*
222  * return handle for file called "name", embedded in one of the hogfiles
223  */
224 FILE * cfile_find_libfile(char * name, int * length)
225 {
226         FILE * fp;
227         int i;
228
229         if ( AltHogfile_initialized )   {
230                 for (i=0; i<AltNum_hogfiles; i++ )      {
231                         if ( !stricmp( AltHogFiles[i].name, name ))     {
232                                 fp = cfile_get_filehandle( AltHogFilename, "rb" );
233                                 if ( fp == NULL ) return NULL;
234                                 fseek( fp,  AltHogFiles[i].offset, SEEK_SET );
235                                 *length = AltHogFiles[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
258         if (D1Hogfile_initialized)      {
259                 for (i = 0; i < D1Num_hogfiles; i++) {
260                         if (!stricmp(D1HogFiles[i].name, name)) {
261                                 fp = cfile_get_filehandle(D1HogFilename, "rb");
262                                 if (fp == NULL) return NULL;
263                                 fseek(fp,  D1HogFiles[i].offset, SEEK_SET);
264                                 *length = D1HogFiles[i].length;
265                                 return fp;
266                         }
267                 }
268         }
269
270         return NULL;
271 }
272
273 int cfile_use_alternate_hogfile( char * name )
274 {
275         if ( name )     {
276                 #ifdef MACINTOSH
277                 char mac_path[255];
278
279                 macify_dospath(name, mac_path);
280                 strcpy( AltHogFilename, mac_path);
281                 #else
282                 strcpy( AltHogFilename, name );
283                 #endif
284                 cfile_init_hogfile( AltHogFilename, AltHogFiles, &AltNum_hogfiles );
285                 AltHogfile_initialized = 1;
286                 return (AltNum_hogfiles > 0);
287         } else {
288                 AltHogfile_initialized = 0;
289                 return 1;
290         }
291 }
292
293 int cfile_use_descent1_hogfile( char * name )
294 {
295         if (name)       {
296 #ifdef MACINTOSH
297                 char mac_path[255];
298
299                 macify_dospath(name, mac_path);
300                 strcpy(D1HogFilename, mac_path);
301 #else
302                 strcpy(D1HogFilename, name);
303 #endif
304                 cfile_init_hogfile(D1HogFilename, D1HogFiles, &D1Num_hogfiles);
305                 D1Hogfile_initialized = 1;
306                 return (D1Num_hogfiles > 0);
307         } else {
308                 D1Hogfile_initialized = 0;
309                 return 1;
310         }
311 }
312
313
314 // cfeof() Tests for end-of-file on a stream
315 //
316 // returns a nonzero value after the first read operation that attempts to read
317 // past the end of the file. It returns 0 if the current position is not end of file.
318 // There is no error return.
319
320 int cfeof(CFILE *cfile)
321 {
322         Assert(cfile != NULL);
323
324         Assert(cfile->file != NULL);
325
326     return (cfile->raw_position >= cfile->size);
327 }
328
329
330 int cferror(CFILE *cfile)
331 {
332         return ferror(cfile->file);
333 }
334
335
336 int cfexist( char * filename )
337 {
338         int length;
339         FILE *fp;
340
341
342         if (filename[0] != '\x01')
343                 fp = cfile_get_filehandle( filename, "rb" );            // Check for non-hog file first...
344         else {
345                 fp = NULL;              //don't look in dir, only in hogfile
346                 filename++;
347         }
348
349         if ( fp )       {
350                 fclose(fp);
351                 return 1;
352         }
353
354         fp = cfile_find_libfile(filename, &length );
355         if ( fp )       {
356                 fclose(fp);
357                 return 2;               // file found in hog
358         }
359
360         return 0;               // Couldn't find it.
361 }
362
363
364 // Deletes a file.
365 int cfile_delete(char *filename)
366 {
367 #ifndef _WIN32_WCE
368         return remove(filename);
369 #else
370         return !DeleteFile(filename);
371 #endif
372 }
373
374
375 // Rename a file.
376 int cfile_rename(char *oldname, char *newname)
377 {
378 #ifndef _WIN32_WCE
379         return rename(oldname, newname);
380 #else
381         return !MoveFile(oldname, newname);
382 #endif
383 }
384
385
386 // Make a directory.
387 int cfile_mkdir(char *pathname)
388 {
389 #ifdef _WIN32
390 # ifdef _WIN32_WCE
391         return !CreateDirectory(pathname, NULL);
392 # else
393         return _mkdir(pathname);
394 # endif
395 #elif defined(macintosh)
396     char        mac_path[256];
397     Str255      pascal_path;
398     long        dirID;  // Insists on returning this
399
400     macify_posix_path(pathname, mac_path);
401     CopyCStringToPascal(mac_path, pascal_path);
402     return DirCreate(0, 0, pascal_path, &dirID);
403 #else
404         return mkdir(pathname, 0755);
405 #endif
406 }
407
408
409 CFILE * cfopen(char * filename, char * mode )
410 {
411         int length;
412         FILE * fp;
413         CFILE *cfile;
414
415         if (filename[0] != '\x01') {
416                 #ifdef MACINTOSH
417                 char mac_path[255];
418
419                 macify_dospath(filename, mac_path);
420                 fp = cfile_get_filehandle( mac_path, mode);
421                 #else
422                 fp = cfile_get_filehandle( filename, mode );            // Check for non-hog file first...
423                 #endif
424         } else {
425                 fp = NULL;              //don't look in dir, only in hogfile
426                 filename++;
427         }
428
429         if ( !fp ) {
430                 fp = cfile_find_libfile(filename, &length );
431                 if ( !fp )
432                         return NULL;            // No file found
433                 if (stricmp(mode, "rb"))
434                         Error("mode must be rb for files in hog.\n");
435                 cfile = d_malloc ( sizeof(CFILE) );
436                 if ( cfile == NULL ) {
437                         fclose(fp);
438                         return NULL;
439                 }
440                 cfile->file = fp;
441                 cfile->size = length;
442                 cfile->lib_offset = ftell( fp );
443                 cfile->raw_position = 0;
444                 return cfile;
445         } else {
446                 cfile = d_malloc ( sizeof(CFILE) );
447                 if ( cfile == NULL ) {
448                         fclose(fp);
449                         return NULL;
450                 }
451                 cfile->file = fp;
452                 cfile->size = ffilelength(fp);
453                 cfile->lib_offset = 0;
454                 cfile->raw_position = 0;
455                 return cfile;
456         }
457 }
458
459 int cfilelength( CFILE *fp )
460 {
461         return fp->size;
462 }
463
464
465 // cfwrite() writes to the file
466 //
467 // returns:   number of full elements actually written
468 //
469 //
470 int cfwrite(void *buf, int elsize, int nelem, CFILE *cfile)
471 {
472         int items_written;
473
474         Assert(cfile != NULL);
475         Assert(buf != NULL);
476         Assert(elsize > 0);
477
478         Assert(cfile->file != NULL);
479         Assert(cfile->lib_offset == 0);
480
481         items_written = fwrite(buf, elsize, nelem, cfile->file);
482         cfile->raw_position = ftell(cfile->file);
483
484         return items_written;
485 }
486
487
488 // cfputc() writes a character to a file
489 //
490 // returns:   success ==> returns character written
491 //            error   ==> EOF
492 //
493 int cfputc(int c, CFILE *cfile)
494 {
495         int char_written;
496
497         Assert(cfile != NULL);
498
499         Assert(cfile->file != NULL);
500         Assert(cfile->lib_offset == 0);
501
502         char_written = fputc(c, cfile->file);
503         cfile->raw_position = ftell(cfile->file);
504
505         return char_written;
506 }
507
508
509 int cfgetc( CFILE * fp )
510 {
511         int c;
512
513         if (fp->raw_position >= fp->size ) return EOF;
514
515         c = getc( fp->file );
516         if (c!=EOF)
517                 fp->raw_position = ftell(fp->file)-fp->lib_offset;
518
519         return c;
520 }
521
522
523 // cfputs() writes a string to a file
524 //
525 // returns:   success ==> non-negative value
526 //            error   ==> EOF
527 //
528 int cfputs(char *str, CFILE *cfile)
529 {
530         int ret;
531
532         Assert(cfile != NULL);
533         Assert(str != NULL);
534
535         Assert(cfile->file != NULL);
536
537         ret = fputs(str, cfile->file);
538         cfile->raw_position = ftell(cfile->file);
539
540         return ret;
541 }
542
543
544 char * cfgets( char * buf, size_t n, CFILE * fp )
545 {
546         char * t = buf;
547         int i;
548         int c;
549
550 #if 0 // don't use the standard fgets, because it will only handle the native line-ending style
551         if (fp->lib_offset == 0) // This is not an archived file
552         {
553                 t = fgets(buf, n, fp->file);
554                 fp->raw_position = ftell(fp->file);
555                 return t;
556         }
557 #endif
558
559         for (i=0; i<n-1; i++ ) {
560                 do {
561                         if (fp->raw_position >= fp->size ) {
562                                 *buf = 0;
563                                 return NULL;
564                         }
565                         c = cfgetc(fp);
566                         if (c == 0 || c == 10)        // Unix line ending
567                                 break;
568                         if (c == 13) {      // Mac or DOS line ending
569                                 int c1;
570
571                                 c1 = cfgetc(fp);
572                                 if (c1 != EOF)  // The file could end with a Mac line ending
573                                         cfseek(fp, -1, SEEK_CUR);
574                                 if ( c1 == 10 ) // DOS line ending
575                                         continue;
576                                 else            // Mac line ending
577                                         break;
578                         }
579                 } while ( c == 13 );
580                 if ( c == 13 )  // because cr-lf is a bad thing on the mac
581                         c = '\n';   // and anyway -- 0xod is CR on mac, not 0x0a
582                 *buf++ = c;
583                 if ( c=='\n' ) break;
584         }
585         *buf++ = 0;
586         return  t;
587 }
588
589 size_t cfread( void * buf, size_t elsize, size_t nelem, CFILE * fp ) 
590 {
591         unsigned int i, size;
592
593         size = elsize * nelem;
594         if ( size < 1 ) return 0;
595
596         i = fread ( buf, 1, size, fp->file );
597         fp->raw_position += i;
598         return i/elsize;
599 }
600
601
602 int cftell( CFILE *fp ) 
603 {
604         return fp->raw_position;
605 }
606
607 int cfseek( CFILE *fp, long int offset, int where )
608 {
609         int c, goal_position;
610
611         switch( where ) {
612         case SEEK_SET:
613                 goal_position = offset;
614                 break;
615         case SEEK_CUR:
616                 goal_position = fp->raw_position+offset;
617                 break;
618         case SEEK_END:
619                 goal_position = fp->size+offset;
620                 break;
621         default:
622                 return 1;
623         }       
624         c = fseek( fp->file, fp->lib_offset + goal_position, SEEK_SET );
625         fp->raw_position = ftell(fp->file)-fp->lib_offset;
626         return c;
627 }
628
629 int cfclose(CFILE *fp)
630 {
631         int result;
632
633         result = fclose(fp->file);
634         d_free(fp);
635
636         return result;
637 }
638
639 // routines to read basic data types from CFILE's.  Put here to
640 // simplify mac/pc reading from cfiles.
641
642 int cfile_read_int(CFILE *file)
643 {
644         int32_t i;
645
646         if (cfread( &i, sizeof(i), 1, file) != 1)
647                 Error( "Error reading int in cfile_read_int()" );
648
649         i = INTEL_INT(i);
650         return i;
651 }
652
653 short cfile_read_short(CFILE *file)
654 {
655         int16_t s;
656
657         if (cfread( &s, sizeof(s), 1, file) != 1)
658                 Error( "Error reading short in cfile_read_short()" );
659
660         s = INTEL_SHORT(s);
661         return s;
662 }
663
664 sbyte cfile_read_byte(CFILE *file)
665 {
666         sbyte b;
667
668         if (cfread( &b, sizeof(b), 1, file) != 1)
669                 Error( "Error reading byte in cfile_read_byte()" );
670
671         return b;
672 }
673
674 fix cfile_read_fix(CFILE *file)
675 {
676         fix f;
677
678         if (cfread( &f, sizeof(f), 1, file) != 1)
679                 Error( "Error reading fix in cfile_read_fix()" );
680
681         f = (fix)INTEL_INT((int)f);
682         return f;
683 }
684
685 fixang cfile_read_fixang(CFILE *file)
686 {
687         fixang f;
688
689         if (cfread(&f, 2, 1, file) != 1)
690                 Error("Error reading fixang in cfile_read_fixang()");
691
692         f = (fixang) INTEL_SHORT((int) f);
693         return f;
694 }
695
696 void cfile_read_vector(vms_vector *v, CFILE *file)
697 {
698         v->x = cfile_read_fix(file);
699         v->y = cfile_read_fix(file);
700         v->z = cfile_read_fix(file);
701 }
702
703 void cfile_read_angvec(vms_angvec *v, CFILE *file)
704 {
705         v->p = cfile_read_fixang(file);
706         v->b = cfile_read_fixang(file);
707         v->h = cfile_read_fixang(file);
708 }
709
710 void cfile_read_matrix(vms_matrix *m,CFILE *file)
711 {
712         cfile_read_vector(&m->rvec,file);
713         cfile_read_vector(&m->uvec,file);
714         cfile_read_vector(&m->fvec,file);
715 }
716
717
718 void cfile_read_string(char *buf, int n, CFILE *file)
719 {
720         char c;
721
722         do {
723                 c = (char)cfile_read_byte(file);
724                 if (n > 0)
725                 {
726                         *buf++ = c;
727                         n--;
728                 }
729         } while (c != 0);
730 }
731
732
733 // equivalent write functions of above read functions follow
734
735 int cfile_write_int(int i, CFILE *file)
736 {
737         i = INTEL_INT(i);
738         return cfwrite(&i, sizeof(i), 1, file);
739 }
740
741
742 int cfile_write_short(short s, CFILE *file)
743 {
744         s = INTEL_SHORT(s);
745         return cfwrite(&s, sizeof(s), 1, file);
746 }
747
748
749 int cfile_write_byte(sbyte b, CFILE *file)
750 {
751         return cfwrite(&b, sizeof(b), 1, file);
752 }
753
754
755 int cfile_write_string(char *buf, CFILE *file)
756 {
757         int len;
758
759         if ((!buf) || (buf && !buf[0]))
760                 return cfile_write_byte(0, file);
761
762         len = strlen(buf);
763         if (!cfwrite(buf, len, 1, file))
764                 return 0;
765
766         return cfile_write_byte(0, file);   // write out NULL termination
767 }