]> icculus.org git repositories - btb/d2x.git/blob - cfile/cfile.c
update raw_position in cfwrite
[btb/d2x.git] / cfile / cfile.c
1 /* $Id: cfile.c,v 1.15 2003-07-25 05:00:12 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 (fp->lib_offset == 0) // This is not an archived file
625         {
626                 t = fgets(buf, n, fp->file);
627                 fp->raw_position = ftell(fp->file);
628                 return t;
629         }
630
631         for (i=0; i<n-1; i++ ) {
632                 do {
633                         if (fp->raw_position >= fp->size ) {
634                                 *buf = 0;
635                                 return NULL;
636                         }
637                         c = fgetc( fp->file );
638                         fp->raw_position++;
639                         if (c == 0 || c == 10)        // Unix line ending
640                                 break;
641                         if (c == 13) {      // Mac or DOS line ending
642                                 int c1;
643
644                                 c1 = fgetc( fp->file );
645                                 fseek( fp->file, -1, SEEK_CUR);
646                                 if ( c1 == 10 ) // DOS line ending
647                                         continue;
648                                 else            // Mac line ending
649                                         break;
650                         }
651                 } while ( c == 13 );
652                 if ( c == 13 )  // because cr-lf is a bad thing on the mac
653                         c = '\n';   // and anyway -- 0xod is CR on mac, not 0x0a
654                 *buf++ = c;
655                 if ( c=='\n' ) break;
656         }
657         *buf++ = 0;
658         return  t;
659 }
660
661 size_t cfread( void * buf, size_t elsize, size_t nelem, CFILE * fp ) 
662 {
663         unsigned int i, size;
664
665         size = elsize * nelem;
666         if ( size < 1 ) return 0;
667
668         i = fread ( buf, 1, size, fp->file );
669         fp->raw_position += i;
670         return i/elsize;
671 }
672
673
674 int cftell( CFILE *fp ) 
675 {
676         return fp->raw_position;
677 }
678
679 int cfseek( CFILE *fp, long int offset, int where )
680 {
681         int c, goal_position;
682
683         switch( where ) {
684         case SEEK_SET:
685                 goal_position = offset;
686                 break;
687         case SEEK_CUR:
688                 goal_position = fp->raw_position+offset;
689                 break;
690         case SEEK_END:
691                 goal_position = fp->size+offset;
692                 break;
693         default:
694                 return 1;
695         }       
696         c = fseek( fp->file, fp->lib_offset + goal_position, SEEK_SET );
697         fp->raw_position = ftell(fp->file)-fp->lib_offset;
698         return c;
699 }
700
701 int cfclose(CFILE *fp)
702 {
703         int result;
704
705         result = fclose(fp->file);
706         d_free(fp);
707
708         return result;
709 }
710
711 // routines to read basic data types from CFILE's.  Put here to
712 // simplify mac/pc reading from cfiles.
713
714 int cfile_read_int(CFILE *file)
715 {
716         int32_t i;
717
718         if (cfread( &i, sizeof(i), 1, file) != 1)
719                 Error( "Error reading int in cfile_read_int()" );
720
721         i = INTEL_INT(i);
722         return i;
723 }
724
725 short cfile_read_short(CFILE *file)
726 {
727         int16_t s;
728
729         if (cfread( &s, sizeof(s), 1, file) != 1)
730                 Error( "Error reading short in cfile_read_short()" );
731
732         s = INTEL_SHORT(s);
733         return s;
734 }
735
736 byte cfile_read_byte(CFILE *file)
737 {
738         byte b;
739
740         if (cfread( &b, sizeof(b), 1, file) != 1)
741                 Error( "Error reading byte in cfile_read_byte()" );
742
743         return b;
744 }
745
746 fix cfile_read_fix(CFILE *file)
747 {
748         fix f;
749
750         if (cfread( &f, sizeof(f), 1, file) != 1)
751                 Error( "Error reading fix in cfile_read_fix()" );
752
753         f = (fix)INTEL_INT((int)f);
754         return f;
755 }
756
757 fixang cfile_read_fixang(CFILE *file)
758 {
759         fixang f;
760
761         if (cfread(&f, 2, 1, file) != 1)
762                 Error("Error reading fixang in cfile_read_fixang()");
763
764         f = (fixang) INTEL_SHORT((int) f);
765         return f;
766 }
767
768 void cfile_read_vector(vms_vector *v, CFILE *file)
769 {
770         v->x = cfile_read_fix(file);
771         v->y = cfile_read_fix(file);
772         v->z = cfile_read_fix(file);
773 }
774
775 void cfile_read_angvec(vms_angvec *v, CFILE *file)
776 {
777         v->p = cfile_read_fixang(file);
778         v->b = cfile_read_fixang(file);
779         v->h = cfile_read_fixang(file);
780 }
781
782 void cfile_read_matrix(vms_matrix *m,CFILE *file)
783 {
784         cfile_read_vector(&m->rvec,file);
785         cfile_read_vector(&m->uvec,file);
786         cfile_read_vector(&m->fvec,file);
787 }
788
789
790 void cfile_read_string(char *buf, int n, CFILE *file)
791 {
792         char c;
793
794         do {
795                 c = (char)cfile_read_byte(file);
796                 if (n > 0)
797                 {
798                         *buf++ = c;
799                         n--;
800                 }
801         } while (c != 0);
802 }
803
804
805 // equivalent write functions of above read functions follow
806
807 int cfile_write_int(int i, CFILE *file)
808 {
809         i = INTEL_INT(i);
810         return cfwrite(&i, sizeof(i), 1, file);
811 }
812
813
814 int cfile_write_short(short s, CFILE *file)
815 {
816         s = INTEL_SHORT(s);
817         return cfwrite(&s, sizeof(s), 1, file);
818 }
819
820
821 int cfile_write_byte(byte b, CFILE *file)
822 {
823         return cfwrite(&b, sizeof(b), 1, file);
824 }
825
826
827 int cfile_write_string(char *buf, CFILE *file)
828 {
829         int len;
830
831         if ((!buf) || (buf && !buf[0]))
832                 return cfile_write_byte(0, file);
833
834         len = strlen(buf);
835         if (!cfwrite(buf, len, 1, file))
836                 return 0;
837
838         return cfile_write_byte(0, file);   // write out NULL termination
839 }