diff --git a/Core/FileLoaders/DiskCachingFileLoader.cpp b/Core/FileLoaders/DiskCachingFileLoader.cpp index e90c0ae7d5..961b611ad8 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.cpp +++ b/Core/FileLoaders/DiskCachingFileLoader.cpp @@ -16,9 +16,11 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include #include #include "file/file_util.h" #include "file/free.h" +#include "util/text/utf8.h" #include "Common/FileUtil.h" #include "Core/FileLoaders/DiskCachingFileLoader.h" #include "Core/System.h" @@ -86,6 +88,17 @@ size_t DiskCachingFileLoader::ReadAt(s64 absolutePos, size_t bytes, void *data) return readSize; } +std::vector DiskCachingFileLoader::GetCachedPathsInUse() { + // This is on the file loader so that it can manage the caches_. + std::vector files; + + for (auto it : caches_) { + files.push_back(it.first); + } + + return files; +} + void DiskCachingFileLoader::InitCache() { std::string path = backend_->Path(); auto &entry = caches_[path]; @@ -326,12 +339,7 @@ u32 DiskCachingFileLoaderCache::AllocateBlock(u32 indexPos) { return INVALID_BLOCK; } -std::string DiskCachingFileLoaderCache::MakeCacheFilePath(const std::string &path) { - std::string dir = cacheDir_; - if (dir.empty()) { - dir = GetSysDirectory(DIRECTORY_CACHE); - } - +std::string DiskCachingFileLoaderCache::MakeCacheFilename(const std::string &path) { static const char *const invalidChars = "?*:/\\^|<>\"'"; std::string filename = path; for (size_t i = 0; i < filename.size(); ++i) { @@ -341,11 +349,20 @@ std::string DiskCachingFileLoaderCache::MakeCacheFilePath(const std::string &pat } } + return filename + ".ppdc"; +} + +std::string DiskCachingFileLoaderCache::MakeCacheFilePath(const std::string &path) { + std::string dir = cacheDir_; + if (dir.empty()) { + dir = GetSysDirectory(DIRECTORY_CACHE); + } + if (!File::Exists(dir)) { File::CreateFullPath(dir); } - return dir + "/" + filename; + return dir + "/" + MakeCacheFilename(path); } s64 DiskCachingFileLoaderCache::GetBlockOffset(u32 block) { @@ -490,6 +507,10 @@ void DiskCachingFileLoaderCache::LoadCacheIndex() { void DiskCachingFileLoaderCache::CreateCacheFile(const std::string &path) { maxBlocks_ = DetermineMaxBlocks(); + if (maxBlocks_ < MAX_BLOCKS_LOWER_BOUND) { + GarbageCollectCacheFiles(MAX_BLOCKS_LOWER_BOUND * DEFAULT_BLOCK_SIZE); + maxBlocks_ = DetermineMaxBlocks(); + } if (maxBlocks_ < MAX_BLOCKS_LOWER_BOUND) { // There's not enough free space to cache, disable. f_ = nullptr; @@ -563,3 +584,51 @@ u32 DiskCachingFileLoaderCache::DetermineMaxBlocks() { // Might be lower than LOWER_BOUND, but that's okay. It means not enough space. return freeBlocks; } + +void DiskCachingFileLoaderCache::GarbageCollectCacheFiles(u64 goalBytes) { + // We attempt to free up at least enough files from the cache to get goalBytes more space. + const std::vector usedPaths = DiskCachingFileLoader::GetCachedPathsInUse(); + std::set used; + for (std::string path : usedPaths) { + used.insert(MakeCacheFilename(path)); + } + + std::string dir = cacheDir_; + if (dir.empty()) { + dir = GetSysDirectory(DIRECTORY_CACHE); + } + + std::vector files; + getFilesInDir(dir.c_str(), &files, "ppdc:"); + + u64 remaining = goalBytes; + // TODO: Could order by LRU or etc. + for (FileInfo file : files) { + if (file.isDirectory) { + continue; + } + if (used.find(file.name) != used.end()) { + // In use, must leave alone. + continue; + } + +#ifdef _WIN32 + const std::wstring w32path = ConvertUTF8ToWString(file.fullName); + bool success = DeleteFileW(w32path.c_str()) != 0; +#else + bool success = unlink(file.fullName.c_str()) == 0; +#endif + + if (success) { + if (file.size > remaining) { + // We're done, huzzah. + break; + } + + // A little bit more. + remaining -= file.size; + } + } + + // At this point, we've done all we can. +} diff --git a/Core/FileLoaders/DiskCachingFileLoader.h b/Core/FileLoaders/DiskCachingFileLoader.h index bf60d2c535..45065fa5bd 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.h +++ b/Core/FileLoaders/DiskCachingFileLoader.h @@ -47,6 +47,8 @@ public: } virtual size_t ReadAt(s64 absolutePos, size_t bytes, void *data) override; + static std::vector GetCachedPathsInUse(); + private: void InitCache(); void ShutdownCache(); @@ -100,12 +102,14 @@ private: s64 GetBlockOffset(u32 block); std::string MakeCacheFilePath(const std::string &path); + std::string MakeCacheFilename(const std::string &path); bool LoadCacheFile(const std::string &path); void LoadCacheIndex(); void CreateCacheFile(const std::string &path); u64 FreeDiskSpace(); u32 DetermineMaxBlocks(); + void GarbageCollectCacheFiles(u64 goalBytes); // File format: // 64 magic @@ -145,7 +149,7 @@ private: u32_le version; u32_le blockSize; s64_le filesize; - s32_le maxBlocks; + u32_le maxBlocks; }; struct BlockInfo {