size_t VirtualDiscFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) {
	EntryMap::iterator iter = entries.find(handle);
	if (iter != entries.end())
	{
		// it's the whole iso... it could reference any of the files on the disc.
		// For now let's just open and close the files on demand. Can certainly be done
		// better though
		if (iter->second.type == VFILETYPE_ISO)
		{
			int fileIndex = getFileListIndex(iter->second.curOffset,size*2048,true);
			if (fileIndex == -1)
			{
				ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Reading from unknown address in %08x at %08llx", handle, iter->second.curOffset);
				return 0;
			}

			OpenFileEntry temp;
			if (fileList[fileIndex].handler != NULL) {
				temp.handler = fileList[fileIndex].handler;
			}
			bool success = temp.Open(basePath, fileList[fileIndex].fileName, FILEACCESS_READ);

			if (!success)
			{
				ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Error opening file %s", fileList[fileIndex].fileName.c_str());
				return 0;
			}

			u32 startOffset = (iter->second.curOffset-fileList[fileIndex].firstBlock)*2048;
			size_t bytesRead;

			temp.Seek(startOffset, FILEMOVE_BEGIN);

			u32 remainingSize = fileList[fileIndex].totalSize-startOffset;
			if (remainingSize < size * 2048)
			{
				// the file doesn't fill the whole last sector
				// read what's there and zero fill the rest like on a real disc
				bytesRead = temp.Read(pointer, remainingSize);
				memset(&pointer[bytesRead], 0, size * 2048 - bytesRead);
			} else {
				bytesRead = temp.Read(pointer, size * 2048);
			}

			temp.Close();

			iter->second.curOffset += size;
			return size;
		}

		size_t bytesRead = iter->second.Read(pointer, size);
		iter->second.curOffset += bytesRead;
		return bytesRead;
	} else {
		//This shouldn't happen...
		ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Cannot read file that hasn't been opened: %08x", handle);
		return 0;
	}
}
size_t VirtualDiscFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size) {
	EntryMap::iterator iter = entries.find(handle);
	if (iter != entries.end())
	{
		// it's the whole iso... it could reference any of the files on the disc.
		// For now let's just open and close the files on demand. Can certainly be done
		// better though
		if (iter->second.type == VFILETYPE_ISO)
		{
			int fileIndex = getFileListIndex(iter->second.curOffset,size*2048);
			if (fileIndex == -1)
			{
				ERROR_LOG(HLE,"VirtualDiscFileSystem: Reading from unknown address %08x", handle);
				return 0;
			}
			
			DirectoryFileHandle hFile;
			bool success = hFile.Open(basePath,fileList[fileIndex].fileName,FILEACCESS_READ);

			if (!success)
			{
				ERROR_LOG(HLE,"VirtualDiscFileSystem: Error opening file %s", fileList[fileIndex].fileName.c_str());
				return 0;
			}
			
			u32 startOffset = (iter->second.curOffset-fileList[fileIndex].firstBlock)*2048;
			size_t bytesRead;

			hFile.Seek(startOffset,FILEMOVE_BEGIN);
			bytesRead = hFile.Read(pointer,size*2048);
			hFile.Close();

			return bytesRead;
		}

		size_t bytesRead = iter->second.hFile.Read(pointer,size);
		iter->second.curOffset += bytesRead;
		return bytesRead;
	} else {
		//This shouldn't happen...
		ERROR_LOG(HLE,"VirtualDiscFileSystem: Cannot read file that hasn't been opened: %08x", handle);
		return 0;
	}
}
PSPFileInfo VirtualDiscFileSystem::GetFileInfo(std::string filename) {
	PSPFileInfo x;
	x.name = filename;
	
	std::string fullName = GetLocalPath(filename);
	if (! File::Exists(fullName)) {
#if HOST_IS_CASE_SENSITIVE
		if (! FixPathCase(basePath,filename, FPC_FILE_MUST_EXIST))
			return x;
		fullName = GetLocalPath(filename);

		if (! File::Exists(fullName))
			return x;
#else
		return x;
#endif
	}

	x.type = File::IsDirectory(fullName) ? FILETYPE_DIRECTORY : FILETYPE_NORMAL;
	x.exists = true;

	if (x.type != FILETYPE_DIRECTORY)
	{
		struct stat s;
		stat(fullName.c_str(), &s);

		x.size = getFileSize(fullName);
	
		int fileIndex = getFileListIndex(filename);
		x.startSector = fileList[fileIndex].firstBlock;
		x.numSectors = (x.size+2047)/2048;

		x.access = s.st_mode & 0x1FF;
		localtime_r((time_t*)&s.st_atime,&x.atime);
		localtime_r((time_t*)&s.st_ctime,&x.ctime);
		localtime_r((time_t*)&s.st_mtime,&x.mtime);
	}

	return x;
}
std::vector<PSPFileInfo> VirtualDiscFileSystem::GetDirListing(std::string path)
{
	std::vector<PSPFileInfo> myVector;
#ifdef _WIN32
	WIN32_FIND_DATA findData;
	HANDLE hFind;

	// TODO: Handler files that are virtual might not be listed.

	std::string w32path = GetLocalPath(path) + "\\*.*";

	hFind = FindFirstFile(ConvertUTF8ToWString(w32path).c_str(), &findData);

	if (hFind == INVALID_HANDLE_VALUE) {
		return myVector; //the empty list
	}

	for (BOOL retval = 1; retval; retval = FindNextFile(hFind, &findData)) {
		if (!wcscmp(findData.cFileName, L"..") || !wcscmp(findData.cFileName, L".")) {
			continue;
		}

		PSPFileInfo entry;
		if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
			entry.type = FILETYPE_DIRECTORY;
		} else {
			entry.type = FILETYPE_NORMAL;
		}

		entry.access = FILEACCESS_READ;
		entry.size = findData.nFileSizeLow | ((u64)findData.nFileSizeHigh<<32);
		entry.name = ConvertWStringToUTF8(findData.cFileName);
		tmFromFiletime(entry.atime, findData.ftLastAccessTime);
		tmFromFiletime(entry.ctime, findData.ftCreationTime);
		tmFromFiletime(entry.mtime, findData.ftLastWriteTime);
		entry.isOnSectorSystem = true;

		std::string fullRelativePath = path + "/" + entry.name;
		int fileIndex = getFileListIndex(fullRelativePath);
		if (fileIndex != -1)
			entry.startSector = fileList[fileIndex].firstBlock;
		myVector.push_back(entry);
	}
	FindClose(hFind);
#else
	dirent *dirp;
	std::string localPath = GetLocalPath(path);
	DIR *dp = opendir(localPath.c_str());

#if HOST_IS_CASE_SENSITIVE
	if(dp == NULL && FixPathCase(basePath,path, FPC_FILE_MUST_EXIST)) {
		// May have failed due to case sensitivity, try again
		localPath = GetLocalPath(path);
		dp = opendir(localPath.c_str());
	}
#endif

	if (dp == NULL) {
		ERROR_LOG(FILESYS,"Error opening directory %s\n", path.c_str());
		return myVector;
	}

	while ((dirp = readdir(dp)) != NULL) {
		if (!strcmp(dirp->d_name, "..") || !strcmp(dirp->d_name, ".")) {
			continue;
		}

		PSPFileInfo entry;
		struct stat s;
		std::string fullName = GetLocalPath(path) + "/"+dirp->d_name;
		stat(fullName.c_str(), &s);
		if (S_ISDIR(s.st_mode))
			entry.type = FILETYPE_DIRECTORY;
		else
			entry.type = FILETYPE_NORMAL;
		entry.access = s.st_mode & 0x1FF;
		entry.name = dirp->d_name;
		entry.size = s.st_size;
		localtime_r((time_t*)&s.st_atime,&entry.atime);
		localtime_r((time_t*)&s.st_ctime,&entry.ctime);
		localtime_r((time_t*)&s.st_mtime,&entry.mtime);
		entry.isOnSectorSystem = true;

		std::string fullRelativePath = path + "/" + entry.name;
		int fileIndex = getFileListIndex(fullRelativePath);
		if (fileIndex != -1)
			entry.startSector = fileList[fileIndex].firstBlock;
		myVector.push_back(entry);
	}
	closedir(dp);
#endif
	return myVector;
}
PSPFileInfo VirtualDiscFileSystem::GetFileInfo(std::string filename) {
	PSPFileInfo x;
	x.name = filename;
	x.access = FILEACCESS_READ;

	if (filename.compare(0,8,"/sce_lbn") == 0) {
		u32 sectorStart = 0xFFFFFFFF, readSize = 0xFFFFFFFF;
		parseLBN(filename, &sectorStart, &readSize);

		PSPFileInfo fileInfo;
		fileInfo.name = filename;
		fileInfo.exists = true;
		fileInfo.size = readSize;
		fileInfo.startSector = sectorStart;
		fileInfo.isOnSectorSystem = true;
		fileInfo.numSectors = (readSize + 2047) / 2048;
		return fileInfo;
	}

	int fileIndex = getFileListIndex(filename);
	if (fileIndex != -1 && fileList[fileIndex].handler != NULL) {
		x.type = FILETYPE_NORMAL;
		x.isOnSectorSystem = true;
		x.startSector = fileList[fileIndex].firstBlock;

		HandlerFileHandle temp;
		if (temp.Open(basePath, filename, FILEACCESS_READ)) {
			x.exists = true;
			x.size = temp.Seek(0, FILEMOVE_END);
			temp.Close();
		}

		// TODO: Probably should include dates or something...
		return x;
	}

	std::string fullName = GetLocalPath(filename);
	if (! File::Exists(fullName)) {
#if HOST_IS_CASE_SENSITIVE
		if (! FixPathCase(basePath,filename, FPC_FILE_MUST_EXIST))
			return x;
		fullName = GetLocalPath(filename);

		if (! File::Exists(fullName))
			return x;
#else
		return x;
#endif
	}

	x.type = File::IsDirectory(fullName) ? FILETYPE_DIRECTORY : FILETYPE_NORMAL;
	x.exists = true;
	if (fileIndex != -1) {
		x.isOnSectorSystem = true;
		x.startSector = fileList[fileIndex].firstBlock;
	}

	if (x.type != FILETYPE_DIRECTORY) {
		struct stat s;
		stat(fullName.c_str(), &s);

		x.size = File::GetSize(fullName);

		x.startSector = fileList[fileIndex].firstBlock;
		x.numSectors = (x.size+2047)/2048;

		localtime_r((time_t*)&s.st_atime,&x.atime);
		localtime_r((time_t*)&s.st_ctime,&x.ctime);
		localtime_r((time_t*)&s.st_mtime,&x.mtime);
	}

	return x;
}
u32 VirtualDiscFileSystem::OpenFile(std::string filename, FileAccess access, const char *devicename)
{
	OpenFileEntry entry;
	entry.curOffset = 0;
	entry.size = 0;
	entry.startOffset = 0;

	if (filename == "")
	{
		entry.type = VFILETYPE_ISO;
		entry.fileIndex = -1;

		u32 newHandle = hAlloc->GetNewHandle();
		entries[newHandle] = entry;

		return newHandle;
	}

	if (filename.compare(0,8,"/sce_lbn") == 0)
	{
		u32 sectorStart = 0xFFFFFFFF, readSize = 0xFFFFFFFF;
		parseLBN(filename, &sectorStart, &readSize);

		entry.type = VFILETYPE_LBN;
		entry.size = readSize;

		int fileIndex = getFileListIndex(sectorStart,readSize);
		if (fileIndex == -1)
		{
			ERROR_LOG(FILESYS, "VirtualDiscFileSystem: sce_lbn used without calling fileinfo.");
			return 0;
		}
		entry.fileIndex = (u32)fileIndex;

		entry.startOffset = (sectorStart-fileList[entry.fileIndex].firstBlock)*2048;

		// now we just need an actual file handle
		if (fileList[entry.fileIndex].handler != NULL) {
			entry.handler = fileList[entry.fileIndex].handler;
		}
		bool success = entry.Open(basePath, fileList[entry.fileIndex].fileName, FILEACCESS_READ);

		if (!success) {
#ifdef _WIN32
			ERROR_LOG(FILESYS, "VirtualDiscFileSystem::OpenFile: FAILED, %i", GetLastError());
#else
			ERROR_LOG(FILESYS, "VirtualDiscFileSystem::OpenFile: FAILED");
#endif
			return 0;
		}

		// seek to start
		entry.Seek(entry.startOffset, FILEMOVE_BEGIN);

		u32 newHandle = hAlloc->GetNewHandle();
		entries[newHandle] = entry;

		return newHandle;
	}

	entry.type = VFILETYPE_NORMAL;
	entry.fileIndex = getFileListIndex(filename);

	if (entry.fileIndex != (u32)-1 && fileList[entry.fileIndex].handler != NULL) {
		entry.handler = fileList[entry.fileIndex].handler;
	}
	bool success = entry.Open(basePath, filename, access);

	if (!success) {
#ifdef _WIN32
		ERROR_LOG(FILESYS, "VirtualDiscFileSystem::OpenFile: FAILED, %i - access = %i", GetLastError(), (int)access);
#else
		ERROR_LOG(FILESYS, "VirtualDiscFileSystem::OpenFile: FAILED, access = %i", (int)access);
#endif
		//wwwwaaaaahh!!
		return 0;
	} else {
		u32 newHandle = hAlloc->GetNewHandle();
		entries[newHandle] = entry;

		return newHandle;
	}
}
size_t VirtualDiscFileSystem::ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) {
	EntryMap::iterator iter = entries.find(handle);
	if (iter != entries.end()) {
		if (size < 0) {
			ERROR_LOG_REPORT(FILESYS, "Invalid read for %lld bytes from virtual umd", size);
			return 0;
		}

		// it's the whole iso... it could reference any of the files on the disc.
		// For now let's just open and close the files on demand. Can certainly be done
		// better though
		if (iter->second.type == VFILETYPE_ISO)
		{
			int fileIndex = getFileListIndex(iter->second.curOffset,size*2048,true);
			if (fileIndex == -1)
			{
				ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Reading from unknown address in %08x at %08llx", handle, iter->second.curOffset);
				return 0;
			}

			OpenFileEntry temp;
			if (fileList[fileIndex].handler != NULL) {
				temp.handler = fileList[fileIndex].handler;
			}
			bool success = temp.Open(basePath, fileList[fileIndex].fileName, FILEACCESS_READ);

			if (!success)
			{
				ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Error opening file %s", fileList[fileIndex].fileName.c_str());
				return 0;
			}

			u32 startOffset = (iter->second.curOffset-fileList[fileIndex].firstBlock)*2048;
			size_t bytesRead;

			temp.Seek(startOffset, FILEMOVE_BEGIN);

			u32 remainingSize = fileList[fileIndex].totalSize-startOffset;
			if (remainingSize < size * 2048)
			{
				// the file doesn't fill the whole last sector
				// read what's there and zero fill the rest like on a real disc
				bytesRead = temp.Read(pointer, remainingSize);
				memset(&pointer[bytesRead], 0, size * 2048 - bytesRead);
			} else {
				bytesRead = temp.Read(pointer, size * 2048);
			}

			temp.Close();

			iter->second.curOffset += size;
			// TODO: This probably isn't enough...
			if (abs((int)lastReadBlock_ - (int)iter->second.curOffset) > 100) {
				// This is an estimate, sometimes it takes 1+ seconds, but it definitely takes time.
				usec = 100000;
			}
			lastReadBlock_ = iter->second.curOffset;
			return size;
		}

		if (iter->second.type == VFILETYPE_LBN && iter->second.curOffset + size > iter->second.size) {
			// Clamp to the remaining size, but read what we can.
			const s64 newSize = iter->second.size - iter->second.curOffset;
			WARN_LOG(FILESYS, "VirtualDiscFileSystem: Reading beyond end of file, clamping size %lld to %lld", size, newSize);
			size = newSize;
		}

		size_t bytesRead = iter->second.Read(pointer, size);
		iter->second.curOffset += bytesRead;
		return bytesRead;
	} else {
		//This shouldn't happen...
		ERROR_LOG(FILESYS,"VirtualDiscFileSystem: Cannot read file that hasn't been opened: %08x", handle);
		return 0;
	}
}