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