2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/CFile/cfile.cpp $
15 * Utilities for operating on files
18 * Revision 1.14 2006/04/26 19:36:57 taylor
19 * address an error handling bug in cfwrite()
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
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
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
34 * Revision 1.10 2004/06/11 00:28:39 tigital
35 * byte-swapping changes for bigendian systems
37 * Revision 1.9 2003/05/25 02:30:42 taylor
40 * Revision 1.8 2003/02/20 17:41:07 theoddone33
41 * Userdir patch from Taylor Richards
43 * Revision 1.7 2002/12/30 03:23:29 relnev
44 * disable root dir check
46 * Revision 1.6 2002/06/17 06:33:08 relnev
47 * ryan's struct patch for gcc 2.95
49 * Revision 1.5 2002/06/09 04:41:15 relnev
50 * added copyright header
52 * Revision 1.4 2002/06/05 08:05:28 relnev
53 * stub/warning removal.
55 * reworked the sound code.
57 * Revision 1.3 2002/05/28 08:52:03 relnev
58 * implemented two assembly stubs.
60 * cleaned up a few warnings.
62 * added a little demo hackery to make it progress a little farther.
64 * Revision 1.2 2002/05/28 06:28:20 theoddone33
65 * Filesystem mods, actually reads some data files now
67 * Revision 1.1.1.1 2002/05/03 03:28:08 root
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.
75 * 19 9/08/99 12:03a Dave
76 * Make squad logos render properly in D3D all the time. Added intel anim
79 * 18 9/06/99 2:35p Dave
80 * Rename briefing and debriefing voice direcrory paths properly.
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
88 * 16 8/31/99 9:46a Dave
89 * Support for new cfile cbanims directory.
91 * 15 8/06/99 11:20a Johns
92 * Don't call game_cd_changed() in a demo or multiplayer beta build.
94 * 14 6/08/99 2:33p Dave
95 * Fixed release build warning. Put in hud config stuff.
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.
101 * 12 4/30/99 12:18p Dave
102 * Several minor bug fixes.
104 * 11 4/07/99 5:54p Dave
105 * Fixes for encryption in updatelauncher.
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 :)
111 * 9 2/22/99 10:31p Andsager
112 * Get rid of unneeded includes.
114 * 8 1/12/99 3:15a Dave
115 * Barracks screen support for selecting squad logos. We need real artwork
118 * 7 11/30/98 1:07p Dave
119 * 16 bit conversion, first run.
121 * 6 10/29/98 10:41a Dave
122 * Change the way cfile initializes exe directory.
124 * 5 10/13/98 9:19a Andsager
125 * Add localization support to cfile. Optional parameter with cfopen that
126 * looks for localized files.
128 * 4 10/12/98 9:54a Dave
129 * Fixed a few file organization things.
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
136 * 2 10/07/98 10:52a Dave
139 * 1 10/07/98 10:48a Dave
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
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.
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.
156 * 107 9/04/98 3:51p Dave
157 * Put in validated mission updating and application during stats
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.
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.
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.
172 * 103 6/17/98 9:30a Allender
173 * fixed red alert replay stats clearing problem
175 * 102 6/05/98 9:46a Lawrance
176 * check for CD change on cfopen()
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
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.
188 * 99 5/11/98 10:59a John
189 * Moved the low-level file reading code into cfilearchive.cpp.
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
197 * 97 5/03/98 11:53a John
198 * Fixed filename case mangling.
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.
204 * 95 5/02/98 11:05p Allender
205 * fix cfilelength for pack files
210 #define _CFILE_INTERNAL
220 #include <winbase.h> /* needed for memory mapping of file functions */
225 #include <sys/stat.h>
226 #include <sys/types.h>
232 //#include "outwnd.h"
233 //#include "vecmat.h"
235 #include "cfilesystem.h"
236 #include "cfilearchive.h"
238 #include "osregistry.h"
240 char Cfile_root_dir[CFILE_ROOT_DIRECTORY_LEN] = "";
241 char Cfile_user_dir[CFILE_ROOT_DIRECTORY_LEN] = "";
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.
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 { CF_TYPE_MAPS, "Data" DIR_SEPARATOR_STR "Maps", ".pcx .ani .tga", CF_TYPE_DATA },
254 { CF_TYPE_TEXT, "Data" DIR_SEPARATOR_STR "Text", ".txt .net", CF_TYPE_DATA },
256 { CF_TYPE_MISSIONS, "Data" DIR_SEPARATOR_STR "Missions", ".fsm .fsc .ntl .ssv", CF_TYPE_DATA },
258 { CF_TYPE_MISSIONS, "Data" DIR_SEPARATOR_STR "Missions", ".fs2 .fc2 .ntl .ssv", CF_TYPE_DATA },
260 { CF_TYPE_MODELS, "Data" DIR_SEPARATOR_STR "Models", ".pof", CF_TYPE_DATA },
261 { CF_TYPE_TABLES, "Data" DIR_SEPARATOR_STR "Tables", ".tbl", CF_TYPE_DATA },
262 { CF_TYPE_SOUNDS, "Data" DIR_SEPARATOR_STR "Sounds", ".wav", CF_TYPE_DATA },
263 { CF_TYPE_SOUNDS_8B22K, "Data" DIR_SEPARATOR_STR "Sounds" DIR_SEPARATOR_STR "8b22k", ".wav", CF_TYPE_SOUNDS },
264 { CF_TYPE_SOUNDS_16B11K, "Data" DIR_SEPARATOR_STR "Sounds" DIR_SEPARATOR_STR "16b11k", ".wav", CF_TYPE_SOUNDS },
265 { CF_TYPE_VOICE, "Data" DIR_SEPARATOR_STR "Voice", "", CF_TYPE_DATA },
267 { CF_TYPE_VOICE_BRIEFINGS, "Data" DIR_SEPARATOR_STR "Voice" DIR_SEPARATOR_STR "Briefings", ".wav", CF_TYPE_VOICE },
269 { CF_TYPE_VOICE_BRIEFINGS, "Data" DIR_SEPARATOR_STR "Voice" DIR_SEPARATOR_STR "Briefing", ".wav", CF_TYPE_VOICE },
271 { CF_TYPE_VOICE_CMD_BRIEF, "Data" DIR_SEPARATOR_STR "Voice" DIR_SEPARATOR_STR "Command_briefings",".wav", CF_TYPE_VOICE },
273 { CF_TYPE_VOICE_DEBRIEFINGS, "Data" DIR_SEPARATOR_STR "Voice" DIR_SEPARATOR_STR "Debriefings", ".wav", CF_TYPE_VOICE },
275 { CF_TYPE_VOICE_DEBRIEFINGS, "Data" DIR_SEPARATOR_STR "Voice" DIR_SEPARATOR_STR "Debriefing", ".wav", CF_TYPE_VOICE },
277 { CF_TYPE_VOICE_PERSONAS, "Data" DIR_SEPARATOR_STR "Voice" DIR_SEPARATOR_STR "Personas", ".wav", CF_TYPE_VOICE },
278 { CF_TYPE_VOICE_SPECIAL, "Data" DIR_SEPARATOR_STR "Voice" DIR_SEPARATOR_STR "Special", ".wav", CF_TYPE_VOICE },
279 { CF_TYPE_VOICE_TRAINING, "Data" DIR_SEPARATOR_STR "Voice" DIR_SEPARATOR_STR "Training", ".wav", CF_TYPE_VOICE },
280 { CF_TYPE_MUSIC, "Data" DIR_SEPARATOR_STR "Music", ".wav", CF_TYPE_VOICE },
281 { CF_TYPE_MOVIES, "Data" DIR_SEPARATOR_STR "Movies", ".mve .msb", CF_TYPE_DATA },
282 { CF_TYPE_INTERFACE, "Data" DIR_SEPARATOR_STR "Interface", ".pcx .ani .tga", CF_TYPE_DATA },
283 { CF_TYPE_FONT, "Data" DIR_SEPARATOR_STR "Fonts", ".vf", CF_TYPE_DATA },
284 { CF_TYPE_EFFECTS, "Data" DIR_SEPARATOR_STR "Effects", ".ani .pcx .neb .tga", CF_TYPE_DATA },
285 { CF_TYPE_HUD, "Data" DIR_SEPARATOR_STR "Hud", ".ani .pcx .tga", CF_TYPE_DATA },
286 { CF_TYPE_PLAYER_MAIN, "Data" DIR_SEPARATOR_STR "Players", "", CF_TYPE_DATA },
287 { CF_TYPE_PLAYER_IMAGES_MAIN, "Data" DIR_SEPARATOR_STR "Players" DIR_SEPARATOR_STR "Images", ".pcx", CF_TYPE_PLAYER_MAIN },
288 { CF_TYPE_CACHE, "Data" DIR_SEPARATOR_STR "Cache", ".clr .tmp", CF_TYPE_DATA }, //clr=cached color
289 { CF_TYPE_PLAYERS, "Data" DIR_SEPARATOR_STR "Players", ".hcf", CF_TYPE_DATA },
290 { CF_TYPE_SINGLE_PLAYERS, "Data" DIR_SEPARATOR_STR "Players" DIR_SEPARATOR_STR "Single", ".plr .csg .css", CF_TYPE_PLAYERS },
291 { CF_TYPE_MULTI_PLAYERS, "Data" DIR_SEPARATOR_STR "Players" DIR_SEPARATOR_STR "Multi", ".plr", CF_TYPE_DATA },
292 { CF_TYPE_MULTI_CACHE, "Data" DIR_SEPARATOR_STR "MultiData", ".pcx .fs2", CF_TYPE_DATA },
293 { CF_TYPE_CONFIG, "Data" DIR_SEPARATOR_STR "Config", ".cfg", CF_TYPE_DATA },
294 { CF_TYPE_SQUAD_IMAGES_MAIN, "Data" DIR_SEPARATOR_STR "Players" DIR_SEPARATOR_STR "Squads", ".pcx", CF_TYPE_DATA },
295 { CF_TYPE_DEMOS, "Data" DIR_SEPARATOR_STR "Demos", ".fsd", CF_TYPE_DATA },
296 { CF_TYPE_CBANIMS, "Data" DIR_SEPARATOR_STR "CBAnims", ".ani", CF_TYPE_DATA },
297 { CF_TYPE_INTEL_ANIMS, "Data" DIR_SEPARATOR_STR "IntelAnims", ".ani", CF_TYPE_DATA },
301 #define CFILE_STACK_MAX 8
303 int cfile_inited = 0;
305 Cfile_block Cfile_block_list[MAX_CFILE_BLOCKS];
306 CFILE Cfile_list[MAX_CFILE_BLOCKS];
309 // Function prototypes for internally-called functions
311 int cfget_cfile_block();
312 CFILE *cf_open_fill_cfblock(FILE * fp, int type);
313 CFILE *cf_open_packed_cfblock(FILE *fp, int type, int offset, int size);
314 CFILE *cf_open_mapped_fill_cfblock(HANDLE hFile, int type);
315 void cf_chksum_long_init();
319 cf_free_secondary_filelist();
322 // determine if the given path is in a root directory (c:\ or c:\freespace2.exe or c:\fred2.exe etc)
323 int cfile_in_root_dir(char *exe_path)
326 char path_copy[2048] = "";
330 if(exe_path == NULL){
335 memset(path_copy, 0, 2048);
336 SDL_strlcpy(path_copy, exe_path, sizeof(path_copy));
338 // count how many slashes there are in the path
339 tok = strtok(path_copy, DIR_SEPARATOR_STR);
345 tok = strtok(NULL, DIR_SEPARATOR_STR);
346 } while(tok != NULL);
348 // root directory if we have <= 1 slash
349 if(token_count <= 2){
353 // not-root directory
357 // cfile_init() initializes the cfile system. Called once at application start.
359 // returns: success ==> 0
360 // error ==> non-zero
366 // initialize encryption
369 if ( !cfile_inited ) {
370 // initialize root and user paths (may have been done already)
371 if ( cfile_init_paths() ) {
377 for ( i = 0; i < MAX_CFILE_BLOCKS; i++ ) {
378 Cfile_block_list[i].type = CFILE_BLOCK_UNUSED;
381 const char *extras_dir = os_config_read_string(NULL, "ExtrasPath", NULL);
383 if ( extras_dir && (strlen(extras_dir) >= MAX_PATH_LEN) ) {
387 cf_build_secondary_filelist(extras_dir);
389 // 32 bit CRC table init
390 cf_chksum_long_init();
394 atexit( cfile_close );
401 // flush (delete all files in) the passed directory (by type), return the # of files deleted
402 // NOTE : WILL NOT DELETE READ-ONLY FILES
403 int cfile_flush_dir(int dir_type)
405 char filespec[MAX_PATH_LEN];
408 SDL_assert( CF_TYPE_SPECIFIED(dir_type) );
410 cf_create_default_path_string(filespec, dir_type);
412 // proceed to delete the files
419 dirp = opendir(filespec);
421 while ( (dir = readdir(dirp)) != NULL ) {
422 if ( !fnmatch("*", dir->d_name, 0) ) {
423 char fn[MAX_PATH_LEN];
424 SDL_snprintf(fn, MAX_PATH_LEN, "%s/%s", filespec, dir->d_name);
427 if (stat(fn, &buf) == -1) {
431 if (!S_ISREG(buf.st_mode)) {
436 cf_delete(dir->d_name, dir_type);
438 // increment the deleted count
449 SDL_strlcat( filespec, "*", sizeof(filespec) );
451 find_handle = _findfirst( filespec, &find );
453 if (find_handle != -1) {
455 if (!(find.attrib & _A_SUBDIR) && !(find.attrib & _A_RDONLY)) {
457 cf_delete(find.name, dir_type);
459 // increment the deleted count
462 } while (!_findnext(find_handle, &find));
464 _findclose( find_handle );
468 // return the # of files deleted
473 // add the given extention to a filename (or filepath) if it doesn't already have this
475 // filename = name of filename or filepath to process
476 // ext = extension to add. Must start with the period
477 // Returns: new filename or filepath with extension.
478 char *cf_add_ext(const char *filename, const char *ext)
481 static char path[MAX_PATH_LEN];
483 flen = strlen(filename);
485 SDL_assert(flen < MAX_PATH_LEN);
486 SDL_strlcpy(path, filename, sizeof(path));
487 if ((flen < 4) || SDL_strcasecmp(path + flen - elen, ext)) {
488 SDL_assert(flen + elen < MAX_PATH_LEN);
489 SDL_strlcat(path, ext, sizeof(path));
496 void cf_delete( const char *filename, int dir_type )
498 char longname[MAX_PATH_LEN];
500 SDL_assert( CF_TYPE_SPECIFIED(dir_type) );
502 cf_create_default_path_string( longname, dir_type, filename );
504 FILE *fp = fopen(longname, "rb");
514 // Same as _access function to read a file's access bits
515 int cf_access( const char *filename, int dir_type, int mode )
517 char longname[MAX_PATH_LEN];
519 SDL_assert( CF_TYPE_SPECIFIED(dir_type) );
521 cf_create_default_path_string( longname, dir_type, filename );
523 return access(longname,mode);
527 // Returns 1 if file exists, 0 if not.
528 int cf_exist( const char *filename, int dir_type )
530 char longname[MAX_PATH_LEN];
532 SDL_assert( CF_TYPE_SPECIFIED(dir_type) );
534 cf_create_default_path_string( longname, dir_type, filename );
536 FILE *fp = fopen(longname, "rb");
545 int cf_rename(const char *old_name, const char *name, int dir_type)
547 SDL_assert( CF_TYPE_SPECIFIED(dir_type) );
550 char old_longname[MAX_PATH_LEN];
551 char new_longname[MAX_PATH_LEN];
553 cf_create_default_path_string( old_longname, dir_type, old_name );
554 cf_create_default_path_string( new_longname, dir_type, name );
556 ret_code = rename(old_longname, new_longname );
560 return CF_RENAME_FAIL_ACCESS;
563 return CF_RENAME_FAIL_EXIST;
567 return CF_RENAME_SUCCESS;
572 // Creates the directory path if it doesn't exist. Even creates all its
574 void cf_create_directory( int dir_type )
577 int dir_tree[CF_MAX_PATH_TYPES];
578 char longname[MAX_PATH_LEN];
580 SDL_assert( CF_TYPE_SPECIFIED(dir_type) );
582 int current_dir = dir_type;
585 SDL_assert( num_dirs < CF_MAX_PATH_TYPES ); // Invalid Pathtypes data?
587 dir_tree[num_dirs++] = current_dir;
588 current_dir = Pathtypes[current_dir].parent_index;
590 } while( current_dir != CF_TYPE_ROOT );
595 for (i=num_dirs-1; i>=0; i-- ) {
596 cf_create_default_path_string( longname, dir_tree[i], NULL );
598 if ( mkdir(longname, 0700) == 0 ) {
599 mprintf(( "CFILE: Created new directory '%s'\n", longname ));
609 // parameters: *filepath ==> name of file to open (may be path+name)
610 // *mode ==> specifies how file should be opened (eg "rb" for read binary)
611 // passing NULL to mode deletes the file if it exists and returns NULL
612 // type ==> one of: CFILE_NORMAL
613 // CFILE_MEMORY_MAPPED
614 // dir_type => override extension check, value is one of CF_TYPE* #defines
616 // NOTE: type parameter is an optional parameter. The default value is CFILE_NORMAL
619 // returns: success ==> address of CFILE structure
623 CFILE *cfopen(const char *file_path, const char *mode, int type, int dir_type, bool localize)
625 char longname[MAX_PATH_LEN];
627 // nprintf(("CFILE", "CFILE -- trying to open %s\n", file_path ));
628 // #if !defined(MULTIPLAYER_BETA_BUILD) && !defined(FS2_DEMO)
630 //================================================
631 // Check that all the parameters make sense
632 SDL_assert(file_path && strlen(file_path));
633 SDL_assert( mode != NULL );
635 // Can only open read-only binary files in memory mapped mode.
636 if ( (type & CFILE_MEMORY_MAPPED) && strcmp(mode,"rb") ) {
641 //===========================================================
642 // If in write mode, just try to open the file straight off
643 // the harddisk. No fancy packfile stuff here!
645 if ( SDL_strchr(mode,'w') ) {
646 // For write-only files, require a full path or a path type
648 if ( strpbrk(file_path, "/") ) {
650 if ( strpbrk(file_path,"/\\:") ) {
653 SDL_strlcpy(longname, file_path, sizeof(longname));
656 SDL_assert( dir_type != CF_TYPE_ANY );
658 // Create the directory if necessary
659 cf_create_directory( dir_type );
661 cf_create_default_path_string( longname, dir_type, file_path );
663 SDL_assert( !(type & CFILE_MEMORY_MAPPED) );
665 // JOHN: TODO, you should create the path if it doesn't exist.
667 FILE *fp = fopen(longname, mode);
669 return cf_open_fill_cfblock(fp, dir_type);
675 //================================================
676 // Search for file on disk, on cdrom, or in a packfile
679 char copy_file_path[MAX_PATH_LEN]; // FIX change in memory from cf_find_file_location
680 SDL_strlcpy(copy_file_path, file_path, sizeof(copy_file_path));
683 if ( cf_find_file_location( copy_file_path, dir_type, longname, &size, &offset, localize ) ) {
685 // Fount it, now create a cfile out of it
687 if ( type & CFILE_MEMORY_MAPPED ) {
689 // Can't open memory mapped files out of pack files
696 hFile = CreateFile((LPCWSTR)longname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
698 if (hFile != INVALID_HANDLE_VALUE) {
699 return cf_open_mapped_fill_cfblock(hFile, dir_type);
706 FILE *fp = fopen( longname, "rb" );
710 // Found it in a pack file
711 return cf_open_packed_cfblock(fp, dir_type, offset, size );
713 // Found it in a normal file
714 return cf_open_fill_cfblock(fp, dir_type);
725 // ------------------------------------------------------------------------
728 // Open up a temporary file. A unique name is automatically generated. The
729 // file will be automatically deleted when file is closed.
731 // return: NULL => tmp file could not be opened
732 // pointer to CFILE => tmp file successfully opened
739 return cf_open_fill_cfblock(fp, 0);
746 // cfget_cfile_block() will try to find an empty Cfile_block structure in the
747 // Cfile_block_list[] array and return the index.
749 // returns: success ==> index in Cfile_block_list[] array
752 int cfget_cfile_block()
757 for ( i = 0; i < MAX_CFILE_BLOCKS; i++ ) {
758 cb = &Cfile_block_list[i];
759 if ( cb->type == CFILE_BLOCK_UNUSED ) {
762 cb->type = CFILE_BLOCK_USED;
767 // If we've reached this point, a free Cfile_block could not be found
768 nprintf(("Warning","A free Cfile_block could not be found.\n"));
769 SDL_assert(0); // out of free cfile blocks
774 // cfclose() closes the file
776 // returns: success ==> 0
779 int cfclose( CFILE * cfile )
783 SDL_assert(cfile != NULL);
785 SDL_assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
786 cb = &Cfile_block_list[cfile->id];
790 // close memory mapped file
794 result = UnmapViewOfFile((void*)cb->data);
796 result = CloseHandle(cb->hInFile);
797 SDL_assert(result); // Ensure file handle is closed properly
798 result = CloseHandle(cb->hMapFile);
799 SDL_assert(result); // Ensure file handle is closed properly
803 } else if ( cb->fp != NULL ) {
804 SDL_assert(cb->fp != NULL);
805 result = fclose(cb->fp);
810 cb->type = CFILE_BLOCK_UNUSED;
817 // cf_open_fill_cfblock() will fill up a Cfile_block element in the Cfile_block_list[] array
818 // for the case of a file being opened by cf_open();
820 // returns: success ==> ptr to CFILE structure.
823 CFILE *cf_open_fill_cfblock(FILE *fp, int type)
825 int cfile_block_index;
827 cfile_block_index = cfget_cfile_block();
828 if ( cfile_block_index == -1 ) {
833 cfbp = &Cfile_block_list[cfile_block_index];
834 cfp = &Cfile_list[cfile_block_index];;
835 cfp->id = cfile_block_index;
839 cfbp->dir_type = type;
841 cf_init_lowlevel_read_code(cfp,0,filelength(fileno(fp)) );
848 // cf_open_packed_cfblock() will fill up a Cfile_block element in the Cfile_block_list[] array
849 // for the case of a file being opened by cf_open();
851 // returns: success ==> ptr to CFILE structure.
854 CFILE *cf_open_packed_cfblock(FILE *fp, int type, int offset, int size)
856 // Found it in a pack file
857 int cfile_block_index;
859 cfile_block_index = cfget_cfile_block();
860 if ( cfile_block_index == -1 ) {
865 cfbp = &Cfile_block_list[cfile_block_index];
867 cfp = &Cfile_list[cfile_block_index];
868 cfp->id = cfile_block_index;
872 cfbp->dir_type = type;
874 cf_init_lowlevel_read_code(cfp,offset, size );
883 // cf_open_mapped_fill_cfblock() will fill up a Cfile_block element in the Cfile_block_list[] array
884 // for the case of a file being opened by cf_open_mapped();
886 // returns: ptr CFILE structure.
888 CFILE *cf_open_mapped_fill_cfblock(HANDLE hFile, int type)
890 int cfile_block_index;
892 cfile_block_index = cfget_cfile_block();
893 if ( cfile_block_index == -1 ) {
899 cfbp = &Cfile_block_list[cfile_block_index];
901 cfp = &Cfile_list[cfile_block_index];
902 cfp->id = cfile_block_index;
904 cfbp->hInFile = hFile;
905 cfbp->dir_type = type;
907 cf_init_lowlevel_read_code(cfp,0 , 0 );
912 cfbp->hMapFile = CreateFileMapping(cfbp->hInFile, NULL, PAGE_READONLY, 0, 0, NULL);
913 if (cfbp->hMapFile == NULL) {
914 nprintf(("Error", "Could not create file-mapping object.\n"));
918 cfbp->data = (ubyte*)MapViewOfFile(cfbp->hMapFile, FILE_MAP_READ, 0, 0, 0);
919 SDL_assert( cfbp->data != NULL );
925 int cf_get_dir_type(CFILE *cfile)
927 return Cfile_block_list[cfile->id].dir_type;
930 // cf_returndata() returns the data pointer for a memory-mapped file that is associated
931 // with the CFILE structure passed as a parameter
935 void *cf_returndata(CFILE *cfile)
937 SDL_assert(cfile != NULL);
939 SDL_assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
940 cb = &Cfile_block_list[cfile->id];
941 SDL_assert(cb->data != NULL);
947 // version number of opened file. Will be 0 unless you put something else here after you
948 // open a file. Once set, you can use minimum version numbers with the read functions.
949 void cf_set_version( CFILE * cfile, int version )
951 SDL_assert(cfile != NULL);
953 cfile->version = version;
956 // routines to read basic data types from CFILE's. Put here to
957 // simplify mac/pc reading from cfiles.
962 float cfread_float(CFILE *file, int ver, float deflt)
966 if (file->version < ver)
969 if (cfread( &f, sizeof(f), 1, file) != 1)
976 int cfread_int(CFILE *file, int ver, int deflt)
980 if (file->version < ver)
983 if (cfread( &i, sizeof(i), 1, file) != 1)
990 uint cfread_uint(CFILE *file, int ver, uint deflt)
994 if (file->version < ver)
997 if (cfread( &i, sizeof(i), 1, file) != 1)
1004 short cfread_short(CFILE *file, int ver, short deflt)
1008 if (file->version < ver)
1011 if (cfread( &s, sizeof(s), 1, file) != 1)
1018 ushort cfread_ushort(CFILE *file, int ver, ushort deflt)
1022 if (file->version < ver)
1025 if (cfread( &s, sizeof(s), 1, file) != 1)
1032 ubyte cfread_ubyte(CFILE *file, int ver, ubyte deflt)
1036 if (file->version < ver)
1039 if (cfread( &b, sizeof(b), 1, file) != 1)
1045 void cfread_vector(vector *vec, CFILE *file, int ver, vector *deflt)
1047 if (file->version < ver) {
1051 vec->xyz.x = vec->xyz.y = vec->xyz.z = 0.0f;
1056 vec->xyz.x = cfread_float(file, ver, deflt ? deflt->xyz.x : 0.0f);
1057 vec->xyz.y = cfread_float(file, ver, deflt ? deflt->xyz.y : 0.0f);
1058 vec->xyz.z = cfread_float(file, ver, deflt ? deflt->xyz.z : 0.0f);
1061 void cfread_angles(angles *ang, CFILE *file, int ver, angles *deflt)
1063 if (file->version < ver) {
1067 ang->p = ang->b = ang->h = 0.0f;
1072 ang->p = cfread_float(file, ver, deflt ? deflt->p : 0.0f);
1073 ang->b = cfread_float(file, ver, deflt ? deflt->b : 0.0f);
1074 ang->h = cfread_float(file, ver, deflt ? deflt->h : 0.0f);
1077 char cfread_char(CFILE *file, int ver, char deflt)
1081 if (file->version < ver)
1084 if (cfread( &b, sizeof(b), 1, file) != 1)
1090 void cfread_string(char *buf, int n, CFILE *file)
1095 c = cfread_char(file);
1103 void cfread_string_len(char *buf,int n, CFILE *file)
1106 len = cfread_int(file);
1107 SDL_assert( len < n );
1109 cfread(buf, len, 1, file);
1114 // equivalent write functions of above read functions follow
1116 int cfwrite_float(float f, CFILE *file)
1119 return cfwrite(&f, sizeof(f), 1, file);
1122 int cfwrite_int(int i, CFILE *file)
1125 return cfwrite(&i, sizeof(i), 1, file);
1128 int cfwrite_uint(uint i, CFILE *file)
1131 return cfwrite(&i, sizeof(i), 1, file);
1134 int cfwrite_short(short s, CFILE *file)
1137 return cfwrite(&s, sizeof(s), 1, file);
1140 int cfwrite_ushort(ushort s, CFILE *file)
1143 return cfwrite(&s, sizeof(s), 1, file);
1146 int cfwrite_ubyte(ubyte b, CFILE *file)
1148 return cfwrite(&b, sizeof(b), 1, file);
1151 int cfwrite_vector(vector *vec, CFILE *file)
1153 if(!cfwrite_float(vec->xyz.x, file)){
1156 if(!cfwrite_float(vec->xyz.y, file)){
1159 return cfwrite_float(vec->xyz.z, file);
1162 int cfwrite_angles(angles *ang, CFILE *file)
1164 if(!cfwrite_float(ang->p, file)){
1167 if(!cfwrite_float(ang->b, file)){
1170 return cfwrite_float(ang->h, file);
1173 int cfwrite_char(char b, CFILE *file)
1175 return cfwrite( &b, sizeof(b), 1, file);
1178 int cfwrite_string(const char *buf, CFILE *file)
1180 if ( (!buf) || (buf && !buf[0]) ) {
1181 return cfwrite_char(0, file);
1183 int len = strlen(buf);
1184 if(!cfwrite(buf, len, 1, file)){
1187 return cfwrite_char(0, file); // write out NULL termination
1190 int cfwrite_string_len(const char *buf, CFILE *file)
1192 int len = strlen(buf);
1194 if(!cfwrite_int(len, file)){
1198 return cfwrite(buf,len,1,file);
1204 // Get the filelength
1205 int cfilelength( CFILE * cfile )
1207 SDL_assert(cfile != NULL);
1209 SDL_assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1210 cb = &Cfile_block_list[cfile->id];
1212 // TODO: return length of memory mapped file
1213 SDL_assert( !cb->data );
1215 SDL_assert(cb->fp != NULL);
1217 // cb->size gets set at cfopen
1221 // cfwrite() writes to the file
1223 // returns: number of full elements actually written
1226 int cfwrite(const void *buf, int elsize, int nelem, CFILE *cfile)
1228 SDL_assert(cfile != NULL);
1229 SDL_assert(buf != NULL);
1230 SDL_assert(elsize > 0);
1231 SDL_assert(nelem > 0);
1234 SDL_assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1235 cb = &Cfile_block_list[cfile->id];
1237 int size = elsize * nelem;
1239 // cfwrite() not supported for memory-mapped files
1240 SDL_assert( !cb->data );
1242 SDL_assert(cb->fp != NULL);
1243 SDL_assert(cb->lib_offset == 0 );
1244 int bytes_written = fwrite( buf, 1, size, cb->fp );
1246 if (bytes_written > 0) {
1247 cb->raw_position += bytes_written;
1250 #if defined(CHECK_POSITION) && !defined(NDEBUG)
1251 int tmp_offset = ftell(cb->fp) - cb->lib_offset;
1252 SDL_assert(tmp_offset == cb->raw_position);
1255 return bytes_written / elsize;
1259 // cfputc() writes a character to a file
1261 // returns: success ==> returns character written
1264 int cfputc(int c, CFILE *cfile)
1268 SDL_assert(cfile != NULL);
1270 SDL_assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1271 cb = &Cfile_block_list[cfile->id];
1273 // cfputc() not supported for memory-mapped files
1274 SDL_assert( !cb->data );
1276 SDL_assert(cb->fp != NULL);
1277 result = fputc(c, cb->fp);
1283 // cfgetc() reads a character from a file
1285 // returns: success ==> returns character read
1288 int cfgetc(CFILE *cfile)
1290 SDL_assert(cfile != NULL);
1294 int result = cfread(&tmp, 1, 1, cfile );
1295 if ( result == 1 ) {
1308 // cfgets() reads a string from a file
1310 // returns: success ==> returns pointer to string
1313 char *cfgets(char *buf, int n, CFILE *cfile)
1315 SDL_assert(cfile != NULL);
1316 SDL_assert(buf != NULL);
1322 for (i=0; i<n-1; i++ ) {
1326 int ret = cfread( &tmp_c, 1, 1, cfile );
1336 } while ( c == 13 );
1338 if ( c=='\n' ) break;
1345 // cfputs() writes a string to a file
1347 // returns: success ==> non-negative value
1350 int cfputs(const char *str, CFILE *cfile)
1352 SDL_assert(cfile != NULL);
1353 SDL_assert(str != NULL);
1356 SDL_assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1357 cb = &Cfile_block_list[cfile->id];
1361 // cfputs() not supported for memory-mapped files
1362 SDL_assert( !cb->data );
1363 SDL_assert(cb->fp != NULL);
1364 result = fputs(str, cb->fp);
1370 // 16 and 32 bit checksum stuff ----------------------------------------------------------
1372 // CRC code for mission validation. given to us by Kevin Bentley on 7/20/98. Some sort of
1373 // checksumming code that he wrote a while ago.
1374 #define CRC32_POLYNOMIAL 0xEDB88320L
1375 unsigned long CRCTable[256];
1377 #define CF_CHKSUM_SAMPLE_SIZE 512
1379 // update cur_chksum with the chksum of the new_data of size new_data_size
1380 ushort cf_add_chksum_short(ushort seed, const char *buffer, int size)
1382 const ubyte * ptr = (const ubyte *)buffer;
1383 unsigned int sum1,sum2;
1385 sum1 = sum2 = (int)(seed);
1389 if (sum1 >= 255 ) sum1 -= 255;
1394 return (unsigned short)((sum1<<8)+ sum2);
1397 // update cur_chksum with the chksum of the new_data of size new_data_size
1398 unsigned long cf_add_chksum_long(unsigned long seed, const char *buffer, int size)
1401 unsigned const char *p;
1402 unsigned long temp1;
1403 unsigned long temp2;
1405 p = (unsigned const char*)buffer;
1409 temp1 = (crc>>8)&0x00FFFFFFL;
1410 temp2 = CRCTable[((int)crc^*p++)&0xff];
1417 void cf_chksum_long_init()
1422 for( i=0;i<=255;i++) {
1426 crc=(crc>>1)^CRC32_POLYNOMIAL;
1434 // single function convenient to use for both short and long checksums
1435 // NOTE : only one of chk_short or chk_long must be non-NULL (indicating which checksum to perform)
1436 int cf_chksum_do(CFILE *cfile, ushort *chk_short, uint *chk_long, int max_size)
1438 char cf_buffer[CF_CHKSUM_SAMPLE_SIZE];
1444 // determine whether we're doing a short or long checksum
1447 SDL_assert(!chk_long);
1450 SDL_assert(chk_long);
1455 // if max_size is -1, set it to be the size of the file
1457 cfseek(cfile, 0, SEEK_SET);
1458 max_size = cfilelength(cfile);
1463 // determine how much we want to read
1464 if((max_size - cf_total) >= CF_CHKSUM_SAMPLE_SIZE){
1465 read_size = CF_CHKSUM_SAMPLE_SIZE;
1467 read_size = max_size - cf_total;
1470 // read in some buffer
1471 cf_len = cfread(cf_buffer, 1, read_size, cfile);
1473 // total we've read so far
1478 // do the proper short or long checksum
1480 *chk_long = cf_add_chksum_long(*chk_long, cf_buffer, cf_len);
1482 *chk_short = cf_add_chksum_short(*chk_short, cf_buffer, cf_len);
1485 } while((cf_len > 0) && (cf_total < max_size));
1490 // get the 2 byte checksum of the passed filename - return 0 if operation failed, 1 if succeeded
1491 int cf_chksum_short(const char *filename, ushort *chksum, int max_size, int cf_type)
1494 CFILE *cfile = NULL;
1496 // zero the checksum
1499 // attempt to open the file
1500 cfile = cfopen(filename,"rt",CFILE_NORMAL,cf_type);
1505 // call the overloaded cf_chksum function()
1506 ret_val = cf_chksum_do(cfile, chksum, NULL, max_size);
1508 // close the file down
1512 // return the result
1516 // get the 2 byte checksum of the passed file - return 0 if operation failed, 1 if succeeded
1517 // NOTE : preserves current file position
1518 int cf_chksum_short(CFILE *file, ushort *chksum, int max_size)
1523 // Returns current position of file.
1524 start_pos = cftell(file);
1525 if(start_pos == -1){
1529 // move to the beginning of the file
1530 if(cfseek(file, 0, CF_SEEK_SET)){
1533 ret_code = cf_chksum_do(file, chksum, NULL, max_size);
1534 // move back to the start position
1535 cfseek(file, start_pos, CF_SEEK_SET);
1540 // get the 32 bit CRC checksum of the passed filename - return 0 if operation failed, 1 if succeeded
1541 int cf_chksum_long(const char *filename, uint *chksum, int max_size, int cf_type)
1544 CFILE *cfile = NULL;
1546 // zero the checksum
1549 // attempt to open the file
1550 cfile = cfopen(filename,"rt",CFILE_NORMAL,cf_type);
1555 // call the overloaded cf_chksum function()
1556 ret_val = cf_chksum_do(cfile, NULL, chksum, max_size);
1558 // close the file down
1562 // return the result
1566 // get the 32 bit CRC checksum of the passed file - return 0 if operation failed, 1 if succeeded
1567 // NOTE : preserves current file position
1568 int cf_chksum_long(CFILE *file, uint *chksum, int max_size)
1573 // Returns current position of file.
1574 start_pos = cftell(file);
1575 if(start_pos == -1){
1579 // move to the beginning of the file
1580 if(cfseek(file, 0, CF_SEEK_SET)){
1583 ret_code = cf_chksum_do(file, NULL, chksum, max_size);
1584 // move back to the start position
1585 cfseek(file, start_pos, CF_SEEK_SET);
1591 // Flush the open file buffer
1593 // exit: 0 - success
1595 int cflush(CFILE *cfile)
1597 SDL_assert(cfile != NULL);
1599 SDL_assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1600 cb = &Cfile_block_list[cfile->id];
1602 // not supported for memory mapped files
1603 SDL_assert( !cb->data );
1605 SDL_assert(cb->fp != NULL);
1606 return fflush(cb->fp);
1609 // fill in Cfile_root_dir[] and Cfile_user_dir[]
1610 // this can be called at any time, even before cfile_init()
1611 // returns: non-zero on error
1612 int cfile_init_paths()
1614 if ( strlen(Cfile_root_dir) && strlen(Cfile_user_dir) ) {
1618 char *t_path = SDL_GetBasePath();
1620 // make sure we have something
1621 if (t_path == NULL) {
1622 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Error trying to determine executable directory!", NULL);
1627 if ( strlen(t_path) >= CFILE_ROOT_DIRECTORY_LEN ) {
1628 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Executable path is too long!", NULL);
1632 // set root directory
1633 SDL_strlcpy(Cfile_root_dir, t_path, sizeof(Cfile_root_dir));
1638 // are we in a root directory?
1639 if ( cfile_in_root_dir(Cfile_root_dir) ) {
1640 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Freespace2/Fred2 cannot be run from a drive root directory!", NULL);
1644 // now for the user/pref directory, the writable location
1645 char *u_path = SDL_GetPrefPath(Osreg_company_name, Osreg_title);
1647 // make sure we have something
1648 if (u_path == NULL) {
1649 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Error trying to determine preferences directory!", NULL);
1654 if ( strlen(u_path) >= CFILE_ROOT_DIRECTORY_LEN ) {
1655 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", "Preferences path is too long!", NULL);
1659 // set user/pref directory
1660 SDL_strlcpy(Cfile_user_dir, u_path, sizeof(Cfile_user_dir));