/*
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.xmb;
import static jpcsp.MainGUI.getUmdPaths;
import static jpcsp.util.Utilities.add;
import static jpcsp.util.Utilities.merge;
import java.io.File;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import jpcsp.GUI.UmdBrowser;
import jpcsp.HLE.TPointer;
import jpcsp.HLE.VFS.AbstractVirtualFileSystem;
import jpcsp.HLE.VFS.IVirtualFile;
import jpcsp.HLE.VFS.IVirtualFileSystem;
import jpcsp.HLE.VFS.local.LocalVirtualFileSystem;
import jpcsp.HLE.kernel.types.SceIoDirent;
import jpcsp.HLE.kernel.types.SceIoStat;
import jpcsp.HLE.modules.IoFileMgrForUser;
import jpcsp.HLE.modules.IoFileMgrForUser.IoOperation;
import jpcsp.HLE.modules.IoFileMgrForUser.IoOperationTiming;
public class XmbVirtualFileSystem extends AbstractVirtualFileSystem {
public static final String PSP_GAME = "PSP/GAME";
private static final String EBOOT_PBP = "EBOOT.PBP";
private static final String ISO_DIR = "ms0/ISO";
private IVirtualFileSystem vfs;
private File[] umdPaths;
private Map<String, IVirtualFileSystem> umdVfs;
private List<VirtualPBP> umdFiles;
private static class VirtualPBP {
String umdFile;
IVirtualFile vFile;
}
public XmbVirtualFileSystem(IVirtualFileSystem vfs) {
this.vfs = vfs;
umdPaths = getUmdPaths(true);
File isoDir = new File(ISO_DIR);
if (isoDir.isDirectory()) {
umdPaths = add(umdPaths, isoDir);
}
umdVfs = new HashMap<String, IVirtualFileSystem>();
for (int i = 0; i < umdPaths.length; i++) {
IVirtualFileSystem localVfs = new LocalVirtualFileSystem(umdPaths[i].getAbsolutePath() + "/", false);
umdVfs.put(umdPaths[i].getAbsolutePath(), localVfs);
}
umdFiles = new LinkedList<XmbVirtualFileSystem.VirtualPBP>();
}
private String[] addUmdFileNames(String dirName, File[] files) {
if (files == null) {
return null;
}
String[] fileNames = new String[files.length];
for (int i = 0; i < files.length; i++) {
VirtualPBP virtualPBP = new VirtualPBP();
virtualPBP.umdFile = files[i].getAbsolutePath();
int umdIndex = umdFiles.size();
umdFiles.add(virtualPBP);
fileNames[i] = String.format("@UMD%d", umdIndex);
if (log.isDebugEnabled()) {
log.debug(String.format("%s=%s", fileNames[i], files[i].getAbsolutePath()));
}
}
return fileNames;
}
private VirtualPBP getVirtualPBP(String fileName, StringBuilder restFileName) {
if (fileName != null) {
int umdMarkerIndex = fileName.indexOf("@UMD");
if (umdMarkerIndex >= 0) {
String umdIndexString = fileName.substring(umdMarkerIndex + 4);
int sepIndex = umdIndexString.indexOf("/");
if (sepIndex >= 0) {
if (restFileName != null) {
restFileName.append(umdIndexString.substring(sepIndex + 1));
}
umdIndexString = umdIndexString.substring(0, sepIndex);
}
int umdIndex = Integer.parseInt(umdIndexString);
if (umdIndex >= 0 && umdIndex < umdFiles.size()) {
return umdFiles.get(umdIndex);
}
}
}
return null;
}
private String getUmdFileName(String fileName, StringBuilder restFileName) {
VirtualPBP virtualPBP = getVirtualPBP(fileName, restFileName);
if (virtualPBP == null) {
return null;
}
return virtualPBP.umdFile;
}
private IVirtualFileSystem getUmdVfs(String umdFileName, StringBuilder localFileName) {
for (String path : umdVfs.keySet()) {
if (umdFileName.startsWith(path)) {
if (localFileName != null) {
localFileName.append(umdFileName.substring(path.length() + 1));
}
return umdVfs.get(path);
}
}
return null;
}
private int umdIoGetstat(String umdFileName, SceIoStat stat) {
StringBuilder localFileName = new StringBuilder();
IVirtualFileSystem vfs = getUmdVfs(umdFileName, localFileName);
if (vfs != null) {
return vfs.ioGetstat(localFileName.toString(), stat);
}
return IO_ERROR;
}
@Override
public String[] ioDopen(String dirName) {
String[] entries = null;
StringBuilder restFileName = new StringBuilder();
String umdFileName = getUmdFileName(dirName, restFileName);
if (umdFileName != null && restFileName.length() == 0) {
entries = new String[] { EBOOT_PBP };
} else if (PSP_GAME.equals(dirName)) {
for (int i = 0; i < umdPaths.length; i++) {
File umdPath = umdPaths[i];
if (umdPath.isDirectory()) {
File[] umdFiles = umdPath.listFiles(new UmdBrowser.UmdFileFilter());
entries = merge(entries, addUmdFileNames(dirName, umdFiles));
}
}
entries = merge(entries, vfs.ioDopen(dirName));
} else {
entries = vfs.ioDopen(dirName);
}
return entries;
}
@Override
public int ioDread(String dirName, SceIoDirent dir) {
StringBuilder restFileName = new StringBuilder();
String umdFileName = getUmdFileName(dirName, restFileName);
if (umdFileName != null && restFileName.length() == 0 && EBOOT_PBP.equals(dir.filename)) {
int result = umdIoGetstat(umdFileName, dir.stat);
if (result < 0) {
return result;
}
return 1;
}
restFileName = new StringBuilder();
umdFileName = getUmdFileName(dir.filename, restFileName);
if (umdFileName != null && restFileName.length() == 0) {
int result = umdIoGetstat(umdFileName, dir.stat);
if (result < 0) {
return result;
}
// Change attribute from "file" to "directory"
dir.stat.attr = (dir.stat.attr & ~0x20) | 0x10;
dir.stat.mode = (dir.stat.mode & ~0x2000) | 0x1000;
return 1;
}
return vfs.ioDread(dirName, dir);
}
@Override
public int ioGetstat(String fileName, SceIoStat stat) {
StringBuilder restFileName = new StringBuilder();
String umdFileName = getUmdFileName(fileName, restFileName);
if (umdFileName != null && EBOOT_PBP.equals(restFileName.toString())) {
return umdIoGetstat(umdFileName, stat);
}
return vfs.ioGetstat(fileName, stat);
}
@Override
public IVirtualFile ioOpen(String fileName, int flags, int mode) {
StringBuilder restFileName = new StringBuilder();
VirtualPBP virtualPBP = getVirtualPBP(fileName, restFileName);
if (virtualPBP != null && EBOOT_PBP.equals(restFileName.toString())) {
String umdFileName = virtualPBP.umdFile;
File umdFile = new File(umdFileName);
// Is it a directory containing an EBOOT.PBP file?
if (umdFile.isDirectory()) {
StringBuilder localFileName = new StringBuilder();
IVirtualFileSystem vfs = getUmdVfs(umdFileName, localFileName);
if (vfs != null) {
// Open the EBOOT.PBP file inside the directory
return vfs.ioOpen(localFileName.toString() + "/" + restFileName.toString(), flags, mode);
}
}
// Map the ISO/CSO file into a virtual PBP file
if (virtualPBP.vFile == null) {
virtualPBP.vFile = new XmbIsoVirtualFile(umdFileName);
}
if (virtualPBP.vFile.length() > 0) {
return virtualPBP.vFile;
}
if (log.isDebugEnabled()) {
log.debug(String.format("XmbVirtualFileSystem.ioOpen could not open UMD file '%s'", umdFileName));
}
}
return vfs.ioOpen(fileName, flags, mode);
}
@Override
public Map<IoOperation, IoOperationTiming> getTimings() {
// Do not delay IO operations on faked EBOOT.PBP files
return IoFileMgrForUser.noDelayTimings;
}
@Override
public int ioRename(String oldFileName, String newFileName) {
return vfs.ioRename(oldFileName, newFileName);
}
@Override
public int ioChstat(String fileName, SceIoStat stat, int bits) {
return vfs.ioChstat(fileName, stat, bits);
}
@Override
public int ioRemove(String name) {
return vfs.ioRemove(name);
}
@Override
public int ioMkdir(String name, int mode) {
return vfs.ioMkdir(name, mode);
}
@Override
public int ioRmdir(String name) {
return vfs.ioRmdir(name);
}
@Override
public int ioChdir(String directoryName) {
return vfs.ioChdir(directoryName);
}
@Override
public int ioMount() {
return vfs.ioMount();
}
@Override
public int ioUmount() {
return vfs.ioUmount();
}
@Override
public int ioDevctl(String deviceName, int command, TPointer inputPointer, int inputLength, TPointer outputPointer, int outputLength) {
return vfs.ioDevctl(deviceName, command, inputPointer, inputLength, outputPointer, outputLength);
}
}