diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f39485a87..e5a9399462 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -923,6 +923,8 @@ add_library(native STATIC ext/native/file/fd_util.h ext/native/file/file_util.cpp ext/native/file/file_util.h + ext/native/file/free.cpp + ext/native/file/free.h ext/native/file/ini_file.cpp ext/native/file/ini_file.h ext/native/file/path.cpp diff --git a/Core/FileLoaders/DiskCachingFileLoader.cpp b/Core/FileLoaders/DiskCachingFileLoader.cpp index 73d5a37b53..5e6b2e22b3 100644 --- a/Core/FileLoaders/DiskCachingFileLoader.cpp +++ b/Core/FileLoaders/DiskCachingFileLoader.cpp @@ -16,13 +16,19 @@ // 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" static const char *CACHEFILE_MAGIC = "ppssppDC"; +static const s64 SAFETY_FREE_DISK_SPACE = 768 * 1024 * 1024; // 768 MB +// Aim to allow this many files cached at once. +static const u32 CACHE_SPACE_FLEX = 4; std::string DiskCachingFileLoaderCache::cacheDir_; @@ -84,6 +90,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]; @@ -117,6 +134,7 @@ void DiskCachingFileLoaderCache::InitCache(const std::string &path) { cacheSize_ = 0; indexCount_ = 0; oldestGeneration_ = 0; + maxBlocks_ = MAX_BLOCKS_LOWER_BOUND; generation_ = 0; const std::string cacheFilePath = MakeCacheFilePath(path); @@ -147,6 +165,10 @@ void DiskCachingFileLoaderCache::ShutdownCache() { size_t DiskCachingFileLoaderCache::ReadFromCache(s64 pos, size_t bytes, void *data) { lock_guard guard(lock_); + if (!f_) { + return 0; + } + s64 cacheStartPos = pos / blockSize_; s64 cacheEndPos = (pos + bytes - 1) / blockSize_; size_t readSize = 0; @@ -164,7 +186,9 @@ size_t DiskCachingFileLoaderCache::ReadFromCache(s64 pos, size_t bytes, void *da } size_t toRead = std::min(bytes - readSize, (size_t)blockSize_ - offset); - ReadBlockData(p + readSize, info, offset, toRead); + if (!ReadBlockData(p + readSize, info, offset, toRead)) { + return readSize; + } readSize += toRead; // Don't need an offset after the first read. @@ -176,6 +200,11 @@ size_t DiskCachingFileLoaderCache::ReadFromCache(s64 pos, size_t bytes, void *da size_t DiskCachingFileLoaderCache::SaveIntoCache(FileLoader *backend, s64 pos, size_t bytes, void *data) { lock_guard guard(lock_); + if (!f_) { + // Just to keep things working. + return backend->ReadAt(pos, bytes, data); + } + s64 cacheStartPos = pos / blockSize_; s64 cacheEndPos = (pos + bytes - 1) / blockSize_; size_t readSize = 0; @@ -248,7 +277,7 @@ size_t DiskCachingFileLoaderCache::SaveIntoCache(FileLoader *backend, s64 pos, s } bool DiskCachingFileLoaderCache::MakeCacheSpaceFor(size_t blocks) { - size_t goal = MAX_BLOCKS_CACHED - blocks; + size_t goal = (size_t)maxBlocks_ - blocks; while (cacheSize_ > goal) { u16 minGeneration = generation_; @@ -323,12 +352,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) { @@ -338,11 +362,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) { @@ -352,49 +385,84 @@ s64 DiskCachingFileLoaderCache::GetBlockOffset(u32 block) { return blockOffset + (s64)block * (s64)blockSize_; } -void DiskCachingFileLoaderCache::ReadBlockData(u8 *dest, BlockInfo &info, size_t offset, size_t size) { +bool DiskCachingFileLoaderCache::ReadBlockData(u8 *dest, BlockInfo &info, size_t offset, size_t size) { + if (!f_) { + return false; + } s64 blockOffset = GetBlockOffset(info.block); + bool failed = false; #ifdef ANDROID if (lseek64(fd_, blockOffset, SEEK_SET) != blockOffset) { - ERROR_LOG(LOADER, "Unable to read disk cache data entry."); + failed = true; } else if (read(fd_, dest + offset, size) != (ssize_t)size) { - ERROR_LOG(LOADER, "Unable to read disk cache data entry."); + failed = true; } #else if (fseeko(f_, blockOffset, SEEK_SET) != 0) { - ERROR_LOG(LOADER, "Unable to read disk cache data entry."); + failed = true; } else if (fread(dest + offset, size, 1, f_) != 1) { - ERROR_LOG(LOADER, "Unable to read disk cache data entry."); + failed = true; } #endif + + if (failed) { + ERROR_LOG(LOADER, "Unable to read disk cache data entry."); + fclose(f_); + f_ = nullptr; + fd_ = 0; + } + return !failed; } void DiskCachingFileLoaderCache::WriteBlockData(BlockInfo &info, u8 *src) { + if (!f_) { + return; + } s64 blockOffset = GetBlockOffset(info.block); + bool failed = false; #ifdef ANDROID if (lseek64(fd_, blockOffset, SEEK_SET) != blockOffset) { - ERROR_LOG(LOADER, "Unable to write disk cache data entry."); + failed = true; } else if (write(fd_, src, blockSize_) != (ssize_t)blockSize_) { - ERROR_LOG(LOADER, "Unable to write disk cache data entry."); + failed = true; } #else if (fseeko(f_, blockOffset, SEEK_SET) != 0) { - ERROR_LOG(LOADER, "Unable to write disk cache data entry."); + failed = true; } else if (fwrite(src, blockSize_, 1, f_) != 1) { - ERROR_LOG(LOADER, "Unable to write disk cache data entry."); + failed = true; } #endif + + if (failed) { + ERROR_LOG(LOADER, "Unable to write disk cache data entry."); + fclose(f_); + f_ = nullptr; + fd_ = 0; + } } void DiskCachingFileLoaderCache::WriteIndexData(u32 indexPos, BlockInfo &info) { + if (!f_) { + return; + } + u32 offset = (u32)sizeof(FileHeader) + indexPos * (u32)sizeof(BlockInfo); + bool failed = false; if (fseek(f_, offset, SEEK_SET) != 0) { - ERROR_LOG(LOADER, "Unable to write disk cache index entry."); + failed = true; } else if (fwrite(&info, sizeof(BlockInfo), 1, f_) != 1) { + failed = true; + } + + if (failed) { ERROR_LOG(LOADER, "Unable to write disk cache index entry."); + fclose(f_); + f_ = nullptr; + fd_ = 0; } } @@ -414,6 +482,9 @@ bool DiskCachingFileLoaderCache::LoadCacheFile(const std::string &path) { valid = false; } else if (header.filesize != filesize_) { valid = false; + } else if (header.maxBlocks < MAX_BLOCKS_LOWER_BOUND || header.maxBlocks > MAX_BLOCKS_UPPER_BOUND) { + // This means it's not in our safety bounds, reject. + valid = false; } // If it's valid, retain the file pointer. @@ -427,6 +498,7 @@ bool DiskCachingFileLoaderCache::LoadCacheFile(const std::string &path) { // Now let's load the index. blockSize_ = header.blockSize; + maxBlocks_ = header.maxBlocks; LoadCacheIndex(); } else { ERROR_LOG(LOADER, "Disk cache file header did not match, recreating cache file"); @@ -446,8 +518,8 @@ void DiskCachingFileLoaderCache::LoadCacheIndex() { indexCount_ = (filesize_ + blockSize_ - 1) / blockSize_; index_.resize(indexCount_); - blockIndexLookup_.resize(MAX_BLOCKS_CACHED); - memset(&blockIndexLookup_[0], INVALID_INDEX, MAX_BLOCKS_CACHED * sizeof(blockIndexLookup_[0])); + blockIndexLookup_.resize(maxBlocks_); + memset(&blockIndexLookup_[0], INVALID_INDEX, maxBlocks_ * sizeof(blockIndexLookup_[0])); if (fread(&index_[0], sizeof(BlockInfo), indexCount_, f_) != indexCount_) { fclose(f_); @@ -462,7 +534,7 @@ void DiskCachingFileLoaderCache::LoadCacheIndex() { cacheSize_ = 0; for (size_t i = 0; i < index_.size(); ++i) { - if (index_[i].block > MAX_BLOCKS_CACHED) { + if (index_[i].block > maxBlocks_) { index_[i].block = INVALID_BLOCK; } if (index_[i].block == INVALID_BLOCK) { @@ -482,6 +554,18 @@ 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; + ERROR_LOG(LOADER, "Not enough free space; disabling disk cache"); + return; + } + f_ = File::OpenCFile(path, "wb+"); if (!f_) { ERROR_LOG(LOADER, "Could not create disk cache file"); @@ -499,6 +583,7 @@ void DiskCachingFileLoaderCache::CreateCacheFile(const std::string &path) { header.version = CACHE_VERSION; header.blockSize = blockSize_; header.filesize = filesize_; + header.maxBlocks = maxBlocks_; if (fwrite(&header, sizeof(header), 1, f_) != 1) { fclose(f_); @@ -509,8 +594,8 @@ void DiskCachingFileLoaderCache::CreateCacheFile(const std::string &path) { indexCount_ = (filesize_ + blockSize_ - 1) / blockSize_; index_.resize(indexCount_); - blockIndexLookup_.resize(MAX_BLOCKS_CACHED); - memset(&blockIndexLookup_[0], INVALID_INDEX, MAX_BLOCKS_CACHED * sizeof(blockIndexLookup_[0])); + blockIndexLookup_.resize(maxBlocks_); + memset(&blockIndexLookup_[0], INVALID_INDEX, maxBlocks_ * sizeof(blockIndexLookup_[0])); if (fwrite(&index_[0], sizeof(BlockInfo), indexCount_, f_) != indexCount_) { fclose(f_); @@ -519,3 +604,101 @@ void DiskCachingFileLoaderCache::CreateCacheFile(const std::string &path) { return; } } + +u64 DiskCachingFileLoaderCache::FreeDiskSpace() { + std::string dir = cacheDir_; + if (dir.empty()) { + dir = GetSysDirectory(DIRECTORY_CACHE); + } + + uint64_t result = 0; + if (free_disk_space(dir, result)) { + return result; + } + + // We can't know for sure how much is free, so we have to assume none. + return 0; +} + +u32 DiskCachingFileLoaderCache::DetermineMaxBlocks() { + const s64 freeBytes = FreeDiskSpace(); + // We want to leave them some room for other stuff. + const u64 availBytes = std::max(0LL, freeBytes - SAFETY_FREE_DISK_SPACE); + const u64 freeBlocks = availBytes / (u64)DEFAULT_BLOCK_SIZE; + + const u32 alreadyCachedCount = CountCachedFiles(); + // This is how many more files of free space we will aim for. + const u32 flex = CACHE_SPACE_FLEX > alreadyCachedCount ? CACHE_SPACE_FLEX - alreadyCachedCount : 1; + + const u64 freeBlocksWithFlex = freeBlocks / flex; + if (freeBlocksWithFlex > MAX_BLOCKS_LOWER_BOUND) { + if (freeBlocksWithFlex > MAX_BLOCKS_UPPER_BOUND) { + return MAX_BLOCKS_UPPER_BOUND; + } + // This might be smaller than what's free, but if they try to launch a second game, + // they'll be happier when it can be cached too. + return freeBlocksWithFlex; + } + + // Might be lower than LOWER_BOUND, but that's okay. That means not enough space. + // We abandon the idea of flex since there's not enough space free anyway. + return freeBlocks; +} + +u32 DiskCachingFileLoaderCache::CountCachedFiles() { + std::string dir = cacheDir_; + if (dir.empty()) { + dir = GetSysDirectory(DIRECTORY_CACHE); + } + + std::vector files; + return (u32)getFilesInDir(dir.c_str(), &files, "ppdc:"); +} + +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 f175470a83..d250ab5e2e 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(); @@ -94,34 +96,41 @@ private: u32 AllocateBlock(u32 indexPos); struct BlockInfo; - void ReadBlockData(u8 *dest, BlockInfo &info, size_t offset, size_t size); + bool ReadBlockData(u8 *dest, BlockInfo &info, size_t offset, size_t size); void WriteBlockData(BlockInfo &info, u8 *src); void WriteIndexData(u32 indexPos, BlockInfo &info); 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(); + u32 CountCachedFiles(); + void GarbageCollectCacheFiles(u64 goalBytes); + // File format: // 64 magic // 32 version // 32 blockSize // 64 filesize + // 32 maxBlocks // index[filesize / blockSize] <-- ~500 KB for 4GB // 32 (fileoffset - headersize) / blockSize -> -1=not present // 16 generation? // 16 hits? - // blocks[MAX_BLOCKS] + // blocks[up to maxBlocks] // 8 * blockSize enum { - CACHE_VERSION = 1, + CACHE_VERSION = 2, DEFAULT_BLOCK_SIZE = 65536, MAX_BLOCKS_PER_READ = 16, - // TODO: Dynamic. - MAX_BLOCKS_CACHED = 4096, // 256 MB + MAX_BLOCKS_LOWER_BOUND = 256, // 16 MB + MAX_BLOCKS_UPPER_BOUND = 8192, // 512 MB INVALID_BLOCK = 0xFFFFFFFF, INVALID_INDEX = 0xFFFFFFFF, }; @@ -131,6 +140,7 @@ private: u32 blockSize_; u16 generation_; u16 oldestGeneration_; + u32 maxBlocks_; size_t cacheSize_; size_t indexCount_; recursive_mutex lock_; @@ -140,6 +150,7 @@ private: u32_le version; u32_le blockSize; s64_le filesize; + u32_le maxBlocks; }; struct BlockInfo { diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index 2d52904dcd..329172b4c1 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include "file/free.h" #include "file/zip_read.h" #include "i18n/i18n.h" #include "util/text/utf8.h" @@ -798,35 +799,18 @@ std::vector DirectoryFileSystem::GetDirListing(std::string path) { } u64 DirectoryFileSystem::FreeSpace(const std::string &path) { -#ifdef _WIN32 - const std::wstring w32path = ConvertUTF8ToWString(GetLocalPath(path)); - ULARGE_INTEGER free; - if (GetDiskFreeSpaceExW(w32path.c_str(), &free, nullptr, nullptr)) - return free.QuadPart; -#elif defined(__SYMBIAN32__) - QSystemStorageInfo storageInfo; - return (u64)storageInfo.availableDiskSpace("E"); -#else - std::string localPath = GetLocalPath(path); - struct statvfs diskstat; - int res = statvfs(localPath.c_str(), &diskstat); + uint64_t result = 0; + if (free_disk_space(GetLocalPath(path), result)) { + return result; + } #if HOST_IS_CASE_SENSITIVE std::string fixedCase = path; - if (res != 0 && FixPathCase(basePath, fixedCase, FPC_FILE_MUST_EXIST)) { + if (FixPathCase(basePath, fixedCase, FPC_FILE_MUST_EXIST)) { // May have failed due to case sensitivity, try again. - localPath = GetLocalPath(fixedCase); - res = statvfs(localPath.c_str(), &diskstat); - } -#endif - - if (res == 0) { -#ifndef ANDROID - if (diskstat.f_flag & ST_RDONLY) { - return 0; + if (free_disk_space(GetLocalPath(fixedCase), result)) { + return result; } -#endif - return (u64)diskstat.f_bavail * (u64)diskstat.f_frsize; } #endif diff --git a/Core/HW/SasReverb.cpp b/Core/HW/SasReverb.cpp index 81125a545c..c1d148342c 100644 --- a/Core/HW/SasReverb.cpp +++ b/Core/HW/SasReverb.cpp @@ -228,7 +228,7 @@ void SasReverb::ProcessReverb(int16_t *output, const int16_t *input, size_t inpu // This runs at 22khz. // Very unoptimized, straight from the description. Can probably be reformulated into something way more efficient. // Or we could actually template the whole thing with the parameters as template arguments, as the presets are fixed. - for (int i = 0; i < inputSize; i++) { + for (size_t i = 0; i < inputSize; i++) { // Dividing by two here is an incorrect hack. Some multiplication factor is needed to prevent the reverb from getting too loud, though. int16_t LeftInput = input[i * 2] >> 1; int16_t RightInput = input[i * 2 + 1] >> 1; diff --git a/Core/Loaders.cpp b/Core/Loaders.cpp index e6260e09b9..832fa0171e 100644 --- a/Core/Loaders.cpp +++ b/Core/Loaders.cpp @@ -36,7 +36,7 @@ FileLoader *ConstructFileLoader(const std::string &filename) { if (filename.find("http://") == 0 || filename.find("https://") == 0) - return new CachingFileLoader(new RetryingFileLoader(new HTTPFileLoader(filename))); + return new CachingFileLoader(new DiskCachingFileLoader(new RetryingFileLoader(new HTTPFileLoader(filename)))); return new LocalFileLoader(filename); } diff --git a/GPU/Common/GPUStateUtils.cpp b/GPU/Common/GPUStateUtils.cpp index fb47c103ca..513b3f2ad1 100644 --- a/GPU/Common/GPUStateUtils.cpp +++ b/GPU/Common/GPUStateUtils.cpp @@ -457,6 +457,9 @@ ReplaceBlendType ReplaceBlendWithShader(bool allowShaderBlend, GEBufferFormat bu return REPLACE_BLEND_STANDARD; } } + + // Should never get here. + return REPLACE_BLEND_STANDARD; } LogicOpReplaceType ReplaceLogicOpType() { diff --git a/ext/native/Android.mk b/ext/native/Android.mk index 7248e605b8..74dcec3855 100644 --- a/ext/native/Android.mk +++ b/ext/native/Android.mk @@ -41,6 +41,7 @@ LOCAL_SRC_FILES :=\ file/fd_util.cpp \ file/chunk_file.cpp \ file/file_util.cpp \ + file/free.cpp \ file/path.cpp \ file/ini_file.cpp \ file/zip_read.cpp \ diff --git a/ext/native/file/CMakeLists.txt b/ext/native/file/CMakeLists.txt index 047f14655b..6cb73d143a 100644 --- a/ext/native/file/CMakeLists.txt +++ b/ext/native/file/CMakeLists.txt @@ -1,7 +1,8 @@ set(SRCS chunk_file.cpp zip_read.cpp - file_util.cpp) + file_util.cpp + free.cpp) set(SRCS ${SRCS}) diff --git a/ext/native/file/file_util.cpp b/ext/native/file/file_util.cpp index 8ff360665b..36a9cc8d09 100644 --- a/ext/native/file/file_util.cpp +++ b/ext/native/file/file_util.cpp @@ -269,7 +269,7 @@ size_t getFilesInDir(const char *directory, std::vector *files, const (virtualName[2] == '\0'))) continue; - // Remove dotfiles (should be made optional?) + // Remove dotfiles (optional with flag.) if (!(flags & GETFILES_GETHIDDEN) && virtualName[0] == '.') continue; diff --git a/ext/native/file/free.cpp b/ext/native/file/free.cpp new file mode 100644 index 0000000000..9522fff346 --- /dev/null +++ b/ext/native/file/free.cpp @@ -0,0 +1,56 @@ +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#else +#include +#include +#include +#if defined(ANDROID) +#include +#include +#define statvfs statfs +#elif defined(__SYMBIAN32__) +#include +QTM_USE_NAMESPACE +#else +#include +#endif +#include +#include +#endif + +#include "base/basictypes.h" +#include "util/text/utf8.h" + +bool free_disk_space(const std::string &dir, uint64_t &space) { +#ifdef _WIN32 + const std::wstring w32path = ConvertUTF8ToWString(dir); + ULARGE_INTEGER free; + if (GetDiskFreeSpaceExW(w32path.c_str(), &free, nullptr, nullptr)) { + space = free.QuadPart; + return true; + } +#elif defined(__SYMBIAN32__) + QSystemStorageInfo storageInfo; + space = (uint64_t)storageInfo.availableDiskSpace("E"); + return true; +#else + struct statvfs diskstat; + int res = statvfs(dir.c_str(), &diskstat); + + if (res == 0) { +#ifndef ANDROID + if (diskstat.f_flag & ST_RDONLY) { + space = 0; + return true; + } +#endif + space = (uint64_t)diskstat.f_bavail * (uint64_t)diskstat.f_frsize; + return true; + } +#endif + + // We can't know how much is free. + return false; +} \ No newline at end of file diff --git a/ext/native/file/free.h b/ext/native/file/free.h new file mode 100644 index 0000000000..1c5690737e --- /dev/null +++ b/ext/native/file/free.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include "base/basictypes.h" + +bool free_disk_space(const std::string &dir, uint64_t &space); diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj index ee844615a8..426fb463df 100644 --- a/ext/native/native.vcxproj +++ b/ext/native/native.vcxproj @@ -226,6 +226,7 @@ + @@ -680,6 +681,7 @@ + diff --git a/ext/native/native.vcxproj.filters b/ext/native/native.vcxproj.filters index aac8a59712..87ee09ad91 100644 --- a/ext/native/native.vcxproj.filters +++ b/ext/native/native.vcxproj.filters @@ -302,6 +302,9 @@ input + + file + @@ -736,6 +739,9 @@ base + + file +