/* This file is part of jpcsp. Jpcsp is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Jpcsp is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Jpcsp. If not, see <http://www.gnu.org/licenses/>. */ package jpcsp.HLE.VFS.iso; import static jpcsp.HLE.kernel.types.SceKernelErrors.ERROR_ERRNO_INVALID_ARGUMENT; import static jpcsp.HLE.modules.IoFileMgrForUser.PSP_O_CREAT; import static jpcsp.HLE.modules.IoFileMgrForUser.PSP_O_TRUNC; import static jpcsp.HLE.modules.IoFileMgrForUser.PSP_O_WRONLY; import java.io.FileNotFoundException; import java.io.IOException; import jpcsp.HLE.SceKernelErrorException; import jpcsp.HLE.TPointer; import jpcsp.HLE.VFS.AbstractVirtualFileSystem; import jpcsp.HLE.VFS.IVirtualFile; import jpcsp.HLE.kernel.types.SceIoStat; import jpcsp.HLE.kernel.types.SceKernelErrors; import jpcsp.HLE.kernel.types.ScePspDateTime; import jpcsp.filesystems.umdiso.UmdIsoFile; import jpcsp.filesystems.umdiso.UmdIsoReader; public class UmdIsoVirtualFileSystem extends AbstractVirtualFileSystem { protected final UmdIsoReader iso; public UmdIsoVirtualFileSystem(UmdIsoReader iso) { this.iso = iso; } @Override public IVirtualFile ioOpen(String fileName, int flags, int mode) { if (hasFlag(flags, PSP_O_WRONLY) || hasFlag(flags, PSP_O_CREAT) || hasFlag(flags, PSP_O_TRUNC)) { throw new SceKernelErrorException(SceKernelErrors.ERROR_ERRNO_READ_ONLY); } UmdIsoFile file; try { file = iso.getFile(fileName); } catch (FileNotFoundException e) { return null; } catch (IOException e) { log.error("ioOpen", e); return null; } // Opening "umd0:" is allowing to read the whole UMD per sectors. boolean sectorBlockMode = (fileName.length() == 0); return new UmdIsoVirtualFile(file, sectorBlockMode, iso); } @Override public int ioGetstat(String fileName, SceIoStat stat) { int mode = 4; // 4 = readable int attr = 0; long size = 0; long timestamp = 0; int startSector = 0; try { // Check for files first. UmdIsoFile file = iso.getFile(fileName); attr |= 0x20; // Is file size = file.length(); timestamp = file.getTimestamp().getTime(); startSector = file.getStartSector(); } catch (FileNotFoundException fnfe) { // If file wasn't found, try looking for a directory. try { if (iso.isDirectory(fileName)) { attr |= 0x10; // Is directory mode |= 1; // 1 = executable } } catch (FileNotFoundException dnfe) { log.warn(String.format("ioGetstat - '%s' umd file/dir not found", fileName)); return SceKernelErrors.ERROR_ERRNO_FILE_NOT_FOUND; } catch (IOException e) { log.warn("ioGetstat", e); return SceKernelErrors.ERROR_ERRNO_FILE_NOT_FOUND; } } catch (IOException e) { log.warn("ioGetstat", e); return SceKernelErrors.ERROR_ERRNO_FILE_NOT_FOUND; } // Octal extend into user and group mode = mode + (mode << 3) + (mode << 6); mode |= attr << 8; ScePspDateTime ctime = ScePspDateTime.fromUnixTime(timestamp); ScePspDateTime atime = ScePspDateTime.fromUnixTime(0); ScePspDateTime mtime = ScePspDateTime.fromUnixTime(timestamp); stat.init(mode, attr, size, ctime, atime, mtime); if (startSector > 0) { stat.setStartSector(startSector); } return 0; } @Override public String[] ioDopen(String dirName) { String[] fileNames = null; try { if (iso.isDirectory(dirName)) { fileNames = iso.listDirectory(dirName); } else { log.warn(String.format("ioDopen file '%s' is not a directory", dirName)); } } catch (FileNotFoundException e) { log.warn(String.format("ioDopen directory '%s' not found", dirName)); } catch (IOException e) { log.warn("ioDopen", e); } return fileNames; } @Override public int ioDevctl(String deviceName, int command, TPointer inputPointer, int inputLength, TPointer outputPointer, int outputLength) { int result; switch (command) { // Get UMD disc type. case 0x01F20001: { log.debug("ioDevctl get disc type"); if (outputPointer.isAddressGood() && outputLength >= 8) { // 0 = No disc. // 0x10 = Game disc. // 0x20 = Video disc. // 0x40 = Audio disc. // 0x80 = Cleaning disc. int out; if (iso == null) { out = 0; } else { out = 0x10; // Always return game disc (if present). } outputPointer.setValue32(4, out); result = 0; } else { result = ERROR_ERRNO_INVALID_ARGUMENT; } break; } // Get UMD current LBA. case 0x01F20002: { log.debug("ioDevctl get current LBA"); if (outputPointer.isAddressGood() && outputLength >= 4) { outputPointer.setValue32(0); // Assume first sector. result = 0; } else { result = ERROR_ERRNO_INVALID_ARGUMENT; } break; } // Seek UMD disc (raw). case 0x01F100A3: { log.debug("ioDevctl seek UMD disc"); if (inputPointer.isAddressGood() && inputLength >= 4) { int sector = inputPointer.getValue32(); if (log.isDebugEnabled()) { log.debug(String.format("ioDevctl seek UMD disc: sector=%d", sector)); } result = 0; } else { result = ERROR_ERRNO_INVALID_ARGUMENT; } break; } // Prepare UMD data into cache. case 0x01F100A4: { log.debug("ioDevctl prepare UMD data to cache"); if (inputPointer.isAddressGood() && inputLength >= 16) { // UMD cache read struct (16-bytes). int unk1 = inputPointer.getValue32(0); // NULL. int sector = inputPointer.getValue32(4); // First sector of data to read. int unk2 = inputPointer.getValue32(8); // NULL. int sectorNum = inputPointer.getValue32(12); // Length of data to read. if (log.isDebugEnabled()) { log.debug(String.format("ioDevctl prepare UMD data to cache: sector=%d, sectorNum=%d, unk1=%d, unk2=%d", sector, sectorNum, unk1, unk2)); } result = 0; } else { result = ERROR_ERRNO_INVALID_ARGUMENT; } break; } // Prepare UMD data into cache and get status. case 0x01F300A5: { log.debug("ioDevctl prepare UMD data to cache and get status"); if (inputPointer.isAddressGood() && inputLength >= 16 && outputPointer.isAddressGood() && outputLength >= 4) { // UMD cache read struct (16-bytes). int unk1 = inputPointer.getValue32(0); // NULL. int sector = inputPointer.getValue32(4); // First sector of data to read. int unk2 = inputPointer.getValue32(8); // NULL. int sectorNum = inputPointer.getValue32(12); // Length of data to read. if (log.isDebugEnabled()) { log.debug(String.format("ioDevctl prepare UMD data to cache and get status: sector=%d, sectorNum=%d, unk1=%d, unk2=%d", sector, sectorNum, unk1, unk2)); } outputPointer.setValue32(1); // Status (unitary index of the requested read, greater or equal to 1). result = 0; } else { result = ERROR_ERRNO_INVALID_ARGUMENT; } break; } default: result = super.ioDevctl(deviceName, command, inputPointer, inputLength, outputPointer, outputLength); } return result; } @Override public void ioExit() { try { iso.close(); } catch (IOException e) { } super.ioExit(); } }