1 /* $Id: cfile.c,v 1.31 2004-10-09 21:47:49 schaffner Exp $ */
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.
17 * Functions for accessing compressed files.
18 * (Actually, the files are not compressed, but concatenated within hogfiles)
30 #elif defined(macintosh)
34 # include <sys/stat.h>
45 /* a file (if offset == 0) or a part of a hog file */
59 #define MAX_FILES_IN_HOG 300
61 /* a hog file is an archive, like a tar file */
65 struct file_in_hog files[MAX_FILES_IN_HOG];
68 hog *builtin_hog = NULL;
72 void free_builtin_hog() { if (builtin_hog) d_free (builtin_hog); }
73 void free_alt_hog() { if (alt_hog) d_free (alt_hog); }
74 void free_d1_hog() { if (d1_hog) d_free (d1_hog); }
77 char AltHogdir_initialized = 0;
79 // routine to take a DOS path and turn it into a macintosh
80 // pathname. This routine is based on the fact that we should
81 // see a \ character in the dos path. The sequence .\ a tthe
82 // beginning of a path is turned into a :
84 // routine to take a POSIX style path and turn it into a pre OS X
85 // pathname. This routine uses CFURL's. This function is necessary
86 // because even though fopen exists in StdCLib
87 // it must take a path in the OS native format.
90 void macify_posix_path(char *posix_path, char *mac_path)
94 url = CFURLCreateWithBytes (kCFAllocatorDefault, (ubyte *) posix_path, strlen(posix_path), GetApplicationTextEncoding(), NULL);
95 CFURLGetFileSystemRepresentation (url, 0, (ubyte *) mac_path, 255);
100 void cfile_use_alternate_hogdir( char * path )
103 strcpy( AltHogDir, path );
104 AltHogdir_initialized = 1;
106 AltHogdir_initialized = 0;
110 //in case no one installs one
111 int default_error_counter=0;
113 //ptr to counter of how many critical errors
114 int *critical_error_counter_ptr=&default_error_counter;
116 //tell cfile about your critical error counter
117 void cfile_set_critical_error_counter_ptr(int *ptr)
119 critical_error_counter_ptr = ptr;
124 FILE * cfile_get_filehandle( char * filename, char * mode )
132 *critical_error_counter_ptr = 0;
134 macify_posix_path(filename, mac_path);
135 fp = fopen( mac_path, mode );
137 fp = fopen( filename, mode );
139 if ( fp && *critical_error_counter_ptr ) {
143 if ( (fp==NULL) && (AltHogdir_initialized) ) {
144 strcpy( temp, AltHogDir );
146 strcat( temp, filename );
147 *critical_error_counter_ptr = 0;
149 macify_posix_path(temp, mac_path);
150 fp = fopen( mac_path, mode );
152 fp = fopen( temp, mode );
154 if ( fp && *critical_error_counter_ptr ) {
162 hog * cfile_init_hogfile (char *fname)
168 fp = cfile_get_filehandle (fname, "rb");
172 // verify that it is really a Descent Hog File
173 fread( id, 3, 1, fp );
174 if ( strncmp( id, "DHF", 3 ) ) {
179 hog *new_hog = (hog *) d_malloc (sizeof (hog));
181 new_hog->num_files = 0;
182 strcpy (new_hog->filename, fname);
184 // read file name or reach EOF
185 while (! feof (fp)) {
186 if (new_hog->num_files >= MAX_FILES_IN_HOG) {
189 Error ("Exceeded max. number of files in hog (%d).\n",
192 i = fread (new_hog->files[new_hog->num_files].name, 13, 1, fp);
194 break; // we assume it is EOF, so it is OK
196 i = fread( &len, 4, 1, fp );
199 new_hog->files[new_hog->num_files].length = INTEL_INT (len);
200 new_hog->files[new_hog->num_files].offset = ftell (fp);
201 // skip over embedded file:
202 i = fseek (fp, INTEL_INT (len), SEEK_CUR);
203 new_hog->num_files++;
209 // Opens builtin hog given its filename. Returns 1 on success.
210 int cfile_init(char *hogname)
212 Assert (builtin_hog == NULL);
214 builtin_hog = cfile_init_hogfile (hogname);
215 atexit (free_builtin_hog);
216 return builtin_hog != NULL ? 1 : 0;
220 int cfile_size(char *hogname)
225 fp = cfopen(hogname, "rb");
228 size = ffilelength(fp->file);
233 FILE * cfile_find_in_hog (char * name, int * length, hog *my_hog) {
236 for (i = 0; i < my_hog->num_files; i++ )
237 if (! stricmp (my_hog->files[i].name, name)) {
238 fp = cfile_get_filehandle (my_hog->filename, "rb");
241 fseek (fp, my_hog->files[i].offset, SEEK_SET);
242 *length = my_hog->files[i].length;
249 * return handle for file called "name", embedded in one of the hogfiles
251 FILE * cfile_find_embedded_file (char * name, int * length)
255 fp = cfile_find_in_hog (name, length, alt_hog);
261 fp = cfile_find_in_hog (name, length, builtin_hog);
267 fp = cfile_find_in_hog (name, length, d1_hog);
275 int cfile_use_alternate_hogfile (char * name)
282 alt_hog = cfile_init_hogfile (name);
283 atexit (free_alt_hog);
290 /* we use the d1 hog for d1 textures... */
291 int cfile_use_descent1_hogfile( char * name )
298 d1_hog = cfile_init_hogfile (name);
299 atexit (free_d1_hog);
306 // cfeof() Tests for end-of-file on a stream
308 // returns a nonzero value after the first read operation that attempts to read
309 // past the end of the file. It returns 0 if the current position is not end of file.
310 // There is no error return.
312 int cfeof(CFILE *cfile)
314 Assert(cfile != NULL);
316 Assert(cfile->file != NULL);
318 return (cfile->raw_position >= cfile->size);
322 int cferror(CFILE *cfile)
324 return ferror(cfile->file);
328 int cfexist( char * filename )
334 if (filename[0] != '\x01')
335 fp = cfile_get_filehandle( filename, "rb" ); // Check for non-hog file first...
337 fp = NULL; //don't look in dir, only in hogfile
346 fp = cfile_find_embedded_file (filename, &length);
349 return 2; // file found in hog
352 return 0; // Couldn't find it.
357 int cfile_delete(char *filename)
360 return remove(filename);
362 return !DeleteFile(filename);
368 int cfile_rename(char *oldname, char *newname)
371 return rename(oldname, newname);
373 return !MoveFile(oldname, newname);
379 int cfile_mkdir(char *pathname)
383 return !CreateDirectory(pathname, NULL);
385 return _mkdir(pathname);
387 #elif defined(macintosh)
390 long dirID; // Insists on returning this
392 macify_posix_path(pathname, mac_path);
393 CopyCStringToPascal(mac_path, pascal_path);
394 return DirCreate(0, 0, pascal_path, &dirID);
396 return mkdir(pathname, 0755);
401 CFILE * cfopen(char * filename, char * mode )
407 if (filename[0] != '\x01')
408 fp = cfile_get_filehandle( filename, mode ); // Check for non-hog file first...
410 fp = NULL; //don't look in dir, only in hogfile
415 fp = cfile_find_embedded_file (filename, &length);
417 return NULL; // No file found
418 if (stricmp(mode, "rb"))
419 Error("mode must be rb for files in hog.\n");
420 cfile = d_malloc ( sizeof(CFILE) );
421 if ( cfile == NULL ) {
426 cfile->size = length;
427 cfile->offset = ftell( fp );
428 cfile->raw_position = 0;
431 cfile = d_malloc ( sizeof(CFILE) );
432 if ( cfile == NULL ) {
437 cfile->size = ffilelength(fp);
439 cfile->raw_position = 0;
444 int cfilelength( CFILE *fp )
450 // cfwrite() writes to the file
452 // returns: number of full elements actually written
455 int cfwrite(void *buf, int elsize, int nelem, CFILE *cfile)
459 Assert(cfile != NULL);
463 Assert(cfile->file != NULL);
464 Assert(cfile->offset == 0);
466 items_written = fwrite(buf, elsize, nelem, cfile->file);
467 cfile->raw_position = ftell(cfile->file);
469 return items_written;
473 // cfputc() writes a character to a file
475 // returns: success ==> returns character written
478 int cfputc(int c, CFILE *cfile)
482 Assert(cfile != NULL);
484 Assert(cfile->file != NULL);
485 Assert(cfile->offset == 0);
487 char_written = fputc(c, cfile->file);
488 cfile->raw_position = ftell(cfile->file);
494 int cfgetc( CFILE * fp )
498 if (fp->raw_position >= fp->size ) return EOF;
500 c = getc( fp->file );
502 fp->raw_position = ftell(fp->file)-fp->offset;
508 // cfputs() writes a string to a file
510 // returns: success ==> non-negative value
513 int cfputs(char *str, CFILE *cfile)
517 Assert(cfile != NULL);
520 Assert(cfile->file != NULL);
522 ret = fputs(str, cfile->file);
523 cfile->raw_position = ftell(cfile->file);
529 char * cfgets( char * buf, size_t n, CFILE * fp )
534 #if 0 // don't use the standard fgets, because it will only handle the native line-ending style
535 if (fp->offset == 0) // This is not an archived file
537 t = fgets(buf, n, fp->file);
538 fp->raw_position = ftell(fp->file);
543 for (i=0; i<n-1; i++ ) {
545 if (fp->raw_position >= fp->size ) {
550 if (c == 0 || c == 10) // Unix line ending
552 if (c == 13) { // Mac or DOS line ending
556 if (c1 != EOF) // The file could end with a Mac line ending
557 cfseek(fp, -1, SEEK_CUR);
558 if ( c1 == 10 ) // DOS line ending
560 else // Mac line ending
564 if ( c == 13 ) // because cr-lf is a bad thing on the mac
565 c = '\n'; // and anyway -- 0xod is CR on mac, not 0x0a
566 if ( c=='\n' ) break;
573 size_t cfread( void * buf, size_t elsize, size_t nelem, CFILE * fp )
575 unsigned int i, size;
577 size = elsize * nelem;
578 if ( size < 1 ) return 0;
580 i = fread ( buf, 1, size, fp->file );
581 fp->raw_position += i;
586 int cftell( CFILE *fp )
588 return fp->raw_position;
591 int cfseek( CFILE *fp, long int offset, int where )
593 int c, goal_position;
597 goal_position = offset;
600 goal_position = fp->raw_position+offset;
603 goal_position = fp->size+offset;
608 c = fseek( fp->file, fp->offset + goal_position, SEEK_SET );
609 fp->raw_position = ftell(fp->file)-fp->offset;
613 int cfclose(CFILE *fp)
617 result = fclose(fp->file);
623 // routines to read basic data types from CFILE's. Put here to
624 // simplify mac/pc reading from cfiles.
626 int cfile_read_int(CFILE *file)
630 if (cfread( &i, sizeof(i), 1, file) != 1)
631 Error( "Error reading int in cfile_read_int()" );
637 short cfile_read_short(CFILE *file)
641 if (cfread( &s, sizeof(s), 1, file) != 1)
642 Error( "Error reading short in cfile_read_short()" );
648 sbyte cfile_read_byte(CFILE *file)
652 if (cfread( &b, sizeof(b), 1, file) != 1)
653 Error( "Error reading byte in cfile_read_byte()" );
658 fix cfile_read_fix(CFILE *file)
662 if (cfread( &f, sizeof(f), 1, file) != 1)
663 Error( "Error reading fix in cfile_read_fix()" );
665 f = (fix)INTEL_INT((int)f);
669 fixang cfile_read_fixang(CFILE *file)
673 if (cfread(&f, 2, 1, file) != 1)
674 Error("Error reading fixang in cfile_read_fixang()");
676 f = (fixang) INTEL_SHORT((int) f);
680 void cfile_read_vector(vms_vector *v, CFILE *file)
682 v->x = cfile_read_fix(file);
683 v->y = cfile_read_fix(file);
684 v->z = cfile_read_fix(file);
687 void cfile_read_angvec(vms_angvec *v, CFILE *file)
689 v->p = cfile_read_fixang(file);
690 v->b = cfile_read_fixang(file);
691 v->h = cfile_read_fixang(file);
694 void cfile_read_matrix(vms_matrix *m,CFILE *file)
696 cfile_read_vector(&m->rvec,file);
697 cfile_read_vector(&m->uvec,file);
698 cfile_read_vector(&m->fvec,file);
702 void cfile_read_string(char *buf, int n, CFILE *file)
707 c = (char)cfile_read_byte(file);
717 // equivalent write functions of above read functions follow
719 int cfile_write_int(int i, CFILE *file)
722 return cfwrite(&i, sizeof(i), 1, file);
726 int cfile_write_short(short s, CFILE *file)
729 return cfwrite(&s, sizeof(s), 1, file);
733 int cfile_write_byte(sbyte b, CFILE *file)
735 return cfwrite(&b, sizeof(b), 1, file);
739 int cfile_write_string(char *buf, CFILE *file)
743 if ((!buf) || (buf && !buf[0]))
744 return cfile_write_byte(0, file);
747 if (!cfwrite(buf, len, 1, file))
750 return cfile_write_byte(0, file); // write out NULL termination