2 * $Logfile: /Freespace2/code/CFile/cfile.cpp $
7 * Utilities for operating on files
10 * Revision 1.3 2002/05/28 08:52:03 relnev
11 * implemented two assembly stubs.
13 * cleaned up a few warnings.
15 * added a little demo hackery to make it progress a little farther.
17 * Revision 1.2 2002/05/28 06:28:20 theoddone33
18 * Filesystem mods, actually reads some data files now
20 * Revision 1.1.1.1 2002/05/03 03:28:08 root
24 * 20 9/08/99 10:01p Dave
25 * Make sure game won't run in a drive's root directory. Make sure
26 * standalone routes suqad war messages properly to the host.
28 * 19 9/08/99 12:03a Dave
29 * Make squad logos render properly in D3D all the time. Added intel anim
32 * 18 9/06/99 2:35p Dave
33 * Rename briefing and debriefing voice direcrory paths properly.
35 * 17 9/03/99 1:31a Dave
36 * CD checking by act. Added support to play 2 cutscenes in a row
37 * seamlessly. Fixed super low level cfile bug related to files in the
38 * root directory of a CD. Added cheat code to set campaign mission # in
41 * 16 8/31/99 9:46a Dave
42 * Support for new cfile cbanims directory.
44 * 15 8/06/99 11:20a Johns
45 * Don't call game_cd_changed() in a demo or multiplayer beta build.
47 * 14 6/08/99 2:33p Dave
48 * Fixed release build warning. Put in hud config stuff.
50 * 13 5/19/99 4:07p Dave
51 * Moved versioning code into a nice isolated common place. Fixed up
52 * updating code on the pxo screen. Fixed several stub problems.
54 * 12 4/30/99 12:18p Dave
55 * Several minor bug fixes.
57 * 11 4/07/99 5:54p Dave
58 * Fixes for encryption in updatelauncher.
60 * 10 3/28/99 5:58p Dave
61 * Added early demo code. Make objects move. Nice and framerate
62 * independant, but not much else. Don't use yet unless you're me :)
64 * 9 2/22/99 10:31p Andsager
65 * Get rid of unneeded includes.
67 * 8 1/12/99 3:15a Dave
68 * Barracks screen support for selecting squad logos. We need real artwork
71 * 7 11/30/98 1:07p Dave
72 * 16 bit conversion, first run.
74 * 6 10/29/98 10:41a Dave
75 * Change the way cfile initializes exe directory.
77 * 5 10/13/98 9:19a Andsager
78 * Add localization support to cfile. Optional parameter with cfopen that
79 * looks for localized files.
81 * 4 10/12/98 9:54a Dave
82 * Fixed a few file organization things.
84 * 3 10/07/98 6:27p Dave
85 * Globalized mission and campaign file extensions. Removed Silent Threat
86 * special code. Moved \cache \players and \multidata into the \data
89 * 2 10/07/98 10:52a Dave
92 * 1 10/07/98 10:48a Dave
94 * 110 9/17/98 10:31a Hoffoss
95 * Changed code to use location of executable as root rather than relying
96 * on the cwd being set correctly. This is most helpful in the case of
97 * Fred launching due to the user double-clicking on an .fsm file in
98 * explorer or something (where the cwd is probably the location of the
101 * 109 9/11/98 4:14p Dave
102 * Fixed file checksumming of < file_size. Put in more verbose kicking and
103 * PXO stats store reporting.
105 * 108 9/09/98 5:53p Dave
106 * Put in new tracker packets in API. Change cfile to be able to checksum
107 * portions of a file.
109 * 107 9/04/98 3:51p Dave
110 * Put in validated mission updating and application during stats
113 * 106 8/31/98 2:06p Dave
114 * Make cfile sort the ordering or vp files. Added support/checks for
115 * recognizing "mission disk" players.
117 * 105 8/20/98 5:30p Dave
118 * Put in handy multiplayer logfile system. Now need to put in useful
119 * applications of it all over the code.
121 * 104 8/12/98 4:53p Dave
122 * Put in 32 bit checksumming for PXO missions. No validation on the
123 * actual tracker yet, though.
125 * 103 6/17/98 9:30a Allender
126 * fixed red alert replay stats clearing problem
128 * 102 6/05/98 9:46a Lawrance
129 * check for CD change on cfopen()
131 * 101 5/19/98 1:19p Allender
132 * new low level reliable socket reading code. Make all missions/campaign
133 * load/save to data missions folder (i.e. we are rid of the player
136 * 100 5/13/98 10:22p John
137 * Added cfile functions to read/write rle compressed blocks of data.
138 * Made palman use it for .clr files. Made alphacolors calculate on the
139 * fly rather than caching to/from disk.
141 * 99 5/11/98 10:59a John
142 * Moved the low-level file reading code into cfilearchive.cpp.
144 * 98 5/06/98 5:30p John
145 * Removed unused cfilearchiver. Removed/replaced some unused/little used
146 * graphics functions, namely gradient_h and _v and pixel_sp. Put in new
147 * DirectX header files and libs that fixed the Direct3D alpha blending
150 * 97 5/03/98 11:53a John
151 * Fixed filename case mangling.
153 * 96 5/03/98 8:33a John
154 * Made sure there weren't any more broken function lurking around like
155 * the one Allender fixed.
157 * 95 5/02/98 11:05p Allender
158 * fix cfilelength for pack files
163 #define _CFILE_INTERNAL
173 #include <winbase.h> /* needed for memory mapping of file functions */
176 #include <sys/stat.h>
177 #include <sys/types.h>
183 //#include "outwnd.h"
184 //#include "vecmat.h"
186 #include "cfilesystem.h"
187 #include "cfilearchive.h"
190 char Cfile_root_dir[CFILE_ROOT_DIRECTORY_LEN] = "";
192 // During cfile_init, verify that Pathtypes[n].index == n for each item
193 // Each path must have a valid parent that can be tracable all the way back to the root
194 // so that we can create directories when we need to.
196 cf_pathtype Pathtypes[CF_MAX_PATH_TYPES] = {
197 // What type this is Path Extensions Parent type
198 { CF_TYPE_INVALID, NULL, NULL, CF_TYPE_INVALID },
199 // Root must be index 1!!
200 { CF_TYPE_ROOT, "", ".mve", CF_TYPE_ROOT },
201 { CF_TYPE_DATA, "Data", ".cfg .log .txt", CF_TYPE_ROOT },
203 { CF_TYPE_MAPS, "Data/Maps", ".pcx .ani .tga", CF_TYPE_DATA },
204 { CF_TYPE_TEXT, "Data/Text", ".txt .net", CF_TYPE_DATA },
205 { CF_TYPE_MISSIONS, "Data/Missions", ".fs2 .fc2 .ntl .ssv", CF_TYPE_DATA },
206 { CF_TYPE_MODELS, "Data/Models", ".pof", CF_TYPE_DATA },
207 { CF_TYPE_TABLES, "Data/Tables", ".tbl", CF_TYPE_DATA },
208 { CF_TYPE_SOUNDS, "Data/Sounds", ".wav", CF_TYPE_DATA },
209 { CF_TYPE_SOUNDS_8B22K, "Data/Sounds/8b22k", ".wav", CF_TYPE_SOUNDS },
210 { CF_TYPE_SOUNDS_16B11K, "Data/Sounds/16b11k", ".wav", CF_TYPE_SOUNDS },
211 { CF_TYPE_VOICE, "Data/Voice", "", CF_TYPE_DATA },
212 { CF_TYPE_VOICE_BRIEFINGS, "Data/Voice/Briefing", ".wav", CF_TYPE_VOICE },
213 { CF_TYPE_VOICE_CMD_BRIEF, "Data/Voice/Command_briefings",".wav", CF_TYPE_VOICE },
214 { CF_TYPE_VOICE_DEBRIEFINGS, "Data/Voice/Debriefing", ".wav", CF_TYPE_VOICE },
215 { CF_TYPE_VOICE_PERSONAS, "Data/Voice/Personas", ".wav", CF_TYPE_VOICE },
216 { CF_TYPE_VOICE_SPECIAL, "Data/Voice/Special", ".wav", CF_TYPE_VOICE },
217 { CF_TYPE_VOICE_TRAINING, "Data/Voice/Training", ".wav", CF_TYPE_VOICE },
218 { CF_TYPE_MUSIC, "Data/Music", ".wav", CF_TYPE_VOICE },
219 { CF_TYPE_MOVIES, "Data/Movies", ".mve .msb", CF_TYPE_DATA },
220 { CF_TYPE_INTERFACE, "Data/Interface", ".pcx .ani .tga", CF_TYPE_DATA },
221 { CF_TYPE_FONT, "Data/Fonts", ".vf", CF_TYPE_DATA },
222 { CF_TYPE_EFFECTS, "Data/Effects", ".ani .pcx .neb .tga", CF_TYPE_DATA },
223 { CF_TYPE_HUD, "Data/Hud", ".ani .pcx .tga", CF_TYPE_DATA },
224 { CF_TYPE_PLAYER_MAIN, "Data/Players", "", CF_TYPE_DATA },
225 { CF_TYPE_PLAYER_IMAGES_MAIN, "Data/Players/Images", ".pcx", CF_TYPE_PLAYER_MAIN },
226 { CF_TYPE_CACHE, "Data/Cache", ".clr .tmp", CF_TYPE_DATA }, //clr=cached color
227 { CF_TYPE_PLAYERS, "Data/Players", ".hcf", CF_TYPE_DATA },
228 { CF_TYPE_SINGLE_PLAYERS, "Data/Players/Single", ".plr .csg .css", CF_TYPE_PLAYERS },
229 { CF_TYPE_MULTI_PLAYERS, "Data/Players/Multi", ".plr", CF_TYPE_DATA },
230 { CF_TYPE_MULTI_CACHE, "Data/MultiData", ".pcx .fs2", CF_TYPE_DATA },
231 { CF_TYPE_CONFIG, "Data/Config", ".cfg", CF_TYPE_DATA },
232 { CF_TYPE_SQUAD_IMAGES_MAIN, "Data/Players/Squads", ".pcx", CF_TYPE_DATA },
233 { CF_TYPE_DEMOS, "Data/Demos", ".fsd", CF_TYPE_DATA },
234 { CF_TYPE_CBANIMS, "Data/CBAnims", ".ani", CF_TYPE_DATA },
235 { CF_TYPE_INTEL_ANIMS, "Data/IntelAnims", ".ani", CF_TYPE_DATA },
237 { CF_TYPE_MAPS, "Data\\Maps", ".pcx .ani .tga", CF_TYPE_DATA },
238 { CF_TYPE_TEXT, "Data\\Text", ".txt .net", CF_TYPE_DATA },
239 { CF_TYPE_MISSIONS, "Data\\Missions", ".fs2 .fc2 .ntl .ssv", CF_TYPE_DATA },
240 { CF_TYPE_MODELS, "Data\\Models", ".pof", CF_TYPE_DATA },
241 { CF_TYPE_TABLES, "Data\\Tables", ".tbl", CF_TYPE_DATA },
242 { CF_TYPE_SOUNDS, "Data\\Sounds", ".wav", CF_TYPE_DATA },
243 { CF_TYPE_SOUNDS_8B22K, "Data\\Sounds\\8b22k", ".wav", CF_TYPE_SOUNDS },
244 { CF_TYPE_SOUNDS_16B11K, "Data\\Sounds\\16b11k", ".wav", CF_TYPE_SOUNDS },
245 { CF_TYPE_VOICE, "Data\\Voice", "", CF_TYPE_DATA },
246 { CF_TYPE_VOICE_BRIEFINGS, "Data\\Voice\\Briefing", ".wav", CF_TYPE_VOICE },
247 { CF_TYPE_VOICE_CMD_BRIEF, "Data\\Voice\\Command_briefings",".wav", CF_TYPE_VOICE },
248 { CF_TYPE_VOICE_DEBRIEFINGS, "Data\\Voice\\Debriefing", ".wav", CF_TYPE_VOICE },
249 { CF_TYPE_VOICE_PERSONAS, "Data\\Voice\\Personas", ".wav", CF_TYPE_VOICE },
250 { CF_TYPE_VOICE_SPECIAL, "Data\\Voice\\Special", ".wav", CF_TYPE_VOICE },
251 { CF_TYPE_VOICE_TRAINING, "Data\\Voice\\Training", ".wav", CF_TYPE_VOICE },
252 { CF_TYPE_MUSIC, "Data\\Music", ".wav", CF_TYPE_VOICE },
253 { CF_TYPE_MOVIES, "Data\\Movies", ".mve .msb", CF_TYPE_DATA },
254 { CF_TYPE_INTERFACE, "Data\\Interface", ".pcx .ani .tga", CF_TYPE_DATA },
255 { CF_TYPE_FONT, "Data\\Fonts", ".vf", CF_TYPE_DATA },
256 { CF_TYPE_EFFECTS, "Data\\Effects", ".ani .pcx .neb .tga", CF_TYPE_DATA },
257 { CF_TYPE_HUD, "Data\\Hud", ".ani .pcx .tga", CF_TYPE_DATA },
258 { CF_TYPE_PLAYER_MAIN, "Data\\Players", "", CF_TYPE_DATA },
259 { CF_TYPE_PLAYER_IMAGES_MAIN, "Data\\Players\\Images", ".pcx", CF_TYPE_PLAYER_MAIN },
260 { CF_TYPE_CACHE, "Data\\Cache", ".clr .tmp", CF_TYPE_DATA }, //clr=cached color
261 { CF_TYPE_PLAYERS, "Data\\Players", ".hcf", CF_TYPE_DATA },
262 { CF_TYPE_SINGLE_PLAYERS, "Data\\Players\\Single", ".plr .csg .css", CF_TYPE_PLAYERS },
263 { CF_TYPE_MULTI_PLAYERS, "Data\\Players\\Multi", ".plr", CF_TYPE_DATA },
264 { CF_TYPE_MULTI_CACHE, "Data\\MultiData", ".pcx .fs2", CF_TYPE_DATA },
265 { CF_TYPE_CONFIG, "Data\\Config", ".cfg", CF_TYPE_DATA },
266 { CF_TYPE_SQUAD_IMAGES_MAIN, "Data\\Players\\Squads", ".pcx", CF_TYPE_DATA },
267 { CF_TYPE_DEMOS, "Data\\Demos", ".fsd", CF_TYPE_DATA },
268 { CF_TYPE_CBANIMS, "Data\\CBAnims", ".ani", CF_TYPE_DATA },
269 { CF_TYPE_INTEL_ANIMS, "Data\\IntelAnims", ".ani", CF_TYPE_DATA },
274 #define CFILE_STACK_MAX 8
276 int cfile_inited = 0;
277 int Cfile_stack_pos = 0;
279 char Cfile_stack[128][CFILE_STACK_MAX];
281 Cfile_block Cfile_block_list[MAX_CFILE_BLOCKS];
282 CFILE Cfile_list[MAX_CFILE_BLOCKS];
284 char *Cfile_cdrom_dir = NULL;
287 // Function prototypes for internally-called functions
289 int cfget_cfile_block();
290 CFILE *cf_open_fill_cfblock(FILE * fp, int type);
291 CFILE *cf_open_packed_cfblock(FILE *fp, int type, int offset, int size);
292 CFILE *cf_open_mapped_fill_cfblock(HANDLE hFile, int type);
293 void cf_chksum_long_init();
297 cf_free_secondary_filelist();
300 // determine if the given path is in a root directory (c:\ or c:\freespace2.exe or c:\fred2.exe etc)
301 int cfile_in_root_dir(char *exe_path)
304 char path_copy[2048] = "";
308 if(exe_path == NULL){
313 memset(path_copy, 0, 2048);
314 strncpy(path_copy, exe_path, 2047);
316 // count how many slashes there are in the path
318 tok = strtok(path_copy, "/");
320 tok = strtok(path_copy, "\\");
328 tok = strtok(NULL, "/");
330 tok = strtok(NULL, "\\");
332 } while(tok != NULL);
334 // root directory if we have <= 1 slash
335 if(token_count <= 2){
339 // not-root directory
343 // cfile_init() initializes the cfile system. Called once at application start.
345 // returns: success ==> 0
346 // error ==> non-zero
348 int cfile_init(char *exe_dir, char *cdrom_dir)
352 // initialize encryption
355 if ( !cfile_inited ) {
360 strcpy(buf, exe_dir);
363 // are we in a root directory?
364 if(cfile_in_root_dir(buf)){
366 fprintf (stderr, "ERROR: Freespace2/Fred2 cannot be run from a drive root directory!\n");
368 MessageBox((HWND)NULL, "Freespace2/Fred2 cannot be run from a drive root directory!", "Error", MB_OK);
388 fprintf (stderr, "Error trying to determine executable root directory!");
390 MessageBox((HWND)NULL, "Error trying to determine executable root directory!", "Error", MB_OK);
395 // set root directory
396 strncpy(Cfile_root_dir, buf, CFILE_ROOT_DIRECTORY_LEN-1);
398 for ( i = 0; i < MAX_CFILE_BLOCKS; i++ ) {
399 Cfile_block_list[i].type = CFILE_BLOCK_UNUSED;
402 Cfile_cdrom_dir = cdrom_dir;
403 cf_build_secondary_filelist(Cfile_cdrom_dir);
405 // 32 bit CRC table init
406 cf_chksum_long_init();
408 atexit( cfile_close );
414 // Call this if pack files got added or removed or the
415 // cdrom changed. This will refresh the list of filenames
416 // stored in packfiles and on the cdrom.
419 cf_build_secondary_filelist(Cfile_cdrom_dir);
423 // Changes to a drive if valid.. 1=A, 2=B, etc
424 // If flag, then changes to it.
425 // Returns 0 if not-valid, 1 if valid.
426 int cfile_chdrive( int DriveNum, int flag )
439 _chdrive( DriveNum );
446 if ( (!flag) && (n != org) )
452 // push current directory on a 'stack' (so we can restore it) and change the directory
453 int cfile_push_chdir(int type)
457 char OriginalDirectory[128];
459 char NoDir[] = "\\.";
461 _getcwd(OriginalDirectory, 127);
462 Assert(Cfile_stack_pos < CFILE_STACK_MAX);
463 strcpy(Cfile_stack[Cfile_stack_pos++], OriginalDirectory);
465 cf_create_default_path_string( dir, type, NULL );
468 Drive = strchr(dir, ':');
471 if (!cfile_chdrive( *(Drive - 1) - 'a' + 1, 1))
486 // This chdir might get a critical error!
489 cfile_chdrive( OriginalDirectory[0] - 'a' + 1, 1 );
497 int cfile_chdir(char *dir)
500 char OriginalDirectory[128];
502 char NoDir[] = "\\.";
504 _getcwd(OriginalDirectory, 127);
508 Drive = strchr(dir, ':');
510 if (!cfile_chdrive( *(Drive - 1) - 'a' + 1, 1))
525 // This chdir might get a critical error!
528 cfile_chdrive( OriginalDirectory[0] - 'a' + 1, 1 );
537 Assert(Cfile_stack_pos);
539 return cfile_chdir(Cfile_stack[Cfile_stack_pos]);
542 // flush (delete all files in) the passed directory (by type), return the # of files deleted
543 // NOTE : WILL NOT DELETE READ-ONLY FILES
544 int cfile_flush_dir(int dir_type)
554 Assert( CF_TYPE_SPECIFIED(dir_type) );
556 // attempt to change the directory to the passed type
557 if(cfile_push_chdir(dir_type)){
561 // proceed to delete the files
562 find_handle = _findfirst( "*", &find );
564 if (find_handle != -1) {
566 if (!(find.attrib & _A_SUBDIR) && !(find.attrib & _A_RDONLY)) {
568 cf_delete(find.name,dir_type);
570 // increment the deleted count
573 } while (!_findnext(find_handle, &find));
574 _findclose( find_handle );
577 // pop the directory back
580 // return the # of files deleted
586 // add the given extention to a filename (or filepath) if it doesn't already have this
588 // filename = name of filename or filepath to process
589 // ext = extension to add. Must start with the period
590 // Returns: new filename or filepath with extension.
591 char *cf_add_ext(char *filename, char *ext)
594 static char path[MAX_PATH_LEN];
596 flen = strlen(filename);
598 Assert(flen < MAX_PATH_LEN);
599 strcpy(path, filename);
600 if ((flen < 4) || stricmp(path + flen - elen, ext)) {
601 Assert(flen + elen < MAX_PATH_LEN);
609 void cf_delete( char *filename, int dir_type )
611 char longname[MAX_PATH_LEN];
613 Assert( CF_TYPE_SPECIFIED(dir_type) );
615 cf_create_default_path_string( longname, dir_type, filename );
617 FILE *fp = fopen(longname, "rb");
627 // Same as _access function to read a file's access bits
628 int cf_access( char *filename, int dir_type, int mode )
630 char longname[MAX_PATH_LEN];
632 Assert( CF_TYPE_SPECIFIED(dir_type) );
634 cf_create_default_path_string( longname, dir_type, filename );
636 return access(longname,mode);
640 // Returns 1 if file exists, 0 if not.
641 int cf_exist( char *filename, int dir_type )
643 char longname[MAX_PATH_LEN];
645 Assert( CF_TYPE_SPECIFIED(dir_type) );
647 cf_create_default_path_string( longname, dir_type, filename );
649 FILE *fp = fopen(longname, "rb");
658 void cf_attrib(char *filename, int set, int clear, int dir_type)
660 char longname[MAX_PATH_LEN];
662 Assert( CF_TYPE_SPECIFIED(dir_type) );
664 cf_create_default_path_string( longname, dir_type, filename );
666 FILE *fp = fopen(longname, "rb");
673 DWORD z = GetFileAttributes(longname);
674 SetFileAttributes(longname, z | set & ~clear);
680 int cf_rename(char *old_name, char *name, int dir_type)
682 Assert( CF_TYPE_SPECIFIED(dir_type) );
685 char old_longname[_MAX_PATH];
686 char new_longname[_MAX_PATH];
688 cf_create_default_path_string( old_longname, dir_type, old_name );
689 cf_create_default_path_string( new_longname, dir_type, name );
691 ret_code = rename(old_longname, new_longname );
695 return CF_RENAME_FAIL_ACCESS;
698 return CF_RENAME_FAIL_EXIST;
702 return CF_RENAME_SUCCESS;
707 // Creates the directory path if it doesn't exist. Even creates all its
709 void cf_create_directory( int dir_type )
712 int dir_tree[CF_MAX_PATH_TYPES];
713 char longname[MAX_PATH_LEN];
715 Assert( CF_TYPE_SPECIFIED(dir_type) );
717 int current_dir = dir_type;
720 Assert( num_dirs < CF_MAX_PATH_TYPES ); // Invalid Pathtypes data?
722 dir_tree[num_dirs++] = current_dir;
723 current_dir = Pathtypes[current_dir].parent_index;
725 } while( current_dir != CF_TYPE_ROOT );
730 for (i=num_dirs-1; i>=0; i-- ) {
731 cf_create_default_path_string( longname, dir_tree[i], NULL );
734 if ( _mkdir(longname, 0777)==0 ) {
735 mprintf(( "CFILE: Created new directory '%s'\n", longname ));
738 if ( _mkdir(longname)==0 ) {
739 mprintf(( "CFILE: Created new directory '%s'\n", longname ));
748 extern int game_cd_changed();
752 // parameters: *filepath ==> name of file to open (may be path+name)
753 // *mode ==> specifies how file should be opened (eg "rb" for read binary)
754 // passing NULL to mode deletes the file if it exists and returns NULL
755 // type ==> one of: CFILE_NORMAL
756 // CFILE_MEMORY_MAPPED
757 // dir_type => override extension check, value is one of CF_TYPE* #defines
759 // NOTE: type parameter is an optional parameter. The default value is CFILE_NORMAL
762 // returns: success ==> address of CFILE structure
766 CFILE *cfopen(char *file_path, char *mode, int type, int dir_type, bool localize)
768 char longname[_MAX_PATH];
770 // nprintf(("CFILE", "CFILE -- trying to open %s\n", file_path ));
771 // #if !defined(MULTIPLAYER_BETA_BUILD) && !defined(FS2_DEMO)
773 // we no longer need to do this, and on machines with crappy-ass drivers it can slow things down horribly.
775 if ( game_cd_changed() ) {
780 //================================================
781 // Check that all the parameters make sense
782 Assert(file_path && strlen(file_path));
783 Assert( mode != NULL );
785 // Can only open read-only binary files in memory mapped mode.
786 if ( (type & CFILE_MEMORY_MAPPED) && strcmp(mode,"rb") ) {
791 //===========================================================
792 // If in write mode, just try to open the file straight off
793 // the harddisk. No fancy packfile stuff here!
795 if ( strchr(mode,'w') ) {
796 // For write-only files, require a full path or a path type
797 if ( strpbrk(file_path,"/\\:") ) {
799 strcpy(longname, file_path );
802 Assert( dir_type != CF_TYPE_ANY );
804 // Create the directory if necessary
805 cf_create_directory( dir_type );
807 cf_create_default_path_string( longname, dir_type, file_path );
809 Assert( !(type & CFILE_MEMORY_MAPPED) );
811 // JOHN: TODO, you should create the path if it doesn't exist.
813 FILE *fp = fopen(longname, mode);
815 return cf_open_fill_cfblock(fp, dir_type);
821 //================================================
822 // Search for file on disk, on cdrom, or in a packfile
825 char copy_file_path[MAX_PATH_LEN]; // FIX change in memory from cf_find_file_location
826 strcpy(copy_file_path, file_path);
829 if ( cf_find_file_location( copy_file_path, dir_type, longname, &size, &offset, localize ) ) {
831 // Fount it, now create a cfile out of it
833 if ( type & CFILE_MEMORY_MAPPED ) {
835 // Can't open memory mapped files out of pack files
842 hFile = CreateFile(longname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
844 if (hFile != INVALID_HANDLE_VALUE) {
845 return cf_open_mapped_fill_cfblock(hFile, dir_type);
852 FILE *fp = fopen( longname, "rb" );
856 // Found it in a pack file
857 return cf_open_packed_cfblock(fp, dir_type, offset, size );
859 // Found it in a normal file
860 return cf_open_fill_cfblock(fp, dir_type);
871 // ------------------------------------------------------------------------
874 // Open up a temporary file. A unique name is automatically generated. The
875 // file will be automatically deleted when file is closed.
877 // return: NULL => tmp file could not be opened
878 // pointer to CFILE => tmp file successfully opened
885 return cf_open_fill_cfblock(fp, 0);
892 // cfget_cfile_block() will try to find an empty Cfile_block structure in the
893 // Cfile_block_list[] array and return the index.
895 // returns: success ==> index in Cfile_block_list[] array
898 int cfget_cfile_block()
903 for ( i = 0; i < MAX_CFILE_BLOCKS; i++ ) {
904 cb = &Cfile_block_list[i];
905 if ( cb->type == CFILE_BLOCK_UNUSED ) {
908 cb->type = CFILE_BLOCK_USED;
913 // If we've reached this point, a free Cfile_block could not be found
914 nprintf(("Warning","A free Cfile_block could not be found.\n"));
915 Assert(0); // out of free cfile blocks
920 // cfclose() closes the file
922 // returns: success ==> 0
925 int cfclose( CFILE * cfile )
929 Assert(cfile != NULL);
931 Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
932 cb = &Cfile_block_list[cfile->id];
936 // close memory mapped file
940 result = UnmapViewOfFile((void*)cb->data);
942 result = CloseHandle(cb->hInFile);
943 Assert(result); // Ensure file handle is closed properly
944 result = CloseHandle(cb->hMapFile);
945 Assert(result); // Ensure file handle is closed properly
949 } else if ( cb->fp != NULL ) {
950 Assert(cb->fp != NULL);
951 result = fclose(cb->fp);
956 cb->type = CFILE_BLOCK_UNUSED;
963 // cf_open_fill_cfblock() will fill up a Cfile_block element in the Cfile_block_list[] array
964 // for the case of a file being opened by cf_open();
966 // returns: success ==> ptr to CFILE structure.
969 CFILE *cf_open_fill_cfblock(FILE *fp, int type)
971 int cfile_block_index;
973 cfile_block_index = cfget_cfile_block();
974 if ( cfile_block_index == -1 ) {
979 cfbp = &Cfile_block_list[cfile_block_index];
980 cfp = &Cfile_list[cfile_block_index];;
981 cfp->id = cfile_block_index;
985 cfbp->dir_type = type;
987 cf_init_lowlevel_read_code(cfp,0,filelength(fileno(fp)) );
994 // cf_open_packed_cfblock() will fill up a Cfile_block element in the Cfile_block_list[] array
995 // for the case of a file being opened by cf_open();
997 // returns: success ==> ptr to CFILE structure.
1000 CFILE *cf_open_packed_cfblock(FILE *fp, int type, int offset, int size)
1002 // Found it in a pack file
1003 int cfile_block_index;
1005 cfile_block_index = cfget_cfile_block();
1006 if ( cfile_block_index == -1 ) {
1011 cfbp = &Cfile_block_list[cfile_block_index];
1013 cfp = &Cfile_list[cfile_block_index];
1014 cfp->id = cfile_block_index;
1018 cfbp->dir_type = type;
1020 cf_init_lowlevel_read_code(cfp,offset, size );
1029 // cf_open_mapped_fill_cfblock() will fill up a Cfile_block element in the Cfile_block_list[] array
1030 // for the case of a file being opened by cf_open_mapped();
1032 // returns: ptr CFILE structure.
1034 CFILE *cf_open_mapped_fill_cfblock(HANDLE hFile, int type)
1036 int cfile_block_index;
1038 cfile_block_index = cfget_cfile_block();
1039 if ( cfile_block_index == -1 ) {
1045 cfbp = &Cfile_block_list[cfile_block_index];
1047 cfp = &Cfile_list[cfile_block_index];
1048 cfp->id = cfile_block_index;
1050 cfbp->hInFile = hFile;
1051 cfbp->dir_type = type;
1053 cf_init_lowlevel_read_code(cfp,0 , 0 );
1058 cfbp->hMapFile = CreateFileMapping(cfbp->hInFile, NULL, PAGE_READONLY, 0, 0, NULL);
1059 if (cfbp->hMapFile == NULL) {
1060 nprintf(("Error", "Could not create file-mapping object.\n"));
1064 cfbp->data = (ubyte*)MapViewOfFile(cfbp->hMapFile, FILE_MAP_READ, 0, 0, 0);
1065 Assert( cfbp->data != NULL );
1071 int cf_get_dir_type(CFILE *cfile)
1073 return Cfile_block_list[cfile->id].dir_type;
1076 // cf_returndata() returns the data pointer for a memory-mapped file that is associated
1077 // with the CFILE structure passed as a parameter
1081 void *cf_returndata(CFILE *cfile)
1083 Assert(cfile != NULL);
1085 Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1086 cb = &Cfile_block_list[cfile->id];
1087 Assert(cb->data != NULL);
1093 // version number of opened file. Will be 0 unless you put something else here after you
1094 // open a file. Once set, you can use minimum version numbers with the read functions.
1095 void cf_set_version( CFILE * cfile, int version )
1097 Assert(cfile != NULL);
1099 cfile->version = version;
1102 // routines to read basic data types from CFILE's. Put here to
1103 // simplify mac/pc reading from cfiles.
1105 float cfread_float(CFILE *file, int ver, float deflt)
1109 if (file->version < ver)
1112 if (cfread( &f, sizeof(f), 1, file) != 1)
1115 // i = INTEL_INT(i); // hmm, not sure what to do here
1119 int cfread_int(CFILE *file, int ver, int deflt)
1123 if (file->version < ver)
1126 if (cfread( &i, sizeof(i), 1, file) != 1)
1133 uint cfread_uint(CFILE *file, int ver, uint deflt)
1137 if (file->version < ver)
1140 if (cfread( &i, sizeof(i), 1, file) != 1)
1147 short cfread_short(CFILE *file, int ver, short deflt)
1151 if (file->version < ver)
1154 if (cfread( &s, sizeof(s), 1, file) != 1)
1161 ushort cfread_ushort(CFILE *file, int ver, ushort deflt)
1165 if (file->version < ver)
1168 if (cfread( &s, sizeof(s), 1, file) != 1)
1175 ubyte cfread_ubyte(CFILE *file, int ver, ubyte deflt)
1179 if (file->version < ver)
1182 if (cfread( &b, sizeof(b), 1, file) != 1)
1188 void cfread_vector(vector *vec, CFILE *file, int ver, vector *deflt)
1190 if (file->version < ver) {
1194 vec->x = vec->y = vec->z = 0.0f;
1199 vec->x = cfread_float(file, ver, deflt ? deflt->x : 0.0f);
1200 vec->y = cfread_float(file, ver, deflt ? deflt->y : 0.0f);
1201 vec->z = cfread_float(file, ver, deflt ? deflt->z : 0.0f);
1204 void cfread_angles(angles *ang, CFILE *file, int ver, angles *deflt)
1206 if (file->version < ver) {
1210 ang->p = ang->b = ang->h = 0.0f;
1215 ang->p = cfread_float(file, ver, deflt ? deflt->p : 0.0f);
1216 ang->b = cfread_float(file, ver, deflt ? deflt->b : 0.0f);
1217 ang->h = cfread_float(file, ver, deflt ? deflt->h : 0.0f);
1220 char cfread_char(CFILE *file, int ver, char deflt)
1224 if (file->version < ver)
1227 if (cfread( &b, sizeof(b), 1, file) != 1)
1233 void cfread_string(char *buf, int n, CFILE *file)
1238 c = cfread_char(file);
1246 void cfread_string_len(char *buf,int n, CFILE *file)
1249 len = cfread_int(file);
1252 cfread(buf, len, 1, file);
1257 // equivalent write functions of above read functions follow
1259 int cfwrite_float(float f, CFILE *file)
1261 // i = INTEL_INT(i); // hmm, not sure what to do here
1262 return cfwrite(&f, sizeof(f), 1, file);
1265 int cfwrite_int(int i, CFILE *file)
1268 return cfwrite(&i, sizeof(i), 1, file);
1271 int cfwrite_uint(uint i, CFILE *file)
1274 return cfwrite(&i, sizeof(i), 1, file);
1277 int cfwrite_short(short s, CFILE *file)
1280 return cfwrite(&s, sizeof(s), 1, file);
1283 int cfwrite_ushort(ushort s, CFILE *file)
1286 return cfwrite(&s, sizeof(s), 1, file);
1289 int cfwrite_ubyte(ubyte b, CFILE *file)
1291 return cfwrite(&b, sizeof(b), 1, file);
1294 int cfwrite_vector(vector *vec, CFILE *file)
1296 if(!cfwrite_float(vec->x, file)){
1299 if(!cfwrite_float(vec->y, file)){
1302 return cfwrite_float(vec->z, file);
1305 int cfwrite_angles(angles *ang, CFILE *file)
1307 if(!cfwrite_float(ang->p, file)){
1310 if(!cfwrite_float(ang->b, file)){
1313 return cfwrite_float(ang->h, file);
1316 int cfwrite_char(char b, CFILE *file)
1318 return cfwrite( &b, sizeof(b), 1, file);
1321 int cfwrite_string(char *buf, CFILE *file)
1323 if ( (!buf) || (buf && !buf[0]) ) {
1324 return cfwrite_char(0, file);
1326 int len = strlen(buf);
1327 if(!cfwrite(buf, len, 1, file)){
1330 return cfwrite_char(0, file); // write out NULL termination
1333 int cfwrite_string_len(char *buf, CFILE *file)
1335 int len = strlen(buf);
1337 if(!cfwrite_int(len, file)){
1341 return cfwrite(buf,len,1,file);
1347 // Get the filelength
1348 int cfilelength( CFILE * cfile )
1350 Assert(cfile != NULL);
1352 Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1353 cb = &Cfile_block_list[cfile->id];
1355 // TODO: return length of memory mapped file
1356 Assert( !cb->data );
1358 Assert(cb->fp != NULL);
1360 // cb->size gets set at cfopen
1364 // cfwrite() writes to the file
1366 // returns: number of full elements actually written
1369 int cfwrite(void *buf, int elsize, int nelem, CFILE *cfile)
1371 Assert(cfile != NULL);
1372 Assert(buf != NULL);
1377 Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1378 cb = &Cfile_block_list[cfile->id];
1382 // cfwrite() not supported for memory-mapped files
1383 Assert( !cb->data );
1385 Assert(cb->fp != NULL);
1386 Assert(cb->lib_offset == 0 );
1387 result = fwrite(buf, elsize, nelem, cb->fp);
1393 // cfputc() writes a character to a file
1395 // returns: success ==> returns character written
1398 int cfputc(int c, CFILE *cfile)
1402 Assert(cfile != NULL);
1404 Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1405 cb = &Cfile_block_list[cfile->id];
1408 // cfputc() not supported for memory-mapped files
1409 Assert( !cb->data );
1411 Assert(cb->fp != NULL);
1412 result = fputc(c, cb->fp);
1418 // cfgetc() reads a character from a file
1420 // returns: success ==> returns character read
1423 int cfgetc(CFILE *cfile)
1425 Assert(cfile != NULL);
1429 int result = cfread(&tmp, 1, 1, cfile );
1430 if ( result == 1 ) {
1443 // cfgets() reads a string from a file
1445 // returns: success ==> returns pointer to string
1448 char *cfgets(char *buf, int n, CFILE *cfile)
1450 Assert(cfile != NULL);
1451 Assert(buf != NULL);
1457 for (i=0; i<n-1; i++ ) {
1461 int ret = cfread( &tmp_c, 1, 1, cfile );
1471 } while ( c == 13 );
1473 if ( c=='\n' ) break;
1480 // cfputs() writes a string to a file
1482 // returns: success ==> non-negative value
1485 int cfputs(char *str, CFILE *cfile)
1487 Assert(cfile != NULL);
1488 Assert(str != NULL);
1491 Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1492 cb = &Cfile_block_list[cfile->id];
1497 // cfputs() not supported for memory-mapped files
1498 Assert( !cb->data );
1499 Assert(cb->fp != NULL);
1500 result = fputs(str, cb->fp);
1506 // 16 and 32 bit checksum stuff ----------------------------------------------------------
1508 // CRC code for mission validation. given to us by Kevin Bentley on 7/20/98. Some sort of
1509 // checksumming code that he wrote a while ago.
1510 #define CRC32_POLYNOMIAL 0xEDB88320L
1511 unsigned long CRCTable[256];
1513 #define CF_CHKSUM_SAMPLE_SIZE 512
1515 // update cur_chksum with the chksum of the new_data of size new_data_size
1516 ushort cf_add_chksum_short(ushort seed, char *buffer, int size)
1518 ubyte * ptr = (ubyte *)buffer;
1519 unsigned int sum1,sum2;
1521 sum1 = sum2 = (int)(seed);
1525 if (sum1 >= 255 ) sum1 -= 255;
1530 return (unsigned short)((sum1<<8)+ sum2);
1533 // update cur_chksum with the chksum of the new_data of size new_data_size
1534 unsigned long cf_add_chksum_long(unsigned long seed, char *buffer, int size)
1538 unsigned long temp1;
1539 unsigned long temp2;
1541 p = (unsigned char*)buffer;
1545 temp1 = (crc>>8)&0x00FFFFFFL;
1546 temp2 = CRCTable[((int)crc^*p++)&0xff];
1553 void cf_chksum_long_init()
1558 for( i=0;i<=255;i++) {
1562 crc=(crc>>1)^CRC32_POLYNOMIAL;
1570 // single function convenient to use for both short and long checksums
1571 // NOTE : only one of chk_short or chk_long must be non-NULL (indicating which checksum to perform)
1572 int cf_chksum_do(CFILE *cfile, ushort *chk_short, uint *chk_long, int max_size)
1574 char cf_buffer[CF_CHKSUM_SAMPLE_SIZE];
1580 // determine whether we're doing a short or long checksum
1591 // if max_size is -1, set it to be the size of the file
1593 cfseek(cfile, 0, SEEK_SET);
1594 max_size = cfilelength(cfile);
1599 // determine how much we want to read
1600 if((max_size - cf_total) >= CF_CHKSUM_SAMPLE_SIZE){
1601 read_size = CF_CHKSUM_SAMPLE_SIZE;
1603 read_size = max_size - cf_total;
1606 // read in some buffer
1607 cf_len = cfread(cf_buffer, 1, read_size, cfile);
1609 // total we've read so far
1614 // do the proper short or long checksum
1616 *chk_long = cf_add_chksum_long(*chk_long, cf_buffer, cf_len);
1618 *chk_short = cf_add_chksum_short(*chk_short, cf_buffer, cf_len);
1621 } while((cf_len > 0) && (cf_total < max_size));
1626 // get the 2 byte checksum of the passed filename - return 0 if operation failed, 1 if succeeded
1627 int cf_chksum_short(char *filename, ushort *chksum, int max_size, int cf_type)
1630 CFILE *cfile = NULL;
1632 // zero the checksum
1635 // attempt to open the file
1636 cfile = cfopen(filename,"rt",CFILE_NORMAL,cf_type);
1641 // call the overloaded cf_chksum function()
1642 ret_val = cf_chksum_do(cfile, chksum, NULL, max_size);
1644 // close the file down
1648 // return the result
1652 // get the 2 byte checksum of the passed file - return 0 if operation failed, 1 if succeeded
1653 // NOTE : preserves current file position
1654 int cf_chksum_short(CFILE *file, ushort *chksum, int max_size)
1659 // Returns current position of file.
1660 start_pos = cftell(file);
1661 if(start_pos == -1){
1665 // move to the beginning of the file
1666 if(cfseek(file, 0, CF_SEEK_SET)){
1669 ret_code = cf_chksum_do(file, chksum, NULL, max_size);
1670 // move back to the start position
1671 cfseek(file, start_pos, CF_SEEK_SET);
1676 // get the 32 bit CRC checksum of the passed filename - return 0 if operation failed, 1 if succeeded
1677 int cf_chksum_long(char *filename, uint *chksum, int max_size, int cf_type)
1680 CFILE *cfile = NULL;
1682 // zero the checksum
1685 // attempt to open the file
1686 cfile = cfopen(filename,"rt",CFILE_NORMAL,cf_type);
1691 // call the overloaded cf_chksum function()
1692 ret_val = cf_chksum_do(cfile, NULL, chksum, max_size);
1694 // close the file down
1698 // return the result
1702 // get the 32 bit CRC checksum of the passed file - return 0 if operation failed, 1 if succeeded
1703 // NOTE : preserves current file position
1704 int cf_chksum_long(CFILE *file, uint *chksum, int max_size)
1709 // Returns current position of file.
1710 start_pos = cftell(file);
1711 if(start_pos == -1){
1715 // move to the beginning of the file
1716 if(cfseek(file, 0, CF_SEEK_SET)){
1719 ret_code = cf_chksum_do(file, NULL, chksum, max_size);
1720 // move back to the start position
1721 cfseek(file, start_pos, CF_SEEK_SET);
1727 // Flush the open file buffer
1729 // exit: 0 - success
1731 int cflush(CFILE *cfile)
1733 Assert(cfile != NULL);
1735 Assert(cfile->id >= 0 && cfile->id < MAX_CFILE_BLOCKS);
1736 cb = &Cfile_block_list[cfile->id];
1738 // not supported for memory mapped files
1739 Assert( !cb->data );
1741 Assert(cb->fp != NULL);
1742 return fflush(cb->fp);