Skip to content

Commit

Permalink
BACKENDS: Make DefaultSaveFileManager case insensitive.
Browse files Browse the repository at this point in the history
For this we introduce a file cache inside DefaultSaveFileManager similar to
what we use inside FSDirectory. However, we only do small updates for newly
created saves (via openForSaving) or for removed saves (via removeSavefile).
Re-caching is done whenever the savepath changes.

Tizen changes have not been tested.
  • Loading branch information
Johannes Schickel committed Feb 25, 2016
1 parent 8c5931b commit d6d63a1
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 77 deletions.
49 changes: 27 additions & 22 deletions backends/platform/tizen/system.cpp
Expand Up @@ -81,36 +81,41 @@ struct TizenSaveFileManager : public DefaultSaveFileManager {
};

bool TizenSaveFileManager::removeSavefile(const Common::String &filename) {
Common::String savePathName = getSavePath();
// Assure the savefile name cache is up-to-date.
assureCached(getSavePath());
if (getError().getCode() != Common::kNoError)
return false;

checkPath(Common::FSNode(savePathName));
if (getError().getCode() != Common::kNoError) {
// Obtain node if exists.
SaveFileCache::const_iterator file = _saveFileCache.find(filename);
if (file == _saveFileCache.end()) {
return false;
}
} else {
const Common::FSNode fileNode = file->_value;
// Remove from cache, this invalidates the 'file' iterator.
_saveFileCache.erase(file);
file = _saveFileCache.end();

// recreate FSNode since checkPath may have changed/created the directory
Common::FSNode savePath(savePathName);
Common::FSNode file = savePath.getChild(filename);
String unicodeFileName;
StringUtil::Utf8ToString(fileNode.getPath().c_str(), unicodeFileName);

String unicodeFileName;
StringUtil::Utf8ToString(file.getPath().c_str(), unicodeFileName);
switch (Tizen::Io::File::Remove(unicodeFileName)) {
case E_SUCCESS:
return true;

switch (Tizen::Io::File::Remove(unicodeFileName)) {
case E_SUCCESS:
return true;
case E_ILLEGAL_ACCESS:
setError(Common::kWritePermissionDenied, "Search or write permission denied: " +
file.getName());
break;

case E_ILLEGAL_ACCESS:
setError(Common::kWritePermissionDenied, "Search or write permission denied: " +
file.getName());
break;
default:
setError(Common::kPathDoesNotExist, "removeSavefile: '" + file.getName() +
"' does not exist or path is invalid");
break;
}

default:
setError(Common::kPathDoesNotExist, "removeSavefile: '" + file.getName() +
"' does not exist or path is invalid");
break;
return false;
}

return false;
}

//
Expand Down
155 changes: 100 additions & 55 deletions backends/saves/default/default-saves.cpp
Expand Up @@ -60,91 +60,97 @@ void DefaultSaveFileManager::checkPath(const Common::FSNode &dir) {
}

Common::StringArray DefaultSaveFileManager::listSavefiles(const Common::String &pattern) {
Common::String savePathName = getSavePath();
checkPath(Common::FSNode(savePathName));
// Assure the savefile name cache is up-to-date.
assureCached(getSavePath());
if (getError().getCode() != Common::kNoError)
return Common::StringArray();

// recreate FSNode since checkPath may have changed/created the directory
Common::FSNode savePath(savePathName);

Common::FSDirectory dir(savePath);
Common::ArchiveMemberList savefiles;
Common::StringArray results;
Common::String search(pattern);

if (dir.listMatchingMembers(savefiles, search) > 0) {
for (Common::ArchiveMemberList::const_iterator file = savefiles.begin(); file != savefiles.end(); ++file) {
results.push_back((*file)->getName());
for (SaveFileCache::const_iterator file = _saveFileCache.begin(), end = _saveFileCache.end(); file != end; ++file) {
if (file->_key.matchString(pattern, true)) {
results.push_back(file->_key);
}
}

return results;
}

Common::InSaveFile *DefaultSaveFileManager::openForLoading(const Common::String &filename) {
// Ensure that the savepath is valid. If not, generate an appropriate error.
Common::String savePathName = getSavePath();
checkPath(Common::FSNode(savePathName));
// Assure the savefile name cache is up-to-date.
assureCached(getSavePath());
if (getError().getCode() != Common::kNoError)
return 0;

// recreate FSNode since checkPath may have changed/created the directory
Common::FSNode savePath(savePathName);
return nullptr;

Common::FSNode file = savePath.getChild(filename);
if (!file.exists())
return 0;

// Open the file for reading
Common::SeekableReadStream *sf = file.createReadStream();

return Common::wrapCompressedReadStream(sf);
SaveFileCache::const_iterator file = _saveFileCache.find(filename);
if (file == _saveFileCache.end()) {
return nullptr;
} else {
// Open the file for loading.
Common::SeekableReadStream *sf = file->_value.createReadStream();
return Common::wrapCompressedReadStream(sf);
}
}

Common::OutSaveFile *DefaultSaveFileManager::openForSaving(const Common::String &filename, bool compress) {
// Ensure that the savepath is valid. If not, generate an appropriate error.
Common::String savePathName = getSavePath();
checkPath(Common::FSNode(savePathName));
// Assure the savefile name cache is up-to-date.
const Common::String savePathName = getSavePath();
assureCached(savePathName);
if (getError().getCode() != Common::kNoError)
return 0;
return nullptr;

// recreate FSNode since checkPath may have changed/created the directory
Common::FSNode savePath(savePathName);
// Obtain node.
SaveFileCache::const_iterator file = _saveFileCache.find(filename);
Common::FSNode fileNode;

Common::FSNode file = savePath.getChild(filename);
// If the file did not exist before, we add it to the cache.
if (file == _saveFileCache.end()) {
const Common::FSNode savePath(savePathName);
fileNode = savePath.getChild(filename);
} else {
fileNode = file->_value;
}

// Open the file for saving
Common::WriteStream *sf = file.createWriteStream();
// Open the file for saving.
Common::WriteStream *const sf = fileNode.createWriteStream();
Common::OutSaveFile *const result = compress ? Common::wrapCompressedWriteStream(sf) : sf;

return compress ? Common::wrapCompressedWriteStream(sf) : sf;
// Add file to cache now that it exists.
_saveFileCache[filename] = Common::FSNode(fileNode.getPath());

return result;
}

bool DefaultSaveFileManager::removeSavefile(const Common::String &filename) {
Common::String savePathName = getSavePath();
checkPath(Common::FSNode(savePathName));
// Assure the savefile name cache is up-to-date.
assureCached(getSavePath());
if (getError().getCode() != Common::kNoError)
return false;

// recreate FSNode since checkPath may have changed/created the directory
Common::FSNode savePath(savePathName);

Common::FSNode file = savePath.getChild(filename);

// FIXME: remove does not exist on all systems. If your port fails to
// compile because of this, please let us know (scummvm-devel or Fingolfin).
// There is a nicely portable workaround, too: Make this method overloadable.
if (remove(file.getPath().c_str()) != 0) {
// Obtain node if exists.
SaveFileCache::const_iterator file = _saveFileCache.find(filename);
if (file == _saveFileCache.end()) {
return false;
} else {
const Common::FSNode fileNode = file->_value;
// Remove from cache, this invalidates the 'file' iterator.
_saveFileCache.erase(file);
file = _saveFileCache.end();

// FIXME: remove does not exist on all systems. If your port fails to
// compile because of this, please let us know (scummvm-devel or Fingolfin).
// There is a nicely portable workaround, too: Make this method overloadable.
if (remove(fileNode.getPath().c_str()) != 0) {
#ifndef _WIN32_WCE
if (errno == EACCES)
setError(Common::kWritePermissionDenied, "Search or write permission denied: "+file.getName());
if (errno == EACCES)
setError(Common::kWritePermissionDenied, "Search or write permission denied: "+fileNode.getName());

if (errno == ENOENT)
setError(Common::kPathDoesNotExist, "removeSavefile: '"+file.getName()+"' does not exist or path is invalid");
if (errno == ENOENT)
setError(Common::kPathDoesNotExist, "removeSavefile: '"+fileNode.getName()+"' does not exist or path is invalid");
#endif
return false;
} else {
return true;
return false;
} else {
return true;
}
}
}

Expand All @@ -171,4 +177,43 @@ Common::String DefaultSaveFileManager::getSavePath() const {
return dir;
}

void DefaultSaveFileManager::assureCached(const Common::String &savePathName) {
// Check that path exists and is usable.
checkPath(Common::FSNode(savePathName));

if (_cachedDirectory == savePathName) {
return;
}

_saveFileCache.clear();
_cachedDirectory.clear();

if (getError().getCode() != Common::kNoError) {
warning("DefaultSaveFileManager::assureCached: Can not cache path '%s': '%s'", savePathName.c_str(), getErrorDesc().c_str());
return;
}

// FSNode can cache its members, thus create it after checkPath to reflect
// actual file system state.
const Common::FSNode savePath(savePathName);

Common::FSList children;
if (!savePath.getChildren(children, Common::FSNode::kListFilesOnly)) {
return;
}

// Build the savefile name cache.
for (Common::FSList::const_iterator file = children.begin(), end = children.end(); file != end; ++file) {
if (_saveFileCache.contains(file->getName())) {
warning("DefaultSaveFileManager::assureCached: Name clash when building cache, ignoring file '%s'", file->getName().c_str());
} else {
_saveFileCache[file->getName()] = *file;
}
}

// Only now store that we cached 'savePathName' to indicate we successfully
// cached the directory.
_cachedDirectory = savePathName;
}

#endif // !defined(DISABLE_DEFAULT_SAVEFILEMANAGER)
25 changes: 25 additions & 0 deletions backends/saves/default/default-saves.h
Expand Up @@ -27,6 +27,7 @@
#include "common/savefile.h"
#include "common/str.h"
#include "common/fs.h"
#include "common/hashmap.h"

/**
* Provides a default savefile manager implementation for common platforms.
Expand Down Expand Up @@ -54,6 +55,30 @@ class DefaultSaveFileManager : public Common::SaveFileManager {
* Sets the internal error and error message accordingly.
*/
virtual void checkPath(const Common::FSNode &dir);

/**
* Assure that the given save path is cached.
*
* @param savePathName String representation of save path to cache.
*/
void assureCached(const Common::String &savePathName);

typedef Common::HashMap<Common::String, Common::FSNode, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> SaveFileCache;

/**
* Cache of all the save files in the currently cached directory.
*
* Modify with caution because we only re-cache when the save path changed!
* This needs to be updated inside at least openForSaving and
* removeSavefile.
*/
SaveFileCache _saveFileCache;

private:
/**
* The currently cached directory.
*/
Common::String _cachedDirectory;
};

#endif

0 comments on commit d6d63a1

Please sign in to comment.