ppsspp/Core/HLE/sceIo.cpp
2012-11-07 16:54:14 +01:00

998 lines
24 KiB
C++

// Copyright (c) 2012- PPSSPP Project.
// This program 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, version 2.0 or later versions.
// This program 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#ifdef _WIN32
#include <windows.h>
#undef DeleteFile
#endif
#include "../System.h"
#include "HLE.h"
#include "../MIPS/MIPS.h"
#include "../HW/MemoryStick.h"
#include "../FileSystems/FileSystem.h"
#include "../FileSystems/MetaFileSystem.h"
#include "../FileSystems/ISOFileSystem.h"
#include "../FileSystems/DirectoryFileSystem.h"
#include "sceIo.h"
#include "sceKernel.h"
#include "sceKernelMemory.h"
#include "sceKernelThread.h"
#define ERROR_ERRNO_FILE_NOT_FOUND 0x80010002
#define ERROR_MEMSTICK_DEVCTL_BAD_PARAMS 0x80220081
#define ERROR_MEMSTICK_DEVCTL_TOO_MANY_CALLBACKS 0x80220082
/*
TODO: async io is missing features!
flash0: - fat access - system file volume
flash1: - fat access - configuration file volume
flashfat#: this too
lflash: - block access - entire flash
fatms: memstick
isofs: fat access - umd
disc0: fat access - umd
ms0: - fat access - memcard
umd: - block access - umd
irda?: - (?=0..9) block access - infra-red port (doesnt support seeking, maybe send/recieve data from port tho)
mscm0: - block access - memstick cm??
umd00: block access - umd
umd01: block access - umd
*/
#define O_RDONLY 0x0001
#define O_WRONLY 0x0002
#define O_RDWR 0x0003
#define O_NBLOCK 0x0010
#define O_APPEND 0x0100
#define O_CREAT 0x0200
#define O_TRUNC 0x0400
#define O_NOWAIT 0x8000
typedef s32 SceMode;
typedef s64 SceOff;
typedef u64 SceIores;
std::string emuDebugOutput;
const std::string &EmuDebugOutput() {
return emuDebugOutput;
}
#define SCE_STM_FDIR 0x1000
#define SCE_STM_FREG 0x2000
#define SCE_STM_FLNK 0x4000
enum
{
TYPE_DIR=0x10,
TYPE_FILE=0x20
};
struct ScePspDateTime {
unsigned short year;
unsigned short month;
unsigned short day;
unsigned short hour;
unsigned short minute;
unsigned short second;
unsigned int microsecond;
};
struct SceIoStat {
SceMode st_mode;
unsigned int st_attr;
SceOff st_size;
ScePspDateTime st_ctime;
ScePspDateTime st_atime;
ScePspDateTime st_mtime;
unsigned int st_private[6];
};
struct SceIoDirEnt
{
SceIoStat d_stat;
char d_name[256];
u32 d_private;
};
struct dirent {
u32 unk0;
u32 type;
u32 size;
u32 unk[19];
char name[0x108];
};
class FileNode : public KernelObject
{
public:
FileNode() : callbackID(0), callbackArg(0), asyncResult(0), pendingAsyncResult(false), sectorBlockMode(false) {}
~FileNode()
{
pspFileSystem.CloseFile(handle);
}
const char *GetName() {return fullpath.c_str();}
const char *GetTypeName() {return "OpenFile";}
void GetQuickInfo(char *ptr, int size)
{
sprintf(ptr, "Seekpos: %08x", (u32)pspFileSystem.GetSeekPos(handle));
}
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_BADF; }
int GetIDType() const { return 0; }
std::string fullpath;
u32 handle;
u32 callbackID;
u32 callbackArg;
u32 asyncResult;
bool pendingAsyncResult;
bool sectorBlockMode;
};
void __IoInit()
{
INFO_LOG(HLE, "Starting up I/O...");
#ifdef _WIN32
char path_buffer[_MAX_PATH], drive[_MAX_DRIVE] ,dir[_MAX_DIR], file[_MAX_FNAME], ext[_MAX_EXT];
char mypath[_MAX_PATH];
GetModuleFileName(NULL,path_buffer,sizeof(path_buffer));
char *winpos = strstr(path_buffer, "Windows");
if (winpos)
*winpos = 0;
strcat(path_buffer, "dummy.txt");
_splitpath_s(path_buffer, drive, dir, file, ext );
// Mount a couple of filesystems
sprintf(mypath, "%s%sMemStick\\", drive, dir);
#else
// TODO
char mypath[256] = "/mount/sdcard/memstick";
#endif
DirectoryFileSystem *memstick;
memstick = new DirectoryFileSystem(&pspFileSystem, mypath);
pspFileSystem.Mount("ms0:", memstick);
pspFileSystem.Mount("fatms0:", memstick);
pspFileSystem.Mount("fatms:", memstick);
pspFileSystem.Mount("flash0:", new EmptyFileSystem());
pspFileSystem.Mount("flash1:", new EmptyFileSystem());
}
void __IoShutdown()
{
}
void sceIoAssign()
{
const char *aliasname = Memory::GetCharPointer(PARAM(0));
const char *physname = Memory::GetCharPointer(PARAM(1));
const char *devname = Memory::GetCharPointer(PARAM(2));
u32 flag = PARAM(3);
ERROR_LOG(HLE,"UNIMPL sceIoAssign(%s, %s, %s, %08x, ...)",aliasname,physname,devname,flag);
RETURN(0);
}
void sceKernelStdin()
{
DEBUG_LOG(HLE,"3=sceKernelStdin()");
RETURN(3);
}
void sceKernelStdout()
{
DEBUG_LOG(HLE,"1=sceKernelStdout()");
RETURN(1);
}
void sceKernelStderr()
{
DEBUG_LOG(HLE,"2=sceKernelStderr()");
RETURN(2);
}
void __IoCompleteAsyncIO(SceUID id)
{
u32 error;
FileNode *f = kernelObjects.Get<FileNode>(id, error);
if (f)
{
if (f->callbackID)
{
// __KernelNotifyCallbackType(THREAD_CALLBACK_IO, __KernelGetCurThread(), f->callbackID, f->callbackArg);
}
}
}
void __IoGetStat(SceIoStat *stat, PSPFileInfo &info)
{
memset(stat, 0xfe, sizeof(SceIoStat));
stat->st_size = (s64)info.size;
int type, attr;
if (info.type & FILETYPE_DIRECTORY)
type = SCE_STM_FDIR, attr = TYPE_DIR;
else
type = SCE_STM_FREG, attr = TYPE_FILE;
stat->st_mode = 0777 | type;
stat->st_attr = attr;
stat->st_size = info.size;
stat->st_private[0] = info.startSector;
}
void sceIoGetstat()
{
const char *filename = Memory::GetCharPointer(PARAM(0));
u32 addr = PARAM(1);
SceIoStat *stat = (SceIoStat*)Memory::GetPointer(addr);
PSPFileInfo info = pspFileSystem.GetFileInfo(filename);
__IoGetStat(stat, info);
DEBUG_LOG(HLE,"sceIoGetstat(%s, %08x) : sector = %08x",filename,addr,info.startSector);
RETURN(0);
}
void sceIoRead() //(int fd, void *data, int size);
{
SceUID id = PARAM(0);
if (id == 3)
{
DEBUG_LOG(HLE,"sceIoRead STDIN");
RETURN(0); //stdin
return;
}
u32 error;
FileNode *f = kernelObjects.Get<FileNode>(id, error);
if (f)
{
if (PARAM(1))
{
u8 *data = (u8*)Memory::GetPointer(PARAM(1));
int size = PARAM(2);
f->asyncResult = RETURN((u32)pspFileSystem.ReadFile(f->handle, data, size));
DEBUG_LOG(HLE,"%i=sceIoRead(%d, %08x , %i)",f->asyncResult, id, PARAM(1), size);
}
else
{
ERROR_LOG(HLE,"sceIoRead Reading into zero pointer");
RETURN(-1);
}
}
else
{
ERROR_LOG(HLE,"sceIoRead ERROR: no file open");
RETURN(error);
}
}
void sceIoWrite() //(int fd, void *data, int size);
{
SceUID id = PARAM(0);
int size = PARAM(2);
if (PARAM(0) == 2)
{
//stderr!
const char *str = Memory::GetCharPointer(PARAM(1));
DEBUG_LOG(HLE,"stderr: %s", str);
RETURN(size);
return;
}
if (PARAM(0) == 1)
{
//stdout!
char *str = (char *)Memory::GetPointer(PARAM(1));
char temp = str[size];
str[size]=0;
DEBUG_LOG(HLE,"stdout: %s", str);
str[size]=temp;
RETURN(size);
return;
}
u32 error;
FileNode *f = kernelObjects.Get<FileNode>(id, error);
if (f)
{
u8 *data = (u8*)Memory::GetPointer(PARAM(1));
f->asyncResult = RETURN((u32)pspFileSystem.WriteFile(f->handle,data,size));
}
else
{
ERROR_LOG(HLE,"sceIoWrite ERROR: no file open");
RETURN(error);
}
}
void sceIoLseek() //(int fd, int64 offset, int whence);
{
SceUID id = PARAM(0);
u32 error;
FileNode *f = kernelObjects.Get<FileNode>(id, error);
s64 offset = ((s64)PARAM(2)) | ((s64)(PARAM(3))<<32);
int whence = PARAM(4);
if (f)
{
FileMove seek = FILEMOVE_BEGIN;
switch (whence)
{
case 0: break;
case 1: seek=FILEMOVE_CURRENT;break;
case 2: seek=FILEMOVE_END;break;
}
f->asyncResult = RETURN((u32)pspFileSystem.SeekFile(f->handle, (s32)offset, seek));
DEBUG_LOG(HLE,"%i = sceIoLseek(%d,%i,%i)",f->asyncResult, id,(int)offset,whence);
}
else
{
ERROR_LOG(HLE,"sceIoLseek(%d, %i, %i) - ERROR: invalid file", id, (int)offset, whence);
RETURN(error);
}
}
void sceIoLseek32() //(int fd, int offset, int whence);
{
SceUID id = PARAM(0);
u32 error;
FileNode *f = kernelObjects.Get<FileNode>(id, error);
if (f)
{
s32 offset = (s32)PARAM(2);
int whence = PARAM(3);
DEBUG_LOG(HLE,"sceIoLseek32(%d,%08x,%i)",id,(int)offset,whence);
FileMove seek=FILEMOVE_BEGIN;
switch (whence)
{
case 0: break;
case 1: seek=FILEMOVE_CURRENT;break;
case 2: seek=FILEMOVE_END;break;
}
f->asyncResult = RETURN((u32)pspFileSystem.SeekFile(f->handle, (s32)offset, seek));
}
else
{
ERROR_LOG(HLE,"sceIoLseek32 ERROR: no file open");
RETURN(error);
}
}
// Try WrapI_CU
void sceIoOpen() //(const char* file, int mode);
{
const char *filename = Memory::GetCharPointer(PARAM(0));
int mode = PARAM(1);
//memory stick filename
int access=FILEACCESS_NONE;
if (mode & O_RDONLY) access |= FILEACCESS_READ;
if (mode & O_WRONLY) access |= FILEACCESS_WRITE;
if (mode & O_APPEND) access |= FILEACCESS_APPEND;
if (mode & O_CREAT) access |= FILEACCESS_CREATE;
u32 h = pspFileSystem.OpenFile(filename, (FileAccess)access);
if (h == 0)
{
ERROR_LOG(HLE,"ERROR_ERRNO_FILE_NOT_FOUND=sceIoOpen(%s, %08x) - file not found", filename, mode);
RETURN(ERROR_ERRNO_FILE_NOT_FOUND);
return;
}
FileNode *f = new FileNode();
SceUID id = kernelObjects.Create(f);
f->handle = h;
f->fullpath = filename;
f->asyncResult = id;
DEBUG_LOG(HLE,"%i=sceIoOpen(%s, %08x)",id,filename,mode);
RETURN(id);
}
void sceIoClose() //(int fd);
{
SceUID f = PARAM(0);
DEBUG_LOG(HLE,"sceIoClose(%d)",f);
RETURN(kernelObjects.Destroy<FileNode>(f));
}
void sceIoRemove() //(const char *file);
{
const char *filename = Memory::GetCharPointer(PARAM(0));
if (pspFileSystem.DeleteFile(filename))
RETURN(0);
else
RETURN(-1);
DEBUG_LOG(HLE,"sceIoRemove(%s)", filename);
}
void sceIoMkdir() //(const char *dir, int mode);
{
const char *filename = Memory::GetCharPointer(PARAM(0));
int mode = PARAM(1);
if (pspFileSystem.MkDir(filename))
RETURN(0);
else
RETURN(-1);
DEBUG_LOG(HLE,"sceIoMkdir(%s, %i)", filename, mode);
}
void sceIoRmdir() //(const char *dir);
{
const char *filename = Memory::GetCharPointer(PARAM(0));
if (pspFileSystem.RmDir(filename))
RETURN(0);
else
RETURN(-1);
DEBUG_LOG(HLE,"sceIoRmdir(%s)", filename);
}
void sceIoSync()
{
DEBUG_LOG(HLE,"UNIMPL sceIoSync not implemented");
}
struct DeviceSize
{
u32 maxSectors;
u32 sectorSize;
u32 sectorsPerCluster;
u32 totalClusters;
u32 freeClusters;
};
void sceIoDevctl() //(const char *name, int cmd, void *arg, size_t arglen, void *buf, size_t *buflen);
{
const char *name = Memory::GetCharPointer(PARAM(0));
int cmd = PARAM(1);
u32 argAddr = PARAM(2);
int argLen = PARAM(3);
u32 outPtr = PARAM(4);
int outLen = PARAM(5);
if (strcmp(name, "emulator:")) {
DEBUG_LOG(HLE,"sceIoDevctl(\"%s\", %08x, %08x, %i, %08x, %i)", name, cmd,argAddr,argLen,outPtr,outLen);
}
// UMD checks
switch (cmd) {
case 0x01F20001: // Get Disc Type.
if (Memory::IsValidAddress(outPtr)) {
Memory::Write_U32(0x10, outPtr); // Game disc
RETURN(0); return;
} else {
RETURN(-1); return;
}
break;
case 0x01F20002: // Get current LBA.
if (Memory::IsValidAddress(outPtr)) {
Memory::Write_U32(0, outPtr); // Game disc
RETURN(0); return;
} else {
RETURN(-1); return;
}
break;
case 0x01F100A3: // Seek
RETURN(0); return;
break;
}
// This should really send it on to a FileSystem implementation instead.
if (!strcmp(name, "mscmhc0:"))
{
switch (cmd)
{
// does one of these set a callback as well? (see coded arms)
case 0x02015804: // Register callback
if (Memory::IsValidAddress(argAddr) && argLen == 4) {
u32 cbId = Memory::Read_U32(argAddr);
if (0 == __KernelRegisterCallback(THREAD_CALLBACK_MEMORYSTICK, cbId)) {
DEBUG_LOG(HLE, "sceIoDevCtl: Memstick callback %i registered, notifying immediately.", cbId);
__KernelNotifyCallbackType(THREAD_CALLBACK_MEMORYSTICK, cbId, MemoryStick_State());
RETURN(0);
} else {
RETURN(ERROR_MEMSTICK_DEVCTL_BAD_PARAMS);
}
return;
}
break;
case 0x02025805: // Unregister callback
if (Memory::IsValidAddress(argAddr) && argLen == 4) {
u32 cbId = Memory::Read_U32(argAddr);
if (0 == __KernelUnregisterCallback(THREAD_CALLBACK_MEMORYSTICK, cbId)) {
DEBUG_LOG(HLE, "sceIoDevCtl: Unregistered memstick callback %i", cbId);
RETURN(0);
} else {
RETURN(ERROR_MEMSTICK_DEVCTL_BAD_PARAMS);
}
return;
}
break;
case 0x02025806: // Memory stick inserted?
case 0x02025801: // Memstick Driver status?
if (Memory::IsValidAddress(outPtr)) {
Memory::Write_U32(1, outPtr);
RETURN(0);
} else {
RETURN(ERROR_MEMSTICK_DEVCTL_BAD_PARAMS);
}
return;
case 0x02425818: // Get memstick size etc
// Pretend we have a 2GB memory stick.
if (Memory::IsValidAddress(argAddr)) { // "Should" be outPtr but isn't
u32 pointer = Memory::Read_U32(argAddr);
u64 totalSize = (u32)2 * 1024 * 1024 * 1024;
u64 freeSize = 1 * 1024 * 1024 * 1024;
DeviceSize deviceSize;
deviceSize.maxSectors = 512;
deviceSize.sectorSize = 0x200;
deviceSize.sectorsPerCluster = 0x08;
deviceSize.totalClusters = (u32)((totalSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster));
deviceSize.freeClusters = (u32)((freeSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster));
Memory::WriteStruct(pointer, &deviceSize);
RETURN(0);
} else {
RETURN(ERROR_MEMSTICK_DEVCTL_BAD_PARAMS);
}
return;
}
}
if (!strcmp(name, "fatms0:"))
{
switch (cmd) {
case 0x02415821: // MScmRegisterMSInsertEjectCallback
{
u32 cbId = Memory::Read_U32(argAddr);
if (0 == __KernelRegisterCallback(THREAD_CALLBACK_MEMORYSTICK_FAT, cbId)) {
DEBUG_LOG(HLE, "sceIoDevCtl: Memstick FAT callback %i registered, notifying immediately.", cbId);
__KernelNotifyCallbackType(THREAD_CALLBACK_MEMORYSTICK_FAT, cbId, MemoryStick_FatState());
RETURN(0);
} else {
RETURN(-1);
}
return;
}
break;
case 0x02415822: // MScmUnregisterMSInsertEjectCallback
{
u32 cbId = Memory::Read_U32(argAddr);
if (0 == __KernelUnregisterCallback(THREAD_CALLBACK_MEMORYSTICK_FAT, cbId)) {
DEBUG_LOG(HLE, "sceIoDevCtl: Unregistered memstick FAT callback %i", cbId);
RETURN(0);
} else {
RETURN(-1);
}
return;
}
case 0x02425823: // Check if valid
if (Memory::IsValidAddress(outPtr))
Memory::Write_U32(1, outPtr); // TODO: Make a headless mode for running tests!
break;
case 0x02425818: // Get memstick size etc
// Pretend we have a 2GB memory stick.
{
if (Memory::IsValidAddress(argAddr)) { // "Should" be outPtr but isn't
u32 pointer = Memory::Read_U32(argAddr);
u64 totalSize = (u32)2 * 1024 * 1024 * 1024;
u64 freeSize = 1 * 1024 * 1024 * 1024;
DeviceSize deviceSize;
deviceSize.maxSectors = 512;
deviceSize.sectorSize = 0x200;
deviceSize.sectorsPerCluster = 0x08;
deviceSize.totalClusters = (u32)((totalSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster));
deviceSize.freeClusters = (u32)((freeSize * 95 / 100) / (deviceSize.sectorSize * deviceSize.sectorsPerCluster));
Memory::WriteStruct(pointer, &deviceSize);
RETURN(0);
} else {
RETURN(ERROR_MEMSTICK_DEVCTL_BAD_PARAMS);
}
return;
}
}
}
if (!strcmp(name, "kemulator:") || !strcmp(name, "emulator:"))
{
// Emulator special tricks!
switch (cmd)
{
case 1: // EMULATOR_DEVCTL__GET_HAS_DISPLAY
if (Memory::IsValidAddress(outPtr))
Memory::Write_U32(0, outPtr); // TODO: Make a headless mode for running tests!
RETURN(0);
return;
case 2: // EMULATOR_DEVCTL__SEND_OUTPUT
{
std::string data(Memory::GetCharPointer(argAddr), argLen);
if (PSP_CoreParameter().printfEmuLog)
{
puts(data.c_str());
#ifdef _WIN32
OutputDebugString(data.c_str());
#endif
// Also collect the debug output
emuDebugOutput += data;
}
else
{
DEBUG_LOG(HLE, "%s", data.c_str());
}
RETURN(0);
return;
}
case 3: // EMULATOR_DEVCTL__IS_EMULATOR
if (Memory::IsValidAddress(outPtr))
Memory::Write_U32(1, outPtr); // TODO: Make a headless mode for running tests!
RETURN(0);
return;
}
ERROR_LOG(HLE, "sceIoDevCtl: UNKNOWN PARAMETERS");
RETURN(0);
return;
}
//089c6d1c weird branch
/*
089c6bdc ]: HLE: sceKernelCreateCallback(name= MemoryStick Detection ,entry= 089c7484 ) (z_un_089c6bc4)
089c6c18 ]: HLE: sceIoDevctl("mscmhc0:", 02015804, 09ffb9c0, 4, 00000000, 0) (z_un_089c6bc4)
089c6c40 ]: HLE: sceKernelCreateCallback(name= MemoryStick Assignment ,entry= 089c7534 ) (z_un_089c6bc4)
089c6c78 ]: HLE: sceIoDevctl("fatms0:", 02415821, 09ffb9c4, 4, 00000000, 0) (z_un_089c6bc4)
089c6cac ]: HLE: sceIoDevctl("mscmhc0:", 02025806, 00000000, 0, 09ffb9c8, 4) (z_un_089c6bc4)
*/
RETURN(SCE_KERNEL_ERROR_UNSUP);
}
void sceIoRename() //(const char *oldname, const char *newname);
{
const char *from = Memory::GetCharPointer(PARAM(0));
const char *to = Memory::GetCharPointer(PARAM(1));
ERROR_LOG(HLE,"UNIMPL sceIoRename(%s, %s)", from, to);
RETURN(0);
}
void sceIoChdir()
{
const char *dir = Memory::GetCharPointer(PARAM(0));
pspFileSystem.ChDir(dir);
DEBUG_LOG(HLE,"sceIoChdir(%s)",dir);
RETURN(1);
}
typedef u32 (*DeferredAction)(SceUID id, int param);
DeferredAction defAction = 0;
u32 defParam;
void sceIoChangeAsyncPriority()
{
ERROR_LOG(HLE,"UNIMPL sceIoChangeAsyncPriority(%d)", PARAM(0));
RETURN(0);
}
u32 __IoClose(SceUID id, int param)
{
DEBUG_LOG(HLE,"Deferred IoClose(%d)",id);
__IoCompleteAsyncIO(id);
return kernelObjects.Destroy<FileNode>(id);
}
void sceIoCloseAsync()
{
DEBUG_LOG(HLE,"sceIoCloseAsync(%d)",PARAM(0));
//sceIoClose();
defAction = &__IoClose;
RETURN(0);
}
void sceIoLseekAsync()
{
sceIoLseek();
__IoCompleteAsyncIO(PARAM(0));
RETURN(0);
}
void sceIoSetAsyncCallback()
{
DEBUG_LOG(HLE,"sceIoSetAsyncCallback(%d, %i, %08x)",PARAM(0), PARAM(1), PARAM(2));
SceUID id = PARAM(0);
u32 error;
FileNode *f = kernelObjects.Get<FileNode>(id, error);
if (f)
{
f->callbackID = PARAM(1);
f->callbackArg = PARAM(2);
RETURN(0);
}
else
{
RETURN(error);
}
}
void sceIoLseek32Async()
{
DEBUG_LOG(HLE,"sceIoLseek32Async(%d) sorta implemented",PARAM(0));
sceIoLseek32();
__IoCompleteAsyncIO(PARAM(0));
RETURN(0);
}
void sceIoOpenAsync()
{
DEBUG_LOG(HLE,"sceIoOpenAsync() sorta implemented");
sceIoOpen();
// __IoCompleteAsyncIO(currentMIPS->r[2]); // The return value
// We have to return a UID here, which may have been destroyed when we reach Wait if it failed.
// Now that we're just faking it, we just don't RETURN(0) here.
}
void sceIoReadAsync()
{
DEBUG_LOG(HLE,"sceIoReadAsync(%d)",PARAM(0));
sceIoRead();
__IoCompleteAsyncIO(PARAM(0));
RETURN(0);
}
void sceIoGetAsyncStat()
{
SceUID id = PARAM(0);
u32 error;
FileNode *f = kernelObjects.Get<FileNode>(id, error);
if (f)
{
u64 *resPtr = (u64*)Memory::GetPointer(PARAM(2));
*resPtr = f->asyncResult;
DEBUG_LOG(HLE,"%i = sceIoGetAsyncStat(%i, %i, %08x) (HACK)", (u32)*resPtr, id, PARAM(1), PARAM(2));
RETURN(0); //completed
}
else
{
ERROR_LOG(HLE,"ERROR - sceIoGetAsyncStat with invalid id %i", id);
RETURN(-1);
}
}
void sceIoWaitAsync()
{
SceUID id = PARAM(0);
u32 error;
FileNode *f = kernelObjects.Get<FileNode>(id, error);
if (f)
{
u64 *resPtr = (u64*)Memory::GetPointer(PARAM(1));
*resPtr = f->asyncResult;
if (defAction)
{
*resPtr = defAction(id, defParam);
defAction = 0;
}
DEBUG_LOG(HLE,"%i = sceIoWaitAsync(%i, %08x) (HACK)", (u32)*resPtr, id, PARAM(1));
RETURN(0); //completed
}
else
{
ERROR_LOG(HLE,"ERROR - sceIoWaitAsync waiting for invalid id %i", id);
RETURN(-1);
}
}
void sceIoWaitAsyncCB()
{
// Should process callbacks here
SceUID id = PARAM(0);
u32 error;
FileNode *f = kernelObjects.Get<FileNode>(id, error);
if (f)
{
u64 *resPtr = (u64*)Memory::GetPointer(PARAM(1));
*resPtr = f->asyncResult;
if (defAction)
{
*resPtr = defAction(id, defParam);
defAction = 0;
}
DEBUG_LOG(HLE,"%i = sceIoWaitAsyncCB(%i, %08x) (HACK)", (u32)*resPtr, id, PARAM(1));
RETURN(0); //completed
}
else
{
ERROR_LOG(HLE,"ERROR - sceIoWaitAsyncCB waiting for invalid id %i", id);
}
}
void sceIoPollAsync()
{
SceUID id = PARAM(0);
u32 error;
FileNode *f = kernelObjects.Get<FileNode>(id, error);
if (f)
{
u64 *resPtr = (u64*)Memory::GetPointer(PARAM(1));
*resPtr = f->asyncResult;
if (defAction)
{
*resPtr = defAction(id, defParam);
defAction = 0;
}
DEBUG_LOG(HLE,"%i = sceIoPollAsync(%i, %08x) (HACK)", (u32)*resPtr, id, PARAM(1));
RETURN(0); //completed
}
else
{
ERROR_LOG(HLE,"ERROR - sceIoPollAsync waiting for invalid id %i", id);
}
}
class DirListing : public KernelObject
{
public:
const char *GetName() {return name.c_str();}
const char *GetTypeName() {return "DirListing";}
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_BADF; }
int GetIDType() const { return 0; }
std::string name;
std::vector<PSPFileInfo> listing;
int index;
};
void sceIoDopen() //(const char *path);
{
const char *path = Memory::GetCharPointer(PARAM(0));
DEBUG_LOG(HLE,"sceIoDopen(\"%s\")",path);
DirListing *dir = new DirListing();
SceUID id = kernelObjects.Create(dir);
// TODO: ERROR_ERRNO_FILE_NOT_FOUND
dir->listing = pspFileSystem.GetDirListing(path);
dir->index = 0;
dir->name = std::string(path);
RETURN(id);
}
void sceIoDread()
{
SceUID id = PARAM(0);
u32 error;
DirListing *dir = kernelObjects.Get<DirListing>(id, error);
if (dir)
{
if (dir->index == (int)dir->listing.size())
{
DEBUG_LOG(HLE,"sceIoDread( %d %08x ) - end of the line", PARAM(0), PARAM(1));
RETURN(0);
return;
}
PSPFileInfo &info = dir->listing[dir->index];
SceIoDirEnt *entry = (SceIoDirEnt*)Memory::GetPointer(PARAM(1));
__IoGetStat(&entry->d_stat, info);
strncpy(entry->d_name, info.name.c_str(), 256);
entry->d_private = 0xC0DEBABE;
DEBUG_LOG(HLE,"sceIoDread( %d %08x ) = %s", PARAM(0), PARAM(1), entry->d_name);
dir->index++;
RETURN((u32)(dir->listing.size()-dir->index+1));
}
else
{
DEBUG_LOG(HLE,"sceIoDread - invalid listing %i, error %08x", id, error);
}
}
void sceIoDclose()
{
u32 id = PARAM(0);
DEBUG_LOG(HLE,"sceIoDclose(%d)",id);
RETURN(kernelObjects.Destroy<DirListing>(id));
}
const HLEFunction IoFileMgrForUser[] =
{
{0xb29ddf9c,sceIoDopen, "sceIoDopen"},
{0xe3eb004c,sceIoDread, "sceIoDread"},
{0xeb092469,sceIoDclose,"sceIoDclose"},
{0xe95a012b,0,"sceIoIoctlAsync"},
{0x63632449,0,"sceIoIoctl"},
{0xace946e8,sceIoGetstat,"sceIoGetstat"},
{0xb8a740f4,0,"sceIoChstat"},
{0x55f4717d,sceIoChdir,"sceIoChdir"},
{0x08bd7374,0,"sceIoGetDevType"},
{0xB2A628C1,sceIoAssign,"sceIoAssign"},
{0xe8bc6571,0,"sceIoCancel"},
{0xb293727f,sceIoChangeAsyncPriority,"sceIoChangeAsyncPriority"},
{0x810C4BC3,sceIoClose, "sceIoClose"}, //(int fd);
{0xff5940b6,sceIoCloseAsync,"sceIoCloseAsync"},
{0x54F5FB11,sceIoDevctl,"sceIoDevctl"}, //(const char *name int cmd, void *arg, size_t arglen, void *buf, size_t *buflen);
{0xcb05f8d6,sceIoGetAsyncStat,"sceIoGetAsyncStat"},
{0x27EB27B8,sceIoLseek, "sceIoLseek"}, //(int fd, int offset, int whence);
{0x68963324,sceIoLseek32,"sceIoLseek32"},
{0x1b385d8f,sceIoLseek32Async,"sceIoLseek32Async"},
{0x71b19e77,sceIoLseekAsync,"sceIoLseekAsync"},
{0x109F50BC,sceIoOpen, "sceIoOpen"}, //(const char* file, int mode);
{0x89AA9906,sceIoOpenAsync,"sceIoOpenAsync"},
{0x06A70004,sceIoMkdir,"sceIoMkdir"}, //(const char *dir, int mode);
{0x3251ea56,sceIoPollAsync,"sceIoPollAsync"},
{0x6A638D83,sceIoRead, "sceIoRead"}, //(int fd, void *data, int size);
{0xa0b5a7c2,sceIoReadAsync,"sceIoReadAsync"},
{0xF27A9C51,sceIoRemove,"sceIoRemove"}, //(const char *file);
{0x779103A0,sceIoRename,"sceIoRename"}, //(const char *oldname, const char *newname);
{0x1117C65F,sceIoRmdir,"sceIoRmdir"}, //(const char *dir);
{0xA12A0514,sceIoSetAsyncCallback,"sceIoSetAsyncCallback"},
{0xab96437f,sceIoSync,"sceIoSync"},
{0x6d08a871,0,"sceIoUnassign"},
{0x42EC03AC,sceIoWrite, "sceIoWrite"}, //(int fd, void *data, int size);
{0x0facab19,0,"sceIoWriteAsync"},
{0x35dbd746,sceIoWaitAsyncCB,"sceIoWaitAsyncCB"},
{0xe23eec33,sceIoWaitAsync,"sceIoWaitAsync"},
};
void Register_IoFileMgrForUser()
{
RegisterModule("IoFileMgrForUser", ARRAY_SIZE(IoFileMgrForUser), IoFileMgrForUser);
}
const HLEFunction StdioForUser[] =
{
{0x172D316E,sceKernelStdin,"sceKernelStdin"},
{0xA6BAB2E9,sceKernelStdout,"sceKernelStdout"},
{0xF78BA90A,sceKernelStderr,"sceKernelStderr"},
};
void Register_StdioForUser()
{
RegisterModule("StdioForUser", ARRAY_SIZE(StdioForUser), StdioForUser);
}