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