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