Skip to content

Commit 5265627

Browse files
committed
Add case check for Windows
1 parent 94ce0ad commit 5265627

File tree

1 file changed

+70
-1
lines changed

1 file changed

+70
-1
lines changed

code/cfile/cfilesystem.cpp

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,25 @@
1616
#include <sstream>
1717
#include <algorithm>
1818
#include <memory>
19+
#include <locale>
20+
#include <codecvt>
1921

2022
#ifdef _WIN32
2123
#include <io.h>
2224
#include <direct.h>
2325
#include <windows.h>
2426
#include <winbase.h> /* needed for memory mapping of file functions */
27+
28+
struct dir_handle_deleter {
29+
typedef HANDLE pointer;
30+
31+
void operator()(HANDLE dirp) {
32+
if (dirp != INVALID_HANDLE_VALUE) {
33+
FindClose(dirp);
34+
}
35+
}
36+
};
37+
typedef std::unique_ptr<HANDLE, dir_handle_deleter> unique_dir_handle_ptr;
2538
#endif
2639

2740
#ifdef SCP_UNIX
@@ -566,6 +579,62 @@ void cf_search_root_path(int root_index)
566579
}
567580

568581
#if defined _WIN32
582+
{
583+
// Check if the case matches the case as specified in Pathtypes
584+
// Since Windows paths are case insensitive this wouldn't cause issues here but other
585+
// platforms would fail to find data paths in this case so we show a nice error if we detect that here
586+
587+
// Ignore the root since the case of that is allowed to differ (it's handled in the mod handling)
588+
if (i != CF_TYPE_ROOT) {
589+
// We use FindFirstFileNameW for this, it should hopefully work...
590+
// First, convert our path from ASCII/UTF-8 to wchar_t
591+
std::string search_string = search_path;
592+
// Remove any trailing directory separators
593+
if (search_string[search_string.size() - 1] == '\\') {
594+
search_string = search_string.substr(0, search_string.size() - 1);
595+
}
596+
597+
char parent_name[MAX_PATH];
598+
memset(parent_name, 0, sizeof(parent_name));
599+
strcpy_s(parent_name, search_string.c_str());
600+
601+
CHAR file_name[MAX_PATH];
602+
memset(file_name, 0, sizeof(file_name));
603+
strcpy_s(file_name, search_string.c_str());
604+
605+
PathStripPathA(file_name);
606+
if (PathRemoveFileSpecA(parent_name)) {
607+
strcat_s(parent_name, "\\*");
608+
609+
WIN32_FIND_DATAA find_data;
610+
auto handle = unique_dir_handle_ptr(FindFirstFileA(parent_name, &find_data));
611+
if (handle.get() != INVALID_HANDLE_VALUE) {
612+
do {
613+
if (stricmp(find_data.cFileName, file_name)) {
614+
// Not the same name, not even if we check case-insensitive
615+
continue;
616+
}
617+
618+
// Same name, might have case differences
619+
if (!strcmp(find_data.cFileName, file_name)) {
620+
// Case matches, everything is alright.
621+
continue;
622+
}
623+
624+
// We need to do some formatting on the parent_name in order to show a nice error message
625+
SCP_string parent_name_str = parent_name;
626+
parent_name_str = parent_name_str.substr(0, parent_name_str.size() - 1); // Remove trailing *
627+
parent_name_str += find_data.cFileName;
628+
629+
// If we are still here then the case didn't match which means that we have to show the error message
630+
Error(LOCATION, "Data directory '%s' for path type '%s' does not match the required case! "
631+
"All data directories must exactly match the case specified by the engine or your mod "
632+
"will not work on other platforms.", parent_name_str.c_str(), Pathtypes[i].path);
633+
} while (FindNextFileA(handle.get(), &find_data) != 0);
634+
}
635+
}
636+
}
637+
}
569638
strcat_s( search_path, "*.*" );
570639

571640
intptr_t find_handle;
@@ -667,7 +736,7 @@ void cf_search_root_path(int root_index)
667736
if (!fnmatch ("*.*", dir->d_name, 0))
668737
{
669738
SCP_string fn;
670-
sprintf(fn, "%s/%s", search_path, dir->d_name);
739+
sprintf(fn, "%s%s", search_path, dir->d_name);
671740

672741
struct stat buf;
673742
if (stat(fn.c_str(), &buf) == -1) {

0 commit comments

Comments
 (0)