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