diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index c35ed2f302..9a4ffca5ee 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -18,6 +18,7 @@ #include "ChunkFile.h" #include "FileUtil.h" #include "DirectoryFileSystem.h" +#include "ISOFileSystem.h" #include "Core/HLE/sceKernel.h" #include "file/zip_read.h" @@ -133,7 +134,17 @@ bool DirectoryFileSystem::FixPathCase(std::string &path, FixPathCaseBehavior beh #endif -DirectoryFileSystem::DirectoryFileSystem(IHandleAllocator *_hAlloc, std::string _basePath) : basePath(_basePath) { +DirectoryFileSystem::DirectoryFileSystem(IHandleAllocator *_hAlloc, std::string _basePath, bool _virtualDisc) + : basePath(_basePath),virtualDisc(_virtualDisc),currentBlockIndex(0) { + +#ifdef _WIN32 + if (basePath.size() > 0 && basePath[basePath.size()-1] != '\\') + basePath = basePath + "\\"; +#else + if (basePath.size() > 0 && basePath[basePath.size()-1] != '/') + basePath = basePath + "/"; +#endif + File::CreateFullPath(basePath); hAlloc = _hAlloc; } @@ -288,6 +299,80 @@ bool DirectoryFileSystem::RemoveFile(const std::string &filename) { } u32 DirectoryFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename) { + + if (filename.compare(0,8,"/sce_lbn") == 0) + { + u32 sectorStart = 0xFFFFFFFF, readSize = 0xFFFFFFFF; + parseLBN(filename, §orStart, &readSize); + + OpenFileEntry entry; + entry.lbnFile = true; + entry.size = readSize; + entry.curOffset = 0; + + std::string actualFileName = ""; + for (int i = 0; i < fileBlocks.size(); i++) + { + if (fileBlocks[i].firstBlock <= sectorStart) + { + u32 sectorOffset = (sectorStart-fileBlocks[i].firstBlock)*2048; + u32 endOffset = sectorOffset+readSize; + if (endOffset <= fileBlocks[i].totalSize) + { + entry.startOffset = sectorOffset; + actualFileName = fileBlocks[i].fileName; + break; + } + } + } + + // no file info has been gathered? shouldn't happen + if (actualFileName == "") + { + ERROR_LOG(FILESYS, "sce_lbn used without calling fileinfo."); + return 0; + } + +#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(actualFileName, 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 + + // now we just need an actual file handle + std::string fullName = GetLocalPath(actualFileName); + +#ifdef _WIN32 + entry.hFile = CreateFile(fullName.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + bool success = entry.hFile != INVALID_HANDLE_VALUE; +#else + entry.hFile = fopen(fullName.c_str(), "rb"); + bool success = entry.hFile != 0; +#endif + + if (!success) + { + ERROR_LOG(HLE, "DiscDirectoryFileSystem::OpenFile: FAILED, %i", GetLastError()); + return 0; + } + + // seek to start +#ifdef _WIN32 + DWORD newPos = SetFilePointer(entry.hFile, (LONG)entry.startOffset, 0, FILE_BEGIN); +#else + fseek(entry.hFile, entry.startOffset, SEEK_SET); +#endif + + u32 newHandle = hAlloc->GetNewHandle(); + entries[newHandle] = entry; + + return newHandle; + } + #if HOST_IS_CASE_SENSITIVE if (access & (FILEACCESS_APPEND|FILEACCESS_CREATE|FILEACCESS_WRITE)) { @@ -303,6 +388,7 @@ u32 DirectoryFileSystem::OpenFile(std::string filename, FileAccess access, const INFO_LOG(HLE,"Actually opening %s (%s)", fullNameC, filename.c_str()); OpenFileEntry entry; + entry.lbnFile = false; //TODO: tests, should append seek to end of file? seeking in a file opened for append? #ifdef _WIN32 @@ -430,6 +516,11 @@ size_t DirectoryFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) { #else bytesRead = fread(pointer, 1, size, iter->second.hFile); #endif + if (iter->second.lbnFile) + { + iter->second.curOffset += bytesRead; + } + return bytesRead; } else { //This shouldn't happen... @@ -459,6 +550,23 @@ size_t DirectoryFileSystem::WriteFile(u32 handle, const u8 *pointer, s64 size) { size_t DirectoryFileSystem::SeekFile(u32 handle, s32 position, FileMove type) { EntryMap::iterator iter = entries.find(handle); if (iter != entries.end()) { + if (iter->second.lbnFile) + { + switch (type) { + case FILEMOVE_BEGIN: iter->second.curOffset = position; break; + case FILEMOVE_CURRENT: iter->second.curOffset += position; break; + case FILEMOVE_END: iter->second.curOffset = iter->second.size-position; break; + } + +#ifdef _WIN32 + u32 off = iter->second.startOffset+iter->second.curOffset; + DWORD newPos = SetFilePointer((*iter).second.hFile, (LONG)off, 0, FILE_BEGIN); +#else + fseek(iter->second.hFile, iter->second.curOffset+iter->second.startOffset, SEEK_SET); +#endif + return iter->second.curOffset; + } + #ifdef _WIN32 DWORD moveMethod = 0; switch (type) { @@ -485,6 +593,19 @@ size_t DirectoryFileSystem::SeekFile(u32 handle, s32 position, FileMove type) { } } +u32 DirectoryFileSystem::GetFileLbn(std::string& fileName) +{ + if (!virtualDisc) return -1; + + for (int i = 0; i < fileBlocks.size(); i++) + { + if (fileBlocks[i].fileName == fileName) + return fileBlocks[i].firstBlock; + } + + return -1; +} + PSPFileInfo DirectoryFileSystem::GetFileInfo(std::string filename) { PSPFileInfo x; x.name = filename; @@ -517,6 +638,22 @@ PSPFileInfo DirectoryFileSystem::GetFileInfo(std::string filename) { #else x.size = s.st_size; #endif + if (virtualDisc) + { + x.startSector = GetFileLbn(filename); + x.numSectors = (x.size+2047)/2048; + if (x.startSector == -1) + { + x.startSector = currentBlockIndex; + currentBlockIndex += x.numSectors; + + LbnMapEntry entry; + entry.fileName = filename; + entry.firstBlock = x.startSector; + entry.totalSize = x.size; + fileBlocks.push_back(entry); + } + } x.access = s.st_mode & 0x1FF; localtime_r((time_t*)&s.st_atime,&x.atime); localtime_r((time_t*)&s.st_ctime,&x.ctime); diff --git a/Core/FileSystems/DirectoryFileSystem.h b/Core/FileSystems/DirectoryFileSystem.h index c0dd2ae17e..08032ec7cc 100644 --- a/Core/FileSystems/DirectoryFileSystem.h +++ b/Core/FileSystems/DirectoryFileSystem.h @@ -49,7 +49,7 @@ typedef void * HANDLE; class DirectoryFileSystem : public IFileSystem { public: - DirectoryFileSystem(IHandleAllocator *_hAlloc, std::string _basePath); + DirectoryFileSystem(IHandleAllocator *_hAlloc, std::string _basePath, bool _virtualDisc = false); ~DirectoryFileSystem(); void DoState(PointerWrap &p); @@ -69,18 +69,34 @@ public: bool GetHostPath(const std::string &inpath, std::string &outpath); private: + u32 GetFileLbn(std::string& fileName); + struct OpenFileEntry { #ifdef _WIN32 HANDLE hFile; #else FILE *hFile; #endif + bool lbnFile; + u32 startOffset; // only used by lbn files + u32 curOffset; // only used by lbn files; + u32 size; // only used by lbn files }; typedef std::map EntryMap; EntryMap entries; std::string basePath; IHandleAllocator *hAlloc; + bool virtualDisc; + + typedef struct { + std::string fileName; + u32 firstBlock; + u32 totalSize; + } LbnMapEntry; + + std::vector fileBlocks; + u32 currentBlockIndex; // In case of Windows: Translate slashes, etc. std::string GetLocalPath(std::string localpath); diff --git a/Core/FileSystems/ISOFileSystem.cpp b/Core/FileSystems/ISOFileSystem.cpp index 1f32cc5f1e..2d3d8ea94b 100644 --- a/Core/FileSystems/ISOFileSystem.cpp +++ b/Core/FileSystems/ISOFileSystem.cpp @@ -26,7 +26,7 @@ const int sectorSize = 2048; -static bool parseLBN(std::string filename, u32 *sectorStart, u32 *readSize) +bool parseLBN(std::string filename, u32 *sectorStart, u32 *readSize) { // The format of this is: "/sce_lbn" "0x"? HEX* ANY* "_size" "0x"? HEX* ANY* // That means that "/sce_lbn/_size1/" is perfectly valid. diff --git a/Core/FileSystems/ISOFileSystem.h b/Core/FileSystems/ISOFileSystem.h index a53fe454b4..a7aca0ca8a 100644 --- a/Core/FileSystems/ISOFileSystem.h +++ b/Core/FileSystems/ISOFileSystem.h @@ -24,6 +24,7 @@ #include "BlockDevices.h" +bool parseLBN(std::string filename, u32 *sectorStart, u32 *readSize); class ISOFileSystem : public IFileSystem { diff --git a/Core/Loaders.cpp b/Core/Loaders.cpp index 59c32f8437..ec32f612d4 100644 --- a/Core/Loaders.cpp +++ b/Core/Loaders.cpp @@ -57,7 +57,14 @@ EmuFileType Identify_File(std::string &filename) if (ebootInfo.exists) { return FILETYPE_PSP_PBP_DIRECTORY; } - } + } + + // check if it's a disc directory + if (getFileInfo((filename + "/PSP_GAME").c_str(), &ebootInfo)) { + if (ebootInfo.exists) { + return FILETYPE_PSP_DISC_DIRECTORY; + } + } } FILE *f = fopen(filename.c_str(), "rb"); @@ -157,6 +164,7 @@ bool LoadFile(std::string &filename, std::string *error_string) { case FILETYPE_PSP_ISO: case FILETYPE_PSP_ISO_NP: + case FILETYPE_PSP_DISC_DIRECTORY: // behaves the same as the mounting is already done by now pspFileSystem.SetStartingDirectory("disc0:/PSP_GAME/USRDIR"); return Load_PSP_ISO(filename.c_str(), error_string); diff --git a/Core/Loaders.h b/Core/Loaders.h index 8693f88eb2..7e16509693 100644 --- a/Core/Loaders.h +++ b/Core/Loaders.h @@ -28,6 +28,8 @@ enum EmuFileType FILETYPE_PSP_ISO, FILETYPE_PSP_ISO_NP, + FILETYPE_PSP_DISC_DIRECTORY, + FILETYPE_UNKNOWN_BIN, FILETYPE_UNKNOWN_ELF, diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index 6afe7c79e6..b010d30b12 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -27,6 +27,7 @@ #include "MIPS/MIPSAnalyst.h" #include "MIPS/MIPSCodeUtils.h" +#include "file/file_util.h" #include "StringUtils.h" #include "Host.h" @@ -44,11 +45,24 @@ // to determine if the emulator should enable extra memory and // double-sized texture coordinates. void InitMemoryForGameISO(std::string fileToStart) { - auto bd = constructBlockDevice(fileToStart.c_str()); - // Can't init anything without a block device... - if (!bd) - return; - ISOFileSystem *umd2 = new ISOFileSystem(&pspFileSystem, bd); + IFileSystem* umd2; + + // check if it's a disc directory + FileInfo info; + if (!getFileInfo(fileToStart.c_str(), &info)) return; + + if (info.isDirectory) + { + umd2 = new DirectoryFileSystem(&pspFileSystem, fileToStart,true); + } + else + { + auto bd = constructBlockDevice(fileToStart.c_str()); + // Can't init anything without a block device... + if (!bd) + return; + umd2 = new ISOFileSystem(&pspFileSystem, bd); + } // Parse PARAM.SFO diff --git a/Core/System.cpp b/Core/System.cpp index 7a6c48ae92..c9b66bed73 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -83,7 +83,7 @@ bool PSP_Init(const CoreParameter &coreParam, std::string *error_string) std::string filename = coreParam.fileToStart; EmuFileType type = Identify_File(filename); - if(type == FILETYPE_PSP_ISO || type == FILETYPE_PSP_ISO_NP) + if(type == FILETYPE_PSP_ISO || type == FILETYPE_PSP_ISO_NP || type == FILETYPE_PSP_DISC_DIRECTORY) InitMemoryForGameISO(filename); Memory::Init(); diff --git a/UI/MainScreen.cpp b/UI/MainScreen.cpp index 3f32666d2b..169c7c00ee 100644 --- a/UI/MainScreen.cpp +++ b/UI/MainScreen.cpp @@ -38,7 +38,7 @@ #ifdef _WIN32 namespace MainWindow { - void BrowseAndBoot(std::string defaultPath); + void BrowseAndBoot(std::string defaultPath, bool browseDirectory = false); } #endif diff --git a/UI/MenuScreens.cpp b/UI/MenuScreens.cpp index 378a296c46..f25ce9414a 100644 --- a/UI/MenuScreens.cpp +++ b/UI/MenuScreens.cpp @@ -74,7 +74,7 @@ #ifdef _WIN32 namespace MainWindow { extern HWND hwndMain; - void BrowseAndBoot(std::string defaultPath); + void BrowseAndBoot(std::string defaultPath, bool browseDirectory = false); } #endif diff --git a/Windows/WndMainWindow.cpp b/Windows/WndMainWindow.cpp index 186ef02bca..0e8916b80e 100644 --- a/Windows/WndMainWindow.cpp +++ b/Windows/WndMainWindow.cpp @@ -303,7 +303,7 @@ namespace MainWindow return TRUE; } - void BrowseAndBoot(std::string defaultPath) + void BrowseAndBoot(std::string defaultPath, bool browseDirectory) { std::string fn; std::string filter = "PSP ROMs (*.iso *.cso *.pbp *.elf)|*.pbp;*.elf;*.iso;*.cso;*.prx|All files (*.*)|*.*||"; @@ -323,7 +323,24 @@ namespace MainWindow Core_EnableStepping(true); } - if (W32Util::BrowseForFileName(true, GetHWND(), "Load File", defaultPath.size() ? defaultPath.c_str() : 0, filter.c_str(),"*.pbp;*.elf;*.iso;*.cso;",fn)) + if (browseDirectory) + { + std::string dir = W32Util::BrowseForFolder(GetHWND(),"Choose directory"); + if (dir == "" && !isPaused) + { + Core_EnableStepping(false); + } + else + { + if (globalUIState == UISTATE_INGAME || globalUIState == UISTATE_PAUSEMENU) + { + Core_EnableStepping(false); + } + + NativeMessageReceived("boot", dir.c_str()); + } + } + else if (W32Util::BrowseForFileName(true, GetHWND(), "Load File", defaultPath.size() ? defaultPath.c_str() : 0, filter.c_str(),"*.pbp;*.elf;*.iso;*.cso;",fn)) { if (globalUIState == UISTATE_INGAME || globalUIState == UISTATE_PAUSEMENU) { @@ -572,6 +589,10 @@ namespace MainWindow BrowseAndBoot(""); break; + case ID_FILE_LOAD_DIR: + BrowseAndBoot("",true); + break; + case ID_FILE_LOAD_MEMSTICK: { std::string memStickDir, flash0dir; diff --git a/Windows/WndMainWindow.h b/Windows/WndMainWindow.h index 1b0485492a..99f3e97ec2 100644 --- a/Windows/WndMainWindow.h +++ b/Windows/WndMainWindow.h @@ -18,7 +18,7 @@ namespace MainWindow HINSTANCE GetHInstance(); HWND GetDisplayHWND(); void SetPlaying(const char*text); - void BrowseAndBoot(std::string defaultPath); + void BrowseAndBoot(std::string defaultPath, bool browseDirectory = false); void SaveStateActionFinished(bool result, void *userdata); void _ViewFullScreen(HWND hWnd); void _ViewNormal(HWND hWnd); diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index 699669b478..350ab3ccb4 100644 Binary files a/Windows/ppsspp.rc and b/Windows/ppsspp.rc differ diff --git a/Windows/resource.h b/Windows/resource.h index f95a7243be..8ebcf38a60 100644 Binary files a/Windows/resource.h and b/Windows/resource.h differ