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