]> icculus.org git repositories - taylor/freespace2.git/blob - src/cfile/cfile.cpp
make mount/sync options clearer with an order change
[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 #else
220 #include <unistd.h>
221 #include <dirent.h>
222 #include <fnmatch.h>
223 #endif
224 #include <sys/stat.h>
225 #include <sys/types.h>
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"
237
238 char Cfile_root_dir[CFILE_ROOT_DIRECTORY_LEN] = "";
239 char Cfile_user_dir[CFILE_ROOT_DIRECTORY_LEN] = "";
240
241 // During cfile_init, verify that Pathtypes[n].index == n for each item
242 // Each path must have a valid parent that can be tracable all the way back to the root 
243 // so that we can create directories when we need to.
244 //
245 cf_pathtype Pathtypes[CF_MAX_PATH_TYPES]  = {
246         // What type this is          Path                             Extensions              Parent type
247         { CF_TYPE_INVALID,                              NULL,                                                                           NULL,                                                   CF_TYPE_INVALID },
248         // Root must be index 1!!       
249         { CF_TYPE_ROOT,                                 "",                                                                             ".mve",                                                 CF_TYPE_ROOT    },
250         { CF_TYPE_DATA,                                 "Data",                                                                 ".cfg .log .txt",                       CF_TYPE_ROOT    },
251         { CF_TYPE_MAPS,                                 "Data" DIR_SEPARATOR_STR "Maps",                                                        ".pcx .ani .tga",                       CF_TYPE_DATA    },
252         { CF_TYPE_TEXT,                                 "Data" DIR_SEPARATOR_STR "Text",                                                        ".txt .net",                            CF_TYPE_DATA    },
253 #ifdef MAKE_FS1
254         { CF_TYPE_MISSIONS,                             "Data" DIR_SEPARATOR_STR "Missions",                                            ".fsm .fsc .ntl .ssv",  CF_TYPE_DATA    },
255 #else
256         { CF_TYPE_MISSIONS,                             "Data" DIR_SEPARATOR_STR "Missions",                                            ".fs2 .fc2 .ntl .ssv",  CF_TYPE_DATA    },
257 #endif
258         { CF_TYPE_MODELS,                                       "Data" DIR_SEPARATOR_STR "Models",                                              ".pof",                                         CF_TYPE_DATA    },
259         { CF_TYPE_TABLES,                                       "Data" DIR_SEPARATOR_STR "Tables",                                              ".tbl",                                         CF_TYPE_DATA    },
260         { CF_TYPE_SOUNDS,                                       "Data" DIR_SEPARATOR_STR "Sounds",                                              ".wav",                                         CF_TYPE_DATA    },
261         { CF_TYPE_SOUNDS_8B22K,                 "Data" DIR_SEPARATOR_STR "Sounds" DIR_SEPARATOR_STR "8b22k",                            ".wav",                                         CF_TYPE_SOUNDS  },
262         { CF_TYPE_SOUNDS_16B11K,                "Data" DIR_SEPARATOR_STR "Sounds" DIR_SEPARATOR_STR "16b11k",                           ".wav",                                         CF_TYPE_SOUNDS  },
263         { CF_TYPE_VOICE,                                        "Data" DIR_SEPARATOR_STR "Voice",                                                       "",                                                     CF_TYPE_DATA    },
264 #ifdef MAKE_FS1
265         { CF_TYPE_VOICE_BRIEFINGS,              "Data" DIR_SEPARATOR_STR "Voice" DIR_SEPARATOR_STR "Briefings",                 ".wav",                                         CF_TYPE_VOICE   },
266 #else
267         { CF_TYPE_VOICE_BRIEFINGS,              "Data" DIR_SEPARATOR_STR "Voice" DIR_SEPARATOR_STR "Briefing",                  ".wav",                                         CF_TYPE_VOICE   },
268 #endif
269         { CF_TYPE_VOICE_CMD_BRIEF,              "Data" DIR_SEPARATOR_STR "Voice" DIR_SEPARATOR_STR "Command_briefings",".wav",                                          CF_TYPE_VOICE   },
270 #ifdef MAKE_FS1
271         { CF_TYPE_VOICE_DEBRIEFINGS,    "Data" DIR_SEPARATOR_STR "Voice" DIR_SEPARATOR_STR "Debriefings",                       ".wav",                                         CF_TYPE_VOICE   },
272 #else
273         { CF_TYPE_VOICE_DEBRIEFINGS,    "Data" DIR_SEPARATOR_STR "Voice" DIR_SEPARATOR_STR "Debriefing",                        ".wav",                                         CF_TYPE_VOICE   },
274 #endif
275         { CF_TYPE_VOICE_PERSONAS,               "Data" DIR_SEPARATOR_STR "Voice" DIR_SEPARATOR_STR "Personas",                  ".wav",                                         CF_TYPE_VOICE   },
276         { CF_TYPE_VOICE_SPECIAL,                "Data" DIR_SEPARATOR_STR "Voice" DIR_SEPARATOR_STR "Special",                           ".wav",                                         CF_TYPE_VOICE   },
277         { CF_TYPE_VOICE_TRAINING,               "Data" DIR_SEPARATOR_STR "Voice" DIR_SEPARATOR_STR "Training",                  ".wav",                                         CF_TYPE_VOICE   },
278         { CF_TYPE_MUSIC,                                        "Data" DIR_SEPARATOR_STR "Music",                                                       ".wav",                                         CF_TYPE_VOICE   },
279         { CF_TYPE_MOVIES,                                       "Data" DIR_SEPARATOR_STR "Movies",                                              ".mve .msb",                            CF_TYPE_DATA    },
280         { CF_TYPE_INTERFACE,                            "Data" DIR_SEPARATOR_STR "Interface",                                   ".pcx .ani .tga",                       CF_TYPE_DATA    },
281         { CF_TYPE_FONT,                                 "Data" DIR_SEPARATOR_STR "Fonts",                                                       ".vf",                                          CF_TYPE_DATA    },
282         { CF_TYPE_EFFECTS,                              "Data" DIR_SEPARATOR_STR "Effects",                                             ".ani .pcx .neb .tga",  CF_TYPE_DATA    },
283         { CF_TYPE_HUD,                                          "Data" DIR_SEPARATOR_STR "Hud",                                                 ".ani .pcx .tga",                       CF_TYPE_DATA    },
284         { CF_TYPE_PLAYER_MAIN,                  "Data" DIR_SEPARATOR_STR "Players",                                             "",                                                     CF_TYPE_DATA    },
285         { CF_TYPE_PLAYER_IMAGES_MAIN,   "Data" DIR_SEPARATOR_STR "Players" DIR_SEPARATOR_STR "Images",                  ".pcx",                                         CF_TYPE_PLAYER_MAIN     },
286 #ifdef MAKE_FS1
287         { CF_TYPE_CACHE,                                        "Cache",                                                        ".clr .tmp",                            CF_TYPE_DATA    },      //clr=cached color
288         { CF_TYPE_PLAYERS,                              "Players",                                              ".hcf",                                         CF_TYPE_ROOT    },
289         { CF_TYPE_SINGLE_PLAYERS,               "Players" DIR_SEPARATOR_STR "Single",                   ".plr .csg .css",                       CF_TYPE_PLAYERS },
290         { CF_TYPE_MULTI_PLAYERS,                "Players" DIR_SEPARATOR_STR "Multi",                            ".plr",                                         CF_TYPE_PLAYERS },
291 #else
292         { CF_TYPE_CACHE,                                        "Data" DIR_SEPARATOR_STR "Cache",                                                       ".clr .tmp",                            CF_TYPE_DATA    },      //clr=cached color
293         { CF_TYPE_PLAYERS,                              "Data" DIR_SEPARATOR_STR "Players",                                             ".hcf",                                         CF_TYPE_DATA    },
294         { CF_TYPE_SINGLE_PLAYERS,               "Data" DIR_SEPARATOR_STR "Players" DIR_SEPARATOR_STR "Single",                  ".plr .csg .css",                       CF_TYPE_PLAYERS },
295         { CF_TYPE_MULTI_PLAYERS,                "Data" DIR_SEPARATOR_STR "Players" DIR_SEPARATOR_STR "Multi",                           ".plr",                                         CF_TYPE_DATA    },
296 #endif
297         { CF_TYPE_MULTI_CACHE,                  "Data" DIR_SEPARATOR_STR "MultiData",                                   ".pcx .fs2",                            CF_TYPE_DATA    },
298         { CF_TYPE_CONFIG,                                       "Data" DIR_SEPARATOR_STR "Config",                                              ".cfg",                                         CF_TYPE_DATA    },
299         { CF_TYPE_SQUAD_IMAGES_MAIN,    "Data" DIR_SEPARATOR_STR "Players" DIR_SEPARATOR_STR "Squads",                  ".pcx",                                         CF_TYPE_DATA    },
300         { CF_TYPE_DEMOS,                                        "Data" DIR_SEPARATOR_STR "Demos",                                                       ".fsd",                                         CF_TYPE_DATA    },
301         { CF_TYPE_CBANIMS,                              "Data" DIR_SEPARATOR_STR "CBAnims",                                             ".ani",                                         CF_TYPE_DATA    },
302         { CF_TYPE_INTEL_ANIMS,                  "Data" DIR_SEPARATOR_STR "IntelAnims",                                  ".ani",                                         CF_TYPE_DATA    },
303 };
304
305
306 #define CFILE_STACK_MAX 8
307
308 int cfile_inited = 0;
309
310 Cfile_block Cfile_block_list[MAX_CFILE_BLOCKS];
311 CFILE Cfile_list[MAX_CFILE_BLOCKS];
312
313 //
314 // Function prototypes for internally-called functions
315 //
316 int cfget_cfile_block();
317 CFILE *cf_open_fill_cfblock(FILE * fp, int type);
318 CFILE *cf_open_packed_cfblock(FILE *fp, int type, int offset, int size);
319 void cf_chksum_long_init();
320
321 void cfile_close()
322 {
323         cf_free_secondary_filelist();
324 }
325
326 // determine if the given path is in a root directory (c:\  or  c:\freespace2.exe  or  c:\fred2.exe   etc)
327 int cfile_in_root_dir(char *exe_path)
328 {
329         int token_count = 0;
330         char path_copy[2048] = "";
331         char *tok;
332
333         // bogus
334         if(exe_path == NULL){
335                 return 1;
336         }
337
338         // copy the path
339         memset(path_copy, 0, 2048);
340         SDL_strlcpy(path_copy, exe_path, SDL_arraysize(path_copy));
341
342         // count how many slashes there are in the path
343         tok = strtok(path_copy, DIR_SEPARATOR_STR);
344         if(tok == NULL){
345                 return 1;
346         }       
347         do {
348                 token_count++;
349                 tok = strtok(NULL, DIR_SEPARATOR_STR);
350         } while(tok != NULL);
351                 
352         // root directory if we have <= 1 slash
353         if(token_count <= 2){
354                 return 1;
355         }
356
357         // not-root directory
358         return 0;
359 }
360
361 // cfile_init() initializes the cfile system.  Called once at application start.
362 //
363 //      returns:  success ==> 0
364 //           error   ==> non-zero
365 //
366 int cfile_init()
367 {
368         int i;
369
370         // initialize encryption
371         encrypt_init(); 
372
373         if ( !cfile_inited ) {
374                 // initialize root and user paths (may have been done already)
375                 if ( cfile_init_paths() ) {
376                         return 1;
377                 }
378
379                 cfile_inited = 1;
380
381                 for ( i = 0; i < MAX_CFILE_BLOCKS; i++ ) {
382                         Cfile_block_list[i].type = CFILE_BLOCK_UNUSED;
383                 }
384
385                 const char *extras_dir = os_config_read_string(NULL, "ExtrasPath", NULL);
386
387                 if ( extras_dir && (strlen(extras_dir) >= MAX_PATH_LEN) ) {
388                         extras_dir = NULL;
389                 }
390
391                 cf_build_secondary_filelist(extras_dir);
392
393                 // 32 bit CRC table init
394                 cf_chksum_long_init();
395
396
397
398                 atexit( cfile_close );
399         }
400
401         return 0;
402 }
403
404
405 // flush (delete all files in) the passed directory (by type), return the # of files deleted
406 // NOTE : WILL NOT DELETE READ-ONLY FILES
407 int cfile_flush_dir(int dir_type)
408 {
409         char filespec[MAX_PATH_LEN];
410         int del_count;
411
412         SDL_assert( CF_TYPE_SPECIFIED(dir_type) );
413
414         cf_create_default_path_string(filespec, dir_type);
415
416         // proceed to delete the files
417         del_count = 0;
418
419 #ifdef PLAT_UNIX
420         DIR *dirp;
421         struct dirent *dir;
422
423         dirp = opendir(filespec);
424         if (dirp) {
425                 while ( (dir = readdir(dirp)) != NULL ) {
426                         if ( !fnmatch("*", dir->d_name, 0) ) {
427                                 char fn[MAX_PATH_LEN];
428                                 SDL_snprintf(fn, MAX_PATH_LEN, "%s/%s", filespec, dir->d_name);
429
430                                 struct stat buf;
431                                 if (stat(fn, &buf) == -1) {
432                                         continue;
433                                 }
434
435                                 if (!S_ISREG(buf.st_mode)) {
436                                         continue;
437                                 }
438
439                                 // delete the file
440                                 cf_delete(dir->d_name, dir_type);
441
442                                 // increment the deleted count
443                                 del_count++;
444                         }
445                 }
446
447                 closedir(dirp);
448         }
449 #else
450         int find_handle;
451         _finddata_t find;
452
453         SDL_strlcat( filespec, "*", SDL_arraysize(filespec) );
454
455         find_handle = _findfirst( filespec, &find );
456
457         if (find_handle != -1) {
458                 do {
459                         if (!(find.attrib & _A_SUBDIR) && !(find.attrib & _A_RDONLY)) {
460                                 // delete the file
461                                 cf_delete(find.name, dir_type);
462
463                                 // increment the deleted count
464                                 del_count++;
465                         }
466                 } while (!_findnext(find_handle, &find));
467
468                 _findclose( find_handle );
469         }
470 #endif
471
472         // return the # of files deleted
473         return del_count;
474 }
475
476
477 // add the given extention to a filename (or filepath) if it doesn't already have this
478 // extension.
479 //    filename = name of filename or filepath to process
480 //    ext = extension to add.  Must start with the period
481 //    Returns: new filename or filepath with extension.
482 char *cf_add_ext(const char *filename, const char *ext)
483 {
484         int flen, elen;
485         static char path[MAX_PATH_LEN];
486
487         flen = strlen(filename);
488         elen = strlen(ext);
489         SDL_assert(flen < MAX_PATH_LEN);
490         SDL_strlcpy(path, filename, SDL_arraysize(path));
491         if ((flen < 4) || SDL_strcasecmp(path + flen - elen, ext)) {
492                 SDL_assert(flen + elen < MAX_PATH_LEN);
493                 SDL_strlcat(path, ext, SDL_arraysize(path));
494         }
495
496         return path;
497 }
498
499 // Deletes a file.
500 void cf_delete( const char *filename, int dir_type )
501 {
502         char longname[MAX_PATH_LEN];
503
504         SDL_assert( CF_TYPE_SPECIFIED(dir_type) );
505
506         cf_create_default_path_string( longname, dir_type, filename );
507
508         FILE *fp = fopen(longname, "rb");
509         if (fp) {
510                 // delete the file
511                 fclose(fp);
512                 unlink(longname);
513         }
514
515 }
516
517
518 // Same as _access function to read a file's access bits
519 int cf_access( const char *filename, int dir_type, int mode )
520 {
521         char longname[MAX_PATH_LEN];
522
523         SDL_assert( CF_TYPE_SPECIFIED(dir_type) );
524
525         cf_create_default_path_string( longname, dir_type, filename );
526
527         return access(longname,mode);
528 }
529
530
531 // Returns 1 if file exists, 0 if not.
532 int cf_exist( const char *filename, int dir_type )
533 {
534         char longname[MAX_PATH_LEN];
535
536         SDL_assert( CF_TYPE_SPECIFIED(dir_type) );
537
538         cf_create_default_path_string( longname, dir_type, filename );
539
540         FILE *fp = fopen(longname, "rb");
541         if (fp) {
542                 fclose(fp);
543                 return 1;
544         }
545
546         return 0;
547 }
548
549 int cf_rename(const char *old_name, const char *name, int dir_type)
550 {
551         SDL_assert( CF_TYPE_SPECIFIED(dir_type) );
552
553         int ret_code;
554         char old_longname[MAX_PATH_LEN];
555         char new_longname[MAX_PATH_LEN];
556         
557         cf_create_default_path_string( old_longname, dir_type, old_name );
558         cf_create_default_path_string( new_longname, dir_type, name );
559
560         ret_code = rename(old_longname, new_longname );         
561         if(ret_code != 0){
562                 switch(errno){
563                 case EACCES :
564                         return CF_RENAME_FAIL_ACCESS;
565                 case ENOENT :
566                 default:
567                         return CF_RENAME_FAIL_EXIST;
568                 }
569         }
570
571         return CF_RENAME_SUCCESS;
572         
573
574 }
575
576 // Creates the directory path if it doesn't exist. Even creates all its
577 // parent paths.
578 void cf_create_directory( int dir_type )
579 {
580         int num_dirs = 0;
581         int dir_tree[CF_MAX_PATH_TYPES];
582         char longname[MAX_PATH_LEN];
583
584         SDL_assert( CF_TYPE_SPECIFIED(dir_type) );
585
586         int current_dir = dir_type;
587
588         do {
589                 SDL_assert( num_dirs < CF_MAX_PATH_TYPES );             // Invalid Pathtypes data?
590
591                 dir_tree[num_dirs++] = current_dir;
592                 current_dir = Pathtypes[current_dir].parent_index;
593
594         } while( current_dir != CF_TYPE_ROOT );
595
596         
597         int i;
598
599         for (i=num_dirs-1; i>=0; i-- )  {
600                 cf_create_default_path_string( longname, dir_tree[i], NULL );
601
602                 if ( mkdir(longname, 0700) == 0 )       {
603                         mprintf(( "CFILE: Created new directory '%s'\n", longname ));
604                 }
605         }
606
607
608 }
609
610
611 // cfopen()
612 //
613 // parameters:  *filepath ==> name of file to open (may be path+name)
614 //              *mode     ==> specifies how file should be opened (eg "rb" for read binary)
615 //                            passing NULL to mode deletes the file if it exists and returns NULL
616 //               type     ==> CFILE_NORMAL
617 //                                        dir_type      =>      override extension check, value is one of CF_TYPE* #defines
618 //
619 //               NOTE: type parameter is an optional parameter.  The default value is CFILE_NORMAL
620 //
621 //
622 // returns:             success ==> address of CFILE structure
623 //                                      error   ==> NULL
624 //
625
626 CFILE *cfopen(const char *file_path, const char *mode, int type, int dir_type, bool localize)
627 {
628         char longname[MAX_PATH_LEN];
629
630 //      nprintf(("CFILE", "CFILE -- trying to open %s\n", file_path ));
631 // #if !defined(MULTIPLAYER_BETA_BUILD) && !defined(FS2_DEMO)
632
633         //================================================
634         // Check that all the parameters make sense
635         SDL_assert(file_path && strlen(file_path));
636         SDL_assert( mode != NULL );
637
638         //===========================================================
639         // If in write mode, just try to open the file straight off
640         // the harddisk.  No fancy packfile stuff here!
641         
642         if ( SDL_strchr(mode,'w') )     {
643 #ifdef PLAT_UNIX
644                 const char *toks = "/";
645 #else
646                 const char *toks = "/\\:";
647 #endif
648
649                 // For write-only files, require a full path or a path type
650                 if ( strpbrk(file_path, toks) ) {
651                         // Full path given?
652                         SDL_strlcpy(longname, file_path, SDL_arraysize(longname));
653                 } else {
654                         // Path type given?
655                         SDL_assert( dir_type != CF_TYPE_ANY );
656
657                         // Create the directory if necessary
658                         cf_create_directory( dir_type );
659
660                         cf_create_default_path_string( longname, dir_type, file_path );
661                 }
662
663                 // JOHN: TODO, you should create the path if it doesn't exist.
664                                 
665                 FILE *fp = fopen(longname, mode);
666                 if (fp) {
667                         return cf_open_fill_cfblock(fp, dir_type);
668                 }
669                 return NULL;
670         } 
671
672
673         //================================================
674         // Search for file on disk, on cdrom, or in a packfile
675
676         int offset, size;
677         char copy_file_path[MAX_PATH_LEN];  // FIX change in memory from cf_find_file_location
678         SDL_strlcpy(copy_file_path, file_path, SDL_arraysize(copy_file_path));
679
680
681         if ( cf_find_file_location( copy_file_path, dir_type, longname, &size, &offset, localize ) )    {
682                 // Fount it, now create a cfile out of it
683                 FILE *fp = fopen( longname, "rb" );
684
685                 if ( fp )       {
686                         if ( offset )   {
687                                 // Found it in a pack file
688                                 return cf_open_packed_cfblock(fp, dir_type, offset, size );
689                         } else {
690                                 // Found it in a normal file
691                                 return cf_open_fill_cfblock(fp, dir_type);
692                         }
693                 }
694         }
695
696         return NULL;
697 }
698
699
700 // ------------------------------------------------------------------------
701 // ctmpfile() 
702 //
703 // Open up a temporary file.  A unique name is automatically generated.  The
704 // file will be automatically deleted when file is closed.
705 //
706 // return:              NULL                                    =>              tmp file could not be opened
707 //                                      pointer to CFILE        =>              tmp file successfully opened
708 //
709 CFILE *ctmpfile()
710 {
711         FILE    *fp;
712         fp = tmpfile();
713         if ( fp )
714                 return cf_open_fill_cfblock(fp, 0);
715         else
716                 return NULL;
717 }
718
719
720
721 // cfget_cfile_block() will try to find an empty Cfile_block structure in the
722 //      Cfile_block_list[] array and return the index.
723 //
724 // returns:   success ==> index in Cfile_block_list[] array
725 //            failure ==> -1
726 //
727 int cfget_cfile_block()
728 {       
729         int i;
730         Cfile_block *cb;
731
732         for ( i = 0; i < MAX_CFILE_BLOCKS; i++ ) {
733                 cb = &Cfile_block_list[i];
734                 if ( cb->type == CFILE_BLOCK_UNUSED ) {
735                         cb->fp = NULL;
736                         cb->type = CFILE_BLOCK_USED;
737                         return i;
738                 }
739         }
740
741         // If we've reached this point, a free Cfile_block could not be found
742         nprintf(("Warning","A free Cfile_block could not be found.\n"));
743         SDL_assert(0);  // out of free cfile blocks
744         return -1;                      
745 }
746
747
748 // cfclose() closes the file
749 //
750 // returns:   success ==> 0
751 //                                failure ==> EOF
752 //
753 int cfclose( CFILE * cfile )
754 {
755         int result;
756
757         SDL_assert(cfile != NULL);
758         Cfile_block *cb;
759         SDL_assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
760         cb = &Cfile_block_list[cfile->id];      
761
762         result = 0;
763
764         if (cb->fp != NULL) {
765                 SDL_assert(cb->fp != NULL);
766                 result = fclose(cb->fp);
767         } else {
768                 // VP  do nothing
769         }
770
771         cb->type = CFILE_BLOCK_UNUSED;
772         return result;
773 }
774
775
776
777
778 // cf_open_fill_cfblock() will fill up a Cfile_block element in the Cfile_block_list[] array
779 // for the case of a file being opened by cf_open();
780 //
781 // returns:   success ==> ptr to CFILE structure.  
782 //            error   ==> NULL
783 //
784 CFILE *cf_open_fill_cfblock(FILE *fp, int type)
785 {
786         int cfile_block_index;
787
788         cfile_block_index = cfget_cfile_block();
789         if ( cfile_block_index == -1 ) {
790                 return NULL;
791         } else {
792                 CFILE *cfp;
793                 Cfile_block *cfbp;
794                 cfbp = &Cfile_block_list[cfile_block_index];
795                 cfp = &Cfile_list[cfile_block_index];;
796                 cfp->id = cfile_block_index;
797                 cfp->version = 0;
798                 cfbp->fp = fp;
799                 cfbp->dir_type = type;
800                 
801                 cf_init_lowlevel_read_code(cfp,0,filelength(fileno(fp)) );
802
803                 return cfp;
804         }
805 }
806
807
808 // cf_open_packed_cfblock() will fill up a Cfile_block element in the Cfile_block_list[] array
809 // for the case of a file being opened by cf_open();
810 //
811 // returns:   success ==> ptr to CFILE structure.  
812 //            error   ==> NULL
813 //
814 CFILE *cf_open_packed_cfblock(FILE *fp, int type, int offset, int size)
815 {
816         // Found it in a pack file
817         int cfile_block_index;
818         
819         cfile_block_index = cfget_cfile_block();
820         if ( cfile_block_index == -1 ) {
821                 return NULL;
822         } else {
823                 CFILE *cfp;
824                 Cfile_block *cfbp;
825                 cfbp = &Cfile_block_list[cfile_block_index];
826         
827                 cfp = &Cfile_list[cfile_block_index];
828                 cfp->id = cfile_block_index;
829                 cfp->version = 0;
830                 cfbp->fp = fp;
831                 cfbp->dir_type = type;
832
833                 cf_init_lowlevel_read_code(cfp,offset, size );
834
835                 return cfp;
836         }
837
838 }
839
840
841 int cf_get_dir_type(CFILE *cfile)
842 {
843         return Cfile_block_list[cfile->id].dir_type;
844 }
845
846
847 // version number of opened file.  Will be 0 unless you put something else here after you
848 // open a file.  Once set, you can use minimum version numbers with the read functions.
849 void cf_set_version( CFILE * cfile, int version )
850 {
851         SDL_assert(cfile != NULL);
852
853         cfile->version = version;
854 }
855
856 // routines to read basic data types from CFILE's.  Put here to
857 // simplify mac/pc reading from cfiles.
858
859 float cfread_float(CFILE *file, int ver, float deflt)
860 {
861         float f;
862
863         if (file->version < ver)
864                 return deflt;
865
866         if (cfread( &f, sizeof(f), 1, file) != 1)
867                 return deflt;
868
869     f = INTEL_FLOAT(f);
870         return f;
871 }
872
873 int cfread_int(CFILE *file, int ver, int deflt)
874 {
875         int i;
876
877         if (file->version < ver)
878                 return deflt;
879
880         if (cfread( &i, sizeof(i), 1, file) != 1)
881                 return deflt;
882
883         i = INTEL_INT(i);
884         return i;
885 }
886
887 uint cfread_uint(CFILE *file, int ver, uint deflt)
888 {
889         uint i;
890
891         if (file->version < ver)
892                 return deflt;
893
894         if (cfread( &i, sizeof(i), 1, file) != 1)
895                 return deflt;
896
897         i = INTEL_INT(i);
898         return i;
899 }
900
901 short cfread_short(CFILE *file, int ver, short deflt)
902 {
903         short s;
904
905         if (file->version < ver)
906                 return deflt;
907
908         if (cfread( &s, sizeof(s), 1, file) != 1)
909                 return deflt;
910
911         s = INTEL_SHORT(s);
912         return s;
913 }
914
915 ushort cfread_ushort(CFILE *file, int ver, ushort deflt)
916 {
917         ushort s;
918
919         if (file->version < ver)
920                 return deflt;
921
922         if (cfread( &s, sizeof(s), 1, file) != 1)
923                 return deflt;
924
925         s = INTEL_SHORT(s);
926         return s;
927 }
928
929 ubyte cfread_ubyte(CFILE *file, int ver, ubyte deflt)
930 {
931         ubyte b;
932
933         if (file->version < ver)
934                 return deflt;
935
936         if (cfread( &b, sizeof(b), 1, file) != 1)
937                 return deflt;
938
939         return b;
940 }
941
942 void cfread_vector(vector *vec, CFILE *file, int ver, vector *deflt)
943 {
944         if (file->version < ver) {
945                 if (deflt)
946                         *vec = *deflt;
947                 else
948                         vec->xyz.x = vec->xyz.y = vec->xyz.z = 0.0f;
949
950                 return;
951         }
952
953         vec->xyz.x = cfread_float(file, ver, deflt ? deflt->xyz.x : 0.0f);
954         vec->xyz.y = cfread_float(file, ver, deflt ? deflt->xyz.y : 0.0f);
955         vec->xyz.z = cfread_float(file, ver, deflt ? deflt->xyz.z : 0.0f);
956 }
957         
958 void cfread_angles(angles *ang, CFILE *file, int ver, angles *deflt)
959 {
960         if (file->version < ver) {
961                 if (deflt)
962                         *ang = *deflt;
963                 else
964                         ang->p = ang->b = ang->h = 0.0f;
965
966                 return;
967         }
968
969         ang->p = cfread_float(file, ver, deflt ? deflt->p : 0.0f);
970         ang->b = cfread_float(file, ver, deflt ? deflt->b : 0.0f);
971         ang->h = cfread_float(file, ver, deflt ? deflt->h : 0.0f);
972 }
973
974 char cfread_char(CFILE *file, int ver, char deflt)
975 {
976         char b;
977
978         if (file->version < ver)
979                 return deflt;
980
981         if (cfread( &b, sizeof(b), 1, file) != 1)
982                 return deflt;
983
984         return b;
985 }
986
987 void cfread_string(char *buf, int n, CFILE *file)
988 {
989         char c;
990
991         do {
992                 c = cfread_char(file);
993                 if ( n > 0 )    {
994                         *buf++ = c;
995                         n--;
996                 }
997         } while (c != 0 );
998 }
999
1000 void cfread_string_len(char *buf,int n, CFILE *file)
1001 {
1002         int len;
1003         len = cfread_int(file);
1004         SDL_assert( len < n );
1005         if (len)
1006                 cfread(buf, len, 1, file);
1007
1008         buf[len] = 0;
1009 }
1010
1011 // equivalent write functions of above read functions follow
1012
1013 int cfwrite_float(float f, CFILE *file)
1014 {
1015     f = INTEL_FLOAT(f);
1016         return cfwrite(&f, sizeof(f), 1, file);
1017 }
1018
1019 int cfwrite_int(int i, CFILE *file)
1020 {
1021         i = INTEL_INT(i);
1022         return cfwrite(&i, sizeof(i), 1, file);
1023 }
1024
1025 int cfwrite_uint(uint i, CFILE *file)
1026 {
1027         i = INTEL_INT(i);
1028         return cfwrite(&i, sizeof(i), 1, file);
1029 }
1030
1031 int cfwrite_short(short s, CFILE *file)
1032 {
1033         s = INTEL_SHORT(s);
1034         return cfwrite(&s, sizeof(s), 1, file);
1035 }
1036
1037 int cfwrite_ushort(ushort s, CFILE *file)
1038 {
1039         s = INTEL_SHORT(s);
1040         return cfwrite(&s, sizeof(s), 1, file);
1041 }
1042
1043 int cfwrite_ubyte(ubyte b, CFILE *file)
1044 {
1045         return cfwrite(&b, sizeof(b), 1, file);
1046 }
1047
1048 int cfwrite_vector(vector *vec, CFILE *file)
1049 {
1050         if(!cfwrite_float(vec->xyz.x, file)){
1051                 return 0;
1052         }
1053         if(!cfwrite_float(vec->xyz.y, file)){
1054                 return 0;
1055         }
1056         return cfwrite_float(vec->xyz.z, file);
1057 }
1058
1059 int cfwrite_angles(angles *ang, CFILE *file)
1060 {
1061         if(!cfwrite_float(ang->p, file)){
1062                 return 0;
1063         }
1064         if(!cfwrite_float(ang->b, file)){
1065                 return 0;
1066         }
1067         return cfwrite_float(ang->h, file);
1068 }
1069
1070 int cfwrite_char(char b, CFILE *file)
1071 {
1072         return cfwrite( &b, sizeof(b), 1, file);
1073 }
1074
1075 int cfwrite_string(const char *buf, CFILE *file)
1076 {
1077         if ( (!buf) || (buf && !buf[0]) ) {
1078                 return cfwrite_char(0, file);
1079         } 
1080         int len = strlen(buf);
1081         if(!cfwrite(buf, len, 1, file)){
1082                 return 0;
1083         }
1084         return cfwrite_char(0, file);                   // write out NULL termination                   
1085 }
1086
1087 int cfwrite_string_len(const char *buf, CFILE *file)
1088 {
1089         int len = strlen(buf);
1090
1091         if(!cfwrite_int(len, file)){
1092                 return 0;
1093         }
1094         if (len){
1095                 return cfwrite(buf,len,1,file);
1096         } 
1097
1098         return 1;
1099 }
1100
1101 // Get the filelength
1102 int cfilelength( CFILE * cfile )
1103 {
1104         SDL_assert(cfile != NULL);
1105         Cfile_block *cb;
1106         SDL_assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1107         cb = &Cfile_block_list[cfile->id];      
1108
1109         SDL_assert(cb->fp != NULL);
1110
1111         // cb->size gets set at cfopen
1112         return cb->size;
1113 }
1114
1115 // cfwrite() writes to the file
1116 //
1117 // returns:   number of full elements actually written
1118 //            
1119 //
1120 int cfwrite(const void *buf, int elsize, int nelem, CFILE *cfile)
1121 {
1122         SDL_assert(cfile != NULL);
1123         SDL_assert(buf != NULL);
1124         SDL_assert(elsize > 0);
1125         SDL_assert(nelem > 0);
1126
1127         Cfile_block *cb;
1128         SDL_assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1129         cb = &Cfile_block_list[cfile->id];      
1130
1131         int size = elsize * nelem;
1132
1133         SDL_assert(cb->fp != NULL);
1134         SDL_assert(cb->lib_offset == 0 );
1135         int bytes_written = fwrite( buf, 1, size, cb->fp );
1136
1137         if (bytes_written > 0) {
1138                 cb->raw_position += bytes_written;
1139         }
1140
1141         #if defined(CHECK_POSITION) && !defined(NDEBUG)
1142                 int tmp_offset = ftell(cb->fp) - cb->lib_offset;
1143                 SDL_assert(tmp_offset == cb->raw_position);
1144         #endif
1145
1146         return bytes_written / elsize;
1147 }
1148
1149
1150 // cfputc() writes a character to a file
1151 //
1152 // returns:   success ==> returns character written
1153 //                                error   ==> EOF
1154 //
1155 int cfputc(int c, CFILE *cfile)
1156 {
1157         int result = 0;
1158
1159         SDL_assert(cfile != NULL);
1160         Cfile_block *cb;
1161         SDL_assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1162         cb = &Cfile_block_list[cfile->id];      
1163
1164         SDL_assert(cb->fp != NULL);
1165         result = fputc(c, cb->fp);
1166
1167         return result;  
1168 }
1169
1170
1171 // cfgetc() reads a character from a file
1172 //
1173 // returns:   success ==> returns character read
1174 //                                error   ==> EOF
1175 //
1176 int cfgetc(CFILE *cfile)
1177 {
1178         SDL_assert(cfile != NULL);
1179         
1180         char tmp;
1181
1182         int result = cfread(&tmp, 1, 1, cfile );
1183         if ( result == 1 )      {
1184                 result = char(tmp);
1185         } else {
1186                 result = CF_EOF;
1187         }
1188
1189         return result;  
1190 }
1191
1192
1193
1194
1195
1196 // cfgets() reads a string from a file
1197 //
1198 // returns:   success ==> returns pointer to string
1199 //                                error   ==> NULL
1200 //
1201 char *cfgets(char *buf, int n, CFILE *cfile)
1202 {
1203         SDL_assert(cfile != NULL);
1204         SDL_assert(buf != NULL);
1205         SDL_assert(n > 0 );
1206
1207         char * t = buf;
1208         int i, c;
1209
1210         for (i=0; i<n-1; i++ )  {
1211                 do {
1212                         char tmp_c;
1213
1214                         int ret = cfread( &tmp_c, 1, 1, cfile );
1215                         if ( ret != 1 ) {
1216                                 *buf = 0;
1217                                 if ( buf > t )  {               
1218                                         return t;
1219                                 } else {
1220                                         return NULL;
1221                                 }
1222                         }
1223                         c = int(tmp_c);
1224                 } while ( c == 13 );
1225                 *buf++ = char(c);
1226                 if ( c=='\n' ) break;
1227         }
1228         *buf++ = 0;
1229
1230         return  t;
1231 }
1232
1233 // cfputs() writes a string to a file
1234 //
1235 // returns:   success ==> non-negative value
1236 //                                error   ==> EOF
1237 //
1238 int cfputs(const char *str, CFILE *cfile)
1239 {
1240         SDL_assert(cfile != NULL);
1241         SDL_assert(str != NULL);
1242
1243         Cfile_block *cb;
1244         SDL_assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1245         cb = &Cfile_block_list[cfile->id];      
1246
1247         int result = 0;
1248
1249         // cfputs() not supported for memory-mapped files
1250         SDL_assert(cb->fp != NULL);
1251         result = fputs(str, cb->fp);
1252
1253         return result;  
1254 }
1255
1256
1257 // 16 and 32 bit checksum stuff ----------------------------------------------------------
1258
1259 // CRC code for mission validation.  given to us by Kevin Bentley on 7/20/98.   Some sort of
1260 // checksumming code that he wrote a while ago.  
1261 #define CRC32_POLYNOMIAL                                        0xEDB88320L
1262 uint CRCTable[256];
1263
1264 #define CF_CHKSUM_SAMPLE_SIZE                           512
1265
1266 // update cur_chksum with the chksum of the new_data of size new_data_size
1267 ushort cf_add_chksum_short(ushort seed, const char *buffer, int size)
1268 {
1269         const ubyte * ptr = (const ubyte *)buffer;
1270         uint sum1,sum2;
1271
1272         sum1 = sum2 = (uint)(seed);
1273
1274         while(size--)   {
1275                 sum1 += *ptr++;
1276                 if (sum1 >= 255 ) sum1 -= 255;
1277                 sum2 += sum1;
1278         }
1279         sum2 %= 255;
1280         
1281         return (ushort)((sum1<<8)+ sum2);
1282 }
1283
1284 // update cur_chksum with the chksum of the new_data of size new_data_size
1285 uint cf_add_chksum_long(uint seed, const char *buffer, int size)
1286 {
1287         uint crc;
1288         unsigned const char *p;
1289         uint temp1;
1290         uint temp2;
1291
1292         p = (unsigned const char*)buffer;
1293         crc = seed;     
1294
1295         while (size--!=0){
1296           temp1 = (crc>>8)&0x00FFFFFFL;
1297           temp2 = CRCTable[((int)crc^*p++)&0xff];
1298           crc = temp1^temp2;
1299         }       
1300         
1301         return crc;
1302 }
1303
1304 void cf_chksum_long_init()
1305 {
1306         int i,j;
1307         uint crc;
1308
1309         for( i=0;i<=255;i++) {
1310                 crc=i;
1311                 for(j=8;j>0;j--) {
1312                         if(crc&1)
1313                                  crc=(crc>>1)^CRC32_POLYNOMIAL;
1314                         else
1315                                  crc>>=1;
1316                 }
1317                 CRCTable[i]=crc;
1318         }       
1319 }
1320
1321 // single function convenient to use for both short and long checksums
1322 // NOTE : only one of chk_short or chk_long must be non-NULL (indicating which checksum to perform)
1323 int cf_chksum_do(CFILE *cfile, ushort *chk_short, uint *chk_long, int max_size)
1324 {
1325         char cf_buffer[CF_CHKSUM_SAMPLE_SIZE];
1326         int is_long;
1327         int cf_len = 0;
1328         int cf_total;
1329         int read_size;
1330
1331         // determine whether we're doing a short or long checksum
1332         is_long = 0;
1333         if(chk_short){
1334                 SDL_assert(!chk_long);          
1335                 *chk_short = 0;
1336         } else {
1337                 SDL_assert(chk_long);
1338                 is_long = 1;
1339                 *chk_long = 0;
1340         }
1341
1342         // if max_size is -1, set it to be the size of the file
1343         if(max_size < 0){
1344                 cfseek(cfile, 0, SEEK_SET);
1345                 max_size = cfilelength(cfile);
1346         }
1347         
1348         cf_total = 0;
1349         do {
1350                 // determine how much we want to read
1351                 if((max_size - cf_total) >= CF_CHKSUM_SAMPLE_SIZE){
1352                         read_size = CF_CHKSUM_SAMPLE_SIZE;
1353                 } else {
1354                         read_size = max_size - cf_total;
1355                 }
1356
1357                 // read in some buffer
1358                 cf_len = cfread(cf_buffer, 1, read_size, cfile);
1359
1360                 // total we've read so far
1361                 cf_total += cf_len;
1362
1363                 // add the checksum
1364                 if(cf_len > 0){
1365                         // do the proper short or long checksum
1366                         if(is_long){
1367                                 *chk_long = cf_add_chksum_long(*chk_long, cf_buffer, cf_len);
1368                         } else {
1369                                 *chk_short = cf_add_chksum_short(*chk_short, cf_buffer, cf_len);
1370                         }
1371                 }
1372         } while((cf_len > 0) && (cf_total < max_size));
1373
1374         return 1;
1375 }
1376
1377 // get the 2 byte checksum of the passed filename - return 0 if operation failed, 1 if succeeded
1378 int cf_chksum_short(const char *filename, ushort *chksum, int max_size, int cf_type)
1379 {
1380         int ret_val;
1381         CFILE *cfile = NULL;            
1382         
1383         // zero the checksum
1384         *chksum = 0;
1385
1386         // attempt to open the file
1387         cfile = cfopen(filename,"rt",CFILE_NORMAL,cf_type);
1388         if(cfile == NULL){              
1389                 return 0;
1390         }
1391         
1392         // call the overloaded cf_chksum function()
1393         ret_val = cf_chksum_do(cfile, chksum, NULL, max_size);
1394
1395         // close the file down
1396         cfclose(cfile);
1397         cfile = NULL;
1398
1399         // return the result
1400         return ret_val;
1401 }
1402
1403 // get the 2 byte checksum of the passed file - return 0 if operation failed, 1 if succeeded
1404 // NOTE : preserves current file position
1405 int cf_chksum_short(CFILE *file, ushort *chksum, int max_size)
1406 {
1407         int ret_code;
1408         int start_pos;
1409         
1410         // Returns current position of file.
1411         start_pos = cftell(file);
1412         if(start_pos == -1){
1413                 return 0;
1414         }
1415         
1416         // move to the beginning of the file
1417         if(cfseek(file, 0, CF_SEEK_SET)){
1418                 return 0;
1419         }
1420         ret_code = cf_chksum_do(file, chksum, NULL, max_size);
1421         // move back to the start position
1422         cfseek(file, start_pos, CF_SEEK_SET);
1423
1424         return ret_code;
1425 }
1426
1427 // get the 32 bit CRC checksum of the passed filename - return 0 if operation failed, 1 if succeeded
1428 int cf_chksum_long(const char *filename, uint *chksum, int max_size, int cf_type)
1429 {
1430         int ret_val;
1431         CFILE *cfile = NULL;            
1432         
1433         // zero the checksum
1434         *chksum = 0;
1435
1436         // attempt to open the file
1437         cfile = cfopen(filename,"rt",CFILE_NORMAL,cf_type);
1438         if(cfile == NULL){              
1439                 return 0;
1440         }
1441         
1442         // call the overloaded cf_chksum function()
1443         ret_val = cf_chksum_do(cfile, NULL, chksum, max_size);
1444
1445         // close the file down
1446         cfclose(cfile);
1447         cfile = NULL;
1448
1449         // return the result
1450         return ret_val; 
1451 }
1452
1453 // get the 32 bit CRC checksum of the passed file - return 0 if operation failed, 1 if succeeded
1454 // NOTE : preserves current file position
1455 int cf_chksum_long(CFILE *file, uint *chksum, int max_size)
1456 {
1457         int ret_code;
1458         int start_pos;
1459         
1460         // Returns current position of file.
1461         start_pos = cftell(file);
1462         if(start_pos == -1){
1463                 return 0;
1464         }
1465         
1466         // move to the beginning of the file
1467         if(cfseek(file, 0, CF_SEEK_SET)){
1468                 return 0;
1469         }
1470         ret_code = cf_chksum_do(file, NULL, chksum, max_size);
1471         // move back to the start position
1472         cfseek(file, start_pos, CF_SEEK_SET);
1473
1474         return ret_code;
1475 }
1476
1477
1478 // Flush the open file buffer
1479 //
1480 // exit: 0 - success
1481 //                      1 - failure
1482 int cflush(CFILE *cfile)
1483 {
1484         SDL_assert(cfile != NULL);
1485         Cfile_block *cb;
1486         SDL_assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1487         cb = &Cfile_block_list[cfile->id];      
1488
1489         SDL_assert(cb->fp != NULL);
1490         return fflush(cb->fp);
1491 }
1492
1493 // fill in Cfile_root_dir[] and Cfile_user_dir[]
1494 // this can be called at any time, even before cfile_init()
1495 //  returns: non-zero on error
1496 int cfile_init_paths()
1497 {
1498         if ( strlen(Cfile_root_dir) && strlen(Cfile_user_dir) ) {
1499                 return 0;
1500         }
1501
1502 #ifndef __EMSCRIPTEN__
1503         char *t_path = SDL_GetBasePath();
1504
1505         // make sure we have something
1506         if (t_path == NULL) {
1507                 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Error trying to determine executable directory!", NULL);
1508                 return 1;
1509         }
1510
1511         // size check
1512         if ( strlen(t_path) >= CFILE_ROOT_DIRECTORY_LEN ) {
1513                 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Executable path is too long!", NULL);
1514                 return 1;
1515         }
1516
1517         // set root directory
1518         SDL_strlcpy(Cfile_root_dir, t_path, SDL_arraysize(Cfile_root_dir));
1519         // free SDL copy
1520         SDL_free(t_path);
1521         t_path = NULL;
1522
1523         // are we in a root directory?
1524         if ( cfile_in_root_dir(Cfile_root_dir) ) {
1525                 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Freespace2/Fred2 cannot be run from a drive root directory!", NULL);
1526                 return 1;
1527         }
1528
1529         // now for the user/pref directory, the writable location
1530         char *u_path = SDL_GetPrefPath(Osreg_company_name, Osreg_app_name);
1531
1532         // make sure we have something
1533         if (u_path == NULL) {
1534                 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Error trying to determine preferences directory!", NULL);
1535                 return 1;
1536         }
1537
1538         // size check
1539         if ( strlen(u_path) >= CFILE_ROOT_DIRECTORY_LEN ) {
1540                 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Preferences path is too long!", NULL);
1541                 return 1;
1542         }
1543
1544         // set user/pref directory
1545         SDL_strlcpy(Cfile_user_dir, u_path, SDL_arraysize(Cfile_user_dir));
1546         // free SDL copy
1547         SDL_free(u_path);
1548         u_path = NULL;
1549 #else
1550         const char *root_path = "/Game";
1551         const char *user_path = "/User";
1552
1553         SDL_snprintf(Cfile_root_dir, SDL_arraysize(Cfile_root_dir), "%s/", root_path);
1554         SDL_snprintf(Cfile_user_dir, SDL_arraysize(Cfile_user_dir), "%s/", user_path);
1555
1556         EM_ASM({
1557                 const user_path = UTF8ToString($0);
1558
1559                 FS.mkdir(user_path);
1560                 FS.mount(IDBFS, { name: UTF8ToString($1) }, user_path);
1561
1562                 Module.sync_in_progress = 1;
1563
1564                 if (Module['setStatus']) {
1565                         Module['setStatus']('Syncing user data...');
1566                 }
1567
1568                 FS.syncfs(true, function(err) {
1569                         if (err && err.code !== 'EEXIST') {
1570                                 console.log('FS.syncfs() load error: ' + err);
1571                         } else {
1572                                 Module.sync_in_progress = 0;
1573
1574                                 // remove initial loading/progress screen
1575                                 var progress = document.getElementById('progress');
1576                                 progress.hidden = true;
1577                         }
1578                 });
1579         }, user_path, Osreg_app_name);
1580 #endif
1581
1582         // see if CF_TYPE_DATA exists for user and if not populate user path
1583         // with full directory tree
1584         char pathname[MAX_PATH_LEN];
1585         struct stat info;
1586
1587         SDL_strlcpy(pathname, Cfile_user_dir, MAX_PATH_LEN);
1588         SDL_strlcat(pathname, Pathtypes[CF_TYPE_DATA].path, MAX_PATH_LEN);
1589
1590         if ( stat(pathname, &info) != 0 ) {
1591                 cf_create_directory(CF_TYPE_MAPS);
1592                 cf_create_directory(CF_TYPE_TEXT);
1593                 cf_create_directory(CF_TYPE_MISSIONS);
1594                 cf_create_directory(CF_TYPE_MODELS);
1595                 cf_create_directory(CF_TYPE_TABLES);
1596                 cf_create_directory(CF_TYPE_SOUNDS_8B22K);
1597                 cf_create_directory(CF_TYPE_SOUNDS_16B11K);
1598                 cf_create_directory(CF_TYPE_VOICE_BRIEFINGS);
1599                 cf_create_directory(CF_TYPE_VOICE_CMD_BRIEF);
1600                 cf_create_directory(CF_TYPE_VOICE_DEBRIEFINGS);
1601                 cf_create_directory(CF_TYPE_VOICE_PERSONAS);
1602                 cf_create_directory(CF_TYPE_VOICE_SPECIAL);
1603                 cf_create_directory(CF_TYPE_VOICE_TRAINING);
1604                 cf_create_directory(CF_TYPE_MUSIC);
1605                 cf_create_directory(CF_TYPE_MOVIES);
1606                 cf_create_directory(CF_TYPE_INTERFACE);
1607                 cf_create_directory(CF_TYPE_FONT);
1608                 cf_create_directory(CF_TYPE_EFFECTS);
1609                 cf_create_directory(CF_TYPE_HUD);
1610                 cf_create_directory(CF_TYPE_PLAYER_IMAGES_MAIN);
1611                 cf_create_directory(CF_TYPE_CACHE);
1612                 cf_create_directory(CF_TYPE_SINGLE_PLAYERS);
1613                 cf_create_directory(CF_TYPE_MULTI_PLAYERS);
1614                 cf_create_directory(CF_TYPE_MULTI_CACHE);
1615                 cf_create_directory(CF_TYPE_CONFIG);
1616                 cf_create_directory(CF_TYPE_SQUAD_IMAGES_MAIN);
1617                 cf_create_directory(CF_TYPE_DEMOS);
1618                 cf_create_directory(CF_TYPE_CBANIMS);
1619                 cf_create_directory(CF_TYPE_INTEL_ANIMS);
1620         }
1621
1622         return 0;
1623 }