Case sensitive file paths on Windows

The intent is to avoid UMC authors accidentally mistyping a file name
and making an addon that doesn't work correctly on GNU/Linux.

Thanks to @Arcanister for suggesting the GetFinalPathNameByHandle function.
This commit is contained in:
Jyrki Vesterinen 2017-10-12 20:46:10 +03:00
parent 640ffe42d4
commit 37225d24ea
4 changed files with 43 additions and 0 deletions

View file

@ -1,4 +1,6 @@
Version 1.13.11:
* WML Engine:
* File paths are now case sensitive even on Windows.
Version 1.13.10:
* Add-ons client:

View file

@ -54,6 +54,9 @@ enum file_name_option { ENTIRE_FILE_PATH, FILE_NAME_ONLY };
enum file_filter_option { NO_FILTER, SKIP_MEDIA_DIR, SKIP_PBL_FILES };
enum file_reorder_option { DONT_REORDER, DO_REORDER };
/** Some tasks to run on startup. */
void init();
/**
* Populates 'files' with all the files and
* 'dirs' with all the directories in dir.

View file

@ -43,6 +43,7 @@ using boost::uintmax_t;
#include "config.hpp"
#include "game_config.hpp"
#include "log.hpp"
#include "serialization/unicode_cast.hpp"
#include "version.hpp"
static lg::log_domain log_filesystem("filesystem");
@ -189,12 +190,42 @@ namespace {
};
static static_runner static_bfs_path_imbuer;
typedef DWORD(WINAPI *GetFinalPathNameByHandleWPtr)(HANDLE, LPWSTR, DWORD, DWORD);
static GetFinalPathNameByHandleWPtr dyn_GetFinalPathNameByHandle;
#endif
bool is_filename_case_correct(const std::string& fname, const boost::iostreams::file_descriptor_source& fd)
{
#ifdef _WIN32
if(dyn_GetFinalPathNameByHandle == nullptr) {
// Windows XP. Just assume that the case is correct.
return true;
}
wchar_t real_path[MAX_PATH];
dyn_GetFinalPathNameByHandle(fd.handle(), real_path, MAX_PATH - 1, VOLUME_NAME_NONE);
std::string real_name = filesystem::base_name(unicode_cast<std::string>(std::wstring(real_path)));
return real_name == filesystem::base_name(fname);
#else
return true;
#endif
}
}
namespace filesystem {
void init()
{
#ifdef _WIN32
HMODULE kernel32 = GetModuleHandle(TEXT("Kernel32.dll"));
// Note that this returns a null pointer on Windows XP!
dyn_GetFinalPathNameByHandle = reinterpret_cast<GetFinalPathNameByHandleWPtr>(
GetProcAddress(kernel32, "GetFinalPathNameByHandleW"));
#endif
}
static void push_if_exists(std::vector<std::string> *vec, const path &file, bool full) {
if (vec != nullptr) {
if (full)
@ -779,6 +810,11 @@ filesystem::scoped_istream istream_file(const std::string &fname, bool treat_fai
//TODO: has this still use ?
if (!fd.is_open() && treat_failure_as_error) {
ERR_FS << "Could not open '" << fname << "' for reading.\n";
} else if (!is_filename_case_correct(fname, fd)) {
ERR_FS << "Not opening '" << fname << "' due to case mismatch.\n";
filesystem::scoped_istream s(new bfs::ifstream());
s->clear(std::ios_base::failbit);
return s;
}
return filesystem::scoped_istream(new boost::iostreams::stream<boost::iostreams::file_descriptor_source>(fd, 4096, 0));
}

View file

@ -1003,6 +1003,8 @@ int main(int argc, char** argv)
#endif
#endif //_OPENMP
filesystem::init();
if(SDL_Init(SDL_INIT_TIMER) < 0) {
fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
return(1);