revert to always using custom fgets
[btb/d2x.git] / cfile / cfile.c
1 /* $Id: cfile.c,v 1.16 2003-08-02 07:17:43 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 /*
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 #include <sys/stat.h>
129
130 #include "pstypes.h"
131 #include "u_mem.h"
132 #include "strutil.h"
133 #include "d_io.h"
134 #include "error.h"
135 #include "cfile.h"
136 #include "byteswap.h"
137
138 struct CFILE {
139         FILE    *file;
140         int     size;
141         int     lib_offset;
142         int     raw_position;
143 };
144
145 typedef struct hogfile {
146         char    name[13];
147         int     offset;
148         int     length;
149 } hogfile;
150
151 #define MAX_HOGFILES 300
152
153 hogfile HogFiles[MAX_HOGFILES];
154 char Hogfile_initialized = 0;
155 int Num_hogfiles = 0;
156 char HogFilename[64];
157
158 hogfile D1HogFiles[MAX_HOGFILES];
159 char D1Hogfile_initialized = 0;
160 int D1Num_hogfiles = 0;
161 char D1HogFilename[64];
162
163 hogfile AltHogFiles[MAX_HOGFILES];
164 char AltHogfile_initialized = 0;
165 int AltNum_hogfiles = 0;
166 char AltHogFilename[64];
167
168 char AltHogDir[64];
169 char AltHogdir_initialized = 0;
170
171 // routine to take a DOS path and turn it into a macintosh
172 // pathname.  This routine is based on the fact that we should
173 // see a \ character in the dos path.  The sequence .\ a tthe
174 // beginning of a path is turned into a :
175
176 #ifdef MACINTOSH
177 void macify_dospath(char *dos_path, char *mac_path)
178 {
179         char *p;
180
181         if (!strncmp(dos_path, ".\\", 2)) {
182                 strcpy(mac_path, ":");
183                 strcat(mac_path, &(dos_path[2]) );
184         } else
185                 strcpy(mac_path, dos_path);
186
187         while ( (p = strchr(mac_path, '\\')) != NULL)
188                 *p = ':';
189
190 }
191 #endif
192
193 void cfile_use_alternate_hogdir( char * path )
194 {
195         if ( path )     {
196                 strcpy( AltHogDir, path );
197                 AltHogdir_initialized = 1;
198         } else {
199                 AltHogdir_initialized = 0;
200         }
201 }
202
203 //in case no one installs one
204 int default_error_counter=0;
205
206 //ptr to counter of how many critical errors
207 int *critical_error_counter_ptr=&default_error_counter;
208
209 //tell cfile about your critical error counter
210 void cfile_set_critical_error_counter_ptr(int *ptr)
211 {
212         critical_error_counter_ptr = ptr;
213
214 }
215
216
217 FILE * cfile_get_filehandle( char * filename, char * mode )
218 {
219         FILE * fp;
220         char temp[128];
221
222         *critical_error_counter_ptr = 0;
223         fp = fopen( filename, mode );
224         if ( fp && *critical_error_counter_ptr )        {
225                 fclose(fp);
226                 fp = NULL;
227         }
228         if ( (fp==NULL) && (AltHogdir_initialized) )    {
229                 strcpy( temp, AltHogDir );
230                 strcat( temp, "/");
231                 strcat( temp, filename );
232                 *critical_error_counter_ptr = 0;
233                 fp = fopen( temp, mode );
234                 if ( fp && *critical_error_counter_ptr )        {
235                         fclose(fp);
236                         fp = NULL;
237                 }
238         }
239         return fp;
240 }
241
242 //returns 1 if file loaded with no errors
243 int cfile_init_hogfile(char *fname, hogfile * hog_files, int * nfiles )
244 {
245         char id[4];
246         FILE * fp;
247         int i, len;
248
249         *nfiles = 0;
250
251         fp = cfile_get_filehandle( fname, "rb" );
252         if ( fp == NULL ) return 0;
253
254         fread( id, 3, 1, fp );
255         if ( strncmp( id, "DHF", 3 ) )  {
256                 fclose(fp);
257                 return 0;
258         }
259
260         while( 1 )
261         {
262                 if ( *nfiles >= MAX_HOGFILES ) {
263                         fclose(fp);
264                         Error( "HOGFILE is limited to %d files.\n",  MAX_HOGFILES );
265                 }
266                 i = fread( hog_files[*nfiles].name, 13, 1, fp );
267                 if ( i != 1 )   {               //eof here is ok
268                         fclose(fp);
269                         return 1;
270                 }
271                 i = fread( &len, 4, 1, fp );
272                 if ( i != 1 )   {
273                         fclose(fp);
274                         return 0;
275                 }
276                 hog_files[*nfiles].length = INTEL_INT(len);
277                 hog_files[*nfiles].offset = ftell( fp );
278                 *nfiles = (*nfiles) + 1;
279                 // Skip over
280                 i = fseek( fp, INTEL_INT(len), SEEK_CUR );
281         }
282 }
283
284 //Specify the name of the hogfile.  Returns 1 if hogfile found & had files
285 int cfile_init(char *hogname)
286 {
287         #ifdef MACINTOSH
288         char mac_path[255];
289
290         macify_dospath(hogname, mac_path);
291         #endif
292
293         Assert(Hogfile_initialized == 0);
294
295         #ifndef MACINTOSH
296         if (cfile_init_hogfile(hogname, HogFiles, &Num_hogfiles )) {
297                 strcpy( HogFilename, hogname );
298         #else
299         if (cfile_init_hogfile(mac_path, HogFiles, &Num_hogfiles )) {
300                 strcpy( HogFilename, mac_path );
301         #endif
302                 Hogfile_initialized = 1;
303                 return 1;
304         }
305         else
306                 return 0;       //not loaded!
307 }
308
309
310 int cfile_size(char *hogname)
311 {
312         CFILE *fp;
313         struct stat statbuf;
314
315         fp = cfopen(hogname, "rb");
316         if (fp == NULL)
317                 return -1;
318         fstat(fileno(fp->file), &statbuf);
319         cfclose(fp);
320         return statbuf.st_size;
321 }
322
323 /*
324  * return handle for file called "name", embedded in one of the hogfiles
325  */
326 FILE * cfile_find_libfile(char * name, int * length)
327 {
328         FILE * fp;
329         int i;
330
331         if ( AltHogfile_initialized )   {
332                 for (i=0; i<AltNum_hogfiles; i++ )      {
333                         if ( !stricmp( AltHogFiles[i].name, name ))     {
334                                 fp = cfile_get_filehandle( AltHogFilename, "rb" );
335                                 if ( fp == NULL ) return NULL;
336                                 fseek( fp,  AltHogFiles[i].offset, SEEK_SET );
337                                 *length = AltHogFiles[i].length;
338                                 return fp;
339                         }
340                 }
341         }
342
343         if ( !Hogfile_initialized )     {
344                 //@@cfile_init_hogfile( "DESCENT2.HOG", HogFiles, &Num_hogfiles );
345                 //@@Hogfile_initialized = 1;
346
347                 //Int3();       //hogfile ought to be initialized
348         }
349
350         for (i=0; i<Num_hogfiles; i++ ) {
351                 if ( !stricmp( HogFiles[i].name, name ))        {
352                         fp = cfile_get_filehandle( HogFilename, "rb" );
353                         if ( fp == NULL ) return NULL;
354                         fseek( fp,  HogFiles[i].offset, SEEK_SET );
355                         *length = HogFiles[i].length;
356                         return fp;
357                 }
358         }
359
360         if (D1Hogfile_initialized)      {
361                 for (i = 0; i < D1Num_hogfiles; i++) {
362                         if (!stricmp(D1HogFiles[i].name, name)) {
363                                 fp = cfile_get_filehandle(D1HogFilename, "rb");
364                                 if (fp == NULL) return NULL;
365                                 fseek(fp,  D1HogFiles[i].offset, SEEK_SET);
366                                 *length = D1HogFiles[i].length;
367                                 return fp;
368                         }
369                 }
370         }
371
372         return NULL;
373 }
374
375 int cfile_use_alternate_hogfile( char * name )
376 {
377         if ( name )     {
378                 #ifdef MACINTOSH
379                 char mac_path[255];
380
381                 macify_dospath(name, mac_path);
382                 strcpy( AltHogFilename, mac_path);
383                 #else
384                 strcpy( AltHogFilename, name );
385                 #endif
386                 cfile_init_hogfile( AltHogFilename, AltHogFiles, &AltNum_hogfiles );
387                 AltHogfile_initialized = 1;
388                 return (AltNum_hogfiles > 0);
389         } else {
390                 AltHogfile_initialized = 0;
391                 return 1;
392         }
393 }
394
395 int cfile_use_descent1_hogfile( char * name )
396 {
397         if (name)       {
398 #ifdef MACINTOSH
399                 char mac_path[255];
400
401                 macify_dospath(name, mac_path);
402                 strcpy(D1HogFilename, mac_path);
403 #else
404                 strcpy(D1HogFilename, name);
405 #endif
406                 cfile_init_hogfile(D1HogFilename, D1HogFiles, &D1Num_hogfiles);
407                 D1Hogfile_initialized = 1;
408                 return (D1Num_hogfiles > 0);
409         } else {
410                 D1Hogfile_initialized = 0;
411                 return 1;
412         }
413 }
414
415
416 // cfeof() Tests for end-of-file on a stream
417 //
418 // returns a nonzero value after the first read operation that attempts to read
419 // past the end of the file. It returns 0 if the current position is not end of file.
420 // There is no error return.
421
422 int cfeof(CFILE *cfile)
423 {
424         Assert(cfile != NULL);
425
426         Assert(cfile->file != NULL);
427
428     return (cfile->raw_position >= cfile->size);
429 }
430
431
432 int cferror(CFILE *cfile)
433 {
434         return ferror(cfile->file);
435 }
436
437
438 int cfexist( char * filename )
439 {
440         int length;
441         FILE *fp;
442
443
444         if (filename[0] != '\x01')
445                 fp = cfile_get_filehandle( filename, "rb" );            // Check for non-hog file first...
446         else {
447                 fp = NULL;              //don't look in dir, only in hogfile
448                 filename++;
449         }
450
451         if ( fp )       {
452                 fclose(fp);
453                 return 1;
454         }
455
456         fp = cfile_find_libfile(filename, &length );
457         if ( fp )       {
458                 fclose(fp);
459                 return 2;               // file found in hog
460         }
461
462         return 0;               // Couldn't find it.
463 }
464
465
466 // Deletes a file.
467 int cfile_delete(char *filename)
468 {
469         return remove(filename);
470 }
471
472
473 // Rename a file.
474 int cfile_rename(char *oldname, char *newname)
475 {
476         return rename(oldname, newname);
477 }
478
479
480 // Make a directory.
481 int cfile_mkdir(char *pathname)
482 {
483 #if defined(__WINDOWS__) || defined(__MINGW32__)
484         return mkdir(pathname);
485 #else
486         return mkdir(pathname, 0755);
487 #endif
488 }
489
490
491 CFILE * cfopen(char * filename, char * mode )
492 {
493         int length;
494         FILE * fp;
495         CFILE *cfile;
496
497         if (filename[0] != '\x01') {
498                 #ifdef MACINTOSH
499                 char mac_path[255];
500
501                 macify_dospath(filename, mac_path);
502                 fp = cfile_get_filehandle( mac_path, mode);
503                 #else
504                 fp = cfile_get_filehandle( filename, mode );            // Check for non-hog file first...
505                 #endif
506         } else {
507                 fp = NULL;              //don't look in dir, only in hogfile
508                 filename++;
509         }
510
511         if ( !fp ) {
512                 fp = cfile_find_libfile(filename, &length );
513                 if ( !fp )
514                         return NULL;            // No file found
515                 if (stricmp(mode, "rb"))
516                         Error("mode must be rb for files in hog.\n");
517                 cfile = d_malloc ( sizeof(CFILE) );
518                 if ( cfile == NULL ) {
519                         fclose(fp);
520                         return NULL;
521                 }
522                 cfile->file = fp;
523                 cfile->size = length;
524                 cfile->lib_offset = ftell( fp );
525                 cfile->raw_position = 0;
526                 return cfile;
527         } else {
528                 cfile = d_malloc ( sizeof(CFILE) );
529                 if ( cfile == NULL ) {
530                         fclose(fp);
531                         return NULL;
532                 }
533                 cfile->file = fp;
534                 cfile->size = filelength( fileno(fp) );
535                 cfile->lib_offset = 0;
536                 cfile->raw_position = 0;
537                 return cfile;
538         }
539 }
540
541 int cfilelength( CFILE *fp )
542 {
543         return fp->size;
544 }
545
546
547 // cfwrite() writes to the file
548 //
549 // returns:   number of full elements actually written
550 //
551 //
552 int cfwrite(void *buf, int elsize, int nelem, CFILE *cfile)
553 {
554         int items_written;
555
556         Assert(cfile != NULL);
557         Assert(buf != NULL);
558         Assert(elsize > 0);
559
560         Assert(cfile->file != NULL);
561         Assert(cfile->lib_offset == 0);
562
563         items_written = fwrite(buf, elsize, nelem, cfile->file);
564         cfile->raw_position = ftell(cfile->file);
565
566         return items_written;
567 }
568
569
570 // cfputc() writes a character to a file
571 //
572 // returns:   success ==> returns character written
573 //            error   ==> EOF
574 //
575 int cfputc(int c, CFILE *cfile)
576 {
577         Assert(cfile != NULL);
578
579         Assert(cfile->file != NULL);
580         Assert(cfile->lib_offset == 0);
581
582         return fputc(c, cfile->file);
583 }
584
585
586 int cfgetc( CFILE * fp )
587 {
588         int c;
589
590         if (fp->raw_position >= fp->size ) return EOF;
591
592         c = getc( fp->file );
593         if (c!=EOF)
594                 fp->raw_position++;
595
596 //      Assert( fp->raw_position==(ftell(fp->file)-fp->lib_offset) );
597
598         return c;
599 }
600
601
602 // cfputs() writes a string to a file
603 //
604 // returns:   success ==> non-negative value
605 //            error   ==> EOF
606 //
607 int cfputs(char *str, CFILE *cfile)
608 {
609         Assert(cfile != NULL);
610         Assert(str != NULL);
611
612         Assert(cfile->file != NULL);
613
614         return fputs(str, cfile->file);
615 }
616
617
618 char * cfgets( char * buf, size_t n, CFILE * fp )
619 {
620         char * t = buf;
621         int i;
622         int c;
623
624 #if 0 // don't use the standard fgets, because it will only handle the native line-ending style
625         if (fp->lib_offset == 0) // This is not an archived file
626         {
627                 t = fgets(buf, n, fp->file);
628                 fp->raw_position = ftell(fp->file);
629                 return t;
630         }
631 #endif
632
633         for (i=0; i<n-1; i++ ) {
634                 do {
635                         if (fp->raw_position >= fp->size ) {
636                                 *buf = 0;
637                                 return NULL;
638                         }
639                         c = fgetc( fp->file );
640                         fp->raw_position++;
641                         if (c == 0 || c == 10)        // Unix line ending
642                                 break;
643                         if (c == 13) {      // Mac or DOS line ending
644                                 int c1;
645
646                                 c1 = fgetc( fp->file );
647                                 fseek( fp->file, -1, SEEK_CUR);
648                                 if ( c1 == 10 ) // DOS line ending
649                                         continue;
650                                 else            // Mac line ending
651                                         break;
652                         }
653                 } while ( c == 13 );
654                 if ( c == 13 )  // because cr-lf is a bad thing on the mac
655                         c = '\n';   // and anyway -- 0xod is CR on mac, not 0x0a
656                 *buf++ = c;
657                 if ( c=='\n' ) break;
658         }
659         *buf++ = 0;
660         return  t;
661 }
662
663 size_t cfread( void * buf, size_t elsize, size_t nelem, CFILE * fp ) 
664 {
665         unsigned int i, size;
666
667         size = elsize * nelem;
668         if ( size < 1 ) return 0;
669
670         i = fread ( buf, 1, size, fp->file );
671         fp->raw_position += i;
672         return i/elsize;
673 }
674
675
676 int cftell( CFILE *fp ) 
677 {
678         return fp->raw_position;
679 }
680
681 int cfseek( CFILE *fp, long int offset, int where )
682 {
683         int c, goal_position;
684
685         switch( where ) {
686         case SEEK_SET:
687                 goal_position = offset;
688                 break;
689         case SEEK_CUR:
690                 goal_position = fp->raw_position+offset;
691                 break;
692         case SEEK_END:
693                 goal_position = fp->size+offset;
694                 break;
695         default:
696                 return 1;
697         }       
698         c = fseek( fp->file, fp->lib_offset + goal_position, SEEK_SET );
699         fp->raw_position = ftell(fp->file)-fp->lib_offset;
700         return c;
701 }
702
703 int cfclose(CFILE *fp)
704 {
705         int result;
706
707         result = fclose(fp->file);
708         d_free(fp);
709
710         return result;
711 }
712
713 // routines to read basic data types from CFILE's.  Put here to
714 // simplify mac/pc reading from cfiles.
715
716 int cfile_read_int(CFILE *file)
717 {
718         int32_t i;
719
720         if (cfread( &i, sizeof(i), 1, file) != 1)
721                 Error( "Error reading int in cfile_read_int()" );
722
723         i = INTEL_INT(i);
724         return i;
725 }
726
727 short cfile_read_short(CFILE *file)
728 {
729         int16_t s;
730
731         if (cfread( &s, sizeof(s), 1, file) != 1)
732                 Error( "Error reading short in cfile_read_short()" );
733
734         s = INTEL_SHORT(s);
735         return s;
736 }
737
738 byte cfile_read_byte(CFILE *file)
739 {
740         byte b;
741
742         if (cfread( &b, sizeof(b), 1, file) != 1)
743                 Error( "Error reading byte in cfile_read_byte()" );
744
745         return b;
746 }
747
748 fix cfile_read_fix(CFILE *file)
749 {
750         fix f;
751
752         if (cfread( &f, sizeof(f), 1, file) != 1)
753                 Error( "Error reading fix in cfile_read_fix()" );
754
755         f = (fix)INTEL_INT((int)f);
756         return f;
757 }
758
759 fixang cfile_read_fixang(CFILE *file)
760 {
761         fixang f;
762
763         if (cfread(&f, 2, 1, file) != 1)
764                 Error("Error reading fixang in cfile_read_fixang()");
765
766         f = (fixang) INTEL_SHORT((int) f);
767         return f;
768 }
769
770 void cfile_read_vector(vms_vector *v, CFILE *file)
771 {
772         v->x = cfile_read_fix(file);
773         v->y = cfile_read_fix(file);
774         v->z = cfile_read_fix(file);
775 }
776
777 void cfile_read_angvec(vms_angvec *v, CFILE *file)
778 {
779         v->p = cfile_read_fixang(file);
780         v->b = cfile_read_fixang(file);
781         v->h = cfile_read_fixang(file);
782 }
783
784 void cfile_read_matrix(vms_matrix *m,CFILE *file)
785 {
786         cfile_read_vector(&m->rvec,file);
787         cfile_read_vector(&m->uvec,file);
788         cfile_read_vector(&m->fvec,file);
789 }
790
791
792 void cfile_read_string(char *buf, int n, CFILE *file)
793 {
794         char c;
795
796         do {
797                 c = (char)cfile_read_byte(file);
798                 if (n > 0)
799                 {
800                         *buf++ = c;
801                         n--;
802                 }
803         } while (c != 0);
804 }
805
806
807 // equivalent write functions of above read functions follow
808
809 int cfile_write_int(int i, CFILE *file)
810 {
811         i = INTEL_INT(i);
812         return cfwrite(&i, sizeof(i), 1, file);
813 }
814
815
816 int cfile_write_short(short s, CFILE *file)
817 {
818         s = INTEL_SHORT(s);
819         return cfwrite(&s, sizeof(s), 1, file);
820 }
821
822
823 int cfile_write_byte(byte b, CFILE *file)
824 {
825         return cfwrite(&b, sizeof(b), 1, file);
826 }
827
828
829 int cfile_write_string(char *buf, CFILE *file)
830 {
831         int len;
832
833         if ((!buf) || (buf && !buf[0]))
834                 return cfile_write_byte(0, file);
835
836         len = strlen(buf);
837         if (!cfwrite(buf, len, 1, file))
838                 return 0;
839
840         return cfile_write_byte(0, file);   // write out NULL termination
841 }