]> icculus.org git repositories - taylor/freespace2.git/blob - src/cfile/cfile.cpp
implemented two assembly stubs.
[taylor/freespace2.git] / src / cfile / cfile.cpp
1 /*
2  * $Logfile: /Freespace2/code/CFile/cfile.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Utilities for operating on files
8  *
9  * $Log$
10  * Revision 1.3  2002/05/28 08:52:03  relnev
11  * implemented two assembly stubs.
12  *
13  * cleaned up a few warnings.
14  *
15  * added a little demo hackery to make it progress a little farther.
16  *
17  * Revision 1.2  2002/05/28 06:28:20  theoddone33
18  * Filesystem mods, actually reads some data files now
19  *
20  * Revision 1.1.1.1  2002/05/03 03:28:08  root
21  * Initial import.
22  *
23  * 
24  * 20    9/08/99 10:01p Dave
25  * Make sure game won't run in a drive's root directory. Make sure
26  * standalone routes suqad war messages properly to the host.
27  * 
28  * 19    9/08/99 12:03a Dave
29  * Make squad logos render properly in D3D all the time. Added intel anim
30  * directory.
31  * 
32  * 18    9/06/99 2:35p Dave
33  * Rename briefing and debriefing voice direcrory paths properly.
34  * 
35  * 17    9/03/99 1:31a Dave
36  * CD checking by act. Added support to play 2 cutscenes in a row
37  * seamlessly. Fixed super low level cfile bug related to files in the
38  * root directory of a CD. Added cheat code to set campaign mission # in
39  * main hall.
40  * 
41  * 16    8/31/99 9:46a Dave
42  * Support for new cfile cbanims directory.
43  * 
44  * 15    8/06/99 11:20a Johns
45  * Don't call game_cd_changed() in a demo or multiplayer beta build.
46  * 
47  * 14    6/08/99 2:33p Dave
48  * Fixed release build warning. Put in hud config stuff.
49  * 
50  * 13    5/19/99 4:07p Dave
51  * Moved versioning code into a nice isolated common place. Fixed up
52  * updating code on the pxo screen. Fixed several stub problems.
53  * 
54  * 12    4/30/99 12:18p Dave
55  * Several minor bug fixes.
56  * 
57  * 11    4/07/99 5:54p Dave
58  * Fixes for encryption in updatelauncher.
59  * 
60  * 10    3/28/99 5:58p Dave
61  * Added early demo code. Make objects move. Nice and framerate
62  * independant, but not much else. Don't use yet unless you're me :)
63  * 
64  * 9     2/22/99 10:31p Andsager
65  * Get rid of unneeded includes.
66  * 
67  * 8     1/12/99 3:15a Dave
68  * Barracks screen support for selecting squad logos. We need real artwork
69  * :)
70  * 
71  * 7     11/30/98 1:07p Dave
72  * 16 bit conversion, first run.
73  * 
74  * 6     10/29/98 10:41a Dave
75  * Change the way cfile initializes exe directory.
76  * 
77  * 5     10/13/98 9:19a Andsager
78  * Add localization support to cfile.  Optional parameter with cfopen that
79  * looks for localized files.
80  * 
81  * 4     10/12/98 9:54a Dave
82  * Fixed a few file organization things.
83  * 
84  * 3     10/07/98 6:27p Dave
85  * Globalized mission and campaign file extensions. Removed Silent Threat
86  * special code. Moved \cache \players and \multidata into the \data
87  * directory.
88  * 
89  * 2     10/07/98 10:52a Dave
90  * Initial checkin.
91  * 
92  * 1     10/07/98 10:48a Dave
93  * 
94  * 110   9/17/98 10:31a Hoffoss
95  * Changed code to use location of executable as root rather than relying
96  * on the cwd being set correctly.  This is most helpful in the case of
97  * Fred launching due to the user double-clicking on an .fsm file in
98  * explorer or something (where the cwd is probably the location of the
99  * .fsm file).
100  * 
101  * 109   9/11/98 4:14p Dave
102  * Fixed file checksumming of < file_size. Put in more verbose kicking and
103  * PXO stats store reporting.
104  * 
105  * 108   9/09/98 5:53p Dave
106  * Put in new tracker packets in API. Change cfile to be able to checksum
107  * portions of a file.
108  * 
109  * 107   9/04/98 3:51p Dave
110  * Put in validated mission updating and application during stats
111  * updating.
112  * 
113  * 106   8/31/98 2:06p Dave
114  * Make cfile sort the ordering or vp files. Added support/checks for
115  * recognizing "mission disk" players.
116  * 
117  * 105   8/20/98 5:30p Dave
118  * Put in handy multiplayer logfile system. Now need to put in useful
119  * applications of it all over the code.
120  * 
121  * 104   8/12/98 4:53p Dave
122  * Put in 32 bit checksumming for PXO missions. No validation on the
123  * actual tracker yet, though.
124  * 
125  * 103   6/17/98 9:30a Allender
126  * fixed red alert replay stats clearing problem
127  * 
128  * 102   6/05/98 9:46a Lawrance
129  * check for CD change on cfopen()
130  * 
131  * 101   5/19/98 1:19p Allender
132  * new low level reliable socket reading code.  Make all missions/campaign
133  * load/save to data missions folder (i.e. we are rid of the player
134  * missions folder)
135  * 
136  * 100   5/13/98 10:22p John
137  * Added cfile functions to read/write rle compressed blocks of data.
138  * Made palman use it for .clr files.  Made alphacolors calculate on the
139  * fly rather than caching to/from disk.
140  * 
141  * 99    5/11/98 10:59a John
142  * Moved the low-level file reading code into cfilearchive.cpp.
143  * 
144  * 98    5/06/98 5:30p John
145  * Removed unused cfilearchiver.  Removed/replaced some unused/little used
146  * graphics functions, namely gradient_h and _v and pixel_sp.   Put in new
147  * DirectX header files and libs that fixed the Direct3D alpha blending
148  * problems.
149  * 
150  * 97    5/03/98 11:53a John
151  * Fixed filename case mangling.
152  * 
153  * 96    5/03/98 8:33a John
154  * Made sure there weren't any more broken function lurking around like
155  * the one Allender fixed.  
156  * 
157  * 95    5/02/98 11:05p Allender
158  * fix cfilelength for pack files
159  * 
160  *
161  * $NoKeywords: $
162  */
163 #define _CFILE_INTERNAL 
164
165 #include <stdlib.h>
166 #include <string.h>
167 #include <stdio.h>
168 #include <errno.h>
169 #ifndef PLAT_UNIX
170 #include <io.h>
171 #include <direct.h>
172 #include <windows.h>
173 #include <winbase.h>            /* needed for memory mapping of file functions */
174 #else
175 #include <unistd.h>
176 #include <sys/stat.h>
177 #include <sys/types.h>
178 #endif
179
180 #include "pstypes.h"
181 #include "cfile.h"
182 #include "encrypt.h"
183 //#include "outwnd.h"
184 //#include "vecmat.h"
185 //#include "timer.h"
186 #include "cfilesystem.h"
187 #include "cfilearchive.h"
188 #include "osapi.h"
189
190 char Cfile_root_dir[CFILE_ROOT_DIRECTORY_LEN] = "";
191
192 // During cfile_init, verify that Pathtypes[n].index == n for each item
193 // Each path must have a valid parent that can be tracable all the way back to the root 
194 // so that we can create directories when we need to.
195 //
196 cf_pathtype Pathtypes[CF_MAX_PATH_TYPES]  = {
197         // What type this is          Path                             Extensions              Parent type
198         { CF_TYPE_INVALID,                              NULL,                                                                           NULL,                                                   CF_TYPE_INVALID },
199         // Root must be index 1!!       
200         { CF_TYPE_ROOT,                                 "",                                                                             ".mve",                                                 CF_TYPE_ROOT    },
201         { CF_TYPE_DATA,                                 "Data",                                                                 ".cfg .log .txt",                       CF_TYPE_ROOT    },
202 #ifdef PLAT_UNIX
203         { CF_TYPE_MAPS,                                 "Data/Maps",                                                    ".pcx .ani .tga",                       CF_TYPE_DATA    },
204         { CF_TYPE_TEXT,                                 "Data/Text",                                                    ".txt .net",                            CF_TYPE_DATA    },
205         { CF_TYPE_MISSIONS,                             "Data/Missions",                                                ".fs2 .fc2 .ntl .ssv",  CF_TYPE_DATA    },
206         { CF_TYPE_MODELS,                                       "Data/Models",                                          ".pof",                                         CF_TYPE_DATA    },
207         { CF_TYPE_TABLES,                                       "Data/Tables",                                          ".tbl",                                         CF_TYPE_DATA    },
208         { CF_TYPE_SOUNDS,                                       "Data/Sounds",                                          ".wav",                                         CF_TYPE_DATA    },
209         { CF_TYPE_SOUNDS_8B22K,                 "Data/Sounds/8b22k",                            ".wav",                                         CF_TYPE_SOUNDS  },
210         { CF_TYPE_SOUNDS_16B11K,                "Data/Sounds/16b11k",                           ".wav",                                         CF_TYPE_SOUNDS  },
211         { CF_TYPE_VOICE,                                        "Data/Voice",                                                   "",                                                     CF_TYPE_DATA    },
212         { CF_TYPE_VOICE_BRIEFINGS,              "Data/Voice/Briefing",                  ".wav",                                         CF_TYPE_VOICE   },
213         { CF_TYPE_VOICE_CMD_BRIEF,              "Data/Voice/Command_briefings",".wav",                                          CF_TYPE_VOICE   },
214         { CF_TYPE_VOICE_DEBRIEFINGS,    "Data/Voice/Debriefing",                        ".wav",                                         CF_TYPE_VOICE   },
215         { CF_TYPE_VOICE_PERSONAS,               "Data/Voice/Personas",                  ".wav",                                         CF_TYPE_VOICE   },
216         { CF_TYPE_VOICE_SPECIAL,                "Data/Voice/Special",                           ".wav",                                         CF_TYPE_VOICE   },
217         { CF_TYPE_VOICE_TRAINING,               "Data/Voice/Training",                  ".wav",                                         CF_TYPE_VOICE   },
218         { CF_TYPE_MUSIC,                                        "Data/Music",                                                   ".wav",                                         CF_TYPE_VOICE   },
219         { CF_TYPE_MOVIES,                                       "Data/Movies",                                          ".mve .msb",                            CF_TYPE_DATA    },
220         { CF_TYPE_INTERFACE,                            "Data/Interface",                                       ".pcx .ani .tga",                       CF_TYPE_DATA    },
221         { CF_TYPE_FONT,                                 "Data/Fonts",                                                   ".vf",                                          CF_TYPE_DATA    },
222         { CF_TYPE_EFFECTS,                              "Data/Effects",                                         ".ani .pcx .neb .tga",  CF_TYPE_DATA    },
223         { CF_TYPE_HUD,                                          "Data/Hud",                                                     ".ani .pcx .tga",                       CF_TYPE_DATA    },
224         { CF_TYPE_PLAYER_MAIN,                  "Data/Players",                                         "",                                                     CF_TYPE_DATA    },
225         { CF_TYPE_PLAYER_IMAGES_MAIN,   "Data/Players/Images",                  ".pcx",                                         CF_TYPE_PLAYER_MAIN     },
226         { CF_TYPE_CACHE,                                        "Data/Cache",                                                   ".clr .tmp",                            CF_TYPE_DATA    },      //clr=cached color
227         { CF_TYPE_PLAYERS,                              "Data/Players",                                         ".hcf",                                         CF_TYPE_DATA    },      
228         { CF_TYPE_SINGLE_PLAYERS,               "Data/Players/Single",                  ".plr .csg .css",                       CF_TYPE_PLAYERS },
229         { CF_TYPE_MULTI_PLAYERS,                "Data/Players/Multi",                           ".plr",                                         CF_TYPE_DATA    },
230         { CF_TYPE_MULTI_CACHE,                  "Data/MultiData",                                       ".pcx .fs2",                            CF_TYPE_DATA    },
231         { CF_TYPE_CONFIG,                                       "Data/Config",                                          ".cfg",                                         CF_TYPE_DATA    },
232         { CF_TYPE_SQUAD_IMAGES_MAIN,    "Data/Players/Squads",                  ".pcx",                                         CF_TYPE_DATA    },
233         { CF_TYPE_DEMOS,                                        "Data/Demos",                                                   ".fsd",                                         CF_TYPE_DATA    },
234         { CF_TYPE_CBANIMS,                              "Data/CBAnims",                                         ".ani",                                         CF_TYPE_DATA    },
235         { CF_TYPE_INTEL_ANIMS,                  "Data/IntelAnims",                                      ".ani",                                         CF_TYPE_DATA    },
236 #else
237         { CF_TYPE_MAPS,                                 "Data\\Maps",                                                   ".pcx .ani .tga",                       CF_TYPE_DATA    },
238         { CF_TYPE_TEXT,                                 "Data\\Text",                                                   ".txt .net",                            CF_TYPE_DATA    },
239         { CF_TYPE_MISSIONS,                             "Data\\Missions",                                               ".fs2 .fc2 .ntl .ssv",  CF_TYPE_DATA    },
240         { CF_TYPE_MODELS,                                       "Data\\Models",                                         ".pof",                                         CF_TYPE_DATA    },
241         { CF_TYPE_TABLES,                                       "Data\\Tables",                                         ".tbl",                                         CF_TYPE_DATA    },
242         { CF_TYPE_SOUNDS,                                       "Data\\Sounds",                                         ".wav",                                         CF_TYPE_DATA    },
243         { CF_TYPE_SOUNDS_8B22K,                 "Data\\Sounds\\8b22k",                          ".wav",                                         CF_TYPE_SOUNDS  },
244         { CF_TYPE_SOUNDS_16B11K,                "Data\\Sounds\\16b11k",                         ".wav",                                         CF_TYPE_SOUNDS  },
245         { CF_TYPE_VOICE,                                        "Data\\Voice",                                                  "",                                                     CF_TYPE_DATA    },
246         { CF_TYPE_VOICE_BRIEFINGS,              "Data\\Voice\\Briefing",                        ".wav",                                         CF_TYPE_VOICE   },
247         { CF_TYPE_VOICE_CMD_BRIEF,              "Data\\Voice\\Command_briefings",".wav",                                                CF_TYPE_VOICE   },
248         { CF_TYPE_VOICE_DEBRIEFINGS,    "Data\\Voice\\Debriefing",                      ".wav",                                         CF_TYPE_VOICE   },
249         { CF_TYPE_VOICE_PERSONAS,               "Data\\Voice\\Personas",                        ".wav",                                         CF_TYPE_VOICE   },
250         { CF_TYPE_VOICE_SPECIAL,                "Data\\Voice\\Special",                         ".wav",                                         CF_TYPE_VOICE   },
251         { CF_TYPE_VOICE_TRAINING,               "Data\\Voice\\Training",                        ".wav",                                         CF_TYPE_VOICE   },
252         { CF_TYPE_MUSIC,                                        "Data\\Music",                                                  ".wav",                                         CF_TYPE_VOICE   },
253         { CF_TYPE_MOVIES,                                       "Data\\Movies",                                         ".mve .msb",                            CF_TYPE_DATA    },
254         { CF_TYPE_INTERFACE,                            "Data\\Interface",                                      ".pcx .ani .tga",                       CF_TYPE_DATA    },
255         { CF_TYPE_FONT,                                 "Data\\Fonts",                                                  ".vf",                                          CF_TYPE_DATA    },
256         { CF_TYPE_EFFECTS,                              "Data\\Effects",                                                ".ani .pcx .neb .tga",  CF_TYPE_DATA    },
257         { CF_TYPE_HUD,                                          "Data\\Hud",                                                    ".ani .pcx .tga",                       CF_TYPE_DATA    },
258         { CF_TYPE_PLAYER_MAIN,                  "Data\\Players",                                                "",                                                     CF_TYPE_DATA    },
259         { CF_TYPE_PLAYER_IMAGES_MAIN,   "Data\\Players\\Images",                        ".pcx",                                         CF_TYPE_PLAYER_MAIN     },
260         { CF_TYPE_CACHE,                                        "Data\\Cache",                                                  ".clr .tmp",                            CF_TYPE_DATA    },      //clr=cached color
261         { CF_TYPE_PLAYERS,                              "Data\\Players",                                                ".hcf",                                         CF_TYPE_DATA    },      
262         { CF_TYPE_SINGLE_PLAYERS,               "Data\\Players\\Single",                        ".plr .csg .css",                       CF_TYPE_PLAYERS },
263         { CF_TYPE_MULTI_PLAYERS,                "Data\\Players\\Multi",                         ".plr",                                         CF_TYPE_DATA    },
264         { CF_TYPE_MULTI_CACHE,                  "Data\\MultiData",                                      ".pcx .fs2",                            CF_TYPE_DATA    },
265         { CF_TYPE_CONFIG,                                       "Data\\Config",                                         ".cfg",                                         CF_TYPE_DATA    },
266         { CF_TYPE_SQUAD_IMAGES_MAIN,    "Data\\Players\\Squads",                        ".pcx",                                         CF_TYPE_DATA    },
267         { CF_TYPE_DEMOS,                                        "Data\\Demos",                                                  ".fsd",                                         CF_TYPE_DATA    },
268         { CF_TYPE_CBANIMS,                              "Data\\CBAnims",                                                ".ani",                                         CF_TYPE_DATA    },
269         { CF_TYPE_INTEL_ANIMS,                  "Data\\IntelAnims",                                     ".ani",                                         CF_TYPE_DATA    },
270 #endif
271 };
272
273
274 #define CFILE_STACK_MAX 8
275
276 int cfile_inited = 0;
277 int Cfile_stack_pos = 0;
278
279 char Cfile_stack[128][CFILE_STACK_MAX];
280
281 Cfile_block Cfile_block_list[MAX_CFILE_BLOCKS];
282 CFILE Cfile_list[MAX_CFILE_BLOCKS];
283
284 char *Cfile_cdrom_dir = NULL;
285
286 //
287 // Function prototypes for internally-called functions
288 //
289 int cfget_cfile_block();
290 CFILE *cf_open_fill_cfblock(FILE * fp, int type);
291 CFILE *cf_open_packed_cfblock(FILE *fp, int type, int offset, int size);
292 CFILE *cf_open_mapped_fill_cfblock(HANDLE hFile, int type);
293 void cf_chksum_long_init();
294
295 void cfile_close()
296 {
297         cf_free_secondary_filelist();
298 }
299
300 // determine if the given path is in a root directory (c:\  or  c:\freespace2.exe  or  c:\fred2.exe   etc)
301 int cfile_in_root_dir(char *exe_path)
302 {
303         int token_count = 0;
304         char path_copy[2048] = "";
305         char *tok;
306
307         // bogus
308         if(exe_path == NULL){
309                 return 1;
310         }
311
312         // copy the path
313         memset(path_copy, 0, 2048);
314         strncpy(path_copy, exe_path, 2047);
315
316         // count how many slashes there are in the path
317 #ifdef PLAT_UNIX
318         tok = strtok(path_copy, "/");
319 #else
320         tok = strtok(path_copy, "\\");
321 #endif
322         if(tok == NULL){
323                 return 1;
324         }       
325         do {
326                 token_count++;
327 #ifdef PLAT_UNIX
328                 tok = strtok(NULL, "/");
329 #else
330                 tok = strtok(NULL, "\\");
331 #endif
332         } while(tok != NULL);
333                 
334         // root directory if we have <= 1 slash
335         if(token_count <= 2){
336                 return 1;
337         }
338
339         // not-root directory
340         return 0;
341 }
342
343 // cfile_init() initializes the cfile system.  Called once at application start.
344 //
345 //      returns:  success ==> 0
346 //           error   ==> non-zero
347 //
348 int cfile_init(char *exe_dir, char *cdrom_dir)
349 {
350         int i;
351
352         // initialize encryption
353         encrypt_init(); 
354
355         if ( !cfile_inited ) {
356                 char buf[128];
357
358                 cfile_inited = 1;
359
360                 strcpy(buf, exe_dir);
361                 i = strlen(buf);
362
363                 // are we in a root directory?          
364                 if(cfile_in_root_dir(buf)){
365 #ifdef PLAT_UNIX
366                         fprintf (stderr, "ERROR: Freespace2/Fred2 cannot be run from a drive root directory!\n");
367 #else
368                         MessageBox((HWND)NULL, "Freespace2/Fred2 cannot be run from a drive root directory!", "Error", MB_OK);
369 #endif
370                         return 1;
371                 }               
372
373                 while (i--) {
374 #ifdef PLAT_UNIX
375                         if (buf[i] == '/'){
376 #else
377                         if (buf[i] == '\\'){
378 #endif
379                                 break;
380                         }
381                 }                                               
382
383                 if (i >= 2) {                                   
384                         buf[i] = 0;                                             
385                         cfile_chdir(buf);
386                 } else {
387 #ifdef PLAT_UNIX
388                         fprintf (stderr, "Error trying to determine executable root directory!");
389 #else
390                         MessageBox((HWND)NULL, "Error trying to determine executable root directory!", "Error", MB_OK);
391 #endif
392                         return 1;
393                 }
394
395                 // set root directory
396                 strncpy(Cfile_root_dir, buf, CFILE_ROOT_DIRECTORY_LEN-1);
397
398                 for ( i = 0; i < MAX_CFILE_BLOCKS; i++ ) {
399                         Cfile_block_list[i].type = CFILE_BLOCK_UNUSED;
400                 }
401
402                 Cfile_cdrom_dir = cdrom_dir;
403                 cf_build_secondary_filelist(Cfile_cdrom_dir);
404
405                 // 32 bit CRC table init
406                 cf_chksum_long_init();
407
408                 atexit( cfile_close );
409         }
410
411         return 0;
412 }
413
414 // Call this if pack files got added or removed or the
415 // cdrom changed.  This will refresh the list of filenames 
416 // stored in packfiles and on the cdrom.
417 void cfile_refresh()
418 {
419         cf_build_secondary_filelist(Cfile_cdrom_dir);
420 }
421
422
423 // Changes to a drive if valid.. 1=A, 2=B, etc
424 // If flag, then changes to it.
425 // Returns 0 if not-valid, 1 if valid.
426 int cfile_chdrive( int DriveNum, int flag )
427 {
428 #ifdef PLAT_UNIX
429         STUB_FUNCTION;
430         return 0;
431 #else
432         int n, org;
433         int Valid = 0;
434
435         org = -1;
436         if (!flag)
437                 org = _getdrive();
438
439         _chdrive( DriveNum );
440         n = _getdrive();
441
442
443         if (n == DriveNum )
444                 Valid = 1;
445
446         if ( (!flag) && (n != org) )
447                 _chdrive( org );
448         return Valid;
449 #endif
450 }
451
452 // push current directory on a 'stack' (so we can restore it) and change the directory
453 int cfile_push_chdir(int type)
454 {
455         int e;
456         char dir[128];
457         char OriginalDirectory[128];
458         char *Drive, *Path;
459         char NoDir[] = "\\.";
460
461         _getcwd(OriginalDirectory, 127);
462         Assert(Cfile_stack_pos < CFILE_STACK_MAX);
463         strcpy(Cfile_stack[Cfile_stack_pos++], OriginalDirectory);
464
465         cf_create_default_path_string( dir, type, NULL );
466         _strlwr(dir);
467 #ifndef PLAT_UNIX
468         Drive = strchr(dir, ':');
469
470         if (Drive) {
471                 if (!cfile_chdrive( *(Drive - 1) - 'a' + 1, 1))
472                         return 1;
473
474                 Path = Drive+1;
475
476         } else 
477 #endif
478         {
479                 Path = dir;
480         }
481
482         if (!(*Path)) {
483                 Path = NoDir;
484         }
485
486         // This chdir might get a critical error!
487         e = _chdir( Path );
488         if (e) {
489                 cfile_chdrive( OriginalDirectory[0] - 'a' + 1, 1 );
490                 return 2;
491         }
492
493         return 0;
494 }
495
496
497 int cfile_chdir(char *dir)
498 {
499         int e;
500         char OriginalDirectory[128];
501         char *Drive, *Path;
502         char NoDir[] = "\\.";
503
504         _getcwd(OriginalDirectory, 127);
505         _strlwr(dir);
506
507 #ifndef PLAT_UNIX
508         Drive = strchr(dir, ':');
509         if (Drive)      {
510                 if (!cfile_chdrive( *(Drive - 1) - 'a' + 1, 1))
511                         return 1;
512
513                 Path = Drive+1;
514
515         } else 
516 #endif
517         {
518                 Path = dir;
519         }
520
521         if (!(*Path)) {
522                 Path = NoDir;
523         }
524
525         // This chdir might get a critical error!
526         e = _chdir( Path );
527         if (e) {
528                 cfile_chdrive( OriginalDirectory[0] - 'a' + 1, 1 );
529                 return 2;
530         }
531
532         return 0;
533 }
534
535 int cfile_pop_dir()
536 {
537         Assert(Cfile_stack_pos);
538         Cfile_stack_pos--;
539         return cfile_chdir(Cfile_stack[Cfile_stack_pos]);
540 }
541
542 // flush (delete all files in) the passed directory (by type), return the # of files deleted
543 // NOTE : WILL NOT DELETE READ-ONLY FILES
544 int cfile_flush_dir(int dir_type)
545 {
546 #ifdef PLAT_UNIX
547         STUB_FUNCTION;
548         return 0;
549 #else
550         int find_handle;
551         int del_count;
552         _finddata_t find;
553
554         Assert( CF_TYPE_SPECIFIED(dir_type) );
555
556         // attempt to change the directory to the passed type
557         if(cfile_push_chdir(dir_type)){
558                 return 0;
559         }
560
561         // proceed to delete the files
562         find_handle = _findfirst( "*", &find );
563         del_count = 0;
564         if (find_handle != -1) {
565                 do {                    
566                         if (!(find.attrib & _A_SUBDIR) && !(find.attrib & _A_RDONLY)) {
567                                 // delete the file
568                                 cf_delete(find.name,dir_type);                          
569
570                                 // increment the deleted count
571                                 del_count++;
572                         }
573                 } while (!_findnext(find_handle, &find));
574                 _findclose( find_handle );
575         }
576
577         // pop the directory back
578         cfile_pop_dir();
579
580         // return the # of files deleted
581         return del_count;
582 #endif
583 }
584
585
586 // add the given extention to a filename (or filepath) if it doesn't already have this
587 // extension.
588 //    filename = name of filename or filepath to process
589 //    ext = extension to add.  Must start with the period
590 //    Returns: new filename or filepath with extension.
591 char *cf_add_ext(char *filename, char *ext)
592 {
593         int flen, elen;
594         static char path[MAX_PATH_LEN];
595
596         flen = strlen(filename);
597         elen = strlen(ext);
598         Assert(flen < MAX_PATH_LEN);
599         strcpy(path, filename);
600         if ((flen < 4) || stricmp(path + flen - elen, ext)) {
601                 Assert(flen + elen < MAX_PATH_LEN);
602                 strcat(path, ext);
603         }
604
605         return path;
606 }
607
608 // Deletes a file.
609 void cf_delete( char *filename, int dir_type )
610 {
611         char longname[MAX_PATH_LEN];
612
613         Assert( CF_TYPE_SPECIFIED(dir_type) );
614
615         cf_create_default_path_string( longname, dir_type, filename );
616
617         FILE *fp = fopen(longname, "rb");
618         if (fp) {
619                 // delete the file
620                 fclose(fp);
621                 _unlink(longname);
622         }
623
624 }
625
626
627 // Same as _access function to read a file's access bits
628 int cf_access( char *filename, int dir_type, int mode )
629 {
630         char longname[MAX_PATH_LEN];
631
632         Assert( CF_TYPE_SPECIFIED(dir_type) );
633
634         cf_create_default_path_string( longname, dir_type, filename );
635
636         return access(longname,mode);
637 }
638
639
640 // Returns 1 if file exists, 0 if not.
641 int cf_exist( char *filename, int dir_type )
642 {
643         char longname[MAX_PATH_LEN];
644
645         Assert( CF_TYPE_SPECIFIED(dir_type) );
646
647         cf_create_default_path_string( longname, dir_type, filename );
648
649         FILE *fp = fopen(longname, "rb");
650         if (fp) {
651                 return 1;
652                 fclose(fp);
653         }
654
655         return 0;
656 }
657
658 void cf_attrib(char *filename, int set, int clear, int dir_type)
659 {
660         char longname[MAX_PATH_LEN];
661
662         Assert( CF_TYPE_SPECIFIED(dir_type) );
663
664         cf_create_default_path_string( longname, dir_type, filename );
665
666         FILE *fp = fopen(longname, "rb");
667         if (fp) {
668                 fclose(fp);
669
670 #ifdef PLAT_UNIX
671                 STUB_FUNCTION;
672 #else
673                 DWORD z = GetFileAttributes(longname);
674                 SetFileAttributes(longname, z | set & ~clear);
675 #endif
676         }
677
678 }
679
680 int cf_rename(char *old_name, char *name, int dir_type)
681 {
682         Assert( CF_TYPE_SPECIFIED(dir_type) );
683
684         int ret_code;
685         char old_longname[_MAX_PATH];
686         char new_longname[_MAX_PATH];
687         
688         cf_create_default_path_string( old_longname, dir_type, old_name );
689         cf_create_default_path_string( new_longname, dir_type, name );
690
691         ret_code = rename(old_longname, new_longname );         
692         if(ret_code != 0){
693                 switch(errno){
694                 case EACCES :
695                         return CF_RENAME_FAIL_ACCESS;
696                 case ENOENT :
697                 default:
698                         return CF_RENAME_FAIL_EXIST;
699                 }
700         }
701
702         return CF_RENAME_SUCCESS;
703         
704
705 }
706
707 // Creates the directory path if it doesn't exist. Even creates all its
708 // parent paths.
709 void cf_create_directory( int dir_type )
710 {
711         int num_dirs = 0;
712         int dir_tree[CF_MAX_PATH_TYPES];
713         char longname[MAX_PATH_LEN];
714
715         Assert( CF_TYPE_SPECIFIED(dir_type) );
716
717         int current_dir = dir_type;
718
719         do {
720                 Assert( num_dirs < CF_MAX_PATH_TYPES );         // Invalid Pathtypes data?
721
722                 dir_tree[num_dirs++] = current_dir;
723                 current_dir = Pathtypes[current_dir].parent_index;
724
725         } while( current_dir != CF_TYPE_ROOT );
726
727         
728         int i;
729
730         for (i=num_dirs-1; i>=0; i-- )  {
731                 cf_create_default_path_string( longname, dir_tree[i], NULL );
732
733 #ifdef PLAT_UNIX
734                 if ( _mkdir(longname, 0777)==0 )        {
735                         mprintf(( "CFILE: Created new directory '%s'\n", longname ));
736                 }
737 #else
738                 if ( _mkdir(longname)==0 )      {
739                         mprintf(( "CFILE: Created new directory '%s'\n", longname ));
740                 }
741 #endif
742         }
743
744
745 }
746
747
748 extern int game_cd_changed();
749
750 // cfopen()
751 //
752 // parameters:  *filepath ==> name of file to open (may be path+name)
753 //              *mode     ==> specifies how file should be opened (eg "rb" for read binary)
754 //                            passing NULL to mode deletes the file if it exists and returns NULL
755 //               type     ==> one of:    CFILE_NORMAL
756 //                                       CFILE_MEMORY_MAPPED
757 //                                        dir_type      =>      override extension check, value is one of CF_TYPE* #defines
758 //
759 //               NOTE: type parameter is an optional parameter.  The default value is CFILE_NORMAL
760 //
761 //
762 // returns:             success ==> address of CFILE structure
763 //                                      error   ==> NULL
764 //
765
766 CFILE *cfopen(char *file_path, char *mode, int type, int dir_type, bool localize)
767 {
768         char longname[_MAX_PATH];
769
770 //      nprintf(("CFILE", "CFILE -- trying to open %s\n", file_path ));
771 // #if !defined(MULTIPLAYER_BETA_BUILD) && !defined(FS2_DEMO)
772
773 // we no longer need to do this, and on machines with crappy-ass drivers it can slow things down horribly.
774 #if 0   
775         if ( game_cd_changed() ) {
776                 cfile_refresh();
777         }
778 #endif
779
780         //================================================
781         // Check that all the parameters make sense
782         Assert(file_path && strlen(file_path));
783         Assert( mode != NULL );
784         
785         // Can only open read-only binary files in memory mapped mode.
786         if ( (type & CFILE_MEMORY_MAPPED) && strcmp(mode,"rb") ) {
787                 Int3();                         
788                 return NULL;
789         }
790
791         //===========================================================
792         // If in write mode, just try to open the file straight off
793         // the harddisk.  No fancy packfile stuff here!
794         
795         if ( strchr(mode,'w') ) {
796                 // For write-only files, require a full path or a path type
797                 if ( strpbrk(file_path,"/\\:")  ) {  
798                         // Full path given?
799                         strcpy(longname, file_path );
800                 } else {
801                         // Path type given?
802                         Assert( dir_type != CF_TYPE_ANY );
803
804                         // Create the directory if necessary
805                         cf_create_directory( dir_type );
806
807                         cf_create_default_path_string( longname, dir_type, file_path );
808                 }
809                 Assert( !(type & CFILE_MEMORY_MAPPED) );
810
811                 // JOHN: TODO, you should create the path if it doesn't exist.
812                                 
813                 FILE *fp = fopen(longname, mode);
814                 if (fp) {
815                         return cf_open_fill_cfblock(fp, dir_type);
816                 }
817                 return NULL;
818         } 
819
820
821         //================================================
822         // Search for file on disk, on cdrom, or in a packfile
823
824         int offset, size;
825         char copy_file_path[MAX_PATH_LEN];  // FIX change in memory from cf_find_file_location
826         strcpy(copy_file_path, file_path);
827
828
829         if ( cf_find_file_location( copy_file_path, dir_type, longname, &size, &offset, localize ) )    {
830
831                 // Fount it, now create a cfile out of it
832                 
833                 if ( type & CFILE_MEMORY_MAPPED ) {
834                 
835                         // Can't open memory mapped files out of pack files
836                         if ( offset == 0 )      {
837 #ifdef PLAT_UNIX
838                                 STUB_FUNCTION;
839 #else
840                                 HANDLE hFile;
841
842                                 hFile = CreateFile(longname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
843
844                                 if (hFile != INVALID_HANDLE_VALUE)      {
845                                         return cf_open_mapped_fill_cfblock(hFile, dir_type);
846                                 }
847 #endif
848                         } 
849
850                 } else {
851
852                         FILE *fp = fopen( longname, "rb" );
853
854                         if ( fp )       {
855                                 if ( offset )   {
856                                         // Found it in a pack file
857                                         return cf_open_packed_cfblock(fp, dir_type, offset, size );
858                                 } else {
859                                         // Found it in a normal file
860                                         return cf_open_fill_cfblock(fp, dir_type);
861                                 } 
862                         }
863                 }
864
865         }
866
867         return NULL;
868 }
869
870
871 // ------------------------------------------------------------------------
872 // ctmpfile() 
873 //
874 // Open up a temporary file.  A unique name is automatically generated.  The
875 // file will be automatically deleted when file is closed.
876 //
877 // return:              NULL                                    =>              tmp file could not be opened
878 //                                      pointer to CFILE        =>              tmp file successfully opened
879 //
880 CFILE *ctmpfile()
881 {
882         FILE    *fp;
883         fp = tmpfile();
884         if ( fp )
885                 return cf_open_fill_cfblock(fp, 0);
886         else
887                 return NULL;
888 }
889
890
891
892 // cfget_cfile_block() will try to find an empty Cfile_block structure in the
893 //      Cfile_block_list[] array and return the index.
894 //
895 // returns:   success ==> index in Cfile_block_list[] array
896 //            failure ==> -1
897 //
898 int cfget_cfile_block()
899 {       
900         int i;
901         Cfile_block *cb;
902
903         for ( i = 0; i < MAX_CFILE_BLOCKS; i++ ) {
904                 cb = &Cfile_block_list[i];
905                 if ( cb->type == CFILE_BLOCK_UNUSED ) {
906                         cb->data = NULL;
907                         cb->fp = NULL;
908                         cb->type = CFILE_BLOCK_USED;
909                         return i;
910                 }
911         }
912
913         // If we've reached this point, a free Cfile_block could not be found
914         nprintf(("Warning","A free Cfile_block could not be found.\n"));
915         Assert(0);      // out of free cfile blocks
916         return -1;                      
917 }
918
919
920 // cfclose() closes the file
921 //
922 // returns:   success ==> 0
923 //                                failure ==> EOF
924 //
925 int cfclose( CFILE * cfile )
926 {
927         int result;
928
929         Assert(cfile != NULL);
930         Cfile_block *cb;
931         Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
932         cb = &Cfile_block_list[cfile->id];      
933
934         result = 0;
935         if ( cb->data ) {
936                 // close memory mapped file
937 #ifdef PLAT_UNIX
938                 STUB_FUNCTION;
939 #else
940                 result = UnmapViewOfFile((void*)cb->data);
941                 Assert(result);
942                 result = CloseHandle(cb->hInFile);              
943                 Assert(result); // Ensure file handle is closed properly
944                 result = CloseHandle(cb->hMapFile);             
945                 Assert(result); // Ensure file handle is closed properly
946 #endif
947                 result = 0;
948
949         } else if ( cb->fp != NULL )    {
950                 Assert(cb->fp != NULL);
951                 result = fclose(cb->fp);
952         } else {
953                 // VP  do nothing
954         }
955
956         cb->type = CFILE_BLOCK_UNUSED;
957         return result;
958 }
959
960
961
962
963 // cf_open_fill_cfblock() will fill up a Cfile_block element in the Cfile_block_list[] array
964 // for the case of a file being opened by cf_open();
965 //
966 // returns:   success ==> ptr to CFILE structure.  
967 //            error   ==> NULL
968 //
969 CFILE *cf_open_fill_cfblock(FILE *fp, int type)
970 {
971         int cfile_block_index;
972
973         cfile_block_index = cfget_cfile_block();
974         if ( cfile_block_index == -1 ) {
975                 return NULL;
976         } else {
977                 CFILE *cfp;
978                 Cfile_block *cfbp;
979                 cfbp = &Cfile_block_list[cfile_block_index];
980                 cfp = &Cfile_list[cfile_block_index];;
981                 cfp->id = cfile_block_index;
982                 cfp->version = 0;
983                 cfbp->data = NULL;
984                 cfbp->fp = fp;
985                 cfbp->dir_type = type;
986                 
987                 cf_init_lowlevel_read_code(cfp,0,filelength(fileno(fp)) );
988
989                 return cfp;
990         }
991 }
992
993
994 // cf_open_packed_cfblock() will fill up a Cfile_block element in the Cfile_block_list[] array
995 // for the case of a file being opened by cf_open();
996 //
997 // returns:   success ==> ptr to CFILE structure.  
998 //            error   ==> NULL
999 //
1000 CFILE *cf_open_packed_cfblock(FILE *fp, int type, int offset, int size)
1001 {
1002         // Found it in a pack file
1003         int cfile_block_index;
1004         
1005         cfile_block_index = cfget_cfile_block();
1006         if ( cfile_block_index == -1 ) {
1007                 return NULL;
1008         } else {
1009                 CFILE *cfp;
1010                 Cfile_block *cfbp;
1011                 cfbp = &Cfile_block_list[cfile_block_index];
1012         
1013                 cfp = &Cfile_list[cfile_block_index];
1014                 cfp->id = cfile_block_index;
1015                 cfp->version = 0;
1016                 cfbp->data = NULL;
1017                 cfbp->fp = fp;
1018                 cfbp->dir_type = type;
1019
1020                 cf_init_lowlevel_read_code(cfp,offset, size );
1021
1022                 return cfp;
1023         }
1024
1025 }
1026
1027
1028
1029 // cf_open_mapped_fill_cfblock() will fill up a Cfile_block element in the Cfile_block_list[] array
1030 // for the case of a file being opened by cf_open_mapped();
1031 //
1032 // returns:   ptr CFILE structure.  
1033 //
1034 CFILE *cf_open_mapped_fill_cfblock(HANDLE hFile, int type)
1035 {
1036         int cfile_block_index;
1037
1038         cfile_block_index = cfget_cfile_block();
1039         if ( cfile_block_index == -1 ) {
1040                 return NULL;
1041         }
1042         else {
1043                 CFILE *cfp;
1044                 Cfile_block *cfbp;
1045                 cfbp = &Cfile_block_list[cfile_block_index];
1046
1047                 cfp = &Cfile_list[cfile_block_index];
1048                 cfp->id = cfile_block_index;
1049                 cfbp->fp = NULL;
1050                 cfbp->hInFile = hFile;
1051                 cfbp->dir_type = type;
1052
1053                 cf_init_lowlevel_read_code(cfp,0 , 0 );
1054
1055 #ifdef PLAT_UNIX
1056                 STUB_FUNCTION;
1057 #else
1058                 cfbp->hMapFile = CreateFileMapping(cfbp->hInFile, NULL, PAGE_READONLY, 0, 0, NULL);
1059                 if (cfbp->hMapFile == NULL) { 
1060                         nprintf(("Error", "Could not create file-mapping object.\n")); 
1061                         return NULL;
1062                 } 
1063         
1064                 cfbp->data = (ubyte*)MapViewOfFile(cfbp->hMapFile, FILE_MAP_READ, 0, 0, 0);
1065                 Assert( cfbp->data != NULL );           
1066 #endif
1067                 return cfp;
1068         }
1069 }
1070
1071 int cf_get_dir_type(CFILE *cfile)
1072 {
1073         return Cfile_block_list[cfile->id].dir_type;
1074 }
1075
1076 // cf_returndata() returns the data pointer for a memory-mapped file that is associated
1077 // with the CFILE structure passed as a parameter
1078 //
1079 // 
1080
1081 void *cf_returndata(CFILE *cfile)
1082 {
1083         Assert(cfile != NULL);
1084         Cfile_block *cb;
1085         Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1086         cb = &Cfile_block_list[cfile->id];      
1087         Assert(cb->data != NULL);
1088         return cb->data;
1089 }
1090
1091
1092
1093 // version number of opened file.  Will be 0 unless you put something else here after you
1094 // open a file.  Once set, you can use minimum version numbers with the read functions.
1095 void cf_set_version( CFILE * cfile, int version )
1096 {
1097         Assert(cfile != NULL);
1098
1099         cfile->version = version;
1100 }
1101
1102 // routines to read basic data types from CFILE's.  Put here to
1103 // simplify mac/pc reading from cfiles.
1104
1105 float cfread_float(CFILE *file, int ver, float deflt)
1106 {
1107         float f;
1108
1109         if (file->version < ver)
1110                 return deflt;
1111
1112         if (cfread( &f, sizeof(f), 1, file) != 1)
1113                 return deflt;
1114
1115 //      i = INTEL_INT(i);                       //  hmm, not sure what to do here
1116         return f;
1117 }
1118
1119 int cfread_int(CFILE *file, int ver, int deflt)
1120 {
1121         int i;
1122
1123         if (file->version < ver)
1124                 return deflt;
1125
1126         if (cfread( &i, sizeof(i), 1, file) != 1)
1127                 return deflt;
1128
1129         i = INTEL_INT(i);
1130         return i;
1131 }
1132
1133 uint cfread_uint(CFILE *file, int ver, uint deflt)
1134 {
1135         uint i;
1136
1137         if (file->version < ver)
1138                 return deflt;
1139
1140         if (cfread( &i, sizeof(i), 1, file) != 1)
1141                 return deflt;
1142
1143         i = INTEL_INT(i);
1144         return i;
1145 }
1146
1147 short cfread_short(CFILE *file, int ver, short deflt)
1148 {
1149         short s;
1150
1151         if (file->version < ver)
1152                 return deflt;
1153
1154         if (cfread( &s, sizeof(s), 1, file) != 1)
1155                 return deflt;
1156
1157         s = INTEL_SHORT(s);
1158         return s;
1159 }
1160
1161 ushort cfread_ushort(CFILE *file, int ver, ushort deflt)
1162 {
1163         ushort s;
1164
1165         if (file->version < ver)
1166                 return deflt;
1167
1168         if (cfread( &s, sizeof(s), 1, file) != 1)
1169                 return deflt;
1170
1171         s = INTEL_SHORT(s);
1172         return s;
1173 }
1174
1175 ubyte cfread_ubyte(CFILE *file, int ver, ubyte deflt)
1176 {
1177         ubyte b;
1178
1179         if (file->version < ver)
1180                 return deflt;
1181
1182         if (cfread( &b, sizeof(b), 1, file) != 1)
1183                 return deflt;
1184
1185         return b;
1186 }
1187
1188 void cfread_vector(vector *vec, CFILE *file, int ver, vector *deflt)
1189 {
1190         if (file->version < ver) {
1191                 if (deflt)
1192                         *vec = *deflt;
1193                 else
1194                         vec->x = vec->y = vec->z = 0.0f;
1195
1196                 return;
1197         }
1198
1199         vec->x = cfread_float(file, ver, deflt ? deflt->x : 0.0f);
1200         vec->y = cfread_float(file, ver, deflt ? deflt->y : 0.0f);
1201         vec->z = cfread_float(file, ver, deflt ? deflt->z : 0.0f);
1202 }
1203         
1204 void cfread_angles(angles *ang, CFILE *file, int ver, angles *deflt)
1205 {
1206         if (file->version < ver) {
1207                 if (deflt)
1208                         *ang = *deflt;
1209                 else
1210                         ang->p = ang->b = ang->h = 0.0f;
1211
1212                 return;
1213         }
1214
1215         ang->p = cfread_float(file, ver, deflt ? deflt->p : 0.0f);
1216         ang->b = cfread_float(file, ver, deflt ? deflt->b : 0.0f);
1217         ang->h = cfread_float(file, ver, deflt ? deflt->h : 0.0f);
1218 }
1219
1220 char cfread_char(CFILE *file, int ver, char deflt)
1221 {
1222         char b;
1223
1224         if (file->version < ver)
1225                 return deflt;
1226
1227         if (cfread( &b, sizeof(b), 1, file) != 1)
1228                 return deflt;
1229
1230         return b;
1231 }
1232
1233 void cfread_string(char *buf, int n, CFILE *file)
1234 {
1235         char c;
1236
1237         do {
1238                 c = cfread_char(file);
1239                 if ( n > 0 )    {
1240                         *buf++ = c;
1241                         n--;
1242                 }
1243         } while (c != 0 );
1244 }
1245
1246 void cfread_string_len(char *buf,int n, CFILE *file)
1247 {
1248         int len;
1249         len = cfread_int(file);
1250         Assert( len < n );
1251         if (len)
1252                 cfread(buf, len, 1, file);
1253
1254         buf[len] = 0;
1255 }
1256
1257 // equivalent write functions of above read functions follow
1258
1259 int cfwrite_float(float f, CFILE *file)
1260 {
1261 //      i = INTEL_INT(i);                       //  hmm, not sure what to do here
1262         return cfwrite(&f, sizeof(f), 1, file);
1263 }
1264
1265 int cfwrite_int(int i, CFILE *file)
1266 {
1267         i = INTEL_INT(i);
1268         return cfwrite(&i, sizeof(i), 1, file);
1269 }
1270
1271 int cfwrite_uint(uint i, CFILE *file)
1272 {
1273         i = INTEL_INT(i);
1274         return cfwrite(&i, sizeof(i), 1, file);
1275 }
1276
1277 int cfwrite_short(short s, CFILE *file)
1278 {
1279         s = INTEL_SHORT(s);
1280         return cfwrite(&s, sizeof(s), 1, file);
1281 }
1282
1283 int cfwrite_ushort(ushort s, CFILE *file)
1284 {
1285         s = INTEL_SHORT(s);
1286         return cfwrite(&s, sizeof(s), 1, file);
1287 }
1288
1289 int cfwrite_ubyte(ubyte b, CFILE *file)
1290 {
1291         return cfwrite(&b, sizeof(b), 1, file);
1292 }
1293
1294 int cfwrite_vector(vector *vec, CFILE *file)
1295 {
1296         if(!cfwrite_float(vec->x, file)){
1297                 return 0;
1298         }
1299         if(!cfwrite_float(vec->y, file)){
1300                 return 0;
1301         }
1302         return cfwrite_float(vec->z, file);
1303 }
1304
1305 int cfwrite_angles(angles *ang, CFILE *file)
1306 {
1307         if(!cfwrite_float(ang->p, file)){
1308                 return 0;
1309         }
1310         if(!cfwrite_float(ang->b, file)){
1311                 return 0;
1312         }
1313         return cfwrite_float(ang->h, file);
1314 }
1315
1316 int cfwrite_char(char b, CFILE *file)
1317 {
1318         return cfwrite( &b, sizeof(b), 1, file);
1319 }
1320
1321 int cfwrite_string(char *buf, CFILE *file)
1322 {
1323         if ( (!buf) || (buf && !buf[0]) ) {
1324                 return cfwrite_char(0, file);
1325         } 
1326         int len = strlen(buf);
1327         if(!cfwrite(buf, len, 1, file)){
1328                 return 0;
1329         }
1330         return cfwrite_char(0, file);                   // write out NULL termination                   
1331 }
1332
1333 int cfwrite_string_len(char *buf, CFILE *file)
1334 {
1335         int len = strlen(buf);
1336
1337         if(!cfwrite_int(len, file)){
1338                 return 0;
1339         }
1340         if (len){
1341                 return cfwrite(buf,len,1,file);
1342         } 
1343
1344         return 1;
1345 }
1346
1347 // Get the filelength
1348 int cfilelength( CFILE * cfile )
1349 {
1350         Assert(cfile != NULL);
1351         Cfile_block *cb;
1352         Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1353         cb = &Cfile_block_list[cfile->id];      
1354
1355         // TODO: return length of memory mapped file
1356         Assert( !cb->data );
1357
1358         Assert(cb->fp != NULL);
1359
1360         // cb->size gets set at cfopen
1361         return cb->size;
1362 }
1363
1364 // cfwrite() writes to the file
1365 //
1366 // returns:   number of full elements actually written
1367 //            
1368 //
1369 int cfwrite(void *buf, int elsize, int nelem, CFILE *cfile)
1370 {
1371         Assert(cfile != NULL);
1372         Assert(buf != NULL);
1373         Assert(elsize > 0);
1374         Assert(nelem > 0);
1375
1376         Cfile_block *cb;
1377         Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1378         cb = &Cfile_block_list[cfile->id];      
1379
1380         int result = 0;
1381
1382         // cfwrite() not supported for memory-mapped files
1383         Assert( !cb->data );
1384
1385         Assert(cb->fp != NULL);
1386         Assert(cb->lib_offset == 0 );
1387         result = fwrite(buf, elsize, nelem, cb->fp);
1388
1389         return result;  
1390 }
1391
1392
1393 // cfputc() writes a character to a file
1394 //
1395 // returns:   success ==> returns character written
1396 //                                error   ==> EOF
1397 //
1398 int cfputc(int c, CFILE *cfile)
1399 {
1400         int result;
1401
1402         Assert(cfile != NULL);
1403         Cfile_block *cb;
1404         Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1405         cb = &Cfile_block_list[cfile->id];      
1406
1407         result = 0;
1408         // cfputc() not supported for memory-mapped files
1409         Assert( !cb->data );
1410
1411         Assert(cb->fp != NULL);
1412         result = fputc(c, cb->fp);
1413
1414         return result;  
1415 }
1416
1417
1418 // cfgetc() reads a character from a file
1419 //
1420 // returns:   success ==> returns character read
1421 //                                error   ==> EOF
1422 //
1423 int cfgetc(CFILE *cfile)
1424 {
1425         Assert(cfile != NULL);
1426         
1427         char tmp;
1428
1429         int result = cfread(&tmp, 1, 1, cfile );
1430         if ( result == 1 )      {
1431                 result = char(tmp);
1432         } else {
1433                 result = CF_EOF;
1434         }
1435
1436         return result;  
1437 }
1438
1439
1440
1441
1442
1443 // cfgets() reads a string from a file
1444 //
1445 // returns:   success ==> returns pointer to string
1446 //                                error   ==> NULL
1447 //
1448 char *cfgets(char *buf, int n, CFILE *cfile)
1449 {
1450         Assert(cfile != NULL);
1451         Assert(buf != NULL);
1452         Assert(n > 0 );
1453
1454         char * t = buf;
1455         int i, c;
1456
1457         for (i=0; i<n-1; i++ )  {
1458                 do {
1459                         char tmp_c;
1460
1461                         int ret = cfread( &tmp_c, 1, 1, cfile );
1462                         if ( ret != 1 ) {
1463                                 *buf = 0;
1464                                 if ( buf > t )  {               
1465                                         return t;
1466                                 } else {
1467                                         return NULL;
1468                                 }
1469                         }
1470                         c = int(tmp_c);
1471                 } while ( c == 13 );
1472                 *buf++ = char(c);
1473                 if ( c=='\n' ) break;
1474         }
1475         *buf++ = 0;
1476
1477         return  t;
1478 }
1479
1480 // cfputs() writes a string to a file
1481 //
1482 // returns:   success ==> non-negative value
1483 //                                error   ==> EOF
1484 //
1485 int cfputs(char *str, CFILE *cfile)
1486 {
1487         Assert(cfile != NULL);
1488         Assert(str != NULL);
1489
1490         Cfile_block *cb;
1491         Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1492         cb = &Cfile_block_list[cfile->id];      
1493
1494         int result;
1495
1496         result = 0;
1497         // cfputs() not supported for memory-mapped files
1498         Assert( !cb->data );
1499         Assert(cb->fp != NULL);
1500         result = fputs(str, cb->fp);
1501
1502         return result;  
1503 }
1504
1505
1506 // 16 and 32 bit checksum stuff ----------------------------------------------------------
1507
1508 // CRC code for mission validation.  given to us by Kevin Bentley on 7/20/98.   Some sort of
1509 // checksumming code that he wrote a while ago.  
1510 #define CRC32_POLYNOMIAL                                        0xEDB88320L
1511 unsigned long CRCTable[256];
1512
1513 #define CF_CHKSUM_SAMPLE_SIZE                           512
1514
1515 // update cur_chksum with the chksum of the new_data of size new_data_size
1516 ushort cf_add_chksum_short(ushort seed, char *buffer, int size)
1517 {
1518         ubyte * ptr = (ubyte *)buffer;
1519         unsigned int sum1,sum2;
1520
1521         sum1 = sum2 = (int)(seed);
1522
1523         while(size--)   {
1524                 sum1 += *ptr++;
1525                 if (sum1 >= 255 ) sum1 -= 255;
1526                 sum2 += sum1;
1527         }
1528         sum2 %= 255;
1529         
1530         return (unsigned short)((sum1<<8)+ sum2);
1531 }
1532
1533 // update cur_chksum with the chksum of the new_data of size new_data_size
1534 unsigned long cf_add_chksum_long(unsigned long seed, char *buffer, int size)
1535 {
1536         unsigned long crc;
1537         unsigned char *p;
1538         unsigned long temp1;
1539         unsigned long temp2;
1540
1541         p = (unsigned char*)buffer;
1542         crc = seed;     
1543
1544         while (size--!=0){
1545           temp1 = (crc>>8)&0x00FFFFFFL;
1546           temp2 = CRCTable[((int)crc^*p++)&0xff];
1547           crc = temp1^temp2;
1548         }       
1549         
1550         return crc;
1551 }
1552
1553 void cf_chksum_long_init()
1554 {
1555         int i,j;
1556         unsigned long crc;      
1557
1558         for( i=0;i<=255;i++) {
1559                 crc=i;
1560                 for(j=8;j>0;j--) {
1561                         if(crc&1)
1562                                  crc=(crc>>1)^CRC32_POLYNOMIAL;
1563                         else
1564                                  crc>>=1;
1565                 }
1566                 CRCTable[i]=crc;
1567         }       
1568 }
1569
1570 // single function convenient to use for both short and long checksums
1571 // NOTE : only one of chk_short or chk_long must be non-NULL (indicating which checksum to perform)
1572 int cf_chksum_do(CFILE *cfile, ushort *chk_short, uint *chk_long, int max_size)
1573 {
1574         char cf_buffer[CF_CHKSUM_SAMPLE_SIZE];
1575         int is_long;
1576         int cf_len = 0;
1577         int cf_total;
1578         int read_size;
1579
1580         // determine whether we're doing a short or long checksum
1581         is_long = 0;
1582         if(chk_short){
1583                 Assert(!chk_long);              
1584                 *chk_short = 0;
1585         } else {
1586                 Assert(chk_long);
1587                 is_long = 1;
1588                 *chk_long = 0;
1589         }
1590
1591         // if max_size is -1, set it to be the size of the file
1592         if(max_size < 0){
1593                 cfseek(cfile, 0, SEEK_SET);
1594                 max_size = cfilelength(cfile);
1595         }
1596         
1597         cf_total = 0;
1598         do {
1599                 // determine how much we want to read
1600                 if((max_size - cf_total) >= CF_CHKSUM_SAMPLE_SIZE){
1601                         read_size = CF_CHKSUM_SAMPLE_SIZE;
1602                 } else {
1603                         read_size = max_size - cf_total;
1604                 }
1605
1606                 // read in some buffer
1607                 cf_len = cfread(cf_buffer, 1, read_size, cfile);
1608
1609                 // total we've read so far
1610                 cf_total += cf_len;
1611
1612                 // add the checksum
1613                 if(cf_len > 0){
1614                         // do the proper short or long checksum
1615                         if(is_long){
1616                                 *chk_long = cf_add_chksum_long(*chk_long, cf_buffer, cf_len);
1617                         } else {
1618                                 *chk_short = cf_add_chksum_short(*chk_short, cf_buffer, cf_len);
1619                         }
1620                 }
1621         } while((cf_len > 0) && (cf_total < max_size));
1622
1623         return 1;
1624 }
1625
1626 // get the 2 byte checksum of the passed filename - return 0 if operation failed, 1 if succeeded
1627 int cf_chksum_short(char *filename, ushort *chksum, int max_size, int cf_type)
1628 {
1629         int ret_val;
1630         CFILE *cfile = NULL;            
1631         
1632         // zero the checksum
1633         *chksum = 0;
1634
1635         // attempt to open the file
1636         cfile = cfopen(filename,"rt",CFILE_NORMAL,cf_type);
1637         if(cfile == NULL){              
1638                 return 0;
1639         }
1640         
1641         // call the overloaded cf_chksum function()
1642         ret_val = cf_chksum_do(cfile, chksum, NULL, max_size);
1643
1644         // close the file down
1645         cfclose(cfile);
1646         cfile = NULL;
1647
1648         // return the result
1649         return ret_val;
1650 }
1651
1652 // get the 2 byte checksum of the passed file - return 0 if operation failed, 1 if succeeded
1653 // NOTE : preserves current file position
1654 int cf_chksum_short(CFILE *file, ushort *chksum, int max_size)
1655 {
1656         int ret_code;
1657         int start_pos;
1658         
1659         // Returns current position of file.
1660         start_pos = cftell(file);
1661         if(start_pos == -1){
1662                 return 0;
1663         }
1664         
1665         // move to the beginning of the file
1666         if(cfseek(file, 0, CF_SEEK_SET)){
1667                 return 0;
1668         }
1669         ret_code = cf_chksum_do(file, chksum, NULL, max_size);
1670         // move back to the start position
1671         cfseek(file, start_pos, CF_SEEK_SET);
1672
1673         return ret_code;
1674 }
1675
1676 // get the 32 bit CRC checksum of the passed filename - return 0 if operation failed, 1 if succeeded
1677 int cf_chksum_long(char *filename, uint *chksum, int max_size, int cf_type)
1678 {
1679         int ret_val;
1680         CFILE *cfile = NULL;            
1681         
1682         // zero the checksum
1683         *chksum = 0;
1684
1685         // attempt to open the file
1686         cfile = cfopen(filename,"rt",CFILE_NORMAL,cf_type);
1687         if(cfile == NULL){              
1688                 return 0;
1689         }
1690         
1691         // call the overloaded cf_chksum function()
1692         ret_val = cf_chksum_do(cfile, NULL, chksum, max_size);
1693
1694         // close the file down
1695         cfclose(cfile);
1696         cfile = NULL;
1697
1698         // return the result
1699         return ret_val; 
1700 }
1701
1702 // get the 32 bit CRC checksum of the passed file - return 0 if operation failed, 1 if succeeded
1703 // NOTE : preserves current file position
1704 int cf_chksum_long(CFILE *file, uint *chksum, int max_size)
1705 {
1706         int ret_code;
1707         int start_pos;
1708         
1709         // Returns current position of file.
1710         start_pos = cftell(file);
1711         if(start_pos == -1){
1712                 return 0;
1713         }
1714         
1715         // move to the beginning of the file
1716         if(cfseek(file, 0, CF_SEEK_SET)){
1717                 return 0;
1718         }
1719         ret_code = cf_chksum_do(file, NULL, chksum, max_size);
1720         // move back to the start position
1721         cfseek(file, start_pos, CF_SEEK_SET);
1722
1723         return ret_code;
1724 }
1725
1726
1727 // Flush the open file buffer
1728 //
1729 // exit: 0 - success
1730 //                      1 - failure
1731 int cflush(CFILE *cfile)
1732 {
1733         Assert(cfile != NULL);
1734         Cfile_block *cb;
1735         Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1736         cb = &Cfile_block_list[cfile->id];      
1737
1738         // not supported for memory mapped files
1739         Assert( !cb->data );
1740
1741         Assert(cb->fp != NULL);
1742         return fflush(cb->fp);
1743 }
1744
1745
1746
1747
1748
1749
1750