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