1515#include < errno.h>
1616#include < sstream>
1717#include < algorithm>
18+ #include < memory>
1819
1920#ifdef _WIN32
2021#include < io.h>
3132#include < sys/stat.h>
3233#include < unistd.h>
3334#include < libgen.h>
34- #include < cstdio>
35+
36+ struct dir_deleter {
37+ void operator ()(DIR* dirp) {
38+ closedir (dirp);
39+ }
40+ };
41+ typedef std::unique_ptr<DIR, dir_deleter> unique_dir_ptr;
3542#endif
3643
3744#include " cfile/cfile.h"
@@ -80,13 +87,12 @@ static int Num_path_roots = 0;
8087
8188// Created by searching all roots in order. This means Files is then sorted by precedence.
8289typedef struct cf_file {
83- char name_ext[CF_MAX_FILENAME_LENGTH]; // Filename and extension
84- int root_index; // Where in Roots this is located
85- int pathtype_index; // Where in Paths this is located
86- time_t write_time; // When it was last written
87- int size; // How big it is in bytes
88- int pack_offset; // For pack files, where it is at. 0 if not in a pack file. This can be used to tell if in a pack file.
89- char * real_name; // For real files, the full path
90+ char name_ext[CF_MAX_FILENAME_LENGTH]; // Filename and extension
91+ int root_index; // Where in Roots this is located
92+ int pathtype_index; // Where in Paths this is located
93+ time_t write_time; // When it was last written
94+ int size; // How big it is in bytes
95+ int pack_offset; // For pack files, where it is at. 0 if not in a pack file. This can be used to tell if in a pack file.
9096} cf_file;
9197
9298#define CF_NUM_FILES_PER_BLOCK 512
@@ -596,77 +602,72 @@ void cf_search_root_path(int root_index)
596602 _findclose ( find_handle );
597603 }
598604#elif defined SCP_UNIX
599- DIR * dirp = nullptr ;
600- SCP_string search_dir;
605+ auto dirp = unique_dir_ptr ( opendir (search_path)) ;
606+ if (!dirp)
601607 {
602- if (i == CF_TYPE_ROOT) {
603- // Don't search for the same name for the root case since we would be searching in other mod directories in that case
604- dirp = opendir (search_path);
605- search_dir.assign (search_path);
606- } else {
607- // On Unix we can have a different case for the search paths so we also need to account for that
608- // We do that by looking at the parent of search_path and enumerating all directories and the check if any of
609- // them are a case-insensitive match
610- SCP_string directory_name;
611-
612- auto parentPathIter = pathTypeToRealPath.find (Pathtypes[i].parent_index );
613-
614- if (parentPathIter == pathTypeToRealPath.end ()) {
615- // No parent known yet, use the standard dirname
616- char dirname_copy[CF_MAX_PATHNAME_LENGTH];
617- memcpy (dirname_copy, search_path, sizeof (search_path));
618- // According to the documentation of directory_name and basename, the return value does not need to be freed
619- directory_name.assign (dirname (dirname_copy));
620- } else {
621- // we have a valid parent path -> use that
622- directory_name = parentPathIter->second ;
623- }
624-
625- char basename_copy[CF_MAX_PATHNAME_LENGTH];
626- memcpy (basename_copy, search_path, sizeof (search_path));
627- // According to the documentation of dirname and basename, the return value does not need to be freed
628- auto search_name = basename (basename_copy);
629-
630- auto parentDirP = opendir (directory_name.c_str ());
631-
632- if (parentDirP) {
633- struct dirent *dir = nullptr ;
634- while ((dir = readdir (parentDirP)) != nullptr ) {
635-
636- if (stricmp (search_name, dir->d_name )) {
637- continue ;
638- }
608+ // If the directory does not exist then check if it might exist with a different case. If that's the case
609+ // we bail out with an error so inform the user that this is not valid.
610+
611+ // On Unix we can have a different case for the search paths so we also need to account for that
612+ // We do that by looking at the parent of search_path and enumerating all directories and then check if any of
613+ // them are a case-insensitive match
614+ SCP_string directory_name;
615+
616+ auto parentPathIter = pathTypeToRealPath.find (Pathtypes[i].parent_index );
617+
618+ // if (parentPathIter == pathTypeToRealPath.end()) {
619+ // No parent known yet, use the standard dirname
620+ char dirname_copy[CF_MAX_PATHNAME_LENGTH];
621+ memcpy (dirname_copy, search_path, sizeof (search_path));
622+ // According to the documentation of directory_name and basename, the return value does not need to be freed
623+ directory_name.assign (dirname (dirname_copy));
624+ // } else {
625+ // // we have a valid parent path -> use that
626+ // directory_name = parentPathIter->second;
627+ // }
628+
629+ char basename_copy[CF_MAX_PATHNAME_LENGTH];
630+ memcpy (basename_copy, search_path, sizeof (search_path));
631+ // According to the documentation of dirname and basename, the return value does not need to be freed
632+ auto search_name = basename (basename_copy);
633+
634+ auto parentDirP = unique_dir_ptr (opendir (directory_name.c_str ()));
635+ if (parentDirP) {
636+ struct dirent *dir = nullptr ;
637+ while ((dir = readdir (parentDirP.get ())) != nullptr ) {
638+
639+ if (stricmp (search_name, dir->d_name )) {
640+ continue ;
641+ }
639642
640- SCP_string fn;
641- sprintf (fn, " %s/%s" , directory_name.c_str (), dir->d_name );
643+ SCP_string fn;
644+ sprintf (fn, " %s/%s" , directory_name.c_str (), dir->d_name );
642645
643- struct stat buf;
644- if (stat (fn.c_str (), &buf) == -1 ) {
645- continue ;
646- }
646+ struct stat buf;
647+ if (stat (fn.c_str (), &buf) == -1 ) {
648+ continue ;
649+ }
647650
648- if (S_ISDIR (buf.st_mode )) {
649- // Found a case insensitive match
650- dirp = opendir (fn. c_str ());
651- search_dir = fn;
652- // We also need to store this in our mapping since we may need it in the future
653- pathTypeToRealPath. insert ( std::make_pair (i, fn));
654- break ;
651+ if (S_ISDIR (buf.st_mode )) {
652+ // Found a case insensitive match
653+ // If the name is not exactly the same as the one we are currently searching then it's an error
654+ if ( strcmp (search_name, dir-> d_name )) {
655+ Error (LOCATION, " Data directory '%s' for path type '%s' does not match the required case! "
656+ " All data directories must exactly match the case specified by the engine or your mod "
657+ " will not work on other platforms. " , fn. c_str (), Pathtypes[i]. path ) ;
655658 }
659+ break ;
656660 }
657- closedir (parentDirP);
658661 }
659662 }
660- }
661-
662- if ( dirp ) {
663+ } else {
663664 struct dirent *dir = nullptr ;
664- while ((dir = readdir (dirp)) != NULL )
665+ while ((dir = readdir (dirp. get () )) != NULL )
665666 {
666667 if (!fnmatch (" *.*" , dir->d_name , 0 ))
667668 {
668669 SCP_string fn;
669- sprintf (fn, " %s/%s" , search_dir. c_str () , dir->d_name );
670+ sprintf (fn, " %s/%s" , search_path , dir->d_name );
670671
671672 struct stat buf;
672673 if (stat (fn.c_str (), &buf) == -1 ) {
@@ -693,15 +694,12 @@ void cf_search_root_path(int root_index)
693694
694695 file->pack_offset = 0 ; // Mark as a non-packed file
695696
696- file->real_name = vm_strdup (fn.c_str ());
697-
698697 num_files++;
699698 // mprintf(( "Found file '%s'\n", file->name_ext ));
700699 }
701700 }
702701 }
703702 }
704- closedir (dirp);
705703 }
706704#endif
707705 }
@@ -896,14 +894,6 @@ void cf_free_secondary_filelist()
896894 // Init the file blocks
897895 for (i=0 ; i<CF_MAX_FILE_BLOCKS; i++ ) {
898896 if ( File_blocks[i] ) {
899- // Free file paths
900- for (auto & f : File_blocks[i]->files ) {
901- if (f.real_name ) {
902- vm_free (f.real_name );
903- f.real_name = nullptr ;
904- }
905- }
906-
907897 vm_free ( File_blocks[i] );
908898 File_blocks[i] = NULL ;
909899 }
@@ -1062,14 +1052,17 @@ int cf_find_file_location( const char *filespec, int pathtype, int max_out, char
10621052 *offset = (size_t )f->pack_offset ;
10631053
10641054 if (pack_filename) {
1055+ cf_root *r = cf_get_root (f->root_index );
1056+
1057+ strncpy ( pack_filename, r->path , max_out );
1058+
10651059 if (f->pack_offset < 1 ) {
1066- // This is a real file, return the actual file path
1067- strncpy ( pack_filename, f->real_name , max_out );
1068- } else {
1069- // File is in a pack file
1070- cf_root *r = cf_get_root (f->root_index );
1060+ strcat_s ( pack_filename, max_out, Pathtypes[f->pathtype_index ].path );
10711061
1072- strncpy ( pack_filename, r->path , max_out );
1062+ if ( pack_filename[strlen (pack_filename)-1 ] != DIR_SEPARATOR_CHAR )
1063+ strcat_s ( pack_filename, max_out, DIR_SEPARATOR_STR );
1064+
1065+ strcat_s ( pack_filename, max_out, f->name_ext );
10731066 }
10741067 }
10751068
@@ -1087,14 +1080,19 @@ int cf_find_file_location( const char *filespec, int pathtype, int max_out, char
10871080 *offset = (size_t )f->pack_offset ;
10881081
10891082 if (pack_filename) {
1083+ cf_root *r = cf_get_root (f->root_index );
1084+
1085+ strcpy ( pack_filename, r->path );
1086+
10901087 if (f->pack_offset < 1 ) {
1091- // This is a real file, return the actual file path
1092- strncpy ( pack_filename, f->real_name , max_out );
1093- } else {
1094- // File is in a pack file
1095- cf_root *r = cf_get_root (f->root_index );
1088+ if ( strlen (Pathtypes[f->pathtype_index ].path ) ) {
1089+ strcat_s ( pack_filename, max_out, Pathtypes[f->pathtype_index ].path );
10961090
1097- strncpy ( pack_filename, r->path , max_out );
1091+ if ( pack_filename[strlen (pack_filename)-1 ] != DIR_SEPARATOR_CHAR )
1092+ strcat_s ( pack_filename, max_out, DIR_SEPARATOR_STR );
1093+ }
1094+
1095+ strcat_s ( pack_filename, max_out, f->name_ext );
10981096 }
10991097 }
11001098
@@ -1329,14 +1327,17 @@ int cf_find_file_location_ext( const char *filename, const int ext_num, const ch
13291327 *offset = f->pack_offset ;
13301328
13311329 if (pack_filename) {
1330+ cf_root *r = cf_get_root (f->root_index );
1331+
1332+ strncpy ( pack_filename, r->path , max_out );
1333+
13321334 if (f->pack_offset < 1 ) {
1333- // This is a real file, return the actual file path
1334- strncpy ( pack_filename, f->real_name , max_out );
1335- } else {
1336- // File is in a pack file
1337- cf_root *r = cf_get_root (f->root_index );
1335+ strcat_s ( pack_filename, max_out, Pathtypes[f->pathtype_index ].path );
13381336
1339- strncpy ( pack_filename, r->path , max_out );
1337+ if ( pack_filename[strlen (pack_filename)-1 ] != DIR_SEPARATOR_CHAR )
1338+ strcat_s ( pack_filename, max_out, DIR_SEPARATOR_STR );
1339+
1340+ strcat_s ( pack_filename, max_out, f->name_ext );
13401341 }
13411342 }
13421343
@@ -1357,14 +1358,20 @@ int cf_find_file_location_ext( const char *filename, const int ext_num, const ch
13571358 *offset = f->pack_offset ;
13581359
13591360 if (pack_filename) {
1361+ cf_root *r = cf_get_root (f->root_index );
1362+
1363+ strcpy ( pack_filename, r->path );
1364+
13601365 if (f->pack_offset < 1 ) {
1361- // This is a real file, return the actual file path
1362- strncpy ( pack_filename, f->real_name , max_out );
1363- } else {
1364- // File is in a pack file
1365- cf_root *r = cf_get_root (f->root_index );
13661366
1367- strncpy ( pack_filename, r->path , max_out );
1367+ if ( strlen (Pathtypes[f->pathtype_index ].path ) ) {
1368+ strcat_s ( pack_filename, max_out, Pathtypes[f->pathtype_index ].path );
1369+
1370+ if ( pack_filename[strlen (pack_filename)-1 ] != DIR_SEPARATOR_CHAR )
1371+ strcat_s ( pack_filename, max_out, DIR_SEPARATOR_STR );
1372+ }
1373+
1374+ strcat_s ( pack_filename, max_out, f->name_ext );
13681375 }
13691376 }
13701377
0 commit comments