Skip to content

Commit fbe9cc1

Browse files
authored
Merge pull request #1204 from asarium/fix/1182
Search for path types regardless of case
2 parents d71a6b0 + 6ff7662 commit fbe9cc1

File tree

3 files changed

+162
-54
lines changed

3 files changed

+162
-54
lines changed

code/cfile/cfilesystem.cpp

Lines changed: 120 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <fnmatch.h>
3131
#include <sys/stat.h>
3232
#include <unistd.h>
33+
#include <libgen.h>
3334
#endif
3435

3536
#include "cfile/cfile.h"
@@ -38,6 +39,7 @@
3839
#include "globalincs/pstypes.h"
3940
#include "localization/localize.h"
4041
#include "osapi/osapi.h"
42+
#include "parse/parselo.h"
4143

4244
#define CF_ROOTTYPE_PATH 0
4345
#define CF_ROOTTYPE_PACK 1
@@ -77,12 +79,13 @@ static int Num_path_roots = 0;
7779

7880
// Created by searching all roots in order. This means Files is then sorted by precedence.
7981
typedef struct cf_file {
80-
char name_ext[CF_MAX_FILENAME_LENGTH]; // Filename and extension
81-
int root_index; // Where in Roots this is located
82-
int pathtype_index; // Where in Paths this is located
83-
time_t write_time; // When it was last written
84-
int size; // How big it is in bytes
85-
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.
82+
char name_ext[CF_MAX_FILENAME_LENGTH]; // Filename and extension
83+
int root_index; // Where in Roots this is located
84+
int pathtype_index; // Where in Paths this is located
85+
time_t write_time; // When it was last written
86+
int size; // How big it is in bytes
87+
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.
88+
char* real_name; // For real files, the full path
8689
} cf_file;
8790

8891
#define CF_NUM_FILES_PER_BLOCK 512
@@ -115,6 +118,7 @@ cf_file *cf_create_file()
115118
if ( File_blocks[block] == NULL ) {
116119
File_blocks[block] = (cf_file_block *)vm_malloc( sizeof(cf_file_block) );
117120
Assert( File_blocks[block] != NULL);
121+
memset(File_blocks[block], 0, sizeof(cf_file_block));
118122
}
119123

120124
Num_files++;
@@ -534,6 +538,10 @@ void cf_search_root_path(int root_index)
534538
mprintf(( "Searching root '%s' ... ", root->path ));
535539

536540
char search_path[CF_MAX_PATHNAME_LENGTH];
541+
#ifdef SCP_UNIX
542+
// This map stores the mapping between a specific path type and the actual path that we use for it
543+
SCP_unordered_map<int, SCP_string> pathTypeToRealPath;
544+
#endif
537545

538546
for (i=CF_TYPE_ROOT; i<CF_MAX_PATH_TYPES; i++ ) {
539547

@@ -552,6 +560,7 @@ void cf_search_root_path(int root_index)
552560
}
553561

554562
#if defined _WIN32
563+
SCP_string search_directory = search_path;
555564
strcat_s( search_path, "*.*" );
556565

557566
intptr_t find_handle;
@@ -576,6 +585,11 @@ void cf_search_root_path(int root_index)
576585
file->size = find.size;
577586
file->pack_offset = 0; // Mark as a non-packed file
578587

588+
SCP_string file_name;
589+
sprintf(file_name, "%s%s%s", search_directory.c_str(), DIR_SEPARATOR_STR, find.name);
590+
591+
file->real_name = vm_strdup(file_name.c_str());
592+
579593
num_files++;
580594
//mprintf(( "Found file '%s'\n", file->name_ext ));
581595
}
@@ -588,21 +602,80 @@ void cf_search_root_path(int root_index)
588602
_findclose( find_handle );
589603
}
590604
#elif defined SCP_UNIX
591-
DIR *dirp;
592-
struct dirent *dir;
605+
DIR *dirp = nullptr;
606+
SCP_string search_dir;
607+
{
608+
if (i == CF_TYPE_ROOT) {
609+
// Don't search for the same name for the root case since we would be searching in other mod directories in that case
610+
dirp = opendir (search_path);
611+
search_dir.assign(search_path);
612+
} else {
613+
// On Unix we can have a different case for the search paths so we also need to account for that
614+
// We do that by looking at the parent of search_path and enumerating all directories and the check if any of
615+
// them are a case-insensitive match
616+
SCP_string directory_name;
617+
618+
auto parentPathIter = pathTypeToRealPath.find(Pathtypes[i].parent_index);
619+
620+
if (parentPathIter == pathTypeToRealPath.end()) {
621+
// No parent known yet, use the standard dirname
622+
char dirname_copy[CF_MAX_PATHNAME_LENGTH];
623+
memcpy(dirname_copy, search_path, sizeof(search_path));
624+
// According to the documentation of directory_name and basename, the return value does not need to be freed
625+
directory_name.assign(dirname(dirname_copy));
626+
} else {
627+
// we have a valid parent path -> use that
628+
directory_name = parentPathIter->second;
629+
}
630+
631+
char basename_copy[CF_MAX_PATHNAME_LENGTH];
632+
memcpy(basename_copy, search_path, sizeof(search_path));
633+
// According to the documentation of dirname and basename, the return value does not need to be freed
634+
auto search_name = basename(basename_copy);
635+
636+
auto parentDirP = opendir(directory_name.c_str());
637+
638+
if (parentDirP) {
639+
struct dirent *dir = nullptr;
640+
while ((dir = readdir (parentDirP)) != nullptr) {
641+
642+
if (stricmp(search_name, dir->d_name)) {
643+
continue;
644+
}
645+
646+
SCP_string fn;
647+
sprintf(fn, "%s/%s", directory_name.c_str(), dir->d_name);
648+
649+
struct stat buf;
650+
if (stat(fn.c_str(), &buf) == -1) {
651+
continue;
652+
}
653+
654+
if (S_ISDIR(buf.st_mode)) {
655+
// Found a case insensitive match
656+
dirp = opendir(fn.c_str());
657+
search_dir = fn;
658+
// We also need to store this in our mapping since we may need it in the future
659+
pathTypeToRealPath.insert(std::make_pair(i, fn));
660+
break;
661+
}
662+
}
663+
closedir(parentDirP);
664+
}
665+
}
666+
}
593667

594-
dirp = opendir (search_path);
595668
if ( dirp ) {
669+
struct dirent *dir = nullptr;
596670
while ((dir = readdir (dirp)) != NULL)
597671
{
598672
if (!fnmatch ("*.*", dir->d_name, 0))
599673
{
600-
char fn[MAX_PATH];
601-
snprintf(fn, MAX_PATH-1, "%s%s", search_path, dir->d_name);
602-
fn[MAX_PATH-1] = 0;
674+
SCP_string fn;
675+
sprintf(fn, "%s/%s", search_dir.c_str(), dir->d_name);
603676

604677
struct stat buf;
605-
if (stat(fn, &buf) == -1) {
678+
if (stat(fn.c_str(), &buf) == -1) {
606679
continue;
607680
}
608681

@@ -626,6 +699,8 @@ void cf_search_root_path(int root_index)
626699

627700
file->pack_offset = 0; // Mark as a non-packed file
628701

702+
file->real_name = vm_strdup(fn.c_str());
703+
629704
num_files++;
630705
//mprintf(( "Found file '%s'\n", file->name_ext ));
631706
}
@@ -827,6 +902,14 @@ void cf_free_secondary_filelist()
827902
// Init the file blocks
828903
for (i=0; i<CF_MAX_FILE_BLOCKS; i++ ) {
829904
if ( File_blocks[i] ) {
905+
// Free file paths
906+
for (auto& f : File_blocks[i]->files) {
907+
if (f.real_name) {
908+
vm_free(f.real_name);
909+
f.real_name = nullptr;
910+
}
911+
}
912+
830913
vm_free( File_blocks[i] );
831914
File_blocks[i] = NULL;
832915
}
@@ -985,17 +1068,14 @@ int cf_find_file_location( const char *filespec, int pathtype, int max_out, char
9851068
*offset = (size_t)f->pack_offset;
9861069

9871070
if (pack_filename) {
988-
cf_root *r = cf_get_root(f->root_index);
989-
990-
strncpy( pack_filename, r->path, max_out );
991-
9921071
if (f->pack_offset < 1) {
993-
strcat_s( pack_filename, max_out, Pathtypes[f->pathtype_index].path );
994-
995-
if ( pack_filename[strlen(pack_filename)-1] != DIR_SEPARATOR_CHAR )
996-
strcat_s( pack_filename, max_out, DIR_SEPARATOR_STR );
1072+
// This is a real file, return the actual file path
1073+
strncpy( pack_filename, f->real_name, max_out );
1074+
} else {
1075+
// File is in a pack file
1076+
cf_root *r = cf_get_root(f->root_index);
9971077

998-
strcat_s( pack_filename, max_out, f->name_ext );
1078+
strncpy( pack_filename, r->path, max_out );
9991079
}
10001080
}
10011081

@@ -1013,19 +1093,14 @@ int cf_find_file_location( const char *filespec, int pathtype, int max_out, char
10131093
*offset = (size_t)f->pack_offset;
10141094

10151095
if (pack_filename) {
1016-
cf_root *r = cf_get_root(f->root_index);
1017-
1018-
strcpy( pack_filename, r->path );
1019-
10201096
if (f->pack_offset < 1) {
1021-
if ( strlen(Pathtypes[f->pathtype_index].path) ) {
1022-
strcat_s( pack_filename, max_out, Pathtypes[f->pathtype_index].path );
1023-
1024-
if ( pack_filename[strlen(pack_filename)-1] != DIR_SEPARATOR_CHAR )
1025-
strcat_s( pack_filename, max_out, DIR_SEPARATOR_STR );
1026-
}
1097+
// This is a real file, return the actual file path
1098+
strncpy( pack_filename, f->real_name, max_out );
1099+
} else {
1100+
// File is in a pack file
1101+
cf_root *r = cf_get_root(f->root_index);
10271102

1028-
strcat_s( pack_filename, max_out, f->name_ext );
1103+
strncpy( pack_filename, r->path, max_out );
10291104
}
10301105
}
10311106

@@ -1260,17 +1335,14 @@ int cf_find_file_location_ext( const char *filename, const int ext_num, const ch
12601335
*offset = f->pack_offset;
12611336

12621337
if (pack_filename) {
1263-
cf_root *r = cf_get_root(f->root_index);
1264-
1265-
strncpy( pack_filename, r->path, max_out );
1266-
12671338
if (f->pack_offset < 1) {
1268-
strcat_s( pack_filename, max_out, Pathtypes[f->pathtype_index].path );
1339+
// This is a real file, return the actual file path
1340+
strncpy( pack_filename, f->real_name, max_out );
1341+
} else {
1342+
// File is in a pack file
1343+
cf_root *r = cf_get_root(f->root_index);
12691344

1270-
if ( pack_filename[strlen(pack_filename)-1] != DIR_SEPARATOR_CHAR )
1271-
strcat_s( pack_filename, max_out, DIR_SEPARATOR_STR );
1272-
1273-
strcat_s( pack_filename, max_out, f->name_ext );
1345+
strncpy( pack_filename, r->path, max_out );
12741346
}
12751347
}
12761348

@@ -1291,20 +1363,14 @@ int cf_find_file_location_ext( const char *filename, const int ext_num, const ch
12911363
*offset = f->pack_offset;
12921364

12931365
if (pack_filename) {
1294-
cf_root *r = cf_get_root(f->root_index);
1295-
1296-
strcpy( pack_filename, r->path );
1297-
12981366
if (f->pack_offset < 1) {
1367+
// This is a real file, return the actual file path
1368+
strncpy( pack_filename, f->real_name, max_out );
1369+
} else {
1370+
// File is in a pack file
1371+
cf_root *r = cf_get_root(f->root_index);
12991372

1300-
if ( strlen(Pathtypes[f->pathtype_index].path) ) {
1301-
strcat_s( pack_filename, max_out, Pathtypes[f->pathtype_index].path );
1302-
1303-
if ( pack_filename[strlen(pack_filename)-1] != DIR_SEPARATOR_CHAR )
1304-
strcat_s( pack_filename, max_out, DIR_SEPARATOR_STR );
1305-
}
1306-
1307-
strcat_s( pack_filename, max_out, f->name_ext );
1373+
strncpy( pack_filename, r->path, max_out );
13081374
}
13091375
}
13101376

test/src/cfile/cfile.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
2+
#include <gtest/gtest.h>
3+
#include <graphics/font.h>
4+
5+
#include "util/FSTestFixture.h"
6+
7+
class CFileTest : public test::FSTestFixture {
8+
public:
9+
CFileTest() : test::FSTestFixture(INIT_NONE) {
10+
pushModDir("cfile");
11+
}
12+
13+
protected:
14+
virtual void SetUp() override {
15+
test::FSTestFixture::SetUp();
16+
}
17+
virtual void TearDown() override {
18+
test::FSTestFixture::TearDown();
19+
20+
cfile_close();
21+
}
22+
};
23+
24+
TEST_F(CFileTest, wrong_data_case) {
25+
SCP_string cfile_dir(TEST_DATA_PATH);
26+
cfile_dir += DIR_SEPARATOR_CHAR;
27+
cfile_dir += "test"; // Cfile expects something after the path
28+
29+
ASSERT_FALSE(cfile_init(cfile_dir.c_str()));
30+
31+
ASSERT_TRUE(cf_exists("ships.tbl", CF_TYPE_TABLES));
32+
}
33+
34+
TEST_F(CFileTest, right_data_case) {
35+
SCP_string cfile_dir(TEST_DATA_PATH);
36+
cfile_dir += DIR_SEPARATOR_CHAR;
37+
cfile_dir += "test"; // Cfile expects something after the path
38+
39+
ASSERT_FALSE(cfile_init(cfile_dir.c_str()));
40+
41+
ASSERT_TRUE(cf_exists("ships.tbl", CF_TYPE_TABLES));
42+
}

test/test_data/cfile/right_data_case/data/tables/ships.tbl

Whitespace-only changes.

0 commit comments

Comments
 (0)