Path: blob/master/Core/FileSystems/VirtualDiscFileSystem.cpp
3186 views
// Copyright (c) 2012- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include "ppsspp_config.h"18#include <ctime>1920#include "Common/File/FileUtil.h"21#include "Common/File/DirListing.h"22#include "Common/Serialize/Serializer.h"23#include "Common/Serialize/SerializeFuncs.h"24#include "Common/SysError.h"25#include "Core/FileSystems/VirtualDiscFileSystem.h"26#include "Core/FileSystems/ISOFileSystem.h"27#include "Core/HLE/sceKernel.h"28#include "Core/Reporting.h"29#include "Common/Data/Encoding/Utf8.h"3031#ifdef _WIN3232#include "Common/CommonWindows.h"33#include <sys/stat.h>34#if PPSSPP_PLATFORM(UWP)35#include <fileapifromapp.h>36#endif37#else38#include <dirent.h>39#include <unistd.h>40#include <sys/stat.h>41#include <ctype.h>42#if !PPSSPP_PLATFORM(SWITCH)43#include <dlfcn.h>44#endif45#endif4647const std::string INDEX_FILENAME = ".ppsspp-index.lst";4849VirtualDiscFileSystem::VirtualDiscFileSystem(IHandleAllocator *_hAlloc, const Path &_basePath)50: basePath(_basePath), currentBlockIndex(0) {51hAlloc = _hAlloc;52LoadFileListIndex();53}5455VirtualDiscFileSystem::~VirtualDiscFileSystem() {56for (auto iter = entries.begin(), end = entries.end(); iter != end; ++iter) {57if (iter->second.type != VFILETYPE_ISO) {58iter->second.Close();59}60}61for (auto iter = handlers.begin(), end = handlers.end(); iter != end; ++iter) {62delete iter->second;63}64}6566void VirtualDiscFileSystem::LoadFileListIndex() {67const Path filename = basePath / INDEX_FILENAME;68if (!File::Exists(filename)) {69return;70}7172FILE *f = File::OpenCFile(filename, "r");73if (!f) {74return;75}7677static const int MAX_LINE_SIZE = 2048;78char linebuf[MAX_LINE_SIZE]{};79while (fgets(linebuf, MAX_LINE_SIZE, f)) {80std::string line = linebuf;81// Strip newline from fgets.82if (!line.empty() && line.back() == '\n')83line.resize(line.size() - 1);8485// Ignore any UTF-8 BOM.86if (line.substr(0, 3) == "\xEF\xBB\xBF") {87line = line.substr(3);88}8990if (line.empty() || line[0] == ';') {91continue;92}9394FileListEntry entry = {""};9596// Syntax: HEXPOS filename or HEXPOS filename:handler97size_t filename_pos = line.find(' ');98if (filename_pos == line.npos) {99ERROR_LOG(Log::FileSystem, "Unexpected line in %s: %s", INDEX_FILENAME.c_str(), line.c_str());100continue;101}102103filename_pos++;104// Strip any slash prefix.105while (filename_pos < line.length() && line[filename_pos] == '/') {106filename_pos++;107}108109// Check if there's a handler specified.110size_t handler_pos = line.find(':', filename_pos);111if (handler_pos != line.npos) {112entry.fileName = line.substr(filename_pos, handler_pos - filename_pos);113114std::string handler = line.substr(handler_pos + 1);115size_t trunc = handler.find_last_not_of("\r\n");116if (trunc != handler.npos && trunc != handler.size())117handler.resize(trunc + 1);118119if (handlers.find(handler) == handlers.end())120handlers[handler] = new Handler(handler.c_str(), this);121if (handlers[handler]->IsValid())122entry.handler = handlers[handler];123} else {124entry.fileName = line.substr(filename_pos);125}126size_t trunc = entry.fileName.find_last_not_of("\r\n");127if (trunc != entry.fileName.npos && trunc != entry.fileName.size())128entry.fileName.resize(trunc + 1);129130entry.firstBlock = (u32)strtol(line.c_str(), NULL, 16);131if (entry.handler != NULL && entry.handler->IsValid()) {132HandlerFileHandle temp = entry.handler;133if (temp.Open(basePath.ToString(), entry.fileName, FILEACCESS_READ)) {134entry.totalSize = (u32)temp.Seek(0, FILEMOVE_END);135temp.Close();136} else {137ERROR_LOG(Log::FileSystem, "Unable to open virtual file: %s", entry.fileName.c_str());138}139} else {140entry.totalSize = File::GetFileSize(GetLocalPath(entry.fileName));141}142143// Try to keep currentBlockIndex sane, in case there are other files.144u32 nextBlock = entry.firstBlock + (entry.totalSize + 2047) / 2048;145if (nextBlock > currentBlockIndex) {146currentBlockIndex = nextBlock;147}148149fileList.push_back(entry);150}151152fclose(f);153}154155void VirtualDiscFileSystem::DoState(PointerWrap &p)156{157auto s = p.Section("VirtualDiscFileSystem", 1, 2);158if (!s)159return;160161int fileListSize = (int)fileList.size();162int entryCount = (int)entries.size();163164Do(p, fileListSize);165Do(p, entryCount);166Do(p, currentBlockIndex);167168FileListEntry dummy = {""};169fileList.resize(fileListSize, dummy);170171for (int i = 0; i < fileListSize; i++)172{173Do(p, fileList[i].fileName);174Do(p, fileList[i].firstBlock);175Do(p, fileList[i].totalSize);176}177178if (p.mode == p.MODE_READ)179{180entries.clear();181182for (int i = 0; i < entryCount; i++)183{184u32 fd = 0;185OpenFileEntry of(Flags());186187Do(p, fd);188Do(p, of.fileIndex);189Do(p, of.type);190Do(p, of.curOffset);191Do(p, of.startOffset);192Do(p, of.size);193194// open file195if (of.type != VFILETYPE_ISO) {196if (fileList[of.fileIndex].handler != NULL) {197of.handler = fileList[of.fileIndex].handler;198}199200bool success = of.Open(basePath, fileList[of.fileIndex].fileName, FILEACCESS_READ);201if (!success) {202ERROR_LOG(Log::FileSystem, "Failed to create file handle for %s.", fileList[of.fileIndex].fileName.c_str());203} else {204if (of.type == VFILETYPE_LBN) {205of.Seek(of.curOffset + of.startOffset, FILEMOVE_BEGIN);206} else {207of.Seek(of.curOffset, FILEMOVE_BEGIN);208}209}210}211212// TODO: I think we only need to write to the map on load?213entries[fd] = of;214}215} else {216for (EntryMap::iterator it = entries.begin(), end = entries.end(); it != end; ++it)217{218OpenFileEntry &of = it->second;219220Do(p, it->first);221Do(p, of.fileIndex);222Do(p, of.type);223Do(p, of.curOffset);224Do(p, of.startOffset);225Do(p, of.size);226}227}228229if (s >= 2) {230Do(p, lastReadBlock_);231} else {232lastReadBlock_ = 0;233}234235// We don't savestate handlers (loaded on fs load), but if they change, it may not load properly.236}237238Path VirtualDiscFileSystem::GetLocalPath(std::string localpath) const {239if (localpath.empty())240return basePath;241242if (localpath[0] == '/')243localpath.erase(0,1);244//Convert slashes245#ifdef _WIN32246for (size_t i = 0; i < localpath.size(); i++) {247if (localpath[i] == '/')248localpath[i] = '\\';249}250#endif251return basePath / localpath;252}253254int VirtualDiscFileSystem::getFileListIndex(std::string &fileName)255{256std::string normalized;257if (fileName.length() >= 1 && fileName[0] == '/') {258normalized = fileName.substr(1);259} else {260normalized = fileName;261}262263for (size_t i = 0; i < fileList.size(); i++)264{265if (fileList[i].fileName == normalized)266return (int)i;267}268269// unknown file - add it270Path fullName = GetLocalPath(fileName);271if (! File::Exists(fullName)) {272#if HOST_IS_CASE_SENSITIVE273if (! FixPathCase(basePath, fileName, FPC_FILE_MUST_EXIST))274return -1;275fullName = GetLocalPath(fileName);276277if (! File::Exists(fullName))278return -1;279#else280return -1;281#endif282}283284if (File::IsDirectory(fullName))285return -1;286287FileListEntry entry = {""};288entry.fileName = normalized;289entry.totalSize = File::GetFileSize(fullName);290entry.firstBlock = currentBlockIndex;291currentBlockIndex += (entry.totalSize+2047)/2048;292293fileList.push_back(entry);294295return (int)fileList.size()-1;296}297298int VirtualDiscFileSystem::getFileListIndex(u32 accessBlock, u32 accessSize, bool blockMode) const {299for (size_t i = 0; i < fileList.size(); i++) {300if (fileList[i].firstBlock <= accessBlock) {301u32 sectorOffset = (accessBlock-fileList[i].firstBlock)*2048;302u32 totalFileSize = blockMode ? (fileList[i].totalSize+2047) & ~2047 : fileList[i].totalSize;303304u32 endOffset = sectorOffset+accessSize;305if (endOffset <= totalFileSize) {306return (int)i;307}308}309}310311return -1;312}313314int VirtualDiscFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename)315{316OpenFileEntry entry(Flags());317entry.curOffset = 0;318entry.size = 0;319entry.startOffset = 0;320321if (filename.empty())322{323entry.type = VFILETYPE_ISO;324entry.fileIndex = -1;325326u32 newHandle = hAlloc->GetNewHandle();327entries[newHandle] = entry;328329return newHandle;330}331332if (filename.compare(0, 8, "/sce_lbn") == 0)333{334u32 sectorStart = 0xFFFFFFFF, readSize = 0xFFFFFFFF;335parseLBN(filename, §orStart, &readSize);336337entry.type = VFILETYPE_LBN;338entry.size = readSize;339340int fileIndex = getFileListIndex(sectorStart,readSize);341if (fileIndex == -1)342{343ERROR_LOG(Log::FileSystem, "VirtualDiscFileSystem: sce_lbn used without calling fileinfo.");344return 0;345}346entry.fileIndex = (u32)fileIndex;347348entry.startOffset = (sectorStart-fileList[entry.fileIndex].firstBlock)*2048;349350// now we just need an actual file handle351if (fileList[entry.fileIndex].handler != NULL) {352entry.handler = fileList[entry.fileIndex].handler;353}354bool success = entry.Open(basePath, fileList[entry.fileIndex].fileName, FILEACCESS_READ);355356if (!success) {357if (!(access & FILEACCESS_PPSSPP_QUIET)) {358#ifdef _WIN32359ERROR_LOG(Log::FileSystem, "VirtualDiscFileSystem::OpenFile: FAILED, %i", (int)GetLastError());360#else361ERROR_LOG(Log::FileSystem, "VirtualDiscFileSystem::OpenFile: FAILED");362#endif363}364return 0;365}366367// seek to start368entry.Seek(entry.startOffset, FILEMOVE_BEGIN);369370u32 newHandle = hAlloc->GetNewHandle();371entries[newHandle] = entry;372373return newHandle;374}375376entry.type = VFILETYPE_NORMAL;377entry.fileIndex = getFileListIndex(filename);378379if (entry.fileIndex != (u32)-1 && fileList[entry.fileIndex].handler != NULL) {380entry.handler = fileList[entry.fileIndex].handler;381}382bool success = entry.Open(basePath, filename, (FileAccess)(access & FILEACCESS_PSP_FLAGS));383384if (!success) {385if (!(access & FILEACCESS_PPSSPP_QUIET)) {386#ifdef _WIN32387ERROR_LOG(Log::FileSystem, "VirtualDiscFileSystem::OpenFile: FAILED, %i - access = %i", (int)GetLastError(), (int)access);388#else389ERROR_LOG(Log::FileSystem, "VirtualDiscFileSystem::OpenFile: FAILED, access = %i", (int)access);390#endif391}392return SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND;393} else {394u32 newHandle = hAlloc->GetNewHandle();395entries[newHandle] = entry;396397return newHandle;398}399}400401size_t VirtualDiscFileSystem::SeekFile(u32 handle, s32 position, FileMove type) {402EntryMap::iterator iter = entries.find(handle);403if (iter != entries.end()) {404auto &entry = iter->second;405switch (entry.type)406{407case VFILETYPE_NORMAL:408{409return entry.Seek(position, type);410}411case VFILETYPE_LBN:412{413switch (type)414{415case FILEMOVE_BEGIN: entry.curOffset = position; break;416case FILEMOVE_CURRENT: entry.curOffset += position; break;417case FILEMOVE_END: entry.curOffset = entry.size + position; break;418}419420u32 off = entry.startOffset + entry.curOffset;421entry.Seek(off, FILEMOVE_BEGIN);422return entry.curOffset;423}424case VFILETYPE_ISO:425{426switch (type)427{428case FILEMOVE_BEGIN: entry.curOffset = position; break;429case FILEMOVE_CURRENT: entry.curOffset += position; break;430case FILEMOVE_END: entry.curOffset = currentBlockIndex + position; break;431}432433return entry.curOffset;434}435}436return 0;437} else {438//This shouldn't happen...439ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot seek in file that hasn't been opened: %08x", handle);440return 0;441}442}443444size_t VirtualDiscFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) {445int ignored;446return ReadFile(handle, pointer, size, ignored);447}448449size_t VirtualDiscFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) {450EntryMap::iterator iter = entries.find(handle);451if (iter != entries.end()) {452if (size < 0) {453ERROR_LOG_REPORT(Log::FileSystem, "Invalid read for %lld bytes from virtual umd", size);454return 0;455}456457// it's the whole iso... it could reference any of the files on the disc.458// For now let's just open and close the files on demand. Can certainly be done459// better though460if (iter->second.type == VFILETYPE_ISO)461{462int fileIndex = getFileListIndex(iter->second.curOffset,size*2048,true);463if (fileIndex == -1)464{465ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Reading from unknown address in %08x at %08llx", handle, iter->second.curOffset);466return 0;467}468469OpenFileEntry temp(Flags());470if (fileList[fileIndex].handler != NULL) {471temp.handler = fileList[fileIndex].handler;472}473bool success = temp.Open(basePath, fileList[fileIndex].fileName, FILEACCESS_READ);474475if (!success)476{477ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Error opening file %s", fileList[fileIndex].fileName.c_str());478return 0;479}480481u32 startOffset = (iter->second.curOffset-fileList[fileIndex].firstBlock)*2048;482size_t bytesRead;483484temp.Seek(startOffset, FILEMOVE_BEGIN);485486u32 remainingSize = fileList[fileIndex].totalSize-startOffset;487if (remainingSize < size * 2048)488{489// the file doesn't fill the whole last sector490// read what's there and zero fill the rest like on a real disc491bytesRead = temp.Read(pointer, remainingSize);492memset(&pointer[bytesRead], 0, size * 2048 - bytesRead);493} else {494bytesRead = temp.Read(pointer, size * 2048);495}496497temp.Close();498499iter->second.curOffset += size;500// TODO: This probably isn't enough...501if (abs((int)lastReadBlock_ - (int)iter->second.curOffset) > 100) {502// This is an estimate, sometimes it takes 1+ seconds, but it definitely takes time.503usec = 100000;504}505lastReadBlock_ = iter->second.curOffset;506return size;507}508509if (iter->second.type == VFILETYPE_LBN && iter->second.curOffset + size > iter->second.size) {510// Clamp to the remaining size, but read what we can.511const s64 newSize = iter->second.size - iter->second.curOffset;512WARN_LOG(Log::FileSystem, "VirtualDiscFileSystem: Reading beyond end of file, clamping size %lld to %lld", size, newSize);513size = newSize;514}515516size_t bytesRead = iter->second.Read(pointer, size);517iter->second.curOffset += bytesRead;518return bytesRead;519} else {520//This shouldn't happen...521ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot read file that hasn't been opened: %08x", handle);522return 0;523}524}525526void VirtualDiscFileSystem::CloseFile(u32 handle) {527EntryMap::iterator iter = entries.find(handle);528if (iter != entries.end()) {529hAlloc->FreeHandle(handle);530iter->second.Close();531entries.erase(iter);532} else {533//This shouldn't happen...534ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot close file that hasn't been opened: %08x", handle);535}536}537538bool VirtualDiscFileSystem::OwnsHandle(u32 handle) {539EntryMap::iterator iter = entries.find(handle);540return (iter != entries.end());541}542543int VirtualDiscFileSystem::Ioctl(u32 handle, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen, int &usec) {544// TODO: How to support these?545return SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED;546}547548PSPDevType VirtualDiscFileSystem::DevType(u32 handle) {549EntryMap::iterator iter = entries.find(handle);550if (iter == entries.end())551return PSPDevType::FILE;552PSPDevType type = iter->second.type == VFILETYPE_ISO ? PSPDevType::BLOCK : PSPDevType::FILE;553if (iter->second.type == VFILETYPE_LBN)554type |= PSPDevType::EMU_LBN;555return type;556}557558PSPFileInfo VirtualDiscFileSystem::GetFileInfo(std::string filename) {559PSPFileInfo x;560x.name = filename;561x.access = FILEACCESS_READ;562563if (filename.compare(0,8,"/sce_lbn") == 0) {564u32 sectorStart = 0xFFFFFFFF, readSize = 0xFFFFFFFF;565parseLBN(filename, §orStart, &readSize);566567PSPFileInfo fileInfo;568fileInfo.name = filename;569fileInfo.exists = true;570fileInfo.type = FILETYPE_NORMAL;571fileInfo.size = readSize;572fileInfo.access = 0444;573fileInfo.startSector = sectorStart;574fileInfo.isOnSectorSystem = true;575fileInfo.numSectors = (readSize + 2047) / 2048;576return fileInfo;577}578579int fileIndex = getFileListIndex(filename);580if (fileIndex != -1 && fileList[fileIndex].handler != NULL) {581x.type = FILETYPE_NORMAL;582x.isOnSectorSystem = true;583x.startSector = fileList[fileIndex].firstBlock;584x.access = 0555;585586HandlerFileHandle temp = fileList[fileIndex].handler;587if (temp.Open(basePath.ToString(), filename, FILEACCESS_READ)) {588x.exists = true;589x.size = temp.Seek(0, FILEMOVE_END);590temp.Close();591}592593// TODO: Probably should include dates or something...594return x;595}596597Path fullName = GetLocalPath(filename);598if (!File::Exists(fullName)) {599#if HOST_IS_CASE_SENSITIVE600if (! FixPathCase(basePath, filename, FPC_FILE_MUST_EXIST))601return x;602fullName = GetLocalPath(filename);603604if (! File::Exists(fullName))605return x;606#else607return x;608#endif609}610611x.type = File::IsDirectory(fullName) ? FILETYPE_DIRECTORY : FILETYPE_NORMAL;612x.exists = true;613x.access = 0555;614if (fileIndex != -1) {615x.isOnSectorSystem = true;616x.startSector = fileList[fileIndex].firstBlock;617}618619if (x.type != FILETYPE_DIRECTORY) {620File::FileInfo details;621if (!File::GetFileInfo(fullName, &details)) {622ERROR_LOG(Log::FileSystem, "DirectoryFileSystem::GetFileInfo: GetFileInfo failed: %s", fullName.c_str());623x.size = 0;624x.access = 0;625} else {626x.size = details.size;627time_t atime = details.atime;628time_t ctime = details.ctime;629time_t mtime = details.mtime;630631localtime_r((time_t*)&atime, &x.atime);632localtime_r((time_t*)&ctime, &x.ctime);633localtime_r((time_t*)&mtime, &x.mtime);634}635636// x.startSector was set above in "if (fileIndex != -1)".637x.numSectors = (x.size+2047)/2048;638}639640return x;641}642643PSPFileInfo VirtualDiscFileSystem::GetFileInfoByHandle(u32 handle) {644WARN_LOG(Log::FileSystem, "GetFileInfoByHandle not yet implemented for VirtualDiscFileSystem");645return PSPFileInfo();646}647648#ifdef _WIN32649#define FILETIME_FROM_UNIX_EPOCH_US 11644473600000000ULL650651static void tmFromFiletime(tm &dest, const FILETIME &src)652{653u64 from_1601_us = (((u64) src.dwHighDateTime << 32ULL) + (u64) src.dwLowDateTime) / 10ULL;654u64 from_1970_us = from_1601_us - FILETIME_FROM_UNIX_EPOCH_US;655656time_t t = (time_t) (from_1970_us / 1000000UL);657localtime_s(&dest, &t);658}659#endif660661std::vector<PSPFileInfo> VirtualDiscFileSystem::GetDirListing(const std::string &path, bool *exists) {662std::vector<PSPFileInfo> myVector;663664// TODO(scoped): Switch this over to GetFilesInDir!665666#ifdef _WIN32667WIN32_FIND_DATA findData;668HANDLE hFind;669670// TODO: Handler files that are virtual might not be listed.671672std::wstring w32path = GetLocalPath(path).ToWString() + L"\\*.*";673674#if PPSSPP_PLATFORM(UWP)675hFind = FindFirstFileExFromAppW(w32path.c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, 0);676#else677hFind = FindFirstFileEx(w32path.c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, 0);678#endif679if (hFind == INVALID_HANDLE_VALUE) {680if (exists)681*exists = false;682return myVector; //the empty list683}684685if (exists)686*exists = true;687688for (BOOL retval = 1; retval; retval = FindNextFile(hFind, &findData)) {689if (!wcscmp(findData.cFileName, L"..") || !wcscmp(findData.cFileName, L".")) {690continue;691}692693PSPFileInfo entry;694if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {695entry.type = FILETYPE_DIRECTORY;696} else {697entry.type = FILETYPE_NORMAL;698}699700entry.access = 0555;701entry.exists = true;702entry.size = findData.nFileSizeLow | ((u64)findData.nFileSizeHigh<<32);703entry.name = ConvertWStringToUTF8(findData.cFileName);704tmFromFiletime(entry.atime, findData.ftLastAccessTime);705tmFromFiletime(entry.ctime, findData.ftCreationTime);706tmFromFiletime(entry.mtime, findData.ftLastWriteTime);707entry.isOnSectorSystem = true;708709std::string fullRelativePath = path + "/" + entry.name;710int fileIndex = getFileListIndex(fullRelativePath);711if (fileIndex != -1)712entry.startSector = fileList[fileIndex].firstBlock;713myVector.push_back(entry);714}715FindClose(hFind);716#else717dirent *dirp;718Path localPath = GetLocalPath(path);719DIR *dp = opendir(localPath.c_str());720721#if HOST_IS_CASE_SENSITIVE722std::string fixedPath = path;723if(dp == NULL && FixPathCase(basePath, fixedPath, FPC_FILE_MUST_EXIST)) {724// May have failed due to case sensitivity, try again725localPath = GetLocalPath(fixedPath);726dp = opendir(localPath.c_str());727}728#endif729730if (dp == NULL) {731ERROR_LOG(Log::FileSystem,"Error opening directory %s\n", path.c_str());732if (exists)733*exists = false;734return myVector;735}736737if (exists)738*exists = true;739740while ((dirp = readdir(dp)) != NULL) {741if (!strcmp(dirp->d_name, "..") || !strcmp(dirp->d_name, ".")) {742continue;743}744745PSPFileInfo entry;746struct stat s;747std::string fullName = (localPath / std::string(dirp->d_name)).ToString();748stat(fullName.c_str(), &s);749if (S_ISDIR(s.st_mode))750entry.type = FILETYPE_DIRECTORY;751else752entry.type = FILETYPE_NORMAL;753entry.access = 0555;754entry.exists = true;755entry.name = dirp->d_name;756entry.size = s.st_size;757localtime_r((time_t*)&s.st_atime,&entry.atime);758localtime_r((time_t*)&s.st_ctime,&entry.ctime);759localtime_r((time_t*)&s.st_mtime,&entry.mtime);760entry.isOnSectorSystem = true;761762std::string fullRelativePath = path + "/" + entry.name;763int fileIndex = getFileListIndex(fullRelativePath);764if (fileIndex != -1)765entry.startSector = fileList[fileIndex].firstBlock;766myVector.push_back(entry);767}768closedir(dp);769#endif770return myVector;771}772773size_t VirtualDiscFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size)774{775ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot write to file on virtual disc");776return 0;777}778779size_t VirtualDiscFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec)780{781ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot write to file on virtual disc");782return 0;783}784785bool VirtualDiscFileSystem::MkDir(const std::string &dirname)786{787ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot create directory on virtual disc");788return false;789}790791bool VirtualDiscFileSystem::RmDir(const std::string &dirname)792{793ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot remove directory on virtual disc");794return false;795}796797int VirtualDiscFileSystem::RenameFile(const std::string &from, const std::string &to)798{799ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot rename file on virtual disc");800return -1;801}802803bool VirtualDiscFileSystem::RemoveFile(const std::string &filename)804{805ERROR_LOG(Log::FileSystem,"VirtualDiscFileSystem: Cannot remove file on virtual disc");806return false;807}808809void VirtualDiscFileSystem::HandlerLogger(void *arg, HandlerHandle handle, LogLevel level, const char *msg) {810VirtualDiscFileSystem *sys = static_cast<VirtualDiscFileSystem *>(arg);811812// TODO: Probably could do this smarter / use a lookup.813const char *filename = NULL;814for (auto it = sys->entries.begin(), end = sys->entries.end(); it != end; ++it) {815if (it->second.fileIndex != (u32)-1 && it->second.handler.handle == handle) {816filename = sys->fileList[it->second.fileIndex].fileName.c_str();817break;818}819}820821if (filename != NULL) {822GENERIC_LOG(Log::FileSystem, level, "%s: %s", filename, msg);823} else {824GENERIC_LOG(Log::FileSystem, level, "%s", msg);825}826}827828VirtualDiscFileSystem::Handler::Handler(const char *filename, VirtualDiscFileSystem *const sys)829: sys_(sys) {830#if !PPSSPP_PLATFORM(SWITCH)831#ifdef _WIN32832#if PPSSPP_PLATFORM(UWP)833#define dlopen(name, ignore) (void *)LoadPackagedLibrary(ConvertUTF8ToWString(name).c_str(), 0)834#define dlsym(mod, name) GetProcAddress((HMODULE)mod, name)835#define dlclose(mod) FreeLibrary((HMODULE)mod)836#else837#define dlopen(name, ignore) (void *)LoadLibrary(ConvertUTF8ToWString(name).c_str())838#define dlsym(mod, name) GetProcAddress((HMODULE)mod, name)839#define dlclose(mod) FreeLibrary((HMODULE)mod)840#endif841#endif842843library = dlopen(filename, RTLD_LOCAL | RTLD_NOW);844if (library != NULL) {845Init = (InitFunc)dlsym(library, "Init");846Shutdown = (ShutdownFunc)dlsym(library, "Shutdown");847Open = (OpenFunc)dlsym(library, "Open");848Seek = (SeekFunc)dlsym(library, "Seek");849Read = (ReadFunc)dlsym(library, "Read");850Close = (CloseFunc)dlsym(library, "Close");851852VersionFunc Version = (VersionFunc)dlsym(library, "Version");853if (Version && Version() >= 2) {854ShutdownV2 = (ShutdownV2Func)Shutdown;855}856857if (!Init || !Shutdown || !Open || !Seek || !Read || !Close) {858ERROR_LOG(Log::FileSystem, "Unable to find all handler functions: %s", filename);859dlclose(library);860library = NULL;861} else if (!Init(&HandlerLogger, sys)) {862ERROR_LOG(Log::FileSystem, "Unable to initialize handler: %s", filename);863dlclose(library);864library = NULL;865}866} else {867ERROR_LOG(Log::FileSystem, "Unable to load handler '%s': %s", filename, GetLastErrorMsg().c_str());868}869#ifdef _WIN32870#undef dlopen871#undef dlsym872#undef dlclose873#endif874#endif875}876877VirtualDiscFileSystem::Handler::~Handler() {878if (library != NULL) {879if (ShutdownV2)880ShutdownV2(sys_);881else882Shutdown();883884#if !PPSSPP_PLATFORM(UWP) && !PPSSPP_PLATFORM(SWITCH)885#ifdef _WIN32886FreeLibrary((HMODULE)library);887#else888dlclose(library);889#endif890#endif891}892}893894895896