mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Simulate case insensitivity on case sensitive platforms.
This commit is contained in:
parent
725094eaef
commit
9e85c01c1f
2 changed files with 245 additions and 18 deletions
|
@ -18,6 +18,7 @@
|
|||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
@ -25,8 +26,105 @@
|
|||
#include "FileUtil.h"
|
||||
#include "DirectoryFileSystem.h"
|
||||
|
||||
// TODO: Simulate case insensitivity on Unix.
|
||||
// NOTE: MacOSX is already case insensitive.
|
||||
|
||||
#if HOST_IS_CASE_SENSITIVE
|
||||
|
||||
static bool FixFilenameCase(const std::string &path, std::string &filename)
|
||||
{
|
||||
// Are we lucky?
|
||||
if (File::Exists(path + filename))
|
||||
return true;
|
||||
|
||||
size_t filenameSize = filename.size();
|
||||
for (size_t i = 0; i < filenameSize; i++)
|
||||
{
|
||||
filename[i] = tolower(filename[i]);
|
||||
}
|
||||
|
||||
//TODO: lookup filename in cache for basePath
|
||||
|
||||
struct dirent_large { struct dirent entry; char padding[FILENAME_MAX+1]; } diren;
|
||||
struct dirent_large;
|
||||
struct dirent *result = NULL;
|
||||
|
||||
DIR *dirp = opendir(path.c_str());
|
||||
if (!dirp)
|
||||
return false;
|
||||
|
||||
bool retValue = false;
|
||||
|
||||
while (!readdir_r(dirp, (dirent*) &diren, &result) && result)
|
||||
{
|
||||
if (result->d_namlen != filenameSize)
|
||||
continue;
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < filenameSize; i++)
|
||||
{
|
||||
if (filename[i] != tolower(result->d_name[i]))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < filenameSize)
|
||||
continue;
|
||||
|
||||
filename = result->d_name;
|
||||
retValue = true;
|
||||
}
|
||||
|
||||
closedir(dirp);
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
bool DirectoryFileSystem::FixPathCase(std::string &path, FixPathCaseBehavior behavior)
|
||||
{
|
||||
size_t len = path.size();
|
||||
|
||||
if (len == 0)
|
||||
return true;
|
||||
|
||||
if (path[len - 1] == '/')
|
||||
{
|
||||
len--;
|
||||
|
||||
if (len == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string fullPath;
|
||||
fullPath.reserve(basePath.size() + len + 1);
|
||||
|
||||
fullPath.clear();
|
||||
fullPath.append(basePath);
|
||||
|
||||
size_t start = 0;
|
||||
while (start < len)
|
||||
{
|
||||
size_t i = path.find('/', start);
|
||||
if (i == std::string::npos)
|
||||
i = len;
|
||||
|
||||
if (i > start)
|
||||
{
|
||||
std::string component = path.substr(start, i - start);
|
||||
|
||||
if (FixFilenameCase(fullPath, component) == false)
|
||||
return (behavior == FPC_FILE_MUST_EXIST || (behavior == FPC_PATH_MUST_EXIST && i >= len));
|
||||
|
||||
path.replace(start, i - start, component);
|
||||
|
||||
fullPath.append(component);
|
||||
fullPath.append(1, '/');
|
||||
}
|
||||
|
||||
start = i + 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
DirectoryFileSystem::DirectoryFileSystem(IHandleAllocator *_hAlloc, std::string _basePath) : basePath(_basePath) {
|
||||
File::CreateFullPath(basePath);
|
||||
|
@ -60,11 +158,37 @@ std::string DirectoryFileSystem::GetLocalPath(std::string localpath) {
|
|||
}
|
||||
|
||||
bool DirectoryFileSystem::MkDir(const std::string &dirname) {
|
||||
|
||||
#if HOST_IS_CASE_SENSITIVE
|
||||
// Must fix case BEFORE attempting, because MkDir would create
|
||||
// duplicate (different case) directories
|
||||
|
||||
std::string fixedCase = dirname;
|
||||
if ( ! FixPathCase(fixedCase, FPC_PARTIAL_ALLOWED) )
|
||||
return false;
|
||||
|
||||
return File::CreateFullPath(GetLocalPath(fixedCase));
|
||||
#else
|
||||
return File::CreateFullPath(GetLocalPath(dirname));
|
||||
#endif
|
||||
}
|
||||
|
||||
bool DirectoryFileSystem::RmDir(const std::string &dirname) {
|
||||
std::string fullName = GetLocalPath(dirname);
|
||||
|
||||
#if HOST_IS_CASE_SENSITIVE
|
||||
// Maybe we're lucky?
|
||||
if (File::DeleteDirRecursively(fullName))
|
||||
return true;
|
||||
|
||||
// Nope, fix case and try again
|
||||
fullName = dirname;
|
||||
if ( ! FixPathCase(fullName, FPC_FILE_MUST_EXIST) )
|
||||
return false; // or go on and attempt (for a better error code than just false?)
|
||||
|
||||
fullName = GetLocalPath(fullName);
|
||||
#endif
|
||||
|
||||
/*#ifdef _WIN32
|
||||
return RemoveDirectory(fullName.c_str()) == TRUE;
|
||||
#else
|
||||
|
@ -74,7 +198,6 @@ bool DirectoryFileSystem::RmDir(const std::string &dirname) {
|
|||
}
|
||||
|
||||
bool DirectoryFileSystem::RenameFile(const std::string &from, const std::string &to) {
|
||||
std::string fullFrom = GetLocalPath(from);
|
||||
std::string fullTo = to;
|
||||
// TO filename may not include path. Intention is that it uses FROM's path
|
||||
if (to.find("/") != std::string::npos) {
|
||||
|
@ -83,26 +206,85 @@ bool DirectoryFileSystem::RenameFile(const std::string &from, const std::string
|
|||
fullTo = from.substr(0, offset + 1) + to;
|
||||
}
|
||||
}
|
||||
fullTo = GetLocalPath(fullTo);
|
||||
#ifdef _WIN32
|
||||
return MoveFile(fullFrom.c_str(), fullTo.c_str()) == TRUE;
|
||||
#else
|
||||
return 0 == rename(fullFrom.c_str(), fullTo.c_str());
|
||||
std::string fullFrom = GetLocalPath(from);
|
||||
|
||||
#if HOST_IS_CASE_SENSITIVE
|
||||
// In case TO should overwrite a file with different case
|
||||
if ( ! FixPathCase(fullTo, FPC_PATH_MUST_EXIST) )
|
||||
return false; // or go on and attempt (for a better error code than just false?)
|
||||
#endif
|
||||
|
||||
fullTo = GetLocalPath(fullTo);
|
||||
const char * fullToC = fullTo.c_str();
|
||||
|
||||
#ifdef _WIN32
|
||||
bool retValue = (MoveFile(fullFrom.c_str(), fullToC) == TRUE);
|
||||
#else
|
||||
bool retValue = (0 == rename(fullFrom.c_str(), fullToC));
|
||||
#endif
|
||||
|
||||
#if HOST_IS_CASE_SENSITIVE
|
||||
if (! retValue)
|
||||
{
|
||||
// May have failed due to case sensitivity on FROM, so try again
|
||||
fullFrom = from;
|
||||
if ( ! FixPathCase(fullFrom, FPC_FILE_MUST_EXIST) )
|
||||
return false; // or go on and attempt (for a better error code than just false?)
|
||||
fullFrom = GetLocalPath(fullFrom);
|
||||
|
||||
#ifdef _WIN32
|
||||
retValue = (MoveFile(fullFrom.c_str(), fullToC) == TRUE);
|
||||
#else
|
||||
retValue = (0 == rename(fullFrom.c_str(), fullToC));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
bool DirectoryFileSystem::DeleteFile(const std::string &filename) {
|
||||
std::string fullName = GetLocalPath(filename);
|
||||
#ifdef _WIN32
|
||||
return ::DeleteFile(fullName.c_str()) == TRUE;
|
||||
bool retValue = (::DeleteFile(fullName.c_str()) == TRUE);
|
||||
#else
|
||||
return 0 == unlink(fullName.c_str());
|
||||
bool retValue = (0 == unlink(fullName.c_str()));
|
||||
#endif
|
||||
|
||||
#if HOST_IS_CASE_SENSITIVE
|
||||
if (! retValue)
|
||||
{
|
||||
// May have failed due to case sensitivity, so try again
|
||||
fullName = filename;
|
||||
if ( ! FixPathCase(fullName, FPC_FILE_MUST_EXIST) )
|
||||
return false; // or go on and attempt (for a better error code than just false?)
|
||||
fullName = GetLocalPath(fullName);
|
||||
|
||||
#ifdef _WIN32
|
||||
retValue = (::DeleteFile(fullName.c_str()) == TRUE);
|
||||
#else
|
||||
retValue = (0 == unlink(fullName.c_str()));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
u32 DirectoryFileSystem::OpenFile(std::string filename, FileAccess access) {
|
||||
#if HOST_IS_CASE_SENSITIVE
|
||||
if (access & (FILEACCESS_APPEND|FILEACCESS_CREATE|FILEACCESS_WRITE))
|
||||
{
|
||||
DEBUG_LOG(HLE, "Checking case for path %s", filename.c_str());
|
||||
if ( ! FixPathCase(filename, FPC_PATH_MUST_EXIST) )
|
||||
return 0; // or go on and attempt (for a better error code than just 0?)
|
||||
}
|
||||
// else we try fopen first (in case we're lucky) before simulating case insensitivity
|
||||
#endif
|
||||
|
||||
std::string fullName = GetLocalPath(filename);
|
||||
INFO_LOG(HLE,"Actually opening %s (%s)", fullName.c_str(), filename.c_str());
|
||||
const char *fullNameC = fullName.c_str();
|
||||
INFO_LOG(HLE,"Actually opening %s (%s)", fullNameC, filename.c_str());
|
||||
|
||||
OpenFileEntry entry;
|
||||
|
||||
|
@ -126,12 +308,8 @@ u32 DirectoryFileSystem::OpenFile(std::string filename, FileAccess access) {
|
|||
openmode = OPEN_EXISTING;
|
||||
}
|
||||
//Let's do it!
|
||||
entry.hFile = CreateFile(fullName.c_str(), desired, sharemode, 0, openmode, 0, 0);
|
||||
entry.hFile = CreateFile(fullNameC, desired, sharemode, 0, openmode, 0, 0);
|
||||
bool success = entry.hFile != INVALID_HANDLE_VALUE;
|
||||
|
||||
if (success && (access & FILEACCESS_APPEND)) {
|
||||
SetFilePointer(entry.hFile, 0, NULL, FILE_END);
|
||||
}
|
||||
#else
|
||||
// Convert flags in access parameter to fopen access mode
|
||||
const char *mode = NULL;
|
||||
|
@ -156,17 +334,48 @@ u32 DirectoryFileSystem::OpenFile(std::string filename, FileAccess access) {
|
|||
mode = "rb";
|
||||
}
|
||||
|
||||
entry.hFile = fopen(fullName.c_str(), mode);
|
||||
entry.hFile = fopen(fullNameC, mode);
|
||||
bool success = entry.hFile != 0;
|
||||
#endif
|
||||
|
||||
#if HOST_IS_CASE_SENSITIVE
|
||||
if (!success &&
|
||||
!(access & FILEACCESS_APPEND) &&
|
||||
!(access & FILEACCESS_CREATE) &&
|
||||
!(access & FILEACCESS_WRITE))
|
||||
{
|
||||
if ( ! FixPathCase(filename, FPC_PATH_MUST_EXIST) )
|
||||
return 0; // or go on and attempt (for a better error code than just 0?)
|
||||
fullName = GetLocalPath(filename);
|
||||
fullNameC = fullName.c_str();
|
||||
|
||||
DEBUG_LOG(HLE, "Case may have been incorrect, second try opening %s (%s)", fullNameC, filename.c_str());
|
||||
|
||||
// And try again with the correct case this time
|
||||
#ifdef _WIN32
|
||||
entry.hFile = CreateFile(fullNameC, desired, sharemode, 0, openmode, 0, 0);
|
||||
success = entry.hFile != INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
entry.hFile = fopen(fullNameC, mode);
|
||||
success = entry.hFile != 0;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!success) {
|
||||
#ifdef _WIN32
|
||||
ERROR_LOG(HLE, "DirectoryFileSystem::OpenFile: FAILED, %i - access = %i", GetLastError(), (int)access);
|
||||
#else
|
||||
ERROR_LOG(HLE, "DirectoryFileSystem::OpenFile: FAILED, access = %i", (int)access);
|
||||
#endif
|
||||
//wwwwaaaaahh!!
|
||||
return 0;
|
||||
} else {
|
||||
#ifdef _WIN32
|
||||
if (access & FILEACCESS_APPEND)
|
||||
SetFilePointer(entry.hFile, 0, NULL, FILE_END);
|
||||
#endif
|
||||
|
||||
u32 newHandle = hAlloc->GetNewHandle();
|
||||
entries[newHandle] = entry;
|
||||
|
||||
|
@ -265,8 +474,17 @@ PSPFileInfo DirectoryFileSystem::GetFileInfo(std::string filename) {
|
|||
x.name = filename;
|
||||
|
||||
std::string fullName = GetLocalPath(filename);
|
||||
if (!File::Exists(fullName)) {
|
||||
if (! File::Exists(fullName)) {
|
||||
#if HOST_IS_CASE_SENSITIVE
|
||||
if (! FixPathCase(filename, FPC_FILE_MUST_EXIST))
|
||||
return x;
|
||||
fullName = GetLocalPath(filename);
|
||||
|
||||
if (! File::Exists(fullName))
|
||||
return x;
|
||||
#else
|
||||
return x;
|
||||
#endif
|
||||
}
|
||||
x.type = File::IsDirectory(fullName) ? FILETYPE_NORMAL : FILETYPE_DIRECTORY;
|
||||
x.exists = true;
|
||||
|
|
|
@ -83,4 +83,13 @@ private:
|
|||
|
||||
// In case of Windows: Translate slashes, etc.
|
||||
std::string GetLocalPath(std::string localpath);
|
||||
|
||||
#if HOST_IS_CASE_SENSITIVE
|
||||
typedef enum {
|
||||
FPC_FILE_MUST_EXIST, // all path components must exist (rmdir, move from)
|
||||
FPC_PATH_MUST_EXIST, // all except the last one must exist - still tries to fix last one (fopen, move to)
|
||||
FPC_PARTIAL_ALLOWED, // don't care how many exist (mkdir recursive)
|
||||
} FixPathCaseBehavior;
|
||||
bool FixPathCase(std::string &path, FixPathCaseBehavior behavior);
|
||||
#endif
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue