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