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