vegastrike  0.5.1.r1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
vsfilesystem.cpp
Go to the documentation of this file.
1 #include <stdio.h>
2 #include <assert.h>
3 #include <stdarg.h>
4 #if defined (_WIN32) && !defined (__CYGWIN__)
5 #include <direct.h>
6 #include <config.h>
7 #include <string.h>
8 #ifndef NOMINMAX
9 #define NOMINMAX
10 #endif //tells VCC not to generate min/max macros
11 #include <windows.h>
12 #include <stdlib.h>
13 struct dirent
14 {
15  char d_name[1];
16 };
17 #else
18 #include <unistd.h>
19 #include <pwd.h>
20 #include <sys/types.h>
21 #include <dirent.h>
22 #endif
23 #include <sys/stat.h>
24 #include "config.h"
25 #include "configxml.h"
26 #include "vsfilesystem.h"
27 #include "vs_globals.h"
28 #include "vegastrike.h"
29 #include "common/common.h"
30 #include "galaxy_gen.h"
31 #include "pk3.h"
32 
33 #include "gnuhash.h"
34 
37 using std::cout;
38 using std::cerr;
39 using std::endl;
40 using std::string;
42 {
43  if (vs_config) {
44  static int vs_debug = XMLSupport::parse_int( vs_config->getVariable( "general", "debug_fs", "0" ) );
45  return vs_debug;
46  }
47  return 0;
48 }
49 char *CONFIGFILE;
50 char pwd[65536];
52 string curmodpath = "";
53 
54 ObjSerial serial_seed = 0; //Server/client serials won't intersect.
55 std::map< ObjSerial, bool >usedSerials;
56 
57 void usedSerial( ObjSerial ser, bool used )
58 {
59  usedSerials[ser] = used;
60 }
61 ObjSerial getUniqueSerial()
62 {
63  int offset = (SERVER ? 2 : 1);
64  size_t maxserial=(1<<(sizeof(ObjSerial)*8));
65  if (usedSerials.size()>=maxserial/3) {
66  static bool firstBadness=true;
67  if (firstBadness) {
68  fprintf(stderr,"Error recycling used serial since serial map is completely full at size %d\n",(int)usedSerials.size());
69  firstBadness=false;
70  }
71  return ((rand()%(maxserial/3-1))+1)*3;//OFFSET..set to emergency zero
72  }
73  std::map< ObjSerial, bool >::const_iterator iter;
74  ObjSerial ret;
75  do {
76  serial_seed = (serial_seed+3)%MAXSERIAL;
77  ret = serial_seed+offset;
78  } while ( ( iter = usedSerials.find( ret ) ) != usedSerials.end()
79  && (*iter).second );
80  usedSerial( ret, true );
81  return ret;
82 }
83 
84 extern string GetUnitDir( string filename );
85 
87 
88 #if defined (__APPLE__)
89 int selectdirs( struct dirent *entry )
90 #else
91 int selectdirs( const struct dirent * entry )
92 #endif
93 {
94  if (string( entry->d_name ) == "." && string( entry->d_name ) == "..")
95  return 0;
96  //Have to check if we have the full path or just relative (which would be a problem)
97  std::string tmp = selectcurrentdir+'/'+entry->d_name;
98  //cerr<<"Read directory entry : "<< tmp <<endl;
99  struct stat s;
100  if (stat( tmp.c_str(), &s ) < 0)
101  return 0;
102  if ( (s.st_mode&S_IFDIR) )
103  return 1;
104  return 0;
105 }
106 
107 #if defined (__FreeBSD__) || defined (__APPLE__)
108 int selectpk3s( struct dirent *entry )
109 #else
110 int selectpk3s( const struct dirent * entry )
111 #endif
112 {
113  //If this is a regular file and we have ".pk3" in it
114  if ( ( string( entry->d_name ).find( ".pk3" ) ) != std::string::npos && ( string( entry->d_name ).find( "data" ) )
115  == std::string::npos )
116  return 1;
117  return 0;
118 }
119 
120 #if defined (__FreeBSD__) || defined (__APPLE__)
121 int selectbigpk3s( struct dirent *entry )
122 #else
123 int selectbigpk3s( const struct dirent * entry )
124 #endif
125 {
126  //If this is a regular file and we have ".pk3" in it
127  if ( ( string( entry->d_name ).find( "data.pk3" ) ) != std::string::npos )
128  return 1;
129  return 0;
130 }
131 
132 namespace VSFileSystem
133 {
134 std::string vegastrike_cwd;
135 
136 void ChangeToProgramDirectory( char *argv0 )
137 {
138  {
139  char pwd[8192];
140  pwd[0] = '\0';
141  if (NULL != getcwd( pwd, 8191 )) {
142  pwd[8191] = '\0';
144  } else {
145  VSFileSystem::vs_fprintf( stderr, "Cannot change to program directory: path too long");
146  }
147  }
148  int ret = -1; /* Should it use argv[0] directly? */
149  char *program = argv0;
150 #ifndef _WIN32
151  char buf[65536];
152  {
153  char linkname[128]; /* /proc/<pid>/exe */
154  linkname[0] = '\0';
155  pid_t pid;
156 
157  /* Get our PID and build the name of the link in /proc */
158  pid = getpid();
159 
160  sprintf( linkname, "/proc/%d/exe", pid );
161  ret = readlink( linkname, buf, 65535 );
162  if (ret <= 0) {
163  sprintf( linkname, "/proc/%d/file", pid );
164  ret = readlink( linkname, buf, 65535 );
165  }
166  if (ret <= 0)
167  ret = readlink( program, buf, 65535 );
168  if (ret > 0) {
169  buf[ret] = '\0';
170  /* Ensure proper NUL termination */
171  program = buf;
172  }
173  }
174 #endif
175 
176  char *parentdir;
177  int pathlen = strlen( program );
178  parentdir = new char[pathlen+1];
179  char *c;
180  strncpy( parentdir, program, pathlen+1 );
181  c = (char*) parentdir;
182  while (*c != '\0') /* go to end */
183  c++;
184  while ( (*c != '/') && (*c != '\\') && c > parentdir ) /* back up to parent */
185  c--;
186  *c = '\0'; /* cut off last part (binary name) */
187  if (strlen( parentdir ) > 0) {
188  if (chdir( parentdir )) /* chdir to the binary app's parent */
189  VSFileSystem::vs_fprintf( stderr, "Cannot change to program directory.");
190  }
191  delete[] parentdir;
192 }
193 
194 VSError CachedFileLookup( FileLookupCache &cache, const string &file, VSFileType type )
195 {
196  string hashName = GetHashName( file );
197  FileLookupCache::iterator it = cache.find( hashName );
198  if ( it != cache.end() )
199  return it->second;
200 
201  else
202  return cache[hashName] = LookForFile( file, type );
203 }
204 
206 {
207  DisplayType( type, std::cerr );
208 }
209 
210  #define CASE( a ) \
211 case a: \
212  ostr<<#a; break;
213 void DisplayType( VSFileSystem::VSFileType type, std::ostream &ostr )
214 {
215  switch (type)
216  {
236  default:
237  ostr<<"VSFileSystem::<undefined VSFileType>";
238  break;
239  }
240 }
241  #undef CASE
242 
243 int GetReadBytes( char *fmt, va_list ap )
244 {
245  int ret = 0;
246 
247  cerr<<"STARTING ARGLIST"<<endl;
248  while (*fmt) {
249  switch (*fmt++)
250  {
251  case 'd':
252  break;
253  case 'f':
254  break;
255  case 's':
256  break;
257  case 'g':
258  break;
259  case 'n': //Number of bytes
260  ret = va_arg( ap, int );
261  cerr<<"\tFound 'n' : "<<ret<<endl;
262  break;
263  default:
264  cerr<<"\tOther arg"<<endl;
265  }
266  }
267  cerr<<"ENDING ARGLIST"<<endl;
268  return ret;
269 }
270 
271 /*
272  ***********************************************************************************************
273  **** VSFileSystem global variables ***
274  ***********************************************************************************************
275  */
276 
280 
281 vector< vector< string > >SubDirectories; //Subdirectories where we should look for VSFileTypes files
282 vector< string > Directories; //Directories where we should look for VSFileTypes files
283 vector< string > Rootdir; //Root directories where we should look for VSFileTypes files
285 string sharedunits;
291 string aidir;
292 string sharedanims;
296 string modname;
297 string moddir;
298 string datadir;
299 string homedir;
300 
301 string config_file;
302 string weapon_list;
304 string HOMESUBDIR( ".vegastrike" );
305 vector< string >current_path;
306 vector< string >current_directory;
307 vector< string >current_subdirectory;
308 vector< VSFileType > current_type;
309 
310 //vs_path only stuff
311 vector< std::string >savedpwd;
312 vector< std::string >curdir; //current dir starting from datadir
313 vector< std::vector< std::string > >savedcurdir; //current dir starting from datadir
314 
315 vector< int >UseVolumes;
316 
317 string failed;
318 
319 //Map of the currently opened PK3 volume/resource files
320 vsUMap< string, CPK3* >pk3_opened_files;
321 
322 /*
323  ***********************************************************************************************
324  **** vs_path functions ***
325  ***********************************************************************************************
326  */
327 
328 std::string GetHashName( const std::string &name )
329 {
330  std::string result( "" );
331  result = current_path.back()+current_directory.back()+current_subdirectory.back()+name;
332  return result;
333 }
334 std::string GetHashName( const std::string &name, const Vector &scale, int faction )
335 {
336  std::string result( "" );
337  result = current_path.back()+current_directory.back()+current_subdirectory.back()+name;
338 
339  result += XMLSupport::VectorToString( scale )+"|"+XMLSupport::tostring( faction );
340  return result;
341 }
342 
343 std::string GetSharedMeshHashName( const std::string &name, const Vector &scale, int faction )
344 {
345  return string( "#" )+XMLSupport::VectorToString( scale )+string( "#" )+name+string( "#" )+XMLSupport::tostring( faction );
346 }
347 std::string GetSharedTextureHashName( const std::string &name )
348 {
349  return string( "#stex#" )+name;
350 }
351 std::string GetSharedSoundHashName( const std::string &name )
352 {
353  return string( "#ssnd#" )+name;
354 }
355 
356 std::string MakeSharedPathReturnHome( const std::string &newpath )
357 {
358  CreateDirectoryHome( newpath );
359  return newpath+string( "/" );
360 }
361 std::string MakeSharedPath( const std::string &s )
362 {
363  VSFileSystem::vs_fprintf( stderr, "MakingSharedPath %s", s.c_str() );
364  return MakeSharedPathReturnHome( s )+s;
365 }
366 std::string MakeSharedStarSysPath( const std::string &s )
367 {
368  string syspath = sharedsectors+"/"+getStarSystemSector( s );
369  return MakeSharedPathReturnHome( syspath )+s;
370 }
371 std::string GetCorrectStarSysPath( const std::string &name, bool &autogenerated )
372 {
373  autogenerated = false;
374  if (name.length() == 0)
375  return string( "" );
376  string newname( name );
377  if (Network != NULL) {
378  std::string::size_type dot = name.find( "." );
379  if (dot != std::string::npos)
380  newname = name.substr( 0, dot );
381  newname += ".net.system";
382  }
383  VSFile f;
384  VSError err = f.OpenReadOnly( newname, SystemFile );
385  autogenerated = false;
386  if (err <= Ok)
387  autogenerated = (err == Shared);
388  return newname;
389 }
390 
391 /*
392  ***********************************************************************************************
393  **** VSFileSystem wrappers to stdio function calls ***
394  ***********************************************************************************************
395  */
396 
397 FILE * vs_open( const char *filename, const char *mode )
398 {
399  if (VSFS_DEBUG() > 1)
400  cerr<<"-= VS_OPEN in mode "<<mode<<" =- ";
401  FILE *fp;
402  string fullpath = homedir+"/"+string( filename );
403  if ( !use_volumes && (string( mode ) == "rb" || string( mode ) == "r") ) {
404  string output( "" );
405  fp = fopen( fullpath.c_str(), mode );
406  if (!fp) {
407  fullpath = string( filename );
408  output += fullpath+"... NOT FOUND\n\tTry loading : "+fullpath;
409  fp = fopen( fullpath.c_str(), mode );
410  }
411  if (!fp) {
412  fullpath = datadir+"/"+string( filename );
413  output += "... NOT FOUND\n\tTry loading : "+fullpath;
414  fp = fopen( fullpath.c_str(), mode );
415  }
416  if ( VSFS_DEBUG() ) {
417  if (fp) {
418  if (VSFS_DEBUG() > 2)
419  cerr<<fullpath<<" SUCCESS !!!"<<endl;
420  } else {
421  cerr<<output<<" NOT FOUND !!!"<<endl;
422  }
423  }
424  } else {
425  fp = fopen( fullpath.c_str(), mode );
426  if (fp) {
427  if (VSFS_DEBUG() > 2)
428  cerr<<fullpath<<" opened for writing SUCCESS !!!"<<endl;
429  } else if ( VSFS_DEBUG() ) {
430  cerr<<fullpath<<" FAILED !!!"<<endl;
431  }
432  }
433  return fp;
434 
435  return NULL;
436 }
437 
438 size_t vs_read( void *ptr, size_t size, size_t nmemb, FILE *fp )
439 {
440  if (!use_volumes) {
441  return fread( ptr, size, nmemb, fp );
442  } else {}
443  return 0;
444 }
445 
446 size_t vs_write( const void *ptr, size_t size, size_t nmemb, FILE *fp )
447 {
448  if (!use_volumes) {
449  return fwrite( ptr, size, nmemb, fp );
450  } else {}
451  return 0;
452 }
453 
454 void vs_close( FILE *fp )
455 {
456  if (!use_volumes) {
457  fclose( fp );
458  } else {}
459 }
460 
461 int vs_fprintf( FILE *fp, const char *format, ... )
462 {
463  if (!use_volumes) {
464  va_list ap;
465  va_start( ap, format );
466 
467  return vfprintf( fp, format, ap );
468  } else {}
469  return 0;
470 }
471 
472 void vs_dprintf( char level, const char *format, ... )
473 {
474  if (!use_volumes && level <= g_game.vsdebug) {
475  va_list ap;
476  va_start( ap, format );
477  vfprintf( stderr, format, ap );
478  }
479 }
480 
481 #if 0
482 int vs_fscanf( FILE *fp, const char *format, ... )
483 {
484  if (!use_volumes) {
485  va_list arglist;
486  va_start( arglist, format );
487  //return _input(fp,(unsigned char*)format,arglist);
488  return vfscanf( fp, format, arglist );
489  } else {}
490  return 0;
491 }
492 #endif
493 
494 int vs_fseek( FILE *fp, long offset, int whence )
495 {
496  return fseek( fp, offset, whence );
497 }
498 
499 long vs_ftell( FILE *fp )
500 {
501  return ftell( fp );
502 }
503 
504 void vs_rewind( FILE *fp )
505 {
506  rewind( fp );
507 }
508 
509 bool vs_feof( FILE *fp )
510 {
511  return feof( fp );
512 }
513 
514 long vs_getsize( FILE *fp )
515 {
516  if (!use_volumes) {
517  struct stat st;
518  if (fstat( fileno( fp ), &st ) == 0)
519  return st.st_size;
520  return -1;
521  }
522  return -1;
523 }
524 
525 /*
526  ***********************************************************************************************
527  **** VSFileSystem functions ***
528  ***********************************************************************************************
529  */
530 
532 {
533  //Setup home directory
534  char *chome_path = NULL;
535 #ifndef _WIN32
536  struct passwd *pwent;
537  pwent = getpwuid( getuid() );
538  chome_path = pwent->pw_dir;
539  if ( !DirectoryExists( chome_path ) ) {
540  cerr<<"!!! ERROR : home directory not found"<<endl;
541  VSExit( 1 );
542  }
543  string user_home_path( chome_path );
544  homedir = user_home_path+"/"+HOMESUBDIR;
545 #else
546  homedir = datadir+"/"+HOMESUBDIR;
547 #endif
548  cerr<<"USING HOMEDIR : "<<homedir<<" As the home directory "<<endl;
550 }
551 
553 {
554  vector< string >data_paths;
555 
556  /* commandline-specified paths come first */
557  if (!datadir.empty())
558  data_paths.push_back( datadir );
559 
560  /* DATA_DIR should no longer be necessary--it will either use the path
561  * to the binary, or the current directory. */
562 #ifdef DATA_DIR
563  data_paths.push_back( DATA_DIR );
564 #endif
565  if ( !vegastrike_cwd.empty() ) {
566  data_paths.push_back( vegastrike_cwd );
567  data_paths.push_back( vegastrike_cwd+"/.." );
568  data_paths.push_back( vegastrike_cwd+"/../data4.x" );
569  data_paths.push_back( vegastrike_cwd+"/../../data4.x" );
570  data_paths.push_back( vegastrike_cwd+"/../../data" );
571  data_paths.push_back( vegastrike_cwd+"/data4.x" );
572  data_paths.push_back( vegastrike_cwd+"/data" );
573  data_paths.push_back( vegastrike_cwd+"/../data" );
574  data_paths.push_back( vegastrike_cwd+"/../Resources" );
575  }
576  data_paths.push_back( "." );
577  data_paths.push_back( ".." );
578  //data_paths.push_back( "../data4.x" ); DELETE 4.x no longer used since .5 release obsolete, removal required.
579  //data_paths.push_back( "../../data4.x" );
580  data_paths.push_back( "../data" );
581  data_paths.push_back( "../../data" );
582  data_paths.push_back( "../Resources" );
583  data_paths.push_back( "../Resources/data" );
584  //data_paths.push_back( "../Resources/data4.x" );
585 
586  //Win32 data should be "."
587  char tmppath[16384];
588  for (vector< string >::iterator vsit = data_paths.begin(); vsit != data_paths.end(); vsit++)
589  //Test if the dir exist and contains config_file
590  if (FileExists( (*vsit), config_file ) >= 0) {
591  cerr<<"Found data in "<<(*vsit)<<endl;
592  if (NULL != getcwd( tmppath, 16384 )) {
593  if ( (*vsit).substr( 0, 1 ) == "." )
594  datadir = string( tmppath )+"/"+(*vsit);
595  else
596  datadir = (*vsit);
597  } else {
598  VSFileSystem::vs_fprintf( stderr, "Cannot get current path: path too long");
599  }
600 
601  if (chdir( datadir.c_str() ) < 0) {
602  cerr<<"Error changing to datadir"<<endl;
603  exit( 1 );
604  }
605  if (NULL != getcwd( tmppath, 16384 )) {
606  datadir = string( tmppath );
607  } else {
608  VSFileSystem::vs_fprintf( stderr, "Cannot get current path: path too long");
609  }
610 
611  cerr<<"Using "<<datadir<<" as data directory"<<endl;
612  break;
613  }
614  data_paths.clear();
615  string versionloc = datadir+"/Version.txt";
616  FILE *version = fopen( versionloc.c_str(), "r" );
617  if (!version) {
618  versionloc = datadir+"Version.txt";
619  version = fopen( versionloc.c_str(), "r" );
620  }
621  if (!version)
622  version = fopen( "Version.txt", "r" );
623  if (version) {
624  string hsd = "";
625  int c;
626  while ( ( c = fgetc( version ) ) != EOF ) {
627  if ( isspace( c ) )
628  break;
629  hsd += (char) c;
630  }
631  fclose( version );
632  if ( hsd.length() ) {
633  HOMESUBDIR = hsd;
634  printf( "Using %s as the home directory\n", hsd.c_str() );
635  }
636  }
637  //Get the mods path
638  moddir = datadir+"/"+string( "mods" );
639  cout<<"Found MODDIR = "<<moddir<<endl;
640 }
641 
642 //Config file has been loaded from data dir but now we look at the specified moddir in order
643 //to see if we should use a mod config file
644 void LoadConfig( string subdir )
645 {
646  bool found = false;
647  bool foundweapons = false;
648  //First check if we have a config file in homedir+"/"+subdir or in datadir+"/"+subdir
649  weapon_list = "weapon_list.xml";
650  if (subdir != "") {
651  modname = subdir;
652  if ( DirectoryExists( homedir+"/mods/"+subdir ) ) {
653  if (FileExists( homedir+"/mods/"+subdir, config_file ) >= 0) {
654  cout<<"CONFIGFILE - Found a config file in home mod directory, using : "
655  <<(homedir+"/mods/"+subdir+"/"+config_file)<<endl;
656  if (FileExists( homedir+"/mods/"+subdir, "weapon_list.xml" ) >= 0) {
657  weapon_list = homedir+"/mods/"+subdir+"/weapon_list.xml";
658  foundweapons = true;
659  }
660  config_file = homedir+"/mods/"+subdir+"/"+config_file;
661  found = true;
662  }
663  }
664  if (!found)
665  cout<<"WARNING : coudn't find a mod named '"<<subdir<<"' in homedir/mods"<<endl;
666  if ( DirectoryExists( moddir+"/"+subdir ) ) {
667  if (FileExists( moddir+"/"+subdir, config_file ) >= 0) {
668  if (!found)
669  cout<<"CONFIGFILE - Found a config file in mods directory, using : "
670  <<(moddir+"/"+subdir+"/"+config_file)<<endl;
671  if ( (!foundweapons) && FileExists( moddir+"/"+subdir, "weapon_list.xml" ) >= 0 ) {
672  weapon_list = moddir+"/"+subdir+"/weapon_list.xml";
673  foundweapons = true;
674  }
675  if (!found) config_file = moddir+"/"+subdir+"/"+config_file;
676  found = true;
677  }
678  } else {
679  cout<<"ERROR : coudn't find a mod named '"<<subdir<<"' in datadir/mods"<<endl;
680  }
681  //}
682  }
683  if (!found) {
684  //Next check if we have a config file in homedir if we haven't found one for mod
685  if (FileExists( homedir, config_file ) >= 0) {
686  cerr<<"CONFIGFILE - Found a config file in home directory, using : "<<(homedir+"/"+config_file)<<endl;
688  } else {
689  cerr<<"CONFIGFILE - No config found in home : "<<(homedir+"/"+config_file)<<endl;
690  if (FileExists( datadir, config_file ) >= 0) {
691  cerr<<"CONFIGFILE - No home config file found, using datadir config file : "<<(datadir+"/"+config_file)<<endl;
692  //We didn't find a config file in home_path so we load the data_path one
693  }
694  else {
695  cerr<<"CONFIGFILE - No config found in data dir : "<<(datadir+"/"+config_file)<<endl;
696  cerr<<"CONFIG FILE NOT FOUND !!!"<<endl;
697  VSExit( 1 );
698  }
699  }
700  } else if (subdir != "") {
701  printf( "Using Mod Directory %s\n", moddir.c_str() );
702  CreateDirectoryHome( "mods" );
703  CreateDirectoryHome( "mods/"+subdir );
704  homedir = homedir+"/mods/"+subdir;
705  }
706  //Delete the default config in order to reallocate it with the right one (if it is a mod)
707  if (vs_config) {
708  fprintf( stderr, "reallocating vs_config \n" );
709  delete vs_config;
710  }
712 
713  //Now check if there is a data directory specified in it
714  //NOTE : THIS IS NOT A GOOD IDEA TO HAVE A DATADIR SPECIFIED IN THE CONFIG FILE
715  static string data_path( vs_config->getVariable( "data", "datadir", "" ) );
716  if (data_path != "") {
717  //We found a path to data in config file
718  cout<<"DATADIR - Found a datadir in config, using : "<<data_path<<endl;
719  datadir = data_path;
720  } else {
721  cout<<"DATADIR - No datadir specified in config file, using ; "<<datadir<<endl;
722  }
723 }
724 
725 void InitMods()
726 {
727  string curpath;
728  struct dirent **dirlist;
729  //new config program should insert hqtextures variable
730  //with value "hqtextures" in data section.
731  string hq = vs_config->getVariable( "data", "hqtextures", "" );
732  if (hq != "") {
733  //HQ Texture dir sits alongside data dir.
734  selectcurrentdir = datadir+"/..";
735  int ret = scandir( selectcurrentdir.c_str(), &dirlist, selectdirs, 0 );
736  if (ret >= 0) {
737  while (ret--) {
738  string dname( dirlist[ret]->d_name );
739  if (dname == hq) {
740  curpath = selectcurrentdir+"/"+dname;
741  cout<<"\n\nAdding HQ Textures Pack\n\n";
742  Rootdir.push_back( curpath );
743  }
744  }
745  }
746  free( dirlist );
747  }
749  int ret = scandir( selectcurrentdir.c_str(), &dirlist, selectdirs, 0 );
750  if (ret < 0) {
751  return;
752  } else {
753  while (ret--) {
754  string dname( dirlist[ret]->d_name );
755  if (dname == modname) {
756  curpath = moddir+"/"+dname;
757  cout<<"Adding mod path : "<<curpath<<endl;
758  Rootdir.push_back( curpath );
759  }
760  }
761  }
762  free( dirlist );
763  //Scan for mods with standard data subtree
764  curmodpath = homedir+"/mods/";
766  ret = scandir( selectcurrentdir.c_str(), &dirlist, selectdirs, 0 );
767  if (ret < 0) {
768  return;
769  } else {
770  while (ret--) {
771  string dname( dirlist[ret]->d_name );
772  if (dname == modname) {
773  curpath = curmodpath+dname;
774  cout<<"Adding mod path : "<<curpath<<endl;
775  Rootdir.push_back( curpath );
776  }
777  }
778  }
779  free( dirlist );
780 }
781 
782 void InitPaths( string conf, string subdir )
783 {
784  config_file = conf;
785 
786  current_path.push_back( "" );
787  current_directory.push_back( "" );
788  current_subdirectory.push_back( "" );
789  current_type.push_back( UnknownFile );
790 
791  int i;
792  for (i = 0; i <= UnknownFile; i++)
793  UseVolumes.push_back( 0 );
794  /************************** Data and home directory settings *************************/
795 
796  InitDataDirectory(); //Need to be first for win32
798  LoadConfig( subdir );
799  //Paths relative to datadir or homedir (both should have the same structure)
800  //Units are in sharedunits/unitname/, sharedunits/subunits/unitname/ or sharedunits/weapons/unitname/ or in sharedunits/faction/unitname/
801  //Meshes are in sharedmeshes/ or in current unit that is being loaded
802  //Textures are in sharedtextures/ or in current unit dir that is being loaded or in the current animation dir that is being loaded or in the current sprite dir that is being loeded
803  //Sounds are in sharedsounds/
804  //Universes are in universe/
805  //Systems are in "sectors"/ config variable
806  //Cockpits are in cockpits/ (also a config var)
807  //Animations are in animations/
808  //VSSprite are in sprites/ or in ./ (when full subpath is provided) or in the current cockpit dir that is being loaded
809  //First allocate an empty directory list for each file type
810  for (i = 0; i < UnknownFile; i++) {
811  vector< string >vec;
812  Directories.push_back( "" );
813  SubDirectories.push_back( vec );
814  }
815  sharedsectors = vs_config->getVariable( "data", "sectors", "sectors" );
816  sharedcockpits = vs_config->getVariable( "data", "cockpits", "cockpits" );
817  shareduniverse = vs_config->getVariable( "data", "universe_path", "universe" );
818  sharedanims = vs_config->getVariable( "data", "animations", "animations" );
819  sharedvideos = vs_config->getVariable( "data", "movies", "movies" );
820  sharedsprites = vs_config->getVariable( "data", "sprites", "sprites" );
821  savedunitpath = vs_config->getVariable( "data", "serialized_xml", "serialized_xml" );
822  sharedtextures = vs_config->getVariable( "data", "sharedtextures", "textures" );
823  sharedsounds = vs_config->getVariable( "data", "sharedsounds", "sounds" );
824  sharedmeshes = vs_config->getVariable( "data", "sharedmeshes", "meshes" );
825  sharedunits = vs_config->getVariable( "data", "sharedunits", "units" );
826  aidir = vs_config->getVariable( "data", "ai_directory", "ai" );
827  universe_name = vs_config->getVariable( "general", "galaxy", "milky_way.xml" );
828 
829  //Setup the directory lists we know about - note these are relative paths to datadir or homedir
830  //----- THE Directories vector contains the resource/volume files name without extension or the main directory to files
832  //Have to put it in first place otherwise VS will find default unit file
833  SubDirectories[UnitFile].push_back( "subunits" );
834  SubDirectories[UnitFile].push_back( "weapons" );
835  SubDirectories[UnitFile].push_back( "" );
836  SubDirectories[UnitFile].push_back( "factions/planets" );
837  SubDirectories[UnitFile].push_back( "factions/upgrades" );
838  SubDirectories[UnitFile].push_back( "factions/neutral" );
839  SubDirectories[UnitFile].push_back( "factions/aera" );
840  SubDirectories[UnitFile].push_back( "factions/confed" );
841  SubDirectories[UnitFile].push_back( "factions/pirates" );
842  SubDirectories[UnitFile].push_back( "factions/rlaan" );
843 
845 
847  SubDirectories[MeshFile].push_back( "mounts" );
848  SubDirectories[MeshFile].push_back( "nav/default" );
849 
851  SubDirectories[TextureFile].push_back( "mounts" );
852  SubDirectories[TextureFile].push_back( "nav/default" );
853 
854  //We will also look in subdirectories with universe name
857 
864 
866  SubDirectories[AiFile].push_back( "events" );
867  SubDirectories[AiFile].push_back( "script" );
868 
869  Directories[MissionFile] = "mission";
870  Directories[CommFile] = "communications";
871  Directories[SaveFile] = "save";
872  Directories[MusicFile] = "music";
873  Directories[PythonFile] = "bases";
874  Directories[AccountFile] = "accounts";
875 
876  simulation_atom_var = atof( vs_config->getVariable( "general", "simulation_atom", "0.1" ).c_str() );
877  cout<<"SIMULATION_ATOM: "<<SIMULATION_ATOM<<endl;
878 
879  /************************* Home directory subdirectories creation ************************/
882  CreateDirectoryHome( sharedtextures+"/backgrounds" );
886  CreateDirectoryHome( "save" );
887 
888  //We will be able to automatically add mods files (set of resources or even directory structure similar to the data tree)
889  //by just adding a subdirectory named with the mod name in the subdirectory "mods"...
890  //I just have to implement that and then add all mods/ subdirs in Rootdir vector
891  Rootdir.push_back( homedir );
892  InitMods();
893  Rootdir.push_back( datadir );
894 
895  //NOTE : UniverseFiles cannot use volumes since some are needed by python
896  //Also : Have to try with systems, not sure it would work well
897  //Setup the use of volumes for certain VSFileType
898  volume_format = vs_config->getVariable( "data", "volume_format", "pk3" );
899  if (volume_format == "vsr")
901 
902  else if (volume_format == "pk3")
904 
905  else
907  if (FileExists( datadir, "/data."+volume_format ) >= 0) {
908  //Every kind of file will use the big volume except Unknown files and python files that needs to remain standard files
909  for (i = 0; i < UnknownFile; i++)
910  UseVolumes[i] = 2;
911  UseVolumes[PythonFile] = 0;
912  UseVolumes[AccountFile] = 0;
913  } else {
914  if (FileExists( datadir, "/"+sharedunits+"."+volume_format ) >= 0) {
915  UseVolumes[UnitFile] = 1;
916  cout<<"Using volume file "<<(datadir+"/"+sharedunits)<<".pk3"<<endl;
917  }
918  if (FileExists( datadir, "/"+sharedmeshes+"."+volume_format ) >= 0) {
919  UseVolumes[MeshFile] = 1;
920  cout<<"Using volume file "<<(datadir+"/"+sharedmeshes)<<".pk3"<<endl;
921  }
922  if (FileExists( datadir, "/"+sharedtextures+"."+volume_format ) >= 0) {
923  UseVolumes[TextureFile] = 1;
924  cout<<"Using volume file "<<(datadir+"/"+sharedtextures)<<".pk3"<<endl;
925  }
926  if (FileExists( datadir, "/"+sharedsounds+"."+volume_format ) >= 0) {
927  UseVolumes[SoundFile] = 1;
928  cout<<"Using volume file "<<(datadir+"/"+sharedsounds)<<".pk3"<<endl;
929  }
930  if (FileExists( datadir, "/"+sharedcockpits+"."+volume_format ) >= 0) {
931  UseVolumes[CockpitFile] = 1;
932  cout<<"Using volume file "<<(datadir+"/"+sharedcockpits)<<".pk3"<<endl;
933  }
934  if (FileExists( datadir, "/"+sharedsprites+"."+volume_format ) >= 0) {
936  cout<<"Using volume file "<<(datadir+"/"+sharedsprites)<<".pk3"<<endl;
937  }
938  if (FileExists( datadir, "/animations."+volume_format ) >= 0) {
939  UseVolumes[AnimFile] = 1;
940  cout<<"Using volume file "<<(datadir+"/animations")<<".pk3"<<endl;
941  }
942  if (FileExists( datadir, "/movies."+volume_format ) >= 0) {
943  UseVolumes[VideoFile] = 1;
944  cout<<"Using volume file "<<(datadir+"/movies")<<".pk3"<<endl;
945  }
946  if (FileExists( datadir, "/communications."+volume_format ) >= 0) {
947  UseVolumes[CommFile] = 1;
948  cout<<"Using volume file "<<(datadir+"/communications")<<".pk3"<<endl;
949  }
950  if (FileExists( datadir, "/mission."+volume_format ) >= 0) {
951  UseVolumes[MissionFile] = 1;
952  cout<<"Using volume file "<<(datadir+"/mission")<<".pk3"<<endl;
953  }
954  if (FileExists( datadir, "/ai."+volume_format ) >= 0) {
955  UseVolumes[AiFile] = 1;
956  cout<<"Using volume file "<<(datadir+"/ai")<<".pk3"<<endl;
957  }
958  UseVolumes[ZoneBuffer] = 0;
959  }
960 }
961 
962 void CreateDirectoryAbs( const char *filename )
963 {
964  int err;
965  if ( !DirectoryExists( filename ) ) {
966  err = mkdir( filename
967 #if !defined (_WIN32) || defined (__CYGWIN__)
968  , 0xFFFFFFFF
969 #endif
970  );
971  if (err < 0 && errno != EEXIST) {
972  cerr<<"Errno="<<errno<<" - FAILED TO CREATE : "<<filename<<endl;
973  GetError( "CreateDirectory" );
974  VSExit( 1 );
975  }
976  }
977 }
978 
979 void CreateDirectoryAbs( const string &filename )
980 {
981  CreateDirectoryAbs( filename.c_str() );
982 }
983 void CreateDirectoryHome( const char *filename )
984 {
985  CreateDirectoryAbs( homedir+"/"+string( filename ) );
986 }
987 void CreateDirectoryHome( const string &filename )
988 {
989  CreateDirectoryHome( filename.c_str() );
990 }
991 void CreateDirectoryData( const char *filename )
992 {
993  CreateDirectoryAbs( datadir+"/"+string( filename ) );
994 }
995 void CreateDirectoryData( const string &filename )
996 {
997  CreateDirectoryData( filename.c_str() );
998 }
999 
1000 //Absolute directory -- DO NOT USE FOR TESTS IN VOLUMES !!
1001 bool DirectoryExists( const char *filename )
1002 {
1003  struct stat s;
1004  if (stat( filename, &s ) < 0)
1005  return false;
1006  if (s.st_mode&S_IFDIR)
1007  return true;
1008  return false;
1009 }
1010 bool DirectoryExists( const string &filename )
1011 {
1012  return DirectoryExists( filename.c_str() );
1013 }
1014 
1015 //root is the path to the type directory or the type volume
1016 //filename is the subdirectory+"/"+filename
1017 int FileExists( const string &root, const char *filename, VSFileType type, bool lookinvolume )
1018 {
1019  int found = -1;
1020  bool volok = false;
1021  string fullpath;
1022  const char *file;
1023  if (filename[0] == '/')
1024  file = filename+1;
1025  else
1026  file = filename;
1027  const char *rootsep = (root == "" || root == "/") ? "" : "/";
1028  if (!UseVolumes[type] || !lookinvolume) {
1029  if (type == UnknownFile)
1030  fullpath = root+rootsep+file;
1031  else
1032  fullpath = root+rootsep+Directories[type]+"/"+file;
1033  struct stat s;
1034  if (stat( fullpath.c_str(), &s ) >= 0) {
1035  if (s.st_mode&S_IFDIR) {
1036  cerr<<" File is a directory ! ";
1037  found = -1;
1038  } else {
1040  found = 1;
1041  }
1042  }
1043  //}
1044  } else {
1045  if (q_volume_format == vfmtVSR) {} else if (q_volume_format == vfmtPK3) {
1046  CPK3 *vol;
1047  string filestr;
1048 
1049  //TRY TO OPEN A DATA.VOLFORMAT FILE IN THE ROOT DIRECTORY PASSED AS AN ARG
1050  filestr = Directories[type]+"/"+file;
1051  fullpath = root+rootsep+"data."+volume_format;
1052  vsUMap< string, CPK3* >::iterator it;
1053  it = pk3_opened_files.find( fullpath );
1054  failed += "Looking for file in VOLUME : "+fullpath+"... ";
1055  if ( it == pk3_opened_files.end() ) {
1056  //File is not opened so we open it and add it in the pk3 file map
1057  vol = new CPK3;
1058  if ( ( volok = vol->Open( fullpath.c_str() ) ) ) {
1059  failed += " VOLUME OPENED\n";
1060  //We add the resource file to the map only if we could have opened it
1061  std::pair< std::string, CPK3* >pk3_pair( fullpath, vol );
1062  pk3_opened_files.insert( pk3_pair );
1063  } else {
1064  failed += " COULD NOT OPEN VOLUME\n";
1065  }
1066  } else {
1067  failed += " VOLUME FOUND\n";
1068  vol = it->second;
1069  volok = true;
1070  }
1071  //Try to get the file index in the archive
1072  if (volok) {
1073  found = vol->FileExists( filestr.c_str() );
1074  if (found >= 0)
1076  } else {
1077  found = -1;
1078  }
1079  if (found < 0) {
1080  //AND THEN A VOLUME FILE BASED ON DIRECTORIES[TYPE]
1081  filestr = string( file );
1082  fullpath = root+rootsep+Directories[type]+"."+volume_format;
1083  it = pk3_opened_files.find( fullpath );
1084  failed += "Looking for file in VOLUME : "+fullpath+"... ";
1085  if ( it == pk3_opened_files.end() ) {
1086  //File is not opened so we open it and add it in the pk3 file map
1087  vol = new CPK3;
1088  if ( ( volok = vol->Open( fullpath.c_str() ) ) ) {
1089  failed += " VOLUME OPENED\n";
1090  //We add the resource file to the map only if we could have opened it
1091  std::pair< std::string, CPK3* >pk3_pair( fullpath, vol );
1092  pk3_opened_files.insert( pk3_pair );
1093  } else {
1094  failed += " COULD NOT OPEN VOLUME\n";
1095  }
1096  } else {
1097  failed += " VOLUME FOUND\n";
1098  vol = it->second;
1099  volok = true;
1100  }
1101  //Try to get the file index in the archive
1102  if (volok) {
1103  found = vol->FileExists( filestr.c_str() );
1104  if (found >= 0)
1106  } else {
1107  found = -1;
1108  }
1109  }
1110  }
1111  }
1112  if (found < 0) {
1113  if (!UseVolumes[type])
1114  failed += "\tTRY LOADING : "+nameof( type )+" "+fullpath+"... NOT FOUND\n";
1115  else if (VSFS_DEBUG() > 1)
1116  failed += "\tTRY LOADING in "+nameof( type )+" "+fullpath+" : "+file+"... NOT FOUND\n";
1117  } else {
1118  if (!UseVolumes[type])
1119  failed = "\tTRY LOADING : "+nameof( type )+" "+fullpath+"... SUCCESS";
1120  else if (VSFS_DEBUG() > 1)
1121  failed = "\tTRY LOADING in "+nameof( type )+" "+fullpath+" : "+file+"... SUCCESS";
1122  else
1123  failed.erase();
1124  }
1125  return found;
1126 }
1127 int FileExists( const string &root, const string &filename, VSFileType type, bool lookinvolume )
1128 {
1129  return FileExists( root, filename.c_str(), type, lookinvolume );
1130 }
1131 
1132 int FileExistsData( const char *filename, VSFileType type )
1133 {
1134  return FileExists( datadir, filename, type );
1135 }
1136 int FileExistsData( const string &filename, VSFileType type )
1137 {
1138  return FileExists( datadir, filename, type );
1139 }
1140 
1141 int FileExistsHome( const char *filename, VSFileType type )
1142 {
1143  return FileExists( homedir, filename, type );
1144 }
1145 int FileExistsHome( const string &filename, VSFileType type )
1146 {
1147  return FileExists( homedir, filename, type );
1148 }
1149 
1150 VSError GetError( const char *str )
1151 {
1152  cerr<<"!!! ERROR/WARNING VSFile : ";
1153  if (str)
1154  cerr<<"on "<<str<<" : ";
1155  if (errno == ENOENT) {
1156  cerr<<"File not found"<<endl;
1157  return FileNotFound;
1158  } else if (errno == EPERM) {
1159  cerr<<"Permission denied"<<endl;
1160  return LocalPermissionDenied;
1161  } else if (errno == EACCES) {
1162  cerr<<"Access denied"<<endl;
1163  return LocalPermissionDenied;
1164  } else {
1165  cerr<<"Unspecified error (maybe to document in VSFile ?)"<<endl;
1166  return Unspecified;
1167  }
1168 }
1169 
1170 VSError LookForFile( const string &file, VSFileType type, VSFileMode mode )
1171 {
1172  VSFile vsfile;
1173  vsfile.SetFilename( file );
1174  VSError err = LookForFile( vsfile, type, mode );
1175  return err;
1176 }
1177 
1179 {
1180  int found = -1, shared = false;
1181  string filepath, curpath, dir, extra( "" ), subdir;
1182  failed.erase();
1183  VSFileType curtype = type;
1184  //First try in the current path
1185  switch (type)
1186  {
1187  case UnitFile:
1188  extra = "/"+GetUnitDir( f.GetFilename() );
1189  break;
1190  case CockpitFile:
1191  //For cockpits we look in subdirectories that have the same name as the cockpit itself
1192  extra = "/"+string( f.GetFilename() );
1193  break;
1194  case AnimFile:
1195  //Animations are always in subdir named like the anim itself
1196  extra += "/"+f.GetFilename();
1197  break;
1198  case UniverseFile:
1199  case SystemFile:
1200  case UnitSaveFile:
1201  case TextureFile:
1202  case SoundFile:
1203  case PythonFile:
1204  case MeshFile:
1205  case CommFile:
1206  case AiFile:
1207  case SaveFile:
1208  case VideoFile:
1209  case VSSpriteFile:
1210  case MissionFile:
1211  case MusicFile:
1212  case AccountFile:
1213  case ZoneBuffer:
1214  case JPEGBuffer:
1215  case UnknownFile:
1216  break;
1217  }
1218  //This test lists all the VSFileType that should be looked for in the current directory
1219  unsigned int i = 0, j = 0;
1220  for (int LC = 0; LC < 2 && found < 0; ( LC += (extra == "" ? 2 : 1) ), extra = "") {
1221  if ( current_path.back() != ""
1222  && (type == TextureFile || type == MeshFile || type == VSSpriteFile || type == AnimFile || type == VideoFile) ) {
1223  for (i = 0; found < 0 && i < Rootdir.size(); i++) {
1224  curpath = Rootdir[i];
1225  subdir = current_subdirectory.back();
1226  if (extra != "")
1227  subdir += extra;
1228  curtype = current_type.back();
1229  found = FileExists( curpath, ( subdir+"/"+f.GetFilename() ).c_str(), curtype );
1230  if (found >= 0) {
1231  shared = false;
1232  f.SetAltType( curtype );
1233  } else {
1234  //Set curtype back to original type if we didn't find the file in the current dir
1235 
1236  curtype = type;
1237  shared = true;
1238  }
1239  }
1240  }
1241  //FIRST LOOK IN HOMEDIR FOR A STANDARD FILE, SO WHEN USING VOLUME WE DO NOT LOOK FIRST IN VOLUMES
1242  if (found < 0 && UseVolumes[curtype]) {
1243  curpath = homedir;
1244  subdir = "";
1245  if (extra != "")
1246  subdir += extra;
1247  found = FileExists( curpath, ( subdir+"/"+f.GetFilename() ).c_str(), type, false );
1248  for (j = 0; found < 0 && j < SubDirectories[curtype].size(); j++) {
1249  subdir = SubDirectories[curtype][j];
1250  if (extra != "")
1251  subdir += extra;
1252  found = FileExists( curpath, ( subdir+"/"+f.GetFilename() ).c_str(), curtype, false );
1253  f.SetVolume( VSFSNone );
1254  }
1255  }
1256  //THEN LOOK IN ALL THE REGISTERED ROOT DIRS
1257  for (i = 0; found < 0 && i < Rootdir.size(); i++) {
1258  curpath = Rootdir[i];
1259  subdir = f.GetSubDirectory();
1260  if (extra != "")
1261  subdir += extra;
1262  found = FileExists( curpath, ( subdir+"/"+f.GetFilename() ).c_str(), curtype );
1263  for (j = 0; found < 0 && j < SubDirectories[curtype].size(); j++) {
1264  curpath = Rootdir[i];
1265  subdir = SubDirectories[curtype][j];
1266  if ( f.GetSubDirectory().length() ) {
1267  if ( subdir.length() ) subdir += "/";
1268  subdir += f.GetSubDirectory();
1269  }
1270  if (extra != "")
1271  subdir += extra;
1272  found = FileExists( curpath, ( subdir+"/"+f.GetFilename() ).c_str(), curtype );
1273  }
1274  }
1275  if (curtype == CockpitFile) {
1276  for (i = 0; found < 0 && i < Rootdir.size(); i++) {
1277  curpath = Rootdir[i];
1278  subdir = "";
1279  found = FileExists( curpath, ( subdir+"/"+f.GetFilename() ).c_str(), type );
1280  for (j = 0; found < 0 && j < SubDirectories[curtype].size(); j++) {
1281  curpath = Rootdir[i];
1282  subdir = SubDirectories[curtype][j];
1283  if (extra != "")
1284  subdir += extra;
1285  found = FileExists( curpath, ( subdir+"/"+f.GetFilename() ).c_str(), curtype );
1286  }
1287  }
1288  }
1289  }
1290  if (VSFS_DEBUG() > 1) {
1291  if (isin_bigvolumes > VSFSNone)
1292  cerr<<failed<<" - INDEX="<<found<<endl<<endl;
1293  else
1294  cerr<<failed<<endl;
1295  }
1296  if (found >= 0) {
1297  if ( (type == SystemFile && i == 0) || (type == SoundFile /*right now only allow shared ones?!*/) /* Rootdir[i]==homedir*/ )
1298  shared = true;
1299  f.SetDirectory( Directories[curtype] );
1300  f.SetSubDirectory( subdir );
1301  f.SetRoot( curpath );
1303  //If we found a file in a volume we store its index in the concerned archive
1304  if (UseVolumes[curtype] && isin_bigvolumes > VSFSNone)
1305  f.SetIndex( found );
1307  if (shared)
1308  return Shared;
1309  else
1310  return Ok;
1311  }
1312  return FileNotFound;
1313 }
1314 
1315 /*
1316  ***********************************************************************************************
1317  **** VSFileSystem::VSFile class member functions ***
1318  ***********************************************************************************************
1319  */
1320 
1321 //IMPORTANT NOTE : IN MOST FILE OPERATION FUNCTIONS WE USE THE "alt_type" MEMBER BECAUSE A FILE WHOSE NATIVE TYPE
1322 //SHOULD BE HANDLED IN VOLUMES MIGHT BE FOUND IN THE CURRENT DIRECTORY OF A TYPE THAT IS NOT HANDLED IN
1323 //VOLUMES -> SO WE HAVE TO USE THE ALT_TYPE IN MOST OF THE TEST TO USE THE CORRECT FILE OPERATIONS
1324 
1325 void VSFile::private_init()
1326 {
1327  fp = NULL;
1328  size = 0;
1329  pk3_file = NULL;
1330  pk3_extracted_file = NULL;
1331  offset = 0;
1332  valid = false;
1333  file_type = alt_type = UnknownFile;
1334  file_index = -1;
1335  volume_type = VSFSNone;
1336 }
1337 
1339 {
1340  private_init();
1341 }
1342 
1343 VSFile::VSFile( const char *buffer, long bufsize, VSFileType type, VSFileMode mode )
1344 {
1345  private_init();
1346  this->size = bufsize;
1347  this->pk3_extracted_file = new char[bufsize+1];
1348  memcpy( this->pk3_extracted_file, buffer, bufsize );
1349  pk3_extracted_file[bufsize] = 0;
1350  this->file_type = this->alt_type = ZoneBuffer;
1351  this->file_mode = mode;
1352  //To say we want to read in volume even if it is not the case then it will read in pk3_extracted_file
1353  this->volume_type = VSFSBig;
1354 }
1355 
1356 VSFile::VSFile( const char *filename, VSFileType type, VSFileMode mode )
1357 {
1358  private_init();
1359  if (mode == ReadOnly)
1360  this->OpenReadOnly( filename, type );
1361  else if (mode == ReadWrite)
1362  this->OpenReadWrite( filename, type );
1363  else if (mode == CreateWrite)
1364  this->OpenCreateWrite( filename, type );
1365 }
1366 
1368 {
1369  if (fp) {
1370  fclose( fp );
1371  this->fp = NULL;
1372  }
1373  if (pk3_extracted_file)
1374  delete[] pk3_extracted_file;
1375 }
1376 
1377 void VSFile::checkExtracted()
1378 {
1379  if (q_volume_format == vfmtPK3) {
1380  if (!pk3_extracted_file) {
1381  string full_vol_path;
1382  if (this->volume_type == VSFSBig)
1383  full_vol_path = this->rootname+"/data."+volume_format;
1384  else
1385  full_vol_path = this->rootname+"/"+Directories[this->alt_type]+"."+volume_format;
1386  vsUMap< string, CPK3* >::iterator it;
1387  it = pk3_opened_files.find( full_vol_path );
1388  if ( it == pk3_opened_files.end() ) {
1389  //File is not opened so we open it and add it in the pk3 file map
1390  CPK3 *pk3newfile = new CPK3;
1391  if ( !pk3newfile->Open( full_vol_path.c_str() ) ) {
1392  cerr<<"!!! ERROR : opening volume : "<<full_vol_path<<endl;
1393  VSExit( 1 );
1394  }
1395  std::pair< std::string, CPK3* >pk3_pair( full_vol_path, pk3newfile );
1396  pk3_opened_files.insert( pk3_pair );
1397 
1398  this->pk3_file = pk3newfile;
1399  } else {
1400  this->pk3_file = it->second;
1401  }
1402  int pk3size = 0;
1403  if (this->file_index != -1)
1404  pk3_extracted_file = (char*) pk3_file->ExtractFile( this->file_index, &pk3size );
1405  else
1406  pk3_extracted_file = (char*) pk3_file->ExtractFile(
1407  (this->subdirectoryname+"/"+this->filename).c_str(), &pk3size );
1408  this->size = pk3size;
1409  cerr<<"EXTRACTING "
1410  <<(this->subdirectoryname+"/"+this->filename)<<" WITH INDEX="<<this->file_index<<" SIZE="<<pk3size<<endl;
1411  }
1412  }
1413 }
1414 
1415 //Open a read only file
1416 VSError VSFile::OpenReadOnly( const char *file, VSFileType type )
1417 {
1418  string filestr;
1419  int found = -1;
1420  this->file_type = this->alt_type = type;
1421  this->file_mode = ReadOnly;
1422  this->filename = string( file );
1423  failed = "";
1424 
1425  VSError err = Ok;
1426  if ( VSFS_DEBUG() )
1427  cerr<<"Loading a "<<type<<" : "<<file<<endl;
1428  if (type < ZoneBuffer || type == UnknownFile) {
1429  //It is a "classic file"
1430  if (!UseVolumes[type]) {
1431  if (type == UnknownFile) {
1432  //We look in the current_path or for a full relative path to either homedir or datadir
1433  if (current_path.back() != "") {
1434  string filestr1 = current_directory.back()
1435  +"/"+current_subdirectory.back()+"/"+string( file );
1436  filestr = current_path.back()+"/"+filestr1;
1437  if ( ( found = FileExists( current_path.back(), filestr1 ) ) < 0 )
1438  failed += "\t"+filestr+" NOT FOUND !\n";
1439  }
1440  if (found < 0) {
1441  for (unsigned int ij = 0; ij < Rootdir.size() && found < 0; ij++) {
1442  filestr = Rootdir[ij]+"/"+file;
1443  found = FileExists( Rootdir[ij], file );
1444  if (found < 0)
1445  failed += "\tRootdir : "+filestr+" NOT FOUND !\n";
1446  }
1447  //Look for relative (to datadir) or absolute named file
1448  if (found < 0) {
1449  filestr = file;
1450  if ( ( found = FileExists( "", filestr ) ) < 0 )
1451  failed += "\tAbs or rel : "+filestr+" NOT FOUND !\n";
1452  }
1453  }
1454  if (found < 0) {
1455  if ( VSFS_DEBUG() )
1456  cerr<<failed<<endl;
1457  this->valid = false;
1458  err = FileNotFound;
1459  } else {
1460  if ( ( this->fp = fopen( filestr.c_str(), "rb" ) ) == NULL ) {
1461  cerr<<"!!! SERIOUS ERROR : failed to open Unknown file "<<filestr<<" - this should not happen"<<endl;
1462  VSExit( 1 );
1463  }
1464  this->valid = true;
1465  if (VSFS_DEBUG() > 1)
1466  cerr<<filestr<<" SUCCESS !!!"<<endl;
1467  }
1468  } else {
1469  err = VSFileSystem::LookForFile( *this, type, file_mode );
1470  if (err > Ok) {
1471  this->valid = false;
1472  return FileNotFound;
1473  }
1474  filestr = this->GetFullPath();
1475  this->fp = fopen( filestr.c_str(), "rb" );
1476  if (!this->fp) {
1477  cerr<<"!!! SERIOUS ERROR : failed to open "<<filestr<<" - this should not happen"<<endl;
1478  this->valid = false;
1479  return FileNotFound; //fault!
1480  }
1481  this->valid = true;
1482  }
1483  } else {
1484  if (q_volume_format == vfmtVSR) {} else if (q_volume_format == vfmtPK3) {
1485  //Here we look for the file but we don't really open it, we just check if it exists
1486  err = VSFileSystem::LookForFile( *this, type, file_mode );
1487  if (err > Ok) {
1488  this->valid = false;
1489  return FileNotFound;
1490  }
1491  //Test if we have found a file in another FileType's dir and if it doesn't use volumes
1492  //If so we open the file as a normal one
1493  if ( this->volume_type == VSFSNone || (this->alt_type != this->file_type && !UseVolumes[this->alt_type]) ) {
1494  filestr = this->GetFullPath();
1495  this->fp = fopen( filestr.c_str(), "rb" );
1496  if (!this->fp) {
1497  cerr<<"!!! SERIOUS ERROR : failed to open "<<filestr<<" - this should not happen"<<endl;
1498  this->valid = false;
1499  return FileNotFound; //fault
1500  }
1501  }
1502  this->valid = true;
1503  }
1504  }
1505  if (err <= Ok) {
1506  //We save the current path only when loading a unit, an animation, a sprite or a cockpit
1507  if ( (type == UnitFile || type == AnimFile || type == VSSpriteFile || type == CockpitFile) ) {
1508  current_path.push_back( this->rootname );
1509  current_subdirectory.push_back( this->subdirectoryname );
1510  current_type.push_back( this->alt_type );
1511  if (VSFS_DEBUG() > 1) {
1512  cerr<<endl<<"BEGINNING OF ";
1513  DisplayType( type );
1514  cerr<<endl;
1515  }
1516  }
1517  }
1518  } else {
1519  //This is a "buffer file"
1520  if (!this->pk3_extracted_file)
1521  err = FileNotFound;
1522  else
1523  err = Ok;
1524  }
1525  return err;
1526 }
1527 
1528 //We will always write in homedir+Directories[FileType][0]
1529 //Open a standard file read/write
1530 VSError VSFile::OpenReadWrite( const char *filename, VSFileType type )
1531 {
1532  if (type >= ZoneBuffer && type != UnknownFile)
1533  return FileNotFound;
1534  this->file_type = this->alt_type = type;
1535  this->file_mode = ReadWrite;
1536 
1537  return Ok;
1538 }
1539 
1540 //We will always write in homedir+Directories[FileType][0]
1541 //Open (truncate) or create a standard file read/write
1542 VSError VSFile::OpenCreateWrite( const char *filenam, VSFileType type )
1543 {
1544  if (type >= ZoneBuffer && type != UnknownFile)
1545  return FileNotFound;
1546  this->file_type = this->alt_type = type;
1547  this->filename = string( filenam );
1548  this->file_mode = CreateWrite;
1549  if (type == SystemFile) {
1550  string dirpath( sharedsectors+"/"+universe_name );
1551  CreateDirectoryHome( dirpath );
1552  CreateDirectoryHome( dirpath+"/"+getStarSystemSector( this->filename ) );
1553  string fpath( homedir+"/"+dirpath+"/"+this->filename );
1554  this->fp = fopen( fpath.c_str(), "wb" );
1555  if (!fp)
1556  return LocalPermissionDenied;
1557  } else if (type == TextureFile) {
1558  string fpath( homedir+"/"+sharedtextures+"/"+this->filename );
1559  this->fp = fopen( fpath.c_str(), "wb" );
1560  if (!fp)
1561  return LocalPermissionDenied;
1562  } else if (type == UnitFile) {
1563  string fpath( homedir+"/"+savedunitpath+"/"+this->filename );
1564  this->rootname = homedir;
1565  this->directoryname = savedunitpath;
1566  this->fp = fopen( fpath.c_str(), "wb" );
1567  if (!fp)
1568  return LocalPermissionDenied;
1569  } else if (type == SaveFile) {
1570  string fpath( homedir+"/save/"+this->filename );
1571  this->fp = fopen( fpath.c_str(), "wb" );
1572  if (!fp)
1573  return LocalPermissionDenied;
1574  } else if (type == AccountFile) {
1575  string fpath( datadir+"/accounts/"+this->filename );
1576  this->fp = fopen( fpath.c_str(), "wb" );
1577  if (!fp)
1578  return LocalPermissionDenied;
1579  } else if (type == UnknownFile) {
1580  string fpath( homedir+"/"+this->filename );
1581  this->rootname = homedir;
1582  this->directoryname = "";
1583  this->fp = fopen( fpath.c_str(), "wb" );
1584  if (!fp)
1585  return LocalPermissionDenied;
1586  }
1587  return Ok;
1588 }
1589 
1590 size_t VSFile::Read( void *ptr, size_t length )
1591 {
1592  size_t nbread = 0;
1593  if (!UseVolumes[this->alt_type] || this->volume_type == VSFSNone) {
1594  assert( fp != NULL );
1595  nbread = fread( ptr, 1, length, this->fp );
1596  } else {
1597  if (q_volume_format == vfmtVSR) {} else if (q_volume_format == vfmtPK3) {
1598  checkExtracted();
1599  if (length > this->size-this->offset)
1600  length = this->size-this->offset;
1601  memcpy( ptr, (pk3_extracted_file+offset), length );
1602  offset += length;
1603  nbread = length;
1604  }
1605  }
1606  return nbread;
1607 }
1608 
1609 VSError VSFile::ReadLine( void *ptr, size_t length )
1610 {
1611  char *ret;
1612  if (!UseVolumes[alt_type] || this->volume_type == VSFSNone) {
1613  ret = fgets( (char*) ptr, length, this->fp );
1614  if (!ret)
1615  return Unspecified;
1616  } else {
1617  if (q_volume_format == vfmtVSR) {} else if (q_volume_format == vfmtPK3) {
1618  checkExtracted();
1619  ret = (char*) ptr;
1620 
1621  bool nl_found = false;
1622  unsigned int i = 0;
1623  if (VSFS_DEBUG() > 1)
1624  cerr<<"READLINE STARTING OFFSET="<<offset;
1625  for (i = 0; !nl_found && i < length && offset < size; offset++, i++) {
1626  if (pk3_extracted_file[offset] == '\n' || pk3_extracted_file[offset] == '\r') {
1627  nl_found = true;
1628  if (VSFS_DEBUG() > 1) {
1629  if (pk3_extracted_file[offset] == '\n')
1630  cerr<<"\\n ";
1631  if (pk3_extracted_file[offset] == '\r')
1632  cerr<<"\\r ";
1633  }
1634  } else {
1635  ret[i] = pk3_extracted_file[offset];
1636  if (VSFS_DEBUG() > 1)
1637  cerr<<std::hex<<ret[i]<<" ";
1638  }
1639  }
1640  this->GoAfterEOL( length );
1641  ret[i] = 0;
1642  if (VSFS_DEBUG() > 1)
1643  cerr<<std::dec<<" - read "<<i<<" char - "<<ret<<endl;
1644  if (!nl_found)
1645  return Unspecified;
1646  }
1647  }
1648  return Ok;
1649 }
1650 
1652 {
1653  if (this->Size() < 0) {
1654  cerr<<"Attempt to call ReadFull on a bad file "<<this->filename<<" "<<this->Size()<<" "<<this->GetFullPath().c_str()<<endl;
1655  return string();
1656  }
1657  if (!UseVolumes[alt_type] || this->volume_type == VSFSNone) {
1658  char *content = new char[this->Size()+1];
1659  content[this->Size()] = 0;
1660  int readsize = fread( content, 1, this->Size(), this->fp );
1661  if (this->Size() != readsize) {
1662  cerr<<"Only read "<<readsize<<" out of "<<this->Size()<<" bytes of "<<this->filename<<endl;
1663  GetError( "ReadFull" );
1664  if (readsize <= 0)
1665  return string();
1666  else
1667  content[readsize] = '\0';
1668  }
1669  string res( content );
1670  delete[] content;
1671  return res;
1672  } else {
1673  if (q_volume_format == vfmtVSR) {} else if (q_volume_format == vfmtPK3) {
1674  checkExtracted();
1675  offset = this->Size();
1676  return string( pk3_extracted_file );
1677  }
1678  }
1679  return string( "" );
1680 }
1681 
1682 size_t VSFile::Write( const void *ptr, size_t length )
1683 {
1684  if (!UseVolumes[this->alt_type] || this->volume_type == VSFSNone) {
1685  size_t nbwritten = fwrite( ptr, 1, length, this->fp );
1686  return nbwritten;
1687  } else {
1688  cerr<<"!!! ERROR : Writing is not supported within resource/volume files"<<endl;
1689  VSExit( 1 );
1690  }
1691  return Ok;
1692 }
1693 
1694 size_t VSFile::Write( const string &content )
1695 {
1696  std::string::size_type length = content.length();
1697  return this->Write( content.c_str(), length );
1698 }
1699 
1700 VSError VSFile::WriteLine( const void *ptr )
1701 {
1702  if (!UseVolumes[alt_type] || this->volume_type == VSFSNone) {
1703  fputs( (const char*) ptr, this->fp );
1704  } else {
1705  cerr<<"!!! ERROR : Writing is not supported within resource/volume files"<<endl;
1706  VSExit( 1 );
1707  }
1708  return Ok;
1709 }
1710 
1711 void VSFile::WriteFull( void *ptr )
1712 {}
1713 
1714 int VSFile::Fprintf( const char *format, ... )
1715 {
1716  if (!UseVolumes[alt_type] || this->volume_type == VSFSNone) {
1717  va_list ap;
1718  va_start( ap, format );
1719 
1720  return vfprintf( this->fp, format, ap );
1721  } else {
1722  cerr<<"!!! ERROR : Writing is not supported within resource/volume files"<<endl;
1723  VSExit( 1 );
1724  }
1725  return 0;
1726 }
1727 
1728 #if 0
1729 #ifdef HAVE_VFSCANF
1730 int VSFile::Fscanf( const char *format, ... )
1731 {
1732  int ret = -1;
1733  int readbytes = 0;
1734  //We add the parameter %n to the format string in order to get the number of bytes read
1735  int format_length = strlen( format );
1736  char *newformat = new char[format_length+3];
1737  memset( newformat, 0, format_length+3 );
1738  memcpy( newformat, format, format_length );
1739  strcat( newformat, "%n" );
1740  va_list arglist;
1741  va_start( arglist, format );
1742  if (!UseVolumes[alt_type] || this->volume_type == VSFSNone) {
1743  //return _input(fp,(unsigned char*)format,arglist);
1744  ret = vfscanf( this->fp, newformat, arglist );
1745  va_end( arglist );
1746  } else {
1747  if (q_volume_format == vfmtVSR) {} else if (q_volume_format == vfmtPK3) {
1748  //If the file has not been extracted yet we do now
1749  checkExtracted();
1750  ret = vsscanf( pk3_extracted_file+offset, newformat, arglist );
1751  readbytes = GetReadBytes( newformat, arglist );
1752  va_end( arglist );
1753  cerr<<" SSCANF : Read "<<readbytes<<" bytes"<<endl;
1754  this->offset += readbytes;
1755  }
1756  }
1757  delete[] newformat;
1758  return ret;
1759 }
1760 #endif
1761 #endif
1762 
1764 {
1765  if (!UseVolumes[alt_type] || this->volume_type == VSFSNone || this->file_mode != ReadOnly) {
1766  fseek( this->fp, 0, SEEK_SET );
1767  } else {
1768  if (q_volume_format == vfmtVSR)
1769  offset = 0;
1770  else if (q_volume_format == vfmtPK3)
1771  offset = 0;
1772  }
1773 }
1774 
1776 {
1777  if (!UseVolumes[alt_type] || this->volume_type == VSFSNone || this->file_mode != ReadOnly) {
1778  fseek( this->fp, 0, SEEK_END );
1779  } else {
1780  if (q_volume_format == vfmtVSR)
1781  offset = size;
1782  else if (q_volume_format == vfmtPK3)
1783  offset = size;
1784  }
1785 }
1786 
1787 void VSFile::GoTo( long foffset ) //Does a VSFileSystem::Fseek( fp, offset, SEEK_SET);
1788 {
1789  if (!UseVolumes[alt_type] || this->volume_type == VSFSNone || this->file_mode != ReadOnly) {
1790  fseek( this->fp, foffset, SEEK_SET );
1791  } else {
1792  if (q_volume_format == vfmtVSR)
1793  offset = foffset;
1794  else if (q_volume_format == vfmtPK3)
1795  offset = foffset;
1796  }
1797 }
1798 
1800 {
1801  if (size == 0) {
1802  if (!UseVolumes[alt_type] || this->volume_type == VSFSNone || file_mode != ReadOnly) {
1803  struct stat st;
1804  if ( (fp != NULL) && fstat( fileno( fp ), &st ) == 0 )
1805  return this->size = st.st_size;
1806  return -1;
1807  } else {
1808  if (q_volume_format == vfmtVSR) {} else if (q_volume_format == vfmtPK3) {
1809  checkExtracted();
1810  return this->size;
1811  }
1812  }
1813  return -1;
1814  }
1815  return this->size;
1816 }
1817 
1819 {
1820  if (!UseVolumes[alt_type] || this->volume_type == VSFSNone || file_mode != ReadOnly) {
1821  fclose( fp );
1822  this->fp = fopen( this->GetFullPath().c_str(), "w+b" );
1823  //This should not happen
1824  if (!fp) {
1825  GetError( "Clear" );
1826  VSExit( 1 );
1827  }
1828  } else {
1829  cerr<<"!!! ERROR : Writing is not supported within resource/volume files"<<endl;
1830  VSExit( 1 );
1831  }
1832 }
1833 
1835 {
1836  long ret = 0;
1837  if (!UseVolumes[alt_type] || this->volume_type == VSFSNone || file_mode != ReadOnly) {
1838  ret = ftell( this->fp );
1839  } else {
1840  if (q_volume_format == vfmtVSR) {} else if (q_volume_format == vfmtPK3) {
1841  ret = offset;
1842  }
1843  }
1844  return ret;
1845 }
1846 
1848 {
1849  bool eof = false;
1850  if (!UseVolumes[alt_type] || this->volume_type == VSFSNone || file_mode != ReadOnly) {
1851  eof = vs_feof( this->fp );
1852  } else {
1853  if (q_volume_format == vfmtVSR) {} else if (q_volume_format == vfmtPK3) {
1854  eof = (this->Size() < 0) || ( offset == (unsigned long)this->Size() );
1855  }
1856  }
1857  return eof;
1858 }
1859 
1861 {
1862  return valid;
1863 }
1864 
1866 {
1867  if (this->file_type >= ZoneBuffer && this->file_type != UnknownFile && this->pk3_extracted_file) {
1868  delete this->pk3_extracted_file;
1869  this->pk3_extracted_file = NULL;
1870  return;
1871  }
1872  if ( this->valid && this->file_mode == ReadOnly
1873  && (file_type == UnitFile || file_type == AnimFile || file_type == VSSpriteFile || file_type == CockpitFile) ) {
1874  assert( current_path.size() > 1 );
1875  current_path.pop_back();
1876  current_subdirectory.pop_back();
1877  current_type.pop_back();
1878  if (VSFS_DEBUG() > 2) {
1879  cerr<<"END OF ";
1880  DisplayType( this->file_type );
1881  cerr<<endl<<endl;
1882  }
1883  }
1884  if (!UseVolumes[file_type] || this->volume_type == VSFSNone || file_mode != ReadOnly) {
1885  fclose( this->fp );
1886  this->fp = NULL;
1887  } else {
1888  if (q_volume_format == vfmtVSR) {} else if (q_volume_format == vfmtPK3) {
1889  if (pk3_extracted_file)
1890  delete[] pk3_extracted_file;
1891  pk3_extracted_file = NULL;
1892  }
1893  }
1894  this->size = -1;
1895  this->valid = false;
1896  this->filename = "";
1897  this->directoryname = "";
1898  this->subdirectoryname = "";
1899  this->rootname = "";
1900  this->offset = 0;
1901  this->file_index = -1;
1902 }
1903 
1904 static void pathAppend( string &dest, string &suffix )
1905 {
1906  if ( suffix.empty() )
1907  return;
1908  if (suffix[0] != '/' && !dest.empty() && dest[dest.length()-1] != '/')
1909  dest += "/";
1910  dest += suffix;
1911 }
1912 
1914 {
1915  string tmp = this->rootname;
1916  pathAppend( tmp, this->directoryname );
1917  pathAppend( tmp, this->subdirectoryname );
1918  pathAppend( tmp, this->filename );
1919  return tmp;
1920 }
1921 
1923 {
1924  string tmp = this->directoryname;
1925  pathAppend( tmp, this->subdirectoryname );
1926  pathAppend( tmp, this->filename );
1927  return tmp;
1928 }
1929 
1931 {
1932  this->file_type = type;
1933 }
1935 {
1936  this->alt_type = type;
1937 }
1938 void VSFile::SetIndex( int index )
1939 {
1940  this->file_index = index;
1941 }
1942 
1944 {
1945  this->volume_type = big;
1946 }
1948 {
1949  return UseVolumes[alt_type] && volume_type != VSFSNone;
1950 }
1951 
1952 void VSFile::GoAfterEOL( unsigned int length )
1953 {
1954  while ( this->offset < length && this->offset < this->size
1955  && (this->pk3_extracted_file[offset] == '\r' || this->pk3_extracted_file[offset] == '\n') )
1956  this->offset++;
1957 }
1959 {
1960  while ( this->offset < this->size && (this->pk3_extracted_file[offset] == '\r' || this->pk3_extracted_file[offset] == '\n') )
1961  this->offset++;
1962 }
1963 }
1964 
1965 #define CASE( a ) \
1966 case a: \
1967  ostr<<#a; break;
1968 std::ostream&operator<<( std::ostream &ostr, VSFileSystem::VSError err )
1969 {
1970  switch (err)
1971  {
1985  default:
1986  ostr<<"VSFileSystem::<undefined VSError>";
1987  break;
1988  }
1989  return ostr;
1990 }
1991 #undef CASE
1992 
1993 std::ostream&operator<<( std::ostream &ostr, VSFileSystem::VSFileType type )
1994 {
1995  VSFileSystem::DisplayType( type, ostr );
1996  return ostr;
1997 }
1998 
2000 {
2001 #define CASE( a ) \
2002 case a: \
2003  return #a; break;
2004  switch (type)
2005  {
2024  default:
2025  return "VSFileSystem::<undefined VSFileType>";
2026 
2027  break;
2028  }
2029 #undef CASE
2030 }
2031 
2032 #if defined (_WIN32) && !defined (__CYGWIN__)
2033 
2034 int scandir( const char *dirname, struct dirent ***namelist, int (*select)( const struct dirent* ), int (*compar)(
2035  const struct dirent**,
2036  const struct dirent** ) )
2037 {
2038  int len;
2039  char *findIn, *d;
2040  WIN32_FIND_DATA find;
2041  HANDLE h;
2042  int nDir = 0, NDir = 0;
2043  struct dirent **dir = 0, *selectDir;
2044  unsigned long ret;
2045 
2046  len = strlen( dirname );
2047  findIn = (char*) malloc( len+5 );
2048  strcpy( findIn, dirname );
2049  for (d = findIn; *d; d++)
2050  if (*d == '/') *d = '\\';
2051  if ( (len == 0) ) strcpy( findIn, ".\\*" );
2052  if ( (len == 1) && (d[-1] == '.') ) strcpy( findIn, ".\\*" );
2053  if ( (len > 0) && (d[-1] == '\\') ) {
2054  *d++ = '*';
2055  *d = 0;
2056  }
2057  if ( (len > 1) && (d[-1] == '.') && (d[-2] == '\\') ) d[-1] = '*';
2058  if ( ( h = FindFirstFile( findIn, &find ) ) == INVALID_HANDLE_VALUE ) {
2059  ret = GetLastError();
2060  if (ret != ERROR_NO_MORE_FILES) {
2061  //TODO: return some error code
2062  }
2063  *namelist = dir;
2064  return nDir;
2065  }
2066  do {
2067  selectDir = (struct dirent*) malloc( sizeof (struct dirent)+strlen( find.cFileName ) );
2068  strcpy( selectDir->d_name, find.cFileName );
2069  if ( !select || (*select)(selectDir) ) {
2070  if (nDir == NDir) {
2071  struct dirent **tempDir = (dirent**) calloc( sizeof (struct dirent*), NDir+33 );
2072  if (NDir) memcpy( tempDir, dir, sizeof (struct dirent*) *NDir );
2073  if (dir) free( dir );
2074  dir = tempDir;
2075  NDir += 32;
2076  }
2077  dir[nDir] = selectDir;
2078  nDir++;
2079  dir[nDir] = 0;
2080  } else {
2081  free( selectDir );
2082  }
2083  } while ( FindNextFile( h, &find ) );
2084  ret = GetLastError();
2085  if (ret != ERROR_NO_MORE_FILES) {
2086  //TODO: return some error code
2087  }
2088  FindClose( h );
2089 
2090  free( findIn );
2091  if (compar)
2092  qsort( dir, nDir, sizeof (*dir),
2093  ( int (*)( const void*, const void* ) )compar );
2094  *namelist = dir;
2095  return nDir;
2096 }
2097 
2098 int alphasort( struct dirent **a, struct dirent **b )
2099 {
2100  return strcmp( (*a)->d_name, (*b)->d_name );
2101 }
2102 
2103 #endif
2104