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