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