mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-02-23 07:31:56 +00:00
Merge pull request #267 from KentuckyCompass/case_insensitive2
Case insensitive2
This commit is contained in:
commit
bafb63ca72
@ -18,6 +18,7 @@
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
@ -25,8 +26,103 @@
|
||||
#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 "path"
|
||||
|
||||
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.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 +156,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,38 +196,97 @@ 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) {
|
||||
int offset = from.find_last_of("/");
|
||||
if (offset >= 0) {
|
||||
size_t offset = from.find_last_of("/");
|
||||
if (offset != std::string::npos) {
|
||||
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;
|
||||
|
||||
//TODO: tests, should append seek to end of file? seeking in a file opened for append?
|
||||
#ifdef _WIN32
|
||||
// Convert parameters to Windows permissions and access
|
||||
DWORD desired = 0;
|
||||
@ -125,20 +306,74 @@ 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;
|
||||
#else
|
||||
entry.hFile = fopen(fullName.c_str(), access & FILEACCESS_WRITE ? "wb" : "rb");
|
||||
bool success = entry.hFile != 0;
|
||||
// Convert flags in access parameter to fopen access mode
|
||||
const char *mode = NULL;
|
||||
if (access & FILEACCESS_APPEND) {
|
||||
if (access & FILEACCESS_READ)
|
||||
mode = "ab+"; // append+read, create if needed
|
||||
else
|
||||
mode = "ab"; // append only, create if needed
|
||||
} else if (access & FILEACCESS_WRITE) {
|
||||
if (access & FILEACCESS_READ) {
|
||||
// FILEACCESS_CREATE is ignored for read only, write only, and append
|
||||
// because C++ standard fopen's nonexistant file creation can only be
|
||||
// customized for files opened read+write
|
||||
if (access & FILEACCESS_CREATE)
|
||||
mode = "wb+"; // read+write, create if needed
|
||||
else
|
||||
mode = "rb+"; // read+write, but don't create
|
||||
} else {
|
||||
mode = "wb"; // write only, create if needed
|
||||
}
|
||||
} else { // neither write nor append, so default to read only
|
||||
mode = "rb"; // read only, don't create
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -209,18 +444,18 @@ size_t DirectoryFileSystem::SeekFile(u32 handle, s32 position, FileMove type) {
|
||||
#ifdef _WIN32
|
||||
DWORD moveMethod = 0;
|
||||
switch (type) {
|
||||
case FILEMOVE_BEGIN: moveMethod = FILE_BEGIN; break;
|
||||
case FILEMOVE_CURRENT: moveMethod = FILE_CURRENT; break;
|
||||
case FILEMOVE_END: moveMethod = FILE_END; break;
|
||||
case FILEMOVE_BEGIN: moveMethod = FILE_BEGIN; break;
|
||||
case FILEMOVE_CURRENT: moveMethod = FILE_CURRENT; break;
|
||||
case FILEMOVE_END: moveMethod = FILE_END; break;
|
||||
}
|
||||
DWORD newPos = SetFilePointer((*iter).second.hFile, (LONG)position, 0, moveMethod);
|
||||
return newPos;
|
||||
#else
|
||||
int moveMethod = 0;
|
||||
switch (type) {
|
||||
case FILEMOVE_BEGIN: moveMethod = SEEK_SET; break;
|
||||
case FILEMOVE_CURRENT: moveMethod = SEEK_CUR; break;
|
||||
case FILEMOVE_END: moveMethod = SEEK_END; break;
|
||||
case FILEMOVE_BEGIN: moveMethod = SEEK_SET; break;
|
||||
case FILEMOVE_CURRENT: moveMethod = SEEK_CUR; break;
|
||||
case FILEMOVE_END: moveMethod = SEEK_END; break;
|
||||
}
|
||||
fseek(iter->second.hFile, position, moveMethod);
|
||||
return ftell(iter->second.hFile);
|
||||
@ -237,8 +472,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;
|
||||
@ -290,6 +534,8 @@ std::vector<PSPFileInfo> DirectoryFileSystem::GetDirListing(std::string path) {
|
||||
if (!retval)
|
||||
break;
|
||||
}
|
||||
#else
|
||||
ERROR_LOG(HLE, "GetDirListing not implemented on non-Windows");
|
||||
#endif
|
||||
return myVector;
|
||||
}
|
||||
|
@ -28,6 +28,26 @@
|
||||
typedef void * HANDLE;
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#define HOST_IS_CASE_SENSITIVE true
|
||||
#elif TARGET_IPHONE_SIMULATOR
|
||||
#define HOST_IS_CASE_SENSITIVE false
|
||||
#else
|
||||
// Mac OSX case sensitivity defaults off, but is user configurable (when
|
||||
// creating a filesytem), so assume the worst:
|
||||
#define HOST_IS_CASE_SENSITIVE true
|
||||
#endif
|
||||
|
||||
#elif defined(_WIN32) || defined(__SYMBIAN32__)
|
||||
#define HOST_IS_CASE_SENSITIVE false
|
||||
|
||||
#else // Android, Linux, BSD (and the rest?)
|
||||
#define HOST_IS_CASE_SENSITIVE true
|
||||
|
||||
#endif
|
||||
|
||||
class DirectoryFileSystem : public IFileSystem {
|
||||
public:
|
||||
DirectoryFileSystem(IHandleAllocator *_hAlloc, std::string _basePath);
|
||||
@ -63,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…
x
Reference in New Issue
Block a user