2012-11-01 15:19:01 +00:00
// 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
2012-11-04 22:01:49 +00:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 15:19:01 +00:00
// 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/.
2013-05-28 06:59:03 +00:00
# include <cstdlib>
2013-09-02 08:25:41 +00:00
# include <set>
2017-02-27 19:51:36 +00:00
# include <thread>
2021-07-24 08:31:15 +00:00
# include <memory>
2013-12-29 23:11:29 +00:00
2020-10-01 07:27:25 +00:00
# include "Common/Thread/ThreadUtil.h"
2020-10-04 08:04:01 +00:00
# include "Common/Profiler/Profiler.h"
2021-09-11 16:06:26 +00:00
# include "Common/TimeUtil.h"
2015-05-13 20:28:02 +00:00
2020-10-04 18:48:47 +00:00
# include "Common/File/FileUtil.h"
2020-08-10 07:12:51 +00:00
# include "Common/Serialize/SerializeFuncs.h"
# include "Common/Serialize/SerializeMap.h"
# include "Common/Serialize/SerializeSet.h"
2021-04-03 23:04:30 +00:00
# include "Common/StringUtils.h"
2023-03-26 00:34:29 +00:00
# include "Common/System/Request.h"
2013-12-29 23:11:29 +00:00
# include "Core/Core.h"
2013-03-28 19:17:45 +00:00
# include "Core/Config.h"
2018-06-17 01:42:31 +00:00
# include "Core/ConfigValues.h"
2021-02-02 08:08:05 +00:00
# include "Core/Debugger/MemBlockInfo.h"
2015-12-27 08:57:01 +00:00
# include "Core/ELF/ParamSFO.h"
2015-04-06 01:09:35 +00:00
# include "Core/MemMapHelpers.h"
2013-03-28 19:17:45 +00:00
# include "Core/System.h"
2015-12-27 08:57:01 +00:00
# include "Core/HDRemaster.h"
2013-03-28 19:17:45 +00:00
# include "Core/SaveState.h"
2013-08-11 18:51:36 +00:00
# include "Core/HLE/HLE.h"
2019-07-28 22:43:19 +00:00
# include "Core/HLE/HLEHelperThread.h"
2014-03-15 18:22:19 +00:00
# include "Core/HLE/FunctionWrappers.h"
2014-03-15 17:48:30 +00:00
# include "Core/HLE/sceKernel.h"
2016-08-06 03:42:58 +00:00
# include "Core/HLE/sceUmd.h"
2022-01-30 17:41:08 +00:00
# include "Core/HW/Display.h"
2013-03-28 19:17:45 +00:00
# include "Core/MIPS/MIPS.h"
# include "Core/HW/MemoryStick.h"
2013-08-11 18:51:36 +00:00
# include "Core/HW/AsyncIOManager.h"
2013-03-10 08:02:36 +00:00
# include "Core/CoreTiming.h"
2013-03-10 22:03:24 +00:00
# include "Core/Reporting.h"
2012-11-01 15:19:01 +00:00
2013-08-11 18:51:36 +00:00
# include "Core/FileSystems/FileSystem.h"
# include "Core/FileSystems/MetaFileSystem.h"
# include "Core/FileSystems/ISOFileSystem.h"
# include "Core/FileSystems/DirectoryFileSystem.h"
2012-11-01 15:19:01 +00:00
2013-02-24 08:28:40 +00:00
extern " C " {
# include "ext/libkirk/amctrl.h"
} ;
2013-08-11 18:51:36 +00:00
# include "Core/HLE/sceIo.h"
# include "Core/HLE/sceRtc.h"
# include "Core/HLE/sceKernel.h"
# include "Core/HLE/sceKernelMemory.h"
# include "Core/HLE/sceKernelThread.h"
2013-08-29 06:15:13 +00:00
# include "Core/HLE/sceKernelInterrupt.h"
2013-09-07 17:26:26 +00:00
# include "Core/HLE/KernelWaitHelpers.h"
2012-11-01 15:19:01 +00:00
2013-01-14 00:35:34 +00:00
// For headless screenshots.
2013-08-11 18:51:36 +00:00
# include "Core/HLE/sceDisplay.h"
2022-11-26 13:55:45 +00:00
// For EMULATOR_DEVCTL__GET_SCALE
2022-12-05 11:46:00 +00:00
# include "System/Display.h"
// For EMULATOR_DEVCTL__GET_AXIS/VKEY
# include "Core/HLE/Plugins.h"
2023-02-01 10:26:17 +00:00
# include "Input/KeyCodes.h"
2022-12-04 22:06:31 +00:00
2016-03-06 13:16:40 +00:00
static const int ERROR_ERRNO_IO_ERROR = 0x80010005 ;
static const int ERROR_MEMSTICK_DEVCTL_BAD_PARAMS = 0x80220081 ;
static const int ERROR_MEMSTICK_DEVCTL_TOO_MANY_CALLBACKS = 0x80220082 ;
static const int ERROR_PGD_INVALID_HEADER = 0x80510204 ;
2012-11-01 15:19:01 +00:00
/*
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
2012-12-05 21:19:14 +00:00
umd01 : block access - umd
2012-11-01 15:19:01 +00:00
*/
2017-02-19 09:04:54 +00:00
# define PSP_O_RDONLY 0x0001
# define PSP_O_WRONLY 0x0002
# define PSP_O_RDWR 0x0003
# define PSP_O_NBLOCK 0x0010
# define PSP_O_APPEND 0x0100
# define PSP_O_CREAT 0x0200
# define PSP_O_TRUNC 0x0400
# define PSP_O_EXCL 0x0800
# define PSP_O_NOWAIT 0x8000
# define PSP_O_NPDRM 0x40000000
2012-11-01 15:19:01 +00:00
2013-03-16 08:38:50 +00:00
// chstat
# define SCE_CST_MODE 0x0001
# define SCE_CST_ATTR 0x0002
# define SCE_CST_SIZE 0x0004
# define SCE_CST_CT 0x0008
# define SCE_CST_AT 0x0010
# define SCE_CST_MT 0x0020
# define SCE_CST_PRVT 0x0040
2012-11-01 15:19:01 +00:00
typedef s32 SceMode ;
typedef s64 SceOff ;
typedef u64 SceIores ;
2013-06-16 17:05:30 +00:00
const int PSP_COUNT_FDS = 64 ;
// TODO: Should be 3, and stdin/stdout/stderr are special values aliased to 0?
const int PSP_MIN_FD = 4 ;
2013-12-27 06:07:41 +00:00
const int PSP_STDOUT = 1 ;
const int PSP_STDERR = 2 ;
const int PSP_STDIN = 3 ;
2013-06-16 17:05:30 +00:00
static int asyncNotifyEvent = - 1 ;
2013-08-11 18:53:21 +00:00
static int syncNotifyEvent = - 1 ;
2013-06-16 17:05:30 +00:00
static SceUID fds [ PSP_COUNT_FDS ] ;
2016-08-06 04:05:48 +00:00
2016-08-06 03:42:58 +00:00
static std : : vector < SceUID > memStickCallbacks ;
static std : : vector < SceUID > memStickFatCallbacks ;
2016-08-06 04:05:48 +00:00
static MemStickState lastMemStickState ;
static MemStickFatState lastMemStickFatState ;
2013-08-11 18:51:36 +00:00
static AsyncIOManager ioManager ;
static bool ioManagerThreadEnabled = false ;
2020-07-05 08:37:44 +00:00
static std : : thread * ioManagerThread ;
2013-06-16 17:05:30 +00:00
2013-08-11 19:04:38 +00:00
// TODO: Is it better to just put all on the thread?
2013-08-21 18:50:45 +00:00
// Let's try. (was 256)
const int IO_THREAD_MIN_DATA_SIZE = 0 ;
2013-08-11 19:04:38 +00:00
2012-11-01 15:19:01 +00:00
# define SCE_STM_FDIR 0x1000
# define SCE_STM_FREG 0x2000
# define SCE_STM_FLNK 0x4000
2013-01-17 08:44:35 +00:00
2012-12-06 14:16:05 +00:00
enum {
2014-03-03 16:16:53 +00:00
TYPE_DIR = 0x10 ,
TYPE_FILE = 0x20
2012-11-01 15:19:01 +00:00
} ;
2012-12-06 14:16:05 +00:00
struct SceIoStat {
2013-07-27 22:41:19 +00:00
SceMode_le st_mode ;
u32_le st_attr ;
SceOff_le st_size ;
2014-06-10 17:10:50 +00:00
ScePspDateTime st_c_time ;
ScePspDateTime st_a_time ;
ScePspDateTime st_m_time ;
2013-07-27 22:41:19 +00:00
u32_le st_private [ 6 ] ;
2012-11-01 15:19:01 +00:00
} ;
2012-12-06 14:16:05 +00:00
struct SceIoDirEnt {
2012-11-01 15:19:01 +00:00
SceIoStat d_stat ;
char d_name [ 256 ] ;
2013-07-27 22:41:19 +00:00
u32_le d_private ;
2012-11-01 15:19:01 +00:00
} ;
2019-07-28 22:43:19 +00:00
enum class IoAsyncOp {
NONE ,
OPEN ,
CLOSE ,
READ ,
WRITE ,
SEEK ,
IOCTL ,
} ;
struct IoAsyncParams {
IoAsyncOp op = IoAsyncOp : : NONE ;
int priority = - 1 ;
union {
struct {
u32 filenameAddr ;
int flags ;
int mode ;
} open ;
struct {
u32 addr ;
u32 size ;
} std ;
struct {
s64 pos ;
int whence ;
} seek ;
struct {
u32 cmd ;
u32 inAddr ;
u32 inSize ;
u32 outAddr ;
u32 outSize ;
} ioctl ;
} ;
} ;
static IoAsyncParams asyncParams [ PSP_COUNT_FDS ] ;
static HLEHelperThread * asyncThreads [ PSP_COUNT_FDS ] { } ;
2019-12-26 19:26:31 +00:00
static int asyncDefaultPriority = - 1 ;
2019-07-28 22:43:19 +00:00
2013-01-02 11:35:37 +00:00
class FileNode : public KernelObject {
public :
2021-06-06 16:56:41 +00:00
FileNode ( ) { }
2013-01-02 11:35:37 +00:00
~ FileNode ( ) {
2021-06-06 16:56:41 +00:00
if ( handle ! = - 1 )
pspFileSystem . CloseFile ( handle ) ;
2013-05-28 06:59:03 +00:00
pgd_close ( pgdInfo ) ;
2013-01-02 11:35:37 +00:00
}
2014-12-08 20:14:35 +00:00
const char * GetName ( ) override { return fullpath . c_str ( ) ; }
2020-07-20 09:01:52 +00:00
const char * GetTypeName ( ) override { return GetStaticTypeName ( ) ; }
static const char * GetStaticTypeName ( ) { return " OpenFile " ; }
2023-04-28 19:04:05 +00:00
void GetQuickInfo ( char * buf , int bufSize ) override {
snprintf ( buf , bufSize , " Seekpos: %08x " , ( u32 ) pspFileSystem . GetSeekPos ( handle ) ) ;
2013-01-02 11:35:37 +00:00
}
static u32 GetMissingErrorCode ( ) { return SCE_KERNEL_ERROR_BADF ; }
2013-06-19 06:54:29 +00:00
static int GetStaticIDType ( ) { return PPSSPP_KERNEL_TMID_File ; }
2014-12-08 20:14:35 +00:00
int GetIDType ( ) const override { return PPSSPP_KERNEL_TMID_File ; }
2013-01-02 11:35:37 +00:00
2013-10-09 06:49:24 +00:00
bool asyncBusy ( ) {
return pendingAsyncResult | | hasAsyncResult ;
}
2022-10-09 15:16:33 +00:00
const PSPFileInfo & FileInfo ( ) {
if ( ! infoReady ) {
info = pspFileSystem . GetFileInfo ( fullpath ) ;
if ( ! info . exists ) {
ERROR_LOG ( IO , " File %s no longer exists when reading info " , fullpath . c_str ( ) ) ;
}
infoReady = true ;
}
return info ;
}
2014-12-08 20:14:35 +00:00
void DoState ( PointerWrap & p ) override {
2021-06-06 16:56:41 +00:00
auto s = p . Section ( " FileNode " , 1 , 3 ) ;
2013-09-15 03:23:03 +00:00
if ( ! s )
return ;
2020-08-10 04:20:42 +00:00
Do ( p , fullpath ) ;
Do ( p , handle ) ;
Do ( p , callbackID ) ;
Do ( p , callbackArg ) ;
Do ( p , asyncResult ) ;
Do ( p , hasAsyncResult ) ;
Do ( p , pendingAsyncResult ) ;
Do ( p , sectorBlockMode ) ;
Do ( p , closePending ) ;
Do ( p , info ) ;
Do ( p , openMode ) ;
2022-10-09 15:16:33 +00:00
if ( p . mode = = p . MODE_READ ) {
infoReady = info . exists ;
}
2020-08-10 04:20:42 +00:00
Do ( p , npdrm ) ;
Do ( p , pgd_offset ) ;
2013-05-28 06:59:03 +00:00
bool hasPGD = pgdInfo ! = NULL ;
2020-08-10 04:20:42 +00:00
Do ( p , hasPGD ) ;
2013-05-28 06:59:03 +00:00
if ( hasPGD ) {
if ( p . mode = = p . MODE_READ ) {
pgdInfo = ( PGD_DESC * ) malloc ( sizeof ( PGD_DESC ) ) ;
}
2022-12-11 05:09:50 +00:00
if ( pgdInfo )
p . DoVoid ( pgdInfo , sizeof ( PGD_DESC ) ) ;
if ( p . mode = = p . MODE_READ & & pgdInfo ) {
2013-05-28 06:59:03 +00:00
pgdInfo - > block_buf = ( u8 * ) malloc ( pgdInfo - > block_size * 2 ) ;
}
}
2013-03-04 00:05:05 +00:00
2020-08-10 04:20:42 +00:00
Do ( p , waitingThreads ) ;
2014-12-14 04:12:37 +00:00
if ( s > = 2 ) {
2020-08-10 04:20:42 +00:00
Do ( p , waitingSyncThreads ) ;
2014-12-14 04:12:37 +00:00
}
2021-06-06 16:56:41 +00:00
if ( s > = 3 ) {
Do ( p , isTTY ) ;
}
2020-08-10 04:20:42 +00:00
Do ( p , pausedWaits ) ;
2013-01-02 11:35:37 +00:00
}
std : : string fullpath ;
u32 handle ;
2021-06-06 16:56:41 +00:00
u32 callbackID = 0 ;
u32 callbackArg = 0 ;
2013-01-02 11:35:37 +00:00
2021-06-06 16:56:41 +00:00
s64 asyncResult = 0 ;
bool hasAsyncResult = false ;
bool pendingAsyncResult = false ;
2013-03-10 09:12:03 +00:00
2021-06-06 16:56:41 +00:00
bool sectorBlockMode = false ;
2013-03-10 09:12:03 +00:00
// TODO: Use an enum instead?
2021-06-06 16:56:41 +00:00
bool closePending = false ;
2013-01-05 23:39:39 +00:00
2022-10-09 15:16:33 +00:00
bool infoReady = false ;
2013-01-05 23:39:39 +00:00
PSPFileInfo info ;
2021-06-06 16:56:41 +00:00
u32 openMode = 0 ;
2013-02-24 08:28:40 +00:00
2021-06-06 16:56:41 +00:00
u32 npdrm = 0 ;
u32 pgd_offset = 0 ;
PGD_DESC * pgdInfo = nullptr ;
2013-09-07 17:09:48 +00:00
std : : vector < SceUID > waitingThreads ;
2014-12-14 04:12:37 +00:00
std : : vector < SceUID > waitingSyncThreads ;
2013-09-07 17:09:48 +00:00
// Key is the callback id it was for, or if no callback, the thread id.
// Value is actually meaningless but kept for consistency with other wait types.
std : : map < SceUID , u64 > pausedWaits ;
2021-06-06 16:56:41 +00:00
bool isTTY = false ;
2013-01-02 11:35:37 +00:00
} ;
2013-02-24 08:28:40 +00:00
/******************************************************************************/
/******************************************************************************/
2013-12-28 00:36:51 +00:00
u64 __IoCompleteAsyncIO ( FileNode * f ) ;
2013-03-10 08:02:36 +00:00
2020-01-03 21:09:58 +00:00
static void IoAsyncCleanupThread ( int fd ) {
if ( asyncThreads [ fd ] ) {
if ( ! asyncThreads [ fd ] - > Stopped ( ) ) {
asyncThreads [ fd ] - > Terminate ( ) ;
}
2019-07-28 23:12:55 +00:00
delete asyncThreads [ fd ] ;
asyncThreads [ fd ] = nullptr ;
}
}
2019-02-06 09:45:12 +00:00
static int GetIOTimingMethod ( ) {
if ( PSP_CoreParameter ( ) . compat . flags ( ) . ForceUMDDelay ) {
return IOTIMING_REALISTIC ;
} else {
return g_Config . iIOTimingMethod ;
}
}
2013-01-11 00:25:30 +00:00
static void TellFsThreadEnded ( SceUID threadID ) {
2013-01-10 11:27:10 +00:00
pspFileSystem . ThreadEnded ( threadID ) ;
}
2014-12-08 09:40:08 +00:00
static FileNode * __IoGetFd ( int fd , u32 & error ) {
2013-06-16 17:05:30 +00:00
if ( fd < 0 | | fd > = PSP_COUNT_FDS ) {
2016-07-24 19:22:26 +00:00
error = SCE_KERNEL_ERROR_BADF ;
2013-06-16 17:05:30 +00:00
return NULL ;
}
return kernelObjects . Get < FileNode > ( fds [ fd ] , error ) ;
}
2014-12-08 09:40:08 +00:00
static int __IoAllocFd ( FileNode * f ) {
2013-06-16 17:05:30 +00:00
// The PSP takes the lowest available id after stderr/etc.
for ( int possible = PSP_MIN_FD ; possible < PSP_COUNT_FDS ; + + possible ) {
if ( fds [ possible ] = = 0 ) {
fds [ possible ] = f - > GetUID ( ) ;
return possible ;
}
}
// Bugger, out of fds...
return SCE_KERNEL_ERROR_MFILE ;
}
2014-12-08 09:40:08 +00:00
static void __IoFreeFd ( int fd , u32 & error ) {
2016-07-24 14:03:34 +00:00
if ( fd = = PSP_STDIN | | fd = = PSP_STDERR | | fd = = PSP_STDOUT ) {
2016-07-24 19:22:26 +00:00
error = SCE_KERNEL_ERROR_ILLEGAL_PERM ;
2016-07-24 14:03:34 +00:00
} else if ( fd < PSP_MIN_FD | | fd > = PSP_COUNT_FDS ) {
2016-07-24 19:22:26 +00:00
error = SCE_KERNEL_ERROR_BADF ;
2013-06-16 17:05:30 +00:00
} else {
2013-09-07 17:26:26 +00:00
FileNode * f = __IoGetFd ( fd , error ) ;
if ( f ) {
2016-07-24 14:03:34 +00:00
// If there are pending results, don't allow closing.
if ( ioManager . HasOperation ( f - > handle ) ) {
2016-07-24 19:22:26 +00:00
error = SCE_KERNEL_ERROR_ASYNC_BUSY ;
2016-07-24 14:03:34 +00:00
return ;
}
2013-09-07 17:26:26 +00:00
// Wake anyone waiting on the file before closing it.
for ( size_t i = 0 ; i < f - > waitingThreads . size ( ) ; + + i ) {
2013-09-15 15:18:20 +00:00
HLEKernel : : ResumeFromWait ( f - > waitingThreads [ i ] , WAITTYPE_ASYNCIO , f - > GetUID ( ) , ( int ) SCE_KERNEL_ERROR_WAIT_DELETE ) ;
2013-09-07 17:26:26 +00:00
}
2014-01-27 02:49:34 +00:00
2014-12-14 04:12:37 +00:00
CoreTiming : : UnscheduleEvent ( asyncNotifyEvent , fd ) ;
for ( size_t i = 0 ; i < f - > waitingSyncThreads . size ( ) ; + + i ) {
CoreTiming : : UnscheduleEvent ( syncNotifyEvent , ( ( u64 ) f - > waitingSyncThreads [ i ] < < 32 ) | fd ) ;
}
2015-05-28 03:02:09 +00:00
PROFILE_THIS_SCOPE ( " io_rw " ) ;
2014-01-27 02:49:34 +00:00
// Discard any pending results.
AsyncIOResult managerResult ;
ioManager . WaitResult ( f - > handle , managerResult ) ;
2019-07-28 23:12:55 +00:00
IoAsyncCleanupThread ( fd ) ;
2013-09-07 17:26:26 +00:00
}
2013-06-16 17:05:30 +00:00
error = kernelObjects . Destroy < FileNode > ( fds [ fd ] ) ;
fds [ fd ] = 0 ;
}
}
2013-03-10 08:02:36 +00:00
// Async IO seems to work roughly like this:
// 1. Game calls SceIo*Async() to start the process.
// 2. This runs a thread with a customizable priority.
// 3. The operation runs, which takes an inconsistent amount of time from UMD.
// 4. Once done (regardless of other syscalls), the fd-registered callback is notified.
// 5. The game can find out via *CB() or sceKernelCheckCallback().
// 6. At this point, the fd is STILL not usable.
// 7. One must call sceIoWaitAsync / sceIoWaitAsyncCB / sceIoPollAsync / possibly sceIoGetAsyncStat.
// 8. Finally, the fd is usable (or closed via sceIoCloseAsync.) Presumably the io thread has joined now.
2013-03-10 09:12:03 +00:00
// TODO: Closed files are a bit special: until the fd is reused (?), the async result is still available.
// Clearly a buffer is used, it doesn't seem like they are actually kernel objects.
2013-12-28 00:36:51 +00:00
// For now, let's at least delay the callback notification.
2014-12-08 09:40:08 +00:00
static void __IoAsyncNotify ( u64 userdata , int cyclesLate ) {
2013-09-07 17:09:48 +00:00
int fd = ( int ) userdata ;
2013-03-10 10:43:50 +00:00
u32 error ;
2013-09-07 17:09:48 +00:00
FileNode * f = __IoGetFd ( fd , error ) ;
if ( ! f ) {
2013-09-07 19:19:21 +00:00
ERROR_LOG_REPORT ( SCEIO , " __IoAsyncNotify: file no longer exists? " ) ;
2013-09-07 17:09:48 +00:00
return ;
}
2019-02-06 09:45:12 +00:00
int ioTimingMethod = GetIOTimingMethod ( ) ;
if ( ioTimingMethod = = IOTIMING_HOST ) {
2014-12-26 15:57:00 +00:00
// Not all async operations actually queue up. Maybe should separate them?
if ( ! ioManager . HasResult ( f - > handle ) & & ioManager . HasOperation ( f - > handle ) ) {
2014-12-26 03:02:49 +00:00
// Try again in another 0.5ms until the IO completes on the host.
CoreTiming : : ScheduleEvent ( usToCycles ( 500 ) - cyclesLate , asyncNotifyEvent , userdata ) ;
return ;
}
2014-12-26 15:49:22 +00:00
__IoCompleteAsyncIO ( f ) ;
2019-02-06 09:45:12 +00:00
} else if ( ioTimingMethod = = IOTIMING_REALISTIC ) {
2014-12-26 09:18:52 +00:00
u64 finishTicks = __IoCompleteAsyncIO ( f ) ;
if ( finishTicks > CoreTiming : : GetTicks ( ) ) {
// Reschedule for later, since we now know how long it ought to take.
CoreTiming : : ScheduleEvent ( finishTicks - CoreTiming : : GetTicks ( ) , asyncNotifyEvent , userdata ) ;
return ;
}
2014-12-26 15:49:22 +00:00
} else {
__IoCompleteAsyncIO ( f ) ;
2013-12-28 00:36:51 +00:00
}
2014-12-26 03:02:49 +00:00
2013-09-07 17:09:48 +00:00
if ( f - > waitingThreads . empty ( ) ) {
return ;
}
SceUID threadID = f - > waitingThreads . front ( ) ;
f - > waitingThreads . erase ( f - > waitingThreads . begin ( ) ) ;
2013-03-10 10:43:50 +00:00
u32 address = __KernelGetWaitValue ( threadID , error ) ;
2013-09-07 18:22:48 +00:00
if ( HLEKernel : : VerifyWait ( threadID , WAITTYPE_ASYNCIO , f - > GetUID ( ) ) ) {
HLEKernel : : ResumeFromWait ( threadID , WAITTYPE_ASYNCIO , f - > GetUID ( ) , 0 ) ;
2013-10-09 06:49:24 +00:00
// Someone woke up, so it's no longer got one.
f - > hasAsyncResult = false ;
2013-03-10 10:43:50 +00:00
2013-09-07 17:09:48 +00:00
if ( Memory : : IsValidAddress ( address ) ) {
2013-03-10 10:43:50 +00:00
Memory : : Write_U64 ( ( u64 ) f - > asyncResult , address ) ;
}
2013-04-16 07:16:22 +00:00
// If this was a sceIoCloseAsync, we should close it at this point.
if ( f - > closePending ) {
2013-06-16 17:05:30 +00:00
__IoFreeFd ( fd , error ) ;
2013-04-16 07:16:22 +00:00
}
2013-03-10 10:43:50 +00:00
}
2013-03-10 08:02:36 +00:00
}
2014-12-08 09:40:08 +00:00
static void __IoSyncNotify ( u64 userdata , int cyclesLate ) {
2015-05-28 03:02:09 +00:00
PROFILE_THIS_SCOPE ( " io_rw " ) ;
2013-08-11 18:53:21 +00:00
SceUID threadID = userdata > > 32 ;
int fd = ( int ) ( userdata & 0xFFFFFFFF ) ;
s64 result = - 1 ;
u32 error ;
FileNode * f = __IoGetFd ( fd , error ) ;
2013-09-07 17:09:48 +00:00
if ( ! f ) {
2013-09-07 19:19:21 +00:00
ERROR_LOG_REPORT ( SCEIO , " __IoSyncNotify: file no longer exists? " ) ;
2013-09-07 17:09:48 +00:00
return ;
}
2013-08-11 18:53:21 +00:00
2019-02-06 09:45:12 +00:00
int ioTimingMethod = GetIOTimingMethod ( ) ;
if ( ioTimingMethod = = IOTIMING_HOST ) {
2014-12-26 03:02:49 +00:00
if ( ! ioManager . HasResult ( f - > handle ) ) {
// Try again in another 0.5ms until the IO completes on the host.
CoreTiming : : ScheduleEvent ( usToCycles ( 500 ) - cyclesLate , syncNotifyEvent , userdata ) ;
return ;
}
2019-02-06 09:45:12 +00:00
} else if ( ioTimingMethod = = IOTIMING_REALISTIC ) {
2014-12-26 09:18:52 +00:00
u64 finishTicks = ioManager . ResultFinishTicks ( f - > handle ) ;
if ( finishTicks > CoreTiming : : GetTicks ( ) ) {
// Reschedule for later when the result should finish.
CoreTiming : : ScheduleEvent ( finishTicks - CoreTiming : : GetTicks ( ) , syncNotifyEvent , userdata ) ;
return ;
}
2013-12-28 00:36:51 +00:00
}
2013-09-07 17:09:48 +00:00
f - > pendingAsyncResult = false ;
2013-10-09 06:49:24 +00:00
f - > hasAsyncResult = false ;
2013-09-07 17:09:48 +00:00
AsyncIOResult managerResult ;
if ( ioManager . WaitResult ( f - > handle , managerResult ) ) {
2013-12-28 00:36:51 +00:00
result = managerResult . result ;
2013-09-07 17:09:48 +00:00
} else {
2013-12-11 14:11:27 +00:00
ERROR_LOG ( SCEIO , " Unable to complete IO operation on %s " , f - > GetName ( ) ) ;
2013-08-11 18:53:21 +00:00
}
2013-12-28 00:36:51 +00:00
f - > pendingAsyncResult = false ;
f - > hasAsyncResult = false ;
2014-06-28 19:29:55 +00:00
HLEKernel : : ResumeFromWait ( threadID , WAITTYPE_IO , fd , result ) ;
2014-12-14 04:12:37 +00:00
f - > waitingSyncThreads . erase ( std : : remove ( f - > waitingSyncThreads . begin ( ) , f - > waitingSyncThreads . end ( ) , threadID ) , f - > waitingSyncThreads . end ( ) ) ;
2013-08-11 18:53:21 +00:00
}
2014-12-08 09:40:08 +00:00
static void __IoAsyncBeginCallback ( SceUID threadID , SceUID prevCallbackId ) {
2013-09-07 18:22:48 +00:00
auto result = HLEKernel : : WaitBeginCallback < FileNode , WAITTYPE_ASYNCIO , SceUID > ( threadID , prevCallbackId , - 1 ) ;
if ( result = = HLEKernel : : WAIT_CB_SUCCESS ) {
2014-06-30 01:55:23 +00:00
DEBUG_LOG ( SCEIO , " sceIoWaitAsync: Suspending wait for callback " ) ;
2013-09-07 18:22:48 +00:00
} else if ( result = = HLEKernel : : WAIT_CB_BAD_WAIT_ID ) {
2013-09-07 19:19:21 +00:00
WARN_LOG_REPORT ( SCEIO , " sceIoWaitAsync: beginning callback with bad wait id? " ) ;
2013-09-07 18:22:48 +00:00
}
}
2014-12-08 09:40:08 +00:00
static bool __IoCheckAsyncWait ( FileNode * f , SceUID threadID , u32 & error , int result , bool & wokeThreads )
2013-09-07 18:22:48 +00:00
{
int fd = - 1 ;
for ( int i = 0 ; i < ( int ) ARRAY_SIZE ( fds ) ; + + i ) {
if ( fds [ i ] = = f - > GetUID ( ) ) {
fd = i ;
break ;
}
}
if ( fd = = - 1 ) {
2013-09-07 19:19:21 +00:00
ERROR_LOG_REPORT ( SCEIO , " __IoCheckAsyncWait: could not find io handle " ) ;
2013-09-07 18:22:48 +00:00
return true ;
}
if ( ! HLEKernel : : VerifyWait ( threadID , WAITTYPE_ASYNCIO , f - > GetUID ( ) ) ) {
return true ;
}
// If result is an error code, we're just letting it go.
if ( result = = 0 ) {
if ( f - > pendingAsyncResult | | ! f - > hasAsyncResult ) {
return false ;
}
u32 address = __KernelGetWaitValue ( threadID , error ) ;
Memory : : Write_U64 ( ( u64 ) f - > asyncResult , address ) ;
f - > hasAsyncResult = false ;
if ( f - > closePending ) {
__IoFreeFd ( fd , error ) ;
}
}
__KernelResumeThreadFromWait ( threadID , result ) ;
wokeThreads = true ;
return true ;
}
2014-12-08 09:40:08 +00:00
static void __IoAsyncEndCallback ( SceUID threadID , SceUID prevCallbackId ) {
2013-09-07 18:22:48 +00:00
auto result = HLEKernel : : WaitEndCallback < FileNode , WAITTYPE_ASYNCIO , SceUID > ( threadID , prevCallbackId , - 1 , __IoCheckAsyncWait ) ;
if ( result = = HLEKernel : : WAIT_CB_RESUMED_WAIT ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceKernelWaitEventFlagCB: Resuming lock wait for callback " ) ;
2013-09-07 18:22:48 +00:00
}
}
2014-12-08 09:40:08 +00:00
static void __IoManagerThread ( ) {
2020-11-30 23:46:26 +00:00
SetCurrentThreadName ( " IO " ) ;
2023-01-03 22:29:22 +00:00
AndroidJNIThreadContext jniContext ;
2020-07-04 18:19:56 +00:00
while ( ioManagerThreadEnabled & & coreState ! = CORE_BOOT_ERROR & & coreState ! = CORE_RUNTIME_ERROR & & coreState ! = CORE_POWERDOWN ) {
2013-08-11 18:51:36 +00:00
ioManager . RunEventsUntil ( CoreTiming : : GetTicks ( ) + msToCycles ( 1000 ) ) ;
}
}
2018-04-22 15:33:22 +00:00
static void __IoWakeManager ( CoreLifecycle stage ) {
2013-10-13 06:20:27 +00:00
// Ping the thread so that it knows to check coreState.
2018-04-22 15:33:22 +00:00
if ( stage = = CoreLifecycle : : STOPPING ) {
ioManagerThreadEnabled = false ;
ioManager . FinishEventLoop ( ) ;
}
2013-09-28 07:45:30 +00:00
}
2016-08-06 04:05:48 +00:00
static void __IoVblank ( ) {
// We update memstick status here just to avoid possible thread safety issues.
// It doesn't actually need to be on a vblank.
2016-08-06 04:27:53 +00:00
// This will only change status if g_Config was changed.
MemoryStick_SetState ( g_Config . bMemStickInserted ? PSP_MEMORYSTICK_STATE_INSERTED : PSP_MEMORYSTICK_STATE_NOT_INSERTED ) ;
2016-08-06 04:05:48 +00:00
MemStickState newState = MemoryStick_State ( ) ;
MemStickFatState newFatState = MemoryStick_FatState ( ) ;
// First, the fat callbacks, these are easy.
if ( lastMemStickFatState ! = newFatState ) {
int notifyMsg = 0 ;
if ( newFatState = = PSP_FAT_MEMORYSTICK_STATE_ASSIGNED ) {
notifyMsg = 1 ;
} else if ( newFatState = = PSP_FAT_MEMORYSTICK_STATE_UNASSIGNED ) {
notifyMsg = 2 ;
}
if ( notifyMsg ! = 0 ) {
for ( SceUID cbId : memStickFatCallbacks ) {
__KernelNotifyCallback ( cbId , notifyMsg ) ;
}
}
}
// Next, the controller notifies mounting (fat) too.
if ( lastMemStickState ! = newState | | lastMemStickFatState ! = newFatState ) {
int notifyMsg = 0 ;
if ( newState = = PSP_MEMORYSTICK_STATE_INSERTED & & newFatState = = PSP_FAT_MEMORYSTICK_STATE_ASSIGNED ) {
notifyMsg = 1 ;
} else if ( newState = = PSP_MEMORYSTICK_STATE_INSERTED & & newFatState = = PSP_FAT_MEMORYSTICK_STATE_UNASSIGNED ) {
// Still mounting (1 will come later.)
notifyMsg = 4 ;
} else if ( newState = = PSP_MEMORYSTICK_STATE_NOT_INSERTED ) {
notifyMsg = 2 ;
}
if ( notifyMsg ! = 0 ) {
for ( SceUID cbId : memStickCallbacks ) {
__KernelNotifyCallback ( cbId , notifyMsg ) ;
}
}
}
lastMemStickState = newState ;
lastMemStickFatState = newFatState ;
}
2012-12-06 14:16:05 +00:00
void __IoInit ( ) {
2013-03-10 08:02:36 +00:00
asyncNotifyEvent = CoreTiming : : RegisterEvent ( " IoAsyncNotify " , __IoAsyncNotify ) ;
2013-08-11 18:53:21 +00:00
syncNotifyEvent = CoreTiming : : RegisterEvent ( " IoSyncNotify " , __IoSyncNotify ) ;
2013-03-10 08:02:36 +00:00
2021-07-24 16:16:12 +00:00
// TODO(scoped): This won't work if memStickDirectory points at the contents of /PSP...
2014-02-08 19:11:50 +00:00
# if defined(USING_WIN_UI) || defined(APPLE)
2021-07-24 08:31:15 +00:00
auto flash0System = std : : shared_ptr < IFileSystem > ( new DirectoryFileSystem ( & pspFileSystem , g_Config . flash0Directory , FileSystemFlags : : FLASH ) ) ;
2013-12-19 13:30:02 +00:00
# else
2021-07-24 08:31:15 +00:00
auto flash0System = std : : shared_ptr < IFileSystem > ( new VFSFileSystem ( & pspFileSystem , " flash0 " ) ) ;
2013-02-26 23:59:30 +00:00
# endif
2021-07-24 16:16:12 +00:00
FileSystemFlags memstickFlags = FileSystemFlags : : SIMULATE_FAT32 | FileSystemFlags : : CARD ;
Path pspDir = GetSysDirectory ( DIRECTORY_PSP ) ;
if ( pspDir = = g_Config . memStickDirectory ) {
// Initially tried to do this with dual mounts, but failed due to save state compatibility issues.
INFO_LOG ( SCEIO , " Enabling /PSP compatibility mode " ) ;
memstickFlags | = FileSystemFlags : : STRIP_PSP ;
}
auto memstickSystem = std : : shared_ptr < IFileSystem > ( new DirectoryFileSystem ( & pspFileSystem , g_Config . memStickDirectory , memstickFlags ) ) ;
2021-07-24 08:31:15 +00:00
2013-08-10 21:02:18 +00:00
pspFileSystem . Mount ( " ms0: " , memstickSystem ) ;
pspFileSystem . Mount ( " fatms0: " , memstickSystem ) ;
pspFileSystem . Mount ( " fatms: " , memstickSystem ) ;
2013-10-13 11:52:48 +00:00
pspFileSystem . Mount ( " pfat0: " , memstickSystem ) ;
2021-07-24 16:16:12 +00:00
2013-08-10 21:02:18 +00:00
pspFileSystem . Mount ( " flash0: " , flash0System ) ;
2015-12-27 08:57:01 +00:00
if ( g_RemasterMode ) {
2021-02-28 18:52:35 +00:00
const std : : string gameId = g_paramSFO . GetDiscID ( ) ;
2021-07-18 20:28:59 +00:00
const Path exdataPath = GetSysDirectory ( DIRECTORY_EXDATA ) / gameId ;
2015-12-27 08:57:01 +00:00
if ( File : : Exists ( exdataPath ) ) {
2021-07-24 08:31:15 +00:00
auto exdataSystem = std : : shared_ptr < IFileSystem > ( new DirectoryFileSystem ( & pspFileSystem , exdataPath , FileSystemFlags : : SIMULATE_FAT32 | FileSystemFlags : : CARD ) ) ;
2015-12-27 08:57:01 +00:00
pspFileSystem . Mount ( " exdata0: " , exdataSystem ) ;
INFO_LOG ( SCEIO , " Mounted exdata/%s/ under memstick for exdata0:/ " , gameId . c_str ( ) ) ;
} else {
INFO_LOG ( SCEIO , " Did not find exdata/%s/ under memstick for exdata0:/ " , gameId . c_str ( ) ) ;
}
}
2013-01-17 08:44:35 +00:00
2013-01-10 11:27:10 +00:00
__KernelListenThreadEnd ( & TellFsThreadEnded ) ;
2013-06-16 17:05:30 +00:00
memset ( fds , 0 , sizeof ( fds ) ) ;
2013-08-11 18:51:36 +00:00
2021-10-09 20:21:55 +00:00
ioManagerThreadEnabled = true ;
ioManager . SetThreadEnabled ( true ) ;
Core_ListenLifecycle ( & __IoWakeManager ) ;
ioManagerThread = new std : : thread ( & __IoManagerThread ) ;
2013-09-07 18:22:48 +00:00
__KernelRegisterWaitTypeFuncs ( WAITTYPE_ASYNCIO , __IoAsyncBeginCallback , __IoAsyncEndCallback ) ;
2016-08-06 04:05:48 +00:00
2020-10-20 05:42:49 +00:00
MemoryStick_Init ( ) ;
2016-08-06 04:05:48 +00:00
lastMemStickState = MemoryStick_State ( ) ;
lastMemStickFatState = MemoryStick_FatState ( ) ;
__DisplayListenVblank ( __IoVblank ) ;
2012-11-01 15:19:01 +00:00
}
2012-12-28 07:08:38 +00:00
void __IoDoState ( PointerWrap & p ) {
2019-12-26 19:26:31 +00:00
auto s = p . Section ( " sceIo " , 1 , 5 ) ;
2013-09-15 03:23:03 +00:00
if ( ! s )
return ;
2013-08-11 18:51:36 +00:00
ioManager . DoState ( p ) ;
2020-08-10 04:20:42 +00:00
DoArray ( p , fds , ARRAY_SIZE ( fds ) ) ;
Do ( p , asyncNotifyEvent ) ;
2013-03-10 08:02:36 +00:00
CoreTiming : : RestoreRegisterEvent ( asyncNotifyEvent , " IoAsyncNotify " , __IoAsyncNotify ) ;
2020-08-10 04:20:42 +00:00
Do ( p , syncNotifyEvent ) ;
2013-08-11 18:53:21 +00:00
CoreTiming : : RestoreRegisterEvent ( syncNotifyEvent , " IoSyncNotify " , __IoSyncNotify ) ;
2016-08-06 03:42:58 +00:00
if ( s < 2 ) {
std : : set < SceUID > legacy ;
memStickCallbacks . clear ( ) ;
memStickFatCallbacks . clear ( ) ;
// Convert from set to vector.
2020-08-10 04:20:42 +00:00
Do ( p , legacy ) ;
2016-08-06 03:42:58 +00:00
for ( SceUID id : legacy ) {
memStickCallbacks . push_back ( id ) ;
}
2020-08-10 04:20:42 +00:00
Do ( p , legacy ) ;
2016-08-06 03:42:58 +00:00
for ( SceUID id : legacy ) {
memStickFatCallbacks . push_back ( id ) ;
}
} else {
2020-08-10 04:20:42 +00:00
Do ( p , memStickCallbacks ) ;
Do ( p , memStickFatCallbacks ) ;
2016-08-06 03:42:58 +00:00
}
2016-08-06 04:05:48 +00:00
if ( s > = 3 ) {
2020-08-10 04:20:42 +00:00
Do ( p , lastMemStickState ) ;
Do ( p , lastMemStickFatState ) ;
2016-08-06 04:05:48 +00:00
}
2019-07-28 23:12:55 +00:00
for ( int i = 0 ; i < PSP_COUNT_FDS ; + + i ) {
2019-12-26 19:05:36 +00:00
auto clearThread = [ & ] ( ) {
if ( asyncThreads [ i ] )
asyncThreads [ i ] - > Forget ( ) ;
delete asyncThreads [ i ] ;
asyncThreads [ i ] = nullptr ;
} ;
2019-07-28 23:12:55 +00:00
if ( s > = 4 ) {
p . DoVoid ( & asyncParams [ i ] , ( int ) sizeof ( IoAsyncParams ) ) ;
bool hasThread = asyncThreads [ i ] ! = nullptr ;
2020-08-10 04:20:42 +00:00
Do ( p , hasThread ) ;
2019-07-28 23:12:55 +00:00
if ( hasThread ) {
2019-12-26 19:05:36 +00:00
if ( p . GetMode ( ) = = p . MODE_READ )
clearThread ( ) ;
2020-08-10 04:20:42 +00:00
DoClass ( p , asyncThreads [ i ] ) ;
2019-07-28 23:12:55 +00:00
} else if ( ! hasThread ) {
2019-12-26 19:05:36 +00:00
clearThread ( ) ;
2019-07-28 23:12:55 +00:00
}
} else {
asyncParams [ i ] . op = IoAsyncOp : : NONE ;
asyncParams [ i ] . priority = - 1 ;
2019-12-26 19:05:36 +00:00
clearThread ( ) ;
2019-07-28 23:12:55 +00:00
}
}
2019-12-26 19:26:31 +00:00
if ( s > = 5 ) {
2020-08-10 04:20:42 +00:00
Do ( p , asyncDefaultPriority ) ;
2019-12-26 19:26:31 +00:00
} else {
asyncDefaultPriority = - 1 ;
}
2012-12-28 07:08:38 +00:00
}
2012-12-06 14:16:05 +00:00
void __IoShutdown ( ) {
2013-08-11 18:51:36 +00:00
ioManagerThreadEnabled = false ;
ioManager . SyncThread ( ) ;
ioManager . FinishEventLoop ( ) ;
2020-07-05 08:37:44 +00:00
if ( ioManagerThread ! = nullptr ) {
ioManagerThread - > join ( ) ;
delete ioManagerThread ;
ioManagerThread = nullptr ;
ioManager . Shutdown ( ) ;
}
2013-08-11 18:51:36 +00:00
2019-10-20 17:25:57 +00:00
for ( int i = 0 ; i < PSP_COUNT_FDS ; + + i ) {
asyncParams [ i ] . op = IoAsyncOp : : NONE ;
asyncParams [ i ] . priority = - 1 ;
if ( asyncThreads [ i ] )
asyncThreads [ i ] - > Forget ( ) ;
delete asyncThreads [ i ] ;
asyncThreads [ i ] = nullptr ;
}
2019-12-26 19:26:31 +00:00
asyncDefaultPriority = - 1 ;
2019-10-20 17:25:57 +00:00
2021-07-27 20:55:02 +00:00
pspFileSystem . Unmount ( " ms0: " ) ;
pspFileSystem . Unmount ( " fatms0: " ) ;
pspFileSystem . Unmount ( " fatms: " ) ;
pspFileSystem . Unmount ( " pfat0: " ) ;
pspFileSystem . Unmount ( " flash0: " ) ;
pspFileSystem . Unmount ( " exdata0: " ) ;
2013-09-02 08:25:41 +00:00
2021-04-16 08:15:23 +00:00
MemoryStick_Shutdown ( ) ;
2013-09-02 08:25:41 +00:00
memStickCallbacks . clear ( ) ;
memStickFatCallbacks . clear ( ) ;
2012-11-01 15:19:01 +00:00
}
2022-12-11 05:02:44 +00:00
static std : : string IODetermineFilename ( const FileNode * f ) {
2021-04-03 23:04:30 +00:00
uint64_t offset = pspFileSystem . GetSeekPos ( f - > handle ) ;
if ( ( pspFileSystem . DevType ( f - > handle ) & PSPDevType : : BLOCK ) ! = 0 ) {
return StringFromFormat ( " %s offset 0x%08llx " , f - > fullpath . c_str ( ) , offset * 2048 ) ;
}
return StringFromFormat ( " %s offset 0x%08llx " , f - > fullpath . c_str ( ) , offset ) ;
}
2013-01-02 11:35:37 +00:00
u32 __IoGetFileHandleFromId ( u32 id , u32 & outError )
{
2013-06-16 17:05:30 +00:00
FileNode * f = __IoGetFd ( id , outError ) ;
2013-01-02 11:35:37 +00:00
if ( ! f ) {
2013-06-16 17:05:30 +00:00
return ( u32 ) - 1 ;
2013-01-02 11:35:37 +00:00
}
return f - > handle ;
}
2019-07-28 23:12:55 +00:00
static void IoStartAsyncThread ( int id , FileNode * f ) {
2020-01-03 21:09:58 +00:00
if ( asyncThreads [ id ] & & ! asyncThreads [ id ] - > Stopped ( ) ) {
// Wake the thread up.
if ( asyncParams [ id ] . priority = = - 1 & & sceKernelGetCompiledSdkVersion ( ) > = 0x04020000 ) {
asyncThreads [ id ] - > ChangePriority ( KernelCurThreadPriority ( ) ) ;
}
asyncThreads [ id ] - > Resume ( WAITTYPE_ASYNCIO , id , 0 ) ;
} else {
IoAsyncCleanupThread ( id ) ;
int priority = asyncParams [ id ] . priority ;
if ( priority = = - 1 )
priority = KernelCurThreadPriority ( ) ;
asyncThreads [ id ] = new HLEHelperThread ( " SceIoAsync " , " IoFileMgrForUser " , " __IoAsyncFinish " , priority , 0x200 ) ;
asyncThreads [ id ] - > Start ( id , 0 ) ;
}
2019-07-28 23:12:55 +00:00
f - > pendingAsyncResult = true ;
}
2014-12-08 09:40:08 +00:00
static u32 sceIoAssign ( u32 alias_addr , u32 physical_addr , u32 filesystem_addr , int mode , u32 arg_addr , int argSize )
2013-01-17 08:44:35 +00:00
{
std : : string alias = Memory : : GetCharPointer ( alias_addr ) ;
std : : string physical_dev = Memory : : GetCharPointer ( physical_addr ) ;
std : : string filesystem_dev = Memory : : GetCharPointer ( filesystem_addr ) ;
std : : string perm ;
switch ( mode ) {
case 0 :
perm = " IOASSIGN_RDWR " ;
break ;
case 1 :
perm = " IOASSIGN_RDONLY " ;
break ;
default :
2013-03-02 21:50:38 +00:00
perm = " unhandled " ;
2013-01-17 08:44:35 +00:00
break ;
}
2014-02-09 20:29:22 +00:00
WARN_LOG_REPORT ( SCEIO , " sceIoAssign(%s, %s, %s, %s, %08x, %i) " , alias . c_str ( ) , physical_dev . c_str ( ) , filesystem_dev . c_str ( ) , perm . c_str ( ) , arg_addr , argSize ) ;
2013-01-17 08:44:35 +00:00
return 0 ;
}
2014-12-08 09:40:08 +00:00
static u32 sceIoUnassign ( const char * alias )
2013-01-17 08:44:35 +00:00
{
2014-02-09 20:29:22 +00:00
WARN_LOG_REPORT ( SCEIO , " sceIoUnassign(%s) " , alias ) ;
2012-12-05 21:19:14 +00:00
return 0 ;
2012-11-01 15:19:01 +00:00
}
2014-12-08 09:40:08 +00:00
static u32 sceKernelStdin ( ) {
2013-12-27 06:07:41 +00:00
DEBUG_LOG ( SCEIO , " %d=sceKernelStdin() " , PSP_STDIN ) ;
return PSP_STDIN ;
2012-11-01 15:19:01 +00:00
}
2012-12-06 14:16:05 +00:00
2014-12-08 09:40:08 +00:00
static u32 sceKernelStdout ( ) {
2013-12-27 06:07:41 +00:00
DEBUG_LOG ( SCEIO , " %d=sceKernelStdout() " , PSP_STDOUT ) ;
return PSP_STDOUT ;
2012-11-01 15:19:01 +00:00
}
2014-12-08 09:40:08 +00:00
static u32 sceKernelStderr ( ) {
2013-12-27 06:07:41 +00:00
DEBUG_LOG ( SCEIO , " %d=sceKernelStderr() " , PSP_STDERR ) ;
return PSP_STDERR ;
2012-11-01 15:19:01 +00:00
}
2013-12-28 00:36:51 +00:00
u64 __IoCompleteAsyncIO ( FileNode * f ) {
2015-05-28 03:02:09 +00:00
PROFILE_THIS_SCOPE ( " io_rw " ) ;
2019-02-06 09:45:12 +00:00
int ioTimingMethod = GetIOTimingMethod ( ) ;
if ( ioTimingMethod = = IOTIMING_REALISTIC ) {
2014-12-26 09:18:52 +00:00
u64 finishTicks = ioManager . ResultFinishTicks ( f - > handle ) ;
if ( finishTicks > CoreTiming : : GetTicks ( ) ) {
return finishTicks ;
}
2013-12-28 00:36:51 +00:00
}
2014-12-26 03:02:49 +00:00
AsyncIOResult managerResult ;
if ( ioManager . WaitResult ( f - > handle , managerResult ) ) {
2013-12-28 00:36:51 +00:00
f - > asyncResult = managerResult . result ;
2014-12-26 03:02:49 +00:00
} else {
// It's okay, not all operations are deferred.
2012-11-01 15:19:01 +00:00
}
2014-12-26 03:02:49 +00:00
if ( f - > callbackID ) {
__KernelNotifyCallback ( f - > callbackID , f - > callbackArg ) ;
}
f - > pendingAsyncResult = false ;
f - > hasAsyncResult = true ;
2013-12-28 00:36:51 +00:00
return 0 ;
2012-11-01 15:19:01 +00:00
}
2013-01-20 23:20:48 +00:00
void __IoCopyDate ( ScePspDateTime & date_out , const tm & date_in )
{
date_out . year = date_in . tm_year + 1900 ;
date_out . month = date_in . tm_mon + 1 ;
date_out . day = date_in . tm_mday ;
date_out . hour = date_in . tm_hour ;
date_out . minute = date_in . tm_min ;
date_out . second = date_in . tm_sec ;
date_out . microsecond = 0 ;
}
2014-12-08 09:40:08 +00:00
static void __IoGetStat ( SceIoStat * stat , PSPFileInfo & info ) {
2012-11-01 15:19:01 +00:00
memset ( stat , 0xfe , sizeof ( SceIoStat ) ) ;
2012-12-05 21:19:14 +00:00
stat - > st_size = ( s64 ) info . size ;
2012-11-01 15:19:01 +00:00
int type , attr ;
if ( info . type & FILETYPE_DIRECTORY )
type = SCE_STM_FDIR , attr = TYPE_DIR ;
else
type = SCE_STM_FREG , attr = TYPE_FILE ;
2013-01-20 23:20:48 +00:00
stat - > st_mode = type | info . access ;
2012-11-01 15:19:01 +00:00
stat - > st_attr = attr ;
stat - > st_size = info . size ;
2014-06-10 17:10:50 +00:00
__IoCopyDate ( stat - > st_a_time , info . atime ) ;
__IoCopyDate ( stat - > st_c_time , info . ctime ) ;
__IoCopyDate ( stat - > st_m_time , info . mtime ) ;
2012-11-01 15:19:01 +00:00
stat - > st_private [ 0 ] = info . startSector ;
}
2014-12-08 09:40:08 +00:00
static void __IoSchedAsync ( FileNode * f , int fd , int usec ) {
2013-09-07 17:09:48 +00:00
CoreTiming : : ScheduleEvent ( usToCycles ( usec ) , asyncNotifyEvent , fd ) ;
2013-03-10 17:16:21 +00:00
f - > pendingAsyncResult = true ;
2013-05-13 00:27:38 +00:00
f - > hasAsyncResult = false ;
2013-03-10 10:43:50 +00:00
}
2014-12-08 09:40:08 +00:00
static void __IoSchedSync ( FileNode * f , int fd , int usec ) {
2013-08-11 18:53:21 +00:00
u64 param = ( ( u64 ) __KernelGetCurThread ( ) ) < < 32 | fd ;
CoreTiming : : ScheduleEvent ( usToCycles ( usec ) , syncNotifyEvent , param ) ;
2014-03-02 21:48:29 +00:00
f - > pendingAsyncResult = false ;
2013-08-11 18:53:21 +00:00
f - > hasAsyncResult = false ;
}
2014-12-08 09:40:08 +00:00
static u32 sceIoGetstat ( const char * filename , u32 addr ) {
2013-03-11 01:57:17 +00:00
// TODO: Improve timing (although this seems normally slow..)
int usec = 1000 ;
2022-09-03 16:13:07 +00:00
auto stat = PSPPointer < SceIoStat > : : Create ( addr ) ;
2012-11-01 15:19:01 +00:00
PSPFileInfo info = pspFileSystem . GetFileInfo ( filename ) ;
2012-12-19 20:21:46 +00:00
if ( info . exists ) {
2022-09-03 16:13:07 +00:00
if ( stat . IsValid ( ) ) {
__IoGetStat ( stat , info ) ;
stat . NotifyWrite ( " IoGetstat " ) ;
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoGetstat(%s, %08x) : sector = %08x " , filename , addr , info . startSector ) ;
2013-03-11 01:57:17 +00:00
return hleDelayResult ( 0 , " io getstat " , usec ) ;
2013-01-05 22:40:39 +00:00
} else {
2013-09-07 19:19:21 +00:00
ERROR_LOG ( SCEIO , " sceIoGetstat(%s, %08x) : bad address " , filename , addr ) ;
2013-03-11 01:57:17 +00:00
return hleDelayResult ( - 1 , " io getstat " , usec ) ;
2013-01-05 22:40:39 +00:00
}
2012-12-19 20:21:46 +00:00
} else {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoGetstat(%s, %08x) : FILE NOT FOUND " , filename , addr ) ;
2018-05-06 17:15:05 +00:00
return hleDelayResult ( SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND , " io getstat " , usec ) ;
2012-12-19 20:21:46 +00:00
}
2012-11-01 15:19:01 +00:00
}
2014-12-08 09:40:08 +00:00
static u32 sceIoChstat ( const char * filename , u32 iostatptr , u32 changebits ) {
2021-04-10 22:11:44 +00:00
auto iostat = PSPPointer < SceIoStat > : : Create ( iostatptr ) ;
if ( ! iostat . IsValid ( ) )
return hleReportError ( SCEIO , SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT , " bad address " ) ;
2020-03-20 03:57:23 +00:00
ERROR_LOG_REPORT ( SCEIO , " UNIMPL sceIoChstat(%s, %08x, %08x) " , filename , iostatptr , changebits ) ;
2013-03-16 08:38:50 +00:00
if ( changebits & SCE_CST_MODE )
2021-04-10 22:11:44 +00:00
ERROR_LOG_REPORT ( SCEIO , " sceIoChstat: change mode to %03o requested " , iostat - > st_mode ) ;
2013-03-16 08:38:50 +00:00
if ( changebits & SCE_CST_ATTR )
2021-04-10 22:11:44 +00:00
ERROR_LOG_REPORT ( SCEIO , " sceIoChstat: change attr to %04x requested " , iostat - > st_attr ) ;
2013-03-16 08:38:50 +00:00
if ( changebits & SCE_CST_SIZE )
2013-09-07 19:19:21 +00:00
ERROR_LOG ( SCEIO , " sceIoChstat: change size requested " ) ;
2013-03-16 08:38:50 +00:00
if ( changebits & SCE_CST_CT )
2013-09-07 19:19:21 +00:00
ERROR_LOG ( SCEIO , " sceIoChstat: change creation time requested " ) ;
2013-03-16 08:38:50 +00:00
if ( changebits & SCE_CST_AT )
2013-09-07 19:19:21 +00:00
ERROR_LOG ( SCEIO , " sceIoChstat: change access time requested " ) ;
2013-03-16 08:38:50 +00:00
if ( changebits & SCE_CST_MT )
2021-04-10 22:11:44 +00:00
ERROR_LOG_REPORT ( SCEIO , " sceIoChstat: change modification time to %04d-%02d-%02d requested " , iostat - > st_m_time . year , iostat - > st_m_time . month , iostat - > st_m_time . day ) ;
2013-03-16 08:38:50 +00:00
if ( changebits & SCE_CST_PRVT )
2013-09-07 19:19:21 +00:00
ERROR_LOG ( SCEIO , " sceIoChstat: change private data requested " ) ;
2013-03-16 08:38:50 +00:00
return 0 ;
}
2014-12-08 09:40:08 +00:00
static u32 npdrmRead ( FileNode * f , u8 * data , int size ) {
2013-02-24 08:28:40 +00:00
PGD_DESC * pgd = f - > pgdInfo ;
2013-05-02 09:47:34 +00:00
u32 block , offset , blockPos ;
2013-02-24 08:28:40 +00:00
u32 remain_size , copy_size ;
block = pgd - > file_offset / pgd - > block_size ;
offset = pgd - > file_offset % pgd - > block_size ;
2020-09-29 08:24:41 +00:00
if ( size > ( int ) pgd - > data_size )
size = ( int ) pgd - > data_size ;
2013-02-24 08:28:40 +00:00
remain_size = size ;
while ( remain_size ) {
if ( pgd - > current_block ! = block ) {
2013-05-02 09:47:34 +00:00
blockPos = block * pgd - > block_size ;
pspFileSystem . SeekFile ( f - > handle , ( s32 ) pgd - > data_offset + blockPos , FILEMOVE_BEGIN ) ;
2013-02-24 08:28:40 +00:00
pspFileSystem . ReadFile ( f - > handle , pgd - > block_buf , pgd - > block_size ) ;
pgd_decrypt_block ( pgd , block ) ;
pgd - > current_block = block ;
}
if ( offset + remain_size > pgd - > block_size ) {
copy_size = pgd - > block_size - offset ;
2013-03-16 08:15:11 +00:00
memcpy ( data , pgd - > block_buf + offset , copy_size ) ;
2013-02-24 08:28:40 +00:00
block + = 1 ;
offset = 0 ;
2014-04-13 21:22:17 +00:00
} else {
2013-02-24 08:28:40 +00:00
copy_size = remain_size ;
2013-03-16 08:15:11 +00:00
memcpy ( data , pgd - > block_buf + offset , copy_size ) ;
2013-02-24 08:28:40 +00:00
}
data + = copy_size ;
remain_size - = copy_size ;
pgd - > file_offset + = copy_size ;
}
return size ;
}
2013-12-28 00:36:51 +00:00
static bool __IoRead ( int & result , int id , u32 data_addr , int size , int & us ) {
2015-05-28 03:02:09 +00:00
PROFILE_THIS_SCOPE ( " io_rw " ) ;
2013-12-28 00:36:51 +00:00
// Low estimate, may be improved later from the ReadFile result.
us = size / 100 ;
if ( us < 100 ) {
us = 100 ;
}
2013-12-27 06:07:41 +00:00
if ( id = = PSP_STDIN ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoRead STDIN " ) ;
2014-06-23 06:18:39 +00:00
result = 0 ; //stdin
return true ;
2012-11-01 15:19:01 +00:00
}
u32 error ;
2013-06-16 17:05:30 +00:00
FileNode * f = __IoGetFd ( id , error ) ;
2012-12-06 14:16:05 +00:00
if ( f ) {
2013-10-09 06:49:24 +00:00
if ( f - > asyncBusy ( ) ) {
result = SCE_KERNEL_ERROR_ASYNC_BUSY ;
return true ;
}
2013-08-11 18:53:21 +00:00
if ( ! ( f - > openMode & FILEACCESS_READ ) ) {
2016-07-24 19:22:26 +00:00
result = SCE_KERNEL_ERROR_BADF ;
2013-08-11 18:53:21 +00:00
return true ;
2014-11-06 16:45:45 +00:00
} else if ( size < 0 ) {
result = SCE_KERNEL_ERROR_ILLEGAL_ADDR ;
return true ;
2013-08-11 18:53:21 +00:00
} else if ( Memory : : IsValidAddress ( data_addr ) ) {
2021-04-03 23:04:30 +00:00
const std : : string tag = " IoRead/ " + IODetermineFilename ( f ) ;
NotifyMemInfo ( MemBlockFlags : : WRITE , data_addr , size , tag . c_str ( ) , tag . size ( ) ) ;
2022-07-24 11:33:03 +00:00
u8 * data = ( u8 * ) Memory : : GetPointerUnchecked ( data_addr ) ;
2021-02-13 16:24:39 +00:00
u32 validSize = Memory : : ValidSize ( data_addr , size ) ;
2013-08-11 18:53:21 +00:00
if ( f - > npdrm ) {
2021-02-13 16:24:39 +00:00
result = npdrmRead ( f , data , validSize ) ;
currentMIPS - > InvalidateICache ( data_addr , validSize ) ;
2013-08-11 18:53:21 +00:00
return true ;
2014-03-02 21:45:12 +00:00
}
bool useThread = __KernelIsDispatchEnabled ( ) & & ioManagerThreadEnabled & & size > IO_THREAD_MIN_DATA_SIZE ;
if ( useThread ) {
// If there's a pending operation on this file, wait for it to finish and don't overwrite it.
useThread = ! ioManager . HasOperation ( f - > handle ) ;
if ( ! useThread ) {
ioManager . SyncThread ( ) ;
}
}
if ( useThread ) {
2013-08-11 18:53:21 +00:00
AsyncIOEvent ev = IO_EVENT_READ ;
ev . handle = f - > handle ;
ev . buf = data ;
2021-02-13 16:24:39 +00:00
ev . bytes = validSize ;
2015-12-28 05:58:29 +00:00
ev . invalidateAddr = data_addr ;
2013-08-11 18:53:21 +00:00
ioManager . ScheduleOperation ( ev ) ;
return false ;
} else {
2019-02-06 09:45:12 +00:00
if ( GetIOTimingMethod ( ) ! = IOTIMING_REALISTIC ) {
2021-02-13 16:24:39 +00:00
result = ( int ) pspFileSystem . ReadFile ( f - > handle , data , validSize ) ;
2014-12-26 15:49:22 +00:00
} else {
2021-02-13 16:24:39 +00:00
result = ( int ) pspFileSystem . ReadFile ( f - > handle , data , validSize , us ) ;
2014-12-26 15:49:22 +00:00
}
2021-02-13 16:24:39 +00:00
currentMIPS - > InvalidateICache ( data_addr , validSize ) ;
2013-08-11 18:53:21 +00:00
return true ;
2013-02-24 08:28:40 +00:00
}
2012-12-06 14:16:05 +00:00
} else {
2014-03-02 21:59:34 +00:00
if ( size ! = 0 ) {
// TODO: For some combinations of bad pointer + size, SCE_KERNEL_ERROR_ILLEGAL_ADDR.
// Seems like only for kernel RAM. For most cases, it really is -1.
result = - 1 ;
} else {
result = 0 ;
}
2013-08-11 18:53:21 +00:00
return true ;
2012-11-01 15:19:01 +00:00
}
2012-12-06 14:16:05 +00:00
} else {
2013-08-11 18:53:21 +00:00
result = error ;
return true ;
2012-11-01 15:19:01 +00:00
}
}
2014-12-08 09:40:08 +00:00
static u32 sceIoRead ( int id , u32 data_addr , int size ) {
2013-08-28 15:13:44 +00:00
u32 error ;
FileNode * f = __IoGetFd ( id , error ) ;
2013-08-29 06:15:13 +00:00
if ( id > 2 & & f ! = NULL ) {
if ( ! __KernelIsDispatchEnabled ( ) ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoRead(%d, %08x, %x): dispatch disabled " , id , data_addr , size ) ;
2013-08-29 06:15:13 +00:00
return SCE_KERNEL_ERROR_CAN_NOT_WAIT ;
}
if ( __IsInInterrupt ( ) ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoRead(%d, %08x, %x): inside interrupt " , id , data_addr , size ) ;
2013-08-29 06:15:13 +00:00
return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT ;
}
2013-08-11 18:53:21 +00:00
}
int result ;
2013-12-28 00:36:51 +00:00
int us ;
bool complete = __IoRead ( result , id , data_addr , size , us ) ;
2013-08-11 18:53:21 +00:00
if ( ! complete ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoRead(%d, %08x, %x): deferring result " , id , data_addr , size ) ;
2013-08-11 18:53:21 +00:00
__IoSchedSync ( f , id , us ) ;
__KernelWaitCurThread ( WAITTYPE_IO , id , 0 , 0 , false , " io read " ) ;
2014-12-14 04:12:37 +00:00
f - > waitingSyncThreads . push_back ( __KernelGetCurThread ( ) ) ;
2013-08-11 18:53:21 +00:00
return 0 ;
} else if ( result > = 0 ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " %x=sceIoRead(%d, %08x, %x) " , result , id , data_addr , size ) ;
2013-07-06 15:42:49 +00:00
return hleDelayResult ( result , " io read " , us ) ;
2013-08-11 18:53:21 +00:00
} else {
2013-10-09 06:49:24 +00:00
WARN_LOG ( SCEIO , " sceIoRead(%d, %08x, %x): error %08x " , id , data_addr , size , result ) ;
2013-03-10 10:06:43 +00:00
return result ;
2013-08-11 18:53:21 +00:00
}
2013-03-10 10:06:43 +00:00
}
2014-12-08 09:40:08 +00:00
static u32 sceIoReadAsync ( int id , u32 data_addr , int size ) {
2013-03-10 10:06:43 +00:00
u32 error ;
2013-06-16 17:05:30 +00:00
FileNode * f = __IoGetFd ( id , error ) ;
2013-03-10 10:06:43 +00:00
if ( f ) {
2013-10-09 06:49:24 +00:00
if ( f - > asyncBusy ( ) ) {
2019-07-28 22:55:44 +00:00
return hleLogWarning ( SCEIO , SCE_KERNEL_ERROR_ASYNC_BUSY , " async busy " ) ;
2013-08-11 18:53:21 +00:00
}
2019-07-28 23:46:56 +00:00
auto & params = asyncParams [ id ] ;
params . op = IoAsyncOp : : READ ;
params . std . addr = data_addr ;
params . std . size = size ;
IoStartAsyncThread ( id , f ) ;
return hleLogSuccessI ( SCEIO , 0 ) ;
2013-03-10 10:06:43 +00:00
} else {
2019-07-28 22:55:44 +00:00
return hleLogError ( SCEIO , error , " bad file descriptor " ) ;
2013-03-10 10:06:43 +00:00
}
}
2013-12-28 00:36:51 +00:00
static bool __IoWrite ( int & result , int id , u32 data_addr , int size , int & us ) {
2015-05-28 03:02:09 +00:00
PROFILE_THIS_SCOPE ( " io_rw " ) ;
2013-12-28 00:36:51 +00:00
// Low estimate, may be improved later from the WriteFile result.
us = size / 100 ;
if ( us < 100 ) {
us = 100 ;
}
2013-10-09 06:49:24 +00:00
const void * data_ptr = Memory : : GetPointer ( data_addr ) ;
2021-02-13 16:24:39 +00:00
const u32 validSize = Memory : : ValidSize ( data_addr , size ) ;
2013-04-10 06:26:27 +00:00
// Let's handle stdout/stderr specially.
2013-12-27 06:07:41 +00:00
if ( id = = PSP_STDOUT | | id = = PSP_STDERR ) {
2013-04-10 06:26:27 +00:00
const char * str = ( const char * ) data_ptr ;
2021-02-13 16:24:39 +00:00
const int str_size = size < = 0 ? 0 : ( str [ validSize - 1 ] = = ' \n ' ? validSize - 1 : validSize ) ;
2013-09-07 19:19:21 +00:00
INFO_LOG ( SCEIO , " %s: %.*s " , id = = 1 ? " stdout " : " stderr " , str_size , str ) ;
2021-02-13 16:24:39 +00:00
result = validSize ;
2013-08-11 19:04:38 +00:00
return true ;
2012-11-01 15:19:01 +00:00
}
u32 error ;
2013-06-16 17:05:30 +00:00
FileNode * f = __IoGetFd ( id , error ) ;
2012-12-06 14:16:05 +00:00
if ( f ) {
2013-10-09 06:49:24 +00:00
if ( f - > asyncBusy ( ) ) {
result = SCE_KERNEL_ERROR_ASYNC_BUSY ;
return true ;
}
2013-08-11 19:04:38 +00:00
if ( ! ( f - > openMode & FILEACCESS_WRITE ) ) {
2016-07-24 19:22:26 +00:00
result = SCE_KERNEL_ERROR_BADF ;
2013-08-11 19:04:38 +00:00
return true ;
}
2014-11-06 16:45:45 +00:00
if ( size < 0 ) {
result = SCE_KERNEL_ERROR_ILLEGAL_ADDR ;
return true ;
}
2014-03-02 21:45:12 +00:00
2021-04-03 23:04:30 +00:00
const std : : string tag = " IoWrite/ " + IODetermineFilename ( f ) ;
NotifyMemInfo ( MemBlockFlags : : READ , data_addr , size , tag . c_str ( ) , tag . size ( ) ) ;
2014-06-12 07:42:25 +00:00
2021-06-06 16:56:41 +00:00
if ( f - > isTTY ) {
const char * str = ( const char * ) data_ptr ;
const int str_size = size < = 0 ? 0 : ( str [ validSize - 1 ] = = ' \n ' ? validSize - 1 : validSize ) ;
INFO_LOG ( SCEIO , " %s: %.*s " , " tty " , str_size , str ) ;
result = validSize ;
return true ;
}
2014-03-02 21:45:12 +00:00
bool useThread = __KernelIsDispatchEnabled ( ) & & ioManagerThreadEnabled & & size > IO_THREAD_MIN_DATA_SIZE ;
if ( useThread ) {
// If there's a pending operation on this file, wait for it to finish and don't overwrite it.
useThread = ! ioManager . HasOperation ( f - > handle ) ;
if ( ! useThread ) {
ioManager . SyncThread ( ) ;
}
}
if ( useThread ) {
2013-08-11 19:04:38 +00:00
AsyncIOEvent ev = IO_EVENT_WRITE ;
ev . handle = f - > handle ;
ev . buf = ( u8 * ) data_ptr ;
2021-02-13 16:24:39 +00:00
ev . bytes = validSize ;
2015-12-28 05:58:29 +00:00
ev . invalidateAddr = 0 ;
2013-08-11 19:04:38 +00:00
ioManager . ScheduleOperation ( ev ) ;
return false ;
} else {
2019-02-06 09:45:12 +00:00
if ( GetIOTimingMethod ( ) ! = IOTIMING_REALISTIC ) {
2021-02-13 16:24:39 +00:00
result = ( int ) pspFileSystem . WriteFile ( f - > handle , ( u8 * ) data_ptr , validSize ) ;
2014-12-26 15:49:22 +00:00
} else {
2021-02-13 16:24:39 +00:00
result = ( int ) pspFileSystem . WriteFile ( f - > handle , ( u8 * ) data_ptr , validSize , us ) ;
2014-12-26 15:49:22 +00:00
}
2013-01-20 23:20:48 +00:00
}
2013-08-11 19:04:38 +00:00
return true ;
2012-12-06 14:16:05 +00:00
} else {
2013-09-07 19:19:21 +00:00
ERROR_LOG ( SCEIO , " sceIoWrite ERROR: no file open " ) ;
2013-08-11 19:04:38 +00:00
result = ( s32 ) error ;
return true ;
2012-11-01 15:19:01 +00:00
}
}
2014-12-08 09:40:08 +00:00
static u32 sceIoWrite ( int id , u32 data_addr , int size ) {
2013-08-28 15:13:44 +00:00
u32 error ;
FileNode * f = __IoGetFd ( id , error ) ;
2013-08-29 06:15:13 +00:00
if ( id > 2 & & f ! = NULL ) {
if ( ! __KernelIsDispatchEnabled ( ) ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoWrite(%d, %08x, %x): dispatch disabled " , id , data_addr , size ) ;
2013-08-29 06:15:13 +00:00
return SCE_KERNEL_ERROR_CAN_NOT_WAIT ;
}
if ( __IsInInterrupt ( ) ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoWrite(%d, %08x, %x): inside interrupt " , id , data_addr , size ) ;
2013-08-29 06:15:13 +00:00
return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT ;
}
2013-08-28 15:13:44 +00:00
}
2013-04-06 20:27:12 +00:00
2013-08-11 19:04:38 +00:00
int result ;
2013-12-28 00:36:51 +00:00
int us ;
bool complete = __IoWrite ( result , id , data_addr , size , us ) ;
2013-08-11 19:04:38 +00:00
if ( ! complete ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoWrite(%d, %08x, %x): deferring result " , id , data_addr , size ) ;
2013-08-11 19:04:38 +00:00
__IoSchedSync ( f , id , us ) ;
__KernelWaitCurThread ( WAITTYPE_IO , id , 0 , 0 , false , " io write " ) ;
2014-12-14 04:12:37 +00:00
f - > waitingSyncThreads . push_back ( __KernelGetCurThread ( ) ) ;
2013-08-11 19:04:38 +00:00
return 0 ;
} else if ( result > = 0 ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " %x=sceIoWrite(%d, %08x, %x) " , result , id , data_addr , size ) ;
2013-08-11 19:04:38 +00:00
if ( __KernelIsDispatchEnabled ( ) ) {
2014-04-12 20:21:35 +00:00
// If we wrote to stdout, return an error (even though we did log it) rather than delaying.
// On actual hardware, it would just return this... we just want the log output.
if ( __IsInInterrupt ( ) ) {
return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT ;
}
2013-07-06 15:42:49 +00:00
return hleDelayResult ( result , " io write " , us ) ;
2013-08-11 19:04:38 +00:00
} else {
2013-04-06 20:27:12 +00:00
return result ;
2013-08-11 19:04:38 +00:00
}
} else {
2013-10-09 06:49:24 +00:00
WARN_LOG ( SCEIO , " sceIoWrite(%d, %08x, %x): error %08x " , id , data_addr , size , result ) ;
2013-03-11 02:14:46 +00:00
return result ;
2013-08-11 19:04:38 +00:00
}
2013-03-11 02:14:46 +00:00
}
2014-12-08 09:40:08 +00:00
static u32 sceIoWriteAsync ( int id , u32 data_addr , int size ) {
2013-03-11 02:14:46 +00:00
u32 error ;
2013-06-16 17:05:30 +00:00
FileNode * f = __IoGetFd ( id , error ) ;
2013-03-11 02:14:46 +00:00
if ( f ) {
2013-10-09 06:49:24 +00:00
if ( f - > asyncBusy ( ) ) {
2019-07-28 22:55:44 +00:00
return hleLogWarning ( SCEIO , SCE_KERNEL_ERROR_ASYNC_BUSY , " async busy " ) ;
2013-08-11 19:04:38 +00:00
}
2019-07-28 23:46:56 +00:00
auto & params = asyncParams [ id ] ;
params . op = IoAsyncOp : : WRITE ;
params . std . addr = data_addr ;
params . std . size = size ;
IoStartAsyncThread ( id , f ) ;
return hleLogSuccessI ( SCEIO , 0 ) ;
2013-03-11 02:14:46 +00:00
} else {
2019-07-28 22:55:44 +00:00
return hleLogError ( SCEIO , error , " bad file descriptor " ) ;
2013-03-11 02:14:46 +00:00
}
2013-01-17 08:44:35 +00:00
}
2014-12-08 09:40:08 +00:00
static u32 sceIoGetDevType ( int id ) {
2013-12-27 06:07:41 +00:00
if ( id = = PSP_STDOUT | | id = = PSP_STDERR | | id = = PSP_STDIN ) {
DEBUG_LOG ( SCEIO , " sceIoGetDevType(%d) " , id ) ;
2020-05-22 00:57:41 +00:00
return ( u32 ) PSPDevType : : FILE ;
2013-12-27 06:07:41 +00:00
}
2013-01-17 08:44:35 +00:00
u32 error ;
2013-06-16 17:05:30 +00:00
FileNode * f = __IoGetFd ( id , error ) ;
2013-01-17 08:44:35 +00:00
int result ;
2013-12-27 06:07:41 +00:00
if ( f ) {
// TODO: When would this return PSP_DEV_TYPE_ALIAS?
WARN_LOG ( SCEIO , " sceIoGetDevType(%d - %s) " , id , f - > fullpath . c_str ( ) ) ;
2021-06-06 16:56:41 +00:00
if ( f - > isTTY )
result = ( u32 ) PSPDevType : : FILE ;
2020-05-22 00:57:41 +00:00
result = ( u32 ) pspFileSystem . DevType ( f - > handle ) & ( u32 ) PSPDevType : : EMU_MASK ;
2013-12-27 06:07:41 +00:00
} else {
2013-09-07 19:19:21 +00:00
ERROR_LOG ( SCEIO , " sceIoGetDevType: unknown id %d " , id ) ;
2016-07-24 19:22:26 +00:00
result = SCE_KERNEL_ERROR_BADF ;
2013-01-17 08:44:35 +00:00
}
return result ;
}
2014-12-08 09:40:08 +00:00
static u32 sceIoCancel ( int id )
2013-01-17 08:44:35 +00:00
{
u32 error ;
2013-06-16 17:05:30 +00:00
FileNode * f = __IoGetFd ( id , error ) ;
2013-01-31 07:54:08 +00:00
if ( f ) {
2020-01-03 21:35:04 +00:00
// It seems like this is unsupported for UMDs and memory sticks, based on tests.
return hleReportError ( SCEIO , SCE_KERNEL_ERROR_UNSUP , " unimplemented or unsupported " ) ;
2013-01-31 07:54:08 +00:00
} else {
2020-01-03 21:35:04 +00:00
return hleLogError ( SCEIO , SCE_KERNEL_ERROR_BADF , " invalid fd " ) ;
2013-01-17 08:44:35 +00:00
}
}
2014-12-08 09:40:08 +00:00
static u32 npdrmLseek ( FileNode * f , s32 where , FileMove whence )
2013-02-24 08:28:40 +00:00
{
2013-05-02 09:47:34 +00:00
u32 newPos , blockPos ;
2013-02-24 08:28:40 +00:00
if ( whence = = FILEMOVE_BEGIN ) {
newPos = where ;
} else if ( whence = = FILEMOVE_CURRENT ) {
newPos = f - > pgdInfo - > file_offset + where ;
} else {
newPos = f - > pgdInfo - > data_size + where ;
}
2015-04-06 14:05:44 +00:00
if ( newPos > f - > pgdInfo - > data_size )
2013-02-24 08:28:40 +00:00
return - EINVAL ;
f - > pgdInfo - > file_offset = newPos ;
2013-05-02 09:47:34 +00:00
blockPos = newPos & ~ ( f - > pgdInfo - > block_size - 1 ) ;
pspFileSystem . SeekFile ( f - > handle , ( s32 ) f - > pgdInfo - > data_offset + blockPos , whence ) ;
2013-03-16 08:15:11 +00:00
2013-02-24 08:28:40 +00:00
return newPos ;
}
2014-12-08 09:40:08 +00:00
static s64 __IoLseekDest ( FileNode * f , s64 offset , int whence , FileMove & seek ) {
2015-05-28 03:02:09 +00:00
PROFILE_THIS_SCOPE ( " io_rw " ) ;
2013-12-17 07:51:04 +00:00
seek = FILEMOVE_BEGIN ;
2014-03-02 21:47:11 +00:00
// Let's make sure this isn't incorrect mid-operation.
if ( ioManager . HasOperation ( f - > handle ) ) {
ioManager . SyncThread ( ) ;
}
2013-12-17 07:51:04 +00:00
s64 newPos = 0 ;
switch ( whence ) {
case 0 :
newPos = offset ;
break ;
case 1 :
newPos = pspFileSystem . GetSeekPos ( f - > handle ) + offset ;
seek = FILEMOVE_CURRENT ;
break ;
case 2 :
2022-10-09 15:16:33 +00:00
newPos = f - > FileInfo ( ) . size + offset ;
2013-12-17 07:51:04 +00:00
seek = FILEMOVE_END ;
break ;
default :
return ( s32 ) SCE_KERNEL_ERROR_INVAL ;
}
// Yes, -1 is the correct return code for this case.
if ( newPos < 0 )
return - 1 ;
return newPos ;
}
2014-12-08 09:40:08 +00:00
static s64 __IoLseek ( SceUID id , s64 offset , int whence ) {
2012-11-01 15:19:01 +00:00
u32 error ;
2013-06-16 17:05:30 +00:00
FileNode * f = __IoGetFd ( id , error ) ;
2012-12-06 14:16:05 +00:00
if ( f ) {
2013-10-09 06:49:24 +00:00
if ( f - > asyncBusy ( ) ) {
WARN_LOG ( SCEIO , " sceIoLseek*(%d, %llx, %i): async busy " , id , offset , whence ) ;
return SCE_KERNEL_ERROR_ASYNC_BUSY ;
}
2013-12-17 07:51:04 +00:00
FileMove seek ;
s64 newPos = __IoLseekDest ( f , offset , whence , seek ) ;
2013-05-02 09:47:34 +00:00
if ( f - > npdrm )
return npdrmLseek ( f , ( s32 ) offset , seek ) ;
2013-03-10 17:16:21 +00:00
if ( newPos < 0 )
2013-12-17 07:51:04 +00:00
return newPos ;
2013-05-02 09:47:34 +00:00
return pspFileSystem . SeekFile ( f - > handle , ( s32 ) offset , seek ) ;
2012-12-06 14:16:05 +00:00
} else {
2013-03-10 09:13:45 +00:00
return ( s32 ) error ;
2012-11-01 15:19:01 +00:00
}
}
2014-12-08 09:40:08 +00:00
static s64 sceIoLseek ( int id , s64 offset , int whence ) {
2013-03-10 17:16:21 +00:00
s64 result = __IoLseek ( id , offset , whence ) ;
2013-09-23 06:37:51 +00:00
if ( result > = 0 | | result = = - 1 ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " %lli = sceIoLseek(%d, %llx, %i) " , result , id , offset , whence ) ;
2013-03-10 17:59:59 +00:00
// Educated guess at timing.
2016-08-27 21:18:59 +00:00
hleEatCycles ( 1400 ) ;
hleReSchedule ( " io seek " ) ;
return result ;
2013-03-10 17:16:21 +00:00
} else {
2019-07-28 22:55:44 +00:00
return hleLogError ( SCEIO , result , " bad file descriptor " ) ;
2013-03-10 17:16:21 +00:00
}
}
2014-12-08 09:40:08 +00:00
static u32 sceIoLseek32 ( int id , int offset , int whence ) {
2013-03-10 17:16:21 +00:00
s32 result = ( s32 ) __IoLseek ( id , offset , whence ) ;
2013-09-23 06:37:51 +00:00
if ( result > = 0 | | result = = - 1 ) {
DEBUG_LOG ( SCEIO , " %i = sceIoLseek32(%d, %x, %i) " , result , id , offset , whence ) ;
2013-03-10 17:59:59 +00:00
// Educated guess at timing.
2016-08-27 21:18:59 +00:00
hleEatCycles ( 1400 ) ;
hleReSchedule ( " io seek " ) ;
return result ;
2013-03-10 17:16:21 +00:00
} else {
2019-07-28 22:55:44 +00:00
return hleLogError ( SCEIO , result , " bad file descriptor " ) ;
2013-03-10 17:16:21 +00:00
}
}
2014-12-08 09:40:08 +00:00
static u32 sceIoLseekAsync ( int id , s64 offset , int whence ) {
2012-11-01 15:19:01 +00:00
u32 error ;
2013-06-16 17:05:30 +00:00
FileNode * f = __IoGetFd ( id , error ) ;
2012-12-06 14:16:05 +00:00
if ( f ) {
2013-09-23 06:37:51 +00:00
if ( whence < 0 | | whence > 2 ) {
2019-07-28 22:55:44 +00:00
return hleLogWarning ( SCEIO , SCE_KERNEL_ERROR_INVAL , " invalid whence " ) ;
2013-09-23 06:37:51 +00:00
}
2013-10-09 06:49:24 +00:00
if ( f - > asyncBusy ( ) ) {
2019-07-28 22:55:44 +00:00
return hleLogWarning ( SCEIO , SCE_KERNEL_ERROR_ASYNC_BUSY , " async busy " ) ;
2013-10-09 06:49:24 +00:00
}
2019-07-28 23:46:56 +00:00
auto & params = asyncParams [ id ] ;
params . op = IoAsyncOp : : SEEK ;
params . seek . pos = offset ;
params . seek . whence = whence ;
IoStartAsyncThread ( id , f ) ;
return hleLogSuccessI ( SCEIO , 0 ) ;
2013-03-10 17:16:21 +00:00
} else {
2019-07-28 22:55:44 +00:00
return hleLogError ( SCEIO , error , " bad file descriptor " ) ;
2013-03-10 17:16:21 +00:00
}
return 0 ;
}
2012-11-01 15:19:01 +00:00
2014-12-08 09:40:08 +00:00
static u32 sceIoLseek32Async ( int id , int offset , int whence ) {
2013-03-10 17:16:21 +00:00
u32 error ;
2013-06-16 17:05:30 +00:00
FileNode * f = __IoGetFd ( id , error ) ;
2013-03-10 17:16:21 +00:00
if ( f ) {
2013-09-23 06:37:51 +00:00
if ( whence < 0 | | whence > 2 ) {
2019-07-28 22:55:44 +00:00
return hleLogWarning ( SCEIO , SCE_KERNEL_ERROR_INVAL , " invalid whence " ) ;
2013-09-23 06:37:51 +00:00
}
2013-10-09 06:49:24 +00:00
if ( f - > asyncBusy ( ) ) {
2019-07-28 22:55:44 +00:00
return hleLogWarning ( SCEIO , SCE_KERNEL_ERROR_ASYNC_BUSY , " async busy " ) ;
2013-10-09 06:49:24 +00:00
}
2019-07-28 23:46:56 +00:00
auto & params = asyncParams [ id ] ;
params . op = IoAsyncOp : : SEEK ;
params . seek . pos = offset ;
params . seek . whence = whence ;
IoStartAsyncThread ( id , f ) ;
return hleLogSuccessI ( SCEIO , 0 ) ;
2012-12-06 14:16:05 +00:00
} else {
2019-07-28 22:55:44 +00:00
return hleLogError ( SCEIO , error , " bad file descriptor " ) ;
2012-11-01 15:19:01 +00:00
}
2013-03-10 17:16:21 +00:00
return 0 ;
2012-11-01 15:19:01 +00:00
}
2020-05-21 22:55:25 +00:00
static FileNode * __IoOpen ( int & error , const char * filename , int flags , int mode ) {
2012-12-05 21:19:14 +00:00
int access = FILEACCESS_NONE ;
2017-02-19 09:04:54 +00:00
if ( flags & PSP_O_RDONLY )
2012-12-05 21:19:14 +00:00
access | = FILEACCESS_READ ;
2017-02-19 09:04:54 +00:00
if ( flags & PSP_O_WRONLY )
2012-12-05 21:19:14 +00:00
access | = FILEACCESS_WRITE ;
2017-02-19 09:04:54 +00:00
if ( flags & PSP_O_APPEND )
2012-12-05 21:19:14 +00:00
access | = FILEACCESS_APPEND ;
2017-02-19 09:04:54 +00:00
if ( flags & PSP_O_CREAT )
2012-12-05 21:19:14 +00:00
access | = FILEACCESS_CREATE ;
2017-02-19 09:04:54 +00:00
if ( flags & PSP_O_TRUNC )
2014-02-12 07:54:30 +00:00
access | = FILEACCESS_TRUNCATE ;
2017-02-19 09:04:54 +00:00
if ( flags & PSP_O_EXCL )
access | = FILEACCESS_EXCL ;
2012-12-05 21:19:14 +00:00
2021-06-06 16:56:41 +00:00
PSPFileInfo info ;
int h = - 1 ;
bool isTTY = false ;
// TODO: Technically, tty1, etc. too and space doesn't matter.
if ( startsWithNoCase ( filename , " tty0: " ) ) {
info . name = filename ;
info . access = 0777 ;
info . exists = true ;
isTTY = true ;
} else {
h = pspFileSystem . OpenFile ( filename , ( FileAccess ) access ) ;
if ( h < 0 ) {
error = h ;
return nullptr ;
}
2012-11-01 15:19:01 +00:00
}
2019-10-20 18:03:37 +00:00
error = 0 ;
2012-11-01 15:19:01 +00:00
FileNode * f = new FileNode ( ) ;
2021-06-06 16:58:46 +00:00
kernelObjects . Create ( f ) ;
2012-11-01 15:19:01 +00:00
f - > handle = h ;
f - > fullpath = filename ;
2021-06-06 16:58:46 +00:00
f - > asyncResult = h ;
2022-10-09 15:16:33 +00:00
if ( isTTY ) {
f - > info = info ;
f - > infoReady = true ;
}
2013-01-20 23:20:48 +00:00
f - > openMode = access ;
2021-06-06 16:56:41 +00:00
f - > isTTY = isTTY ;
2013-02-24 08:28:40 +00:00
2017-02-19 09:04:54 +00:00
f - > npdrm = ( flags & PSP_O_NPDRM ) ? true : false ;
2013-04-20 05:51:40 +00:00
f - > pgd_offset = 0 ;
2013-02-24 08:28:40 +00:00
2013-03-10 10:43:50 +00:00
return f ;
}
2014-12-08 09:40:08 +00:00
static u32 sceIoOpen ( const char * filename , int flags , int mode ) {
2020-05-21 22:42:43 +00:00
hleEatCycles ( 18000 ) ;
2020-05-21 22:08:10 +00:00
if ( ! __KernelIsDispatchEnabled ( ) ) {
2020-05-21 22:42:43 +00:00
hleEatCycles ( 48000 ) ;
2020-05-21 22:08:10 +00:00
return hleLogError ( SCEIO , SCE_KERNEL_ERROR_CAN_NOT_WAIT , " dispatch disabled " ) ;
}
2013-04-06 20:05:29 +00:00
2013-08-10 16:54:14 +00:00
int error ;
FileNode * f = __IoOpen ( error , filename , flags , mode ) ;
2020-05-21 22:08:10 +00:00
if ( ! f ) {
2020-08-15 22:38:55 +00:00
_assert_ ( error ! = 0 ) ;
2020-05-21 22:08:10 +00:00
if ( error = = ( int ) SCE_KERNEL_ERROR_NOCWD ) {
// TODO: Timing is not accurate.
return hleLogError ( SCEIO , hleDelayResult ( error , " file opened " , 10000 ) , " no current working directory " ) ;
2020-05-21 22:42:43 +00:00
} else if ( error = = ( int ) SCE_KERNEL_ERROR_NODEV ) {
return hleLogError ( SCEIO , error , " device not found " ) ;
2020-05-21 22:08:10 +00:00
} else if ( error = = ( int ) SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND ) {
2020-05-21 23:28:05 +00:00
// UMD: Varies between 5-10ms, could take longer if disc spins up.
// TODO: Bad filename at root (disc0:/no.exist) should take ~200us.
// Card: Path depth matters, but typically between 10-13ms on a standard Pro Duo.
2020-05-21 23:58:19 +00:00
// TODO: If a UMD and spun down, this can easily take 1s+.
2020-05-21 23:28:05 +00:00
int delay = pspFileSystem . FlagsFromFilename ( filename ) & FileSystemFlags : : UMD ? 6000 : 10000 ;
return hleLogWarning ( SCEIO , hleDelayResult ( error , " file opened " , delay ) , " file not found " ) ;
2020-05-21 22:08:10 +00:00
} else {
return hleLogError ( SCEIO , hleDelayResult ( error , " file opened " , 10000 ) ) ;
2013-05-30 20:15:15 +00:00
}
2013-03-10 10:43:50 +00:00
}
2013-06-16 17:05:30 +00:00
int id = __IoAllocFd ( f ) ;
if ( id < 0 ) {
kernelObjects . Destroy < FileNode > ( f - > GetUID ( ) ) ;
2020-05-21 23:28:05 +00:00
return hleLogError ( SCEIO , hleDelayResult ( id , " file opened " , 1000 ) , " out of fds " ) ;
2013-06-16 17:05:30 +00:00
} else {
2020-01-03 21:09:58 +00:00
asyncParams [ id ] . priority = asyncDefaultPriority ;
2020-05-22 00:57:41 +00:00
IFileSystem * sys = pspFileSystem . GetSystemFromFilename ( filename ) ;
2021-06-06 16:56:41 +00:00
if ( sys & & ! f - > isTTY & & ( sys - > DevType ( f - > handle ) & ( PSPDevType : : BLOCK | PSPDevType : : EMU_LBN ) ) ) {
2020-05-22 00:57:41 +00:00
// These are fast to open, no delay or even rescheduling happens.
return hleLogSuccessI ( SCEIO , id ) ;
}
2020-05-21 23:28:05 +00:00
// UMD: Speed varies from 1-6ms.
// Card: Path depth matters, but typically between 10-13ms on a standard Pro Duo.
int delay = pspFileSystem . FlagsFromFilename ( filename ) & FileSystemFlags : : UMD ? 4000 : 10000 ;
return hleLogSuccessI ( SCEIO , hleDelayResult ( id , " file opened " , delay ) ) ;
2013-06-16 17:05:30 +00:00
}
2012-11-01 15:19:01 +00:00
}
2014-12-08 09:40:08 +00:00
static u32 sceIoClose ( int id ) {
2013-02-24 08:28:40 +00:00
u32 error ;
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoClose(%d) " , id ) ;
2013-06-16 17:05:30 +00:00
__IoFreeFd ( id , error ) ;
2013-03-11 01:57:17 +00:00
// Timing is not accurate, aiming low for now.
2013-06-16 17:05:30 +00:00
return hleDelayResult ( error , " file closed " , 100 ) ;
2012-11-01 15:19:01 +00:00
}
2014-12-08 09:40:08 +00:00
static u32 sceIoRemove ( const char * filename ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoRemove(%s) " , filename ) ;
2013-01-20 23:20:48 +00:00
2013-03-11 02:26:49 +00:00
// TODO: This timing isn't necessarily accurate, low end for now.
2013-01-20 23:20:48 +00:00
if ( ! pspFileSystem . GetFileInfo ( filename ) . exists )
2018-05-06 17:15:05 +00:00
return hleDelayResult ( SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND , " file removed " , 100 ) ;
2013-01-20 23:20:48 +00:00
2013-02-08 18:20:52 +00:00
pspFileSystem . RemoveFile ( filename ) ;
2013-03-11 02:26:49 +00:00
return hleDelayResult ( 0 , " file removed " , 100 ) ;
2012-11-01 15:19:01 +00:00
}
2014-12-08 09:40:08 +00:00
static u32 sceIoMkdir ( const char * dirname , int mode ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoMkdir(%s, %i) " , dirname , mode ) ;
2013-03-11 02:26:49 +00:00
// TODO: Improve timing.
2012-12-05 21:19:14 +00:00
if ( pspFileSystem . MkDir ( dirname ) )
2013-03-11 02:26:49 +00:00
return hleDelayResult ( 0 , " mkdir " , 1000 ) ;
2012-11-01 15:19:01 +00:00
else
2016-07-24 19:22:26 +00:00
return hleDelayResult ( SCE_KERNEL_ERROR_ERRNO_FILE_ALREADY_EXISTS , " mkdir " , 1000 ) ;
2012-11-01 15:19:01 +00:00
}
2014-12-08 09:40:08 +00:00
static u32 sceIoRmdir ( const char * dirname ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoRmdir(%s) " , dirname ) ;
2013-03-11 02:26:49 +00:00
// TODO: Improve timing.
2012-12-05 21:19:14 +00:00
if ( pspFileSystem . RmDir ( dirname ) )
2013-03-11 02:26:49 +00:00
return hleDelayResult ( 0 , " rmdir " , 1000 ) ;
2012-11-01 15:19:01 +00:00
else
2018-05-06 17:15:05 +00:00
return hleDelayResult ( SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND , " rmdir " , 1000 ) ;
2012-11-01 15:19:01 +00:00
}
2014-12-08 09:40:08 +00:00
static u32 sceIoSync ( const char * devicename , int flag ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " UNIMPL sceIoSync(%s, %i) " , devicename , flag ) ;
2013-01-17 08:44:35 +00:00
return 0 ;
2012-11-01 15:19:01 +00:00
}
2012-12-06 14:16:05 +00:00
struct DeviceSize {
2013-07-27 22:41:19 +00:00
u32_le maxClusters ;
u32_le freeClusters ;
u32_le maxSectors ;
u32_le sectorSize ;
u32_le sectorCount ;
2012-11-01 15:19:01 +00:00
} ;
2014-12-08 09:40:08 +00:00
static u32 sceIoDevctl ( const char * name , int cmd , u32 argAddr , int argLen , u32 outPtr , int outLen ) {
2012-12-06 14:16:05 +00:00
if ( strcmp ( name , " emulator: " ) ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoDevctl( \" %s \" , %08x, %08x, %i, %08x, %i) " , name , cmd , argAddr , argLen , outPtr , outLen ) ;
2012-12-06 14:16:05 +00:00
}
// UMD checks
switch ( cmd ) {
2013-04-28 11:39:47 +00:00
case 0x01F20001 :
// Get UMD disc type
if ( Memory : : IsValidAddress ( outPtr ) & & outLen > = 8 ) {
Memory : : Write_U32 ( 0x10 , outPtr + 4 ) ; // Always return game disc (if present)
2012-12-06 14:16:05 +00:00
return 0 ;
2013-04-28 11:39:47 +00:00
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS ;
2012-12-06 14:16:05 +00:00
}
2013-04-28 11:39:47 +00:00
break ;
case 0x01F20002 :
// Get UMD current LBA
if ( Memory : : IsValidAddress ( outPtr ) & & outLen > = 4 ) {
Memory : : Write_U32 ( 0x10 , outPtr ) ; // Assume first sector
return 0 ;
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS ;
}
break ;
case 0x01F20003 :
if ( Memory : : IsValidAddress ( argAddr ) & & argLen > = 4 ) {
2013-03-10 23:44:47 +00:00
PSPFileInfo info = pspFileSystem . GetFileInfo ( " umd1: " ) ;
2014-01-23 06:24:30 +00:00
Memory : : Write_U32 ( ( u32 ) ( info . size ) - 1 , outPtr ) ;
2012-12-06 14:16:05 +00:00
return 0 ;
2013-04-28 11:39:47 +00:00
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS ;
2012-12-06 14:16:05 +00:00
}
2013-04-28 11:39:47 +00:00
break ;
case 0x01F100A3 :
// Seek UMD disc (raw)
if ( Memory : : IsValidAddress ( argAddr ) & & argLen > = 4 ) {
return hleDelayResult ( 0 , " dev seek " , 100 ) ;
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS ;
}
break ;
case 0x01F100A4 :
// Prepare UMD data into cache.
if ( Memory : : IsValidAddress ( argAddr ) & & argLen > = 4 ) {
2013-03-11 00:08:12 +00:00
return 0 ;
2013-04-28 11:39:47 +00:00
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS ;
2013-03-11 00:08:12 +00:00
}
2013-04-28 11:39:47 +00:00
break ;
case 0x01F300A5 :
// Prepare UMD data into cache and get status
if ( Memory : : IsValidAddress ( argAddr ) & & argLen > = 4 ) {
Memory : : Write_U32 ( 1 , outPtr ) ; // Status (unitary index of the requested read, greater or equal to 1)
return 0 ;
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS ;
}
break ;
case 0x01F300A7 :
// Wait for the UMD data cache thread
if ( Memory : : IsValidAddress ( argAddr ) & & argLen > = 4 ) {
// TODO :
// Place the calling thread in wait state
return 0 ;
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS ;
}
break ;
case 0x01F300A8 :
// Poll the UMD data cache thread
if ( Memory : : IsValidAddress ( argAddr ) & & argLen > = 4 ) {
// 0 - UMD data cache thread has finished
// 0x10 - UMD data cache thread is waiting
// 0x20 - UMD data cache thread is running
return 0 ; // Return finished
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS ;
}
break ;
case 0x01F300A9 :
// Cancel the UMD data cache thread
if ( Memory : : IsValidAddress ( argAddr ) & & argLen > = 4 ) {
// TODO :
// Wake up the thread waiting for the UMD data cache handling.
return 0 ;
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS ;
}
break ;
2013-12-28 05:38:20 +00:00
// TODO: What do these do? Seem to require a u32 in, no output.
2013-03-11 00:08:12 +00:00
case 0x01F100A6 :
case 0x01F100A8 :
case 0x01F100A9 :
2013-09-07 19:19:21 +00:00
ERROR_LOG_REPORT ( SCEIO , " UNIMPL sceIoDevctl( \" %s \" , %08x, %08x, %i, %08x, %i) " , name , cmd , argAddr , argLen , outPtr , outLen ) ;
2013-03-11 00:08:12 +00:00
return 0 ;
2012-12-06 14:16:05 +00:00
}
// This should really send it on to a FileSystem implementation instead.
2014-02-10 09:41:28 +00:00
if ( ! strcmp ( name , " mscmhc0: " ) | | ! strcmp ( name , " ms0: " ) | | ! strcmp ( name , " memstick: " ) )
2012-12-06 14:16:05 +00:00
{
2016-08-06 03:42:58 +00:00
// MemoryStick checks
2013-04-28 11:39:47 +00:00
switch ( cmd ) {
case 0x02025801 :
2016-08-06 03:42:58 +00:00
// Check the MemoryStick's driver status (mscmhc0: only.)
if ( Memory : : IsValidAddress ( outPtr ) & & outLen > = 4 ) {
if ( MemoryStick_State ( ) = = PSP_MEMORYSTICK_STATE_INSERTED ) {
// 1 = not inserted (ready), 4 = inserted
Memory : : Write_U32 ( PSP_MEMORYSTICK_STATE_DEVICE_INSERTED , outPtr ) ;
} else {
Memory : : Write_U32 ( PSP_MEMORYSTICK_STATE_DRIVER_READY , outPtr ) ;
}
2013-04-28 11:39:47 +00:00
return 0 ;
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS ;
}
break ;
2016-08-06 03:42:58 +00:00
case 0x02015804 :
2013-04-28 11:39:47 +00:00
// Register MemoryStick's insert/eject callback (mscmhc0)
2016-08-06 03:42:58 +00:00
if ( Memory : : IsValidAddress ( argAddr ) & & outPtr = = 0 & & argLen > = 4 ) {
2012-12-06 14:16:05 +00:00
u32 cbId = Memory : : Read_U32 ( argAddr ) ;
2016-08-06 03:42:58 +00:00
int type = - 1 ;
kernelObjects . GetIDType ( cbId , & type ) ;
if ( memStickCallbacks . size ( ) < 32 & & type = = SCE_KERNEL_TMID_Callback ) {
memStickCallbacks . push_back ( cbId ) ;
if ( MemoryStick_State ( ) = = PSP_MEMORYSTICK_STATE_INSERTED ) {
// Only fired immediately if the card is currently inserted.
// Values observed:
// * 1 = Memory stick inserted
// * 2 = Memory stick removed
// * 4 = Memory stick mounting? (followed by a 1 about 500ms later)
DEBUG_LOG ( SCEIO , " sceIoDevctl: Memstick callback %i registered, notifying immediately " , cbId ) ;
__KernelNotifyCallback ( cbId , MemoryStick_State ( ) ) ;
} else {
DEBUG_LOG ( SCEIO , " sceIoDevctl: Memstick callback %i registered " , cbId ) ;
}
2012-12-06 14:16:05 +00:00
return 0 ;
} else {
2016-08-06 03:42:58 +00:00
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
2012-12-06 14:16:05 +00:00
}
2013-04-28 11:39:47 +00:00
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS ;
2012-12-06 14:16:05 +00:00
}
break ;
2016-08-06 03:42:58 +00:00
case 0x02015805 :
2013-04-28 11:39:47 +00:00
// Unregister MemoryStick's insert/eject callback (mscmhc0)
2016-08-06 03:42:58 +00:00
if ( Memory : : IsValidAddress ( argAddr ) & & argLen > = 4 ) {
2016-09-24 21:38:11 +00:00
SceUID cbId = Memory : : Read_U32 ( argAddr ) ;
2016-08-06 03:42:58 +00:00
size_t slot = ( size_t ) - 1 ;
// We want to only remove one at a time.
for ( size_t i = 0 ; i < memStickCallbacks . size ( ) ; + + i ) {
if ( memStickCallbacks [ i ] = = cbId ) {
slot = i ;
break ;
}
}
if ( slot ! = ( size_t ) - 1 ) {
memStickCallbacks . erase ( memStickCallbacks . begin ( ) + slot ) ;
DEBUG_LOG ( SCEIO , " sceIoDevctl: Unregistered memstick callback %i " , cbId ) ;
2012-12-06 14:16:05 +00:00
return 0 ;
} else {
2016-08-06 03:42:58 +00:00
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
2012-12-06 14:16:05 +00:00
}
2012-12-19 10:11:27 +00:00
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS ;
}
2013-04-28 11:39:47 +00:00
break ;
case 0x02025806 :
// Check if the device is inserted (mscmhc0)
2016-08-06 03:42:58 +00:00
if ( Memory : : IsValidAddress ( outPtr ) & & outLen > = 4 ) {
2013-04-28 11:39:47 +00:00
// 1 = Inserted.
2016-08-06 03:42:58 +00:00
// 2 = Not inserted.
Memory : : Write_U32 ( MemoryStick_State ( ) , outPtr ) ;
2012-12-06 14:16:05 +00:00
return 0 ;
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS ;
}
2013-04-28 11:39:47 +00:00
break ;
case 0x02425818 :
2016-08-06 03:42:58 +00:00
// Get MS capacity (fatms0).
if ( MemoryStick_State ( ) ! = PSP_MEMORYSTICK_STATE_INSERTED ) {
return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND ;
}
// TODO: Pretend we have a 2GB memory stick? Should we check MemoryStick_FreeSpace?
2022-09-03 16:13:07 +00:00
if ( Memory : : IsValidRange ( argAddr , 4 ) & & argLen > = 4 ) { // NOTE: not outPtr
u32 pointer = Memory : : ReadUnchecked_U32 ( argAddr ) ;
2012-12-19 20:21:46 +00:00
u32 sectorSize = 0x200 ;
u32 memStickSectorSize = 32 * 1024 ;
u32 sectorCount = memStickSectorSize / sectorSize ;
u64 freeSize = 1 * 1024 * 1024 * 1024 ;
2022-09-03 16:13:07 +00:00
auto deviceSize = PSPPointer < DeviceSize > : : Create ( pointer ) ;
if ( deviceSize . IsValid ( ) ) {
deviceSize - > maxClusters = ( u32 ) ( ( freeSize * 95 / 100 ) / ( sectorSize * sectorCount ) ) ;
deviceSize - > freeClusters = deviceSize - > maxClusters ;
deviceSize - > maxSectors = deviceSize - > maxClusters ;
deviceSize - > sectorSize = sectorSize ;
deviceSize - > sectorCount = sectorCount ;
deviceSize . NotifyWrite ( " ms0:02425818 " ) ;
}
2012-12-06 14:16:05 +00:00
return 0 ;
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS ;
}
2013-04-28 11:39:47 +00:00
break ;
2016-08-06 03:42:58 +00:00
case 0x02425824 :
// Check if write protected
if ( MemoryStick_State ( ) ! = PSP_MEMORYSTICK_STATE_INSERTED ) {
return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND ;
}
2022-09-03 16:13:07 +00:00
if ( Memory : : IsValidRange ( outPtr , 4 ) & & outLen = = 4 ) {
Memory : : WriteUnchecked_U32 ( 0 , outPtr ) ;
2013-03-11 01:00:15 +00:00
return 0 ;
} else {
2013-09-07 19:19:21 +00:00
ERROR_LOG ( SCEIO , " Failed 0x02425824 fat " ) ;
2013-03-11 01:00:15 +00:00
return - 1 ;
}
2013-04-28 11:39:47 +00:00
break ;
2012-12-06 14:16:05 +00:00
}
}
if ( ! strcmp ( name , " fatms0: " ) )
{
switch ( cmd ) {
2016-08-06 03:42:58 +00:00
case 0x0240d81e :
// TODO: Invalidate MS driver file table cache (nop)
break ;
case 0x02415821 :
// MScmRegisterMSInsertEjectCallback
if ( Memory : : IsValidAddress ( argAddr ) & & argLen > = 4 ) {
2012-12-06 14:16:05 +00:00
u32 cbId = Memory : : Read_U32 ( argAddr ) ;
2016-08-06 03:42:58 +00:00
int type = - 1 ;
kernelObjects . GetIDType ( cbId , & type ) ;
if ( memStickFatCallbacks . size ( ) < 32 & & type = = SCE_KERNEL_TMID_Callback ) {
memStickFatCallbacks . push_back ( cbId ) ;
if ( MemoryStick_State ( ) = = PSP_MEMORYSTICK_STATE_INSERTED ) {
// Only fired immediately if the card is currently inserted.
// Values observed:
// * 1 = Memory stick inserted
// * 2 = Memory stick removed
DEBUG_LOG ( SCEIO , " sceIoDevCtl: Memstick FAT callback %i registered, notifying immediately " , cbId ) ;
__KernelNotifyCallback ( cbId , MemoryStick_FatState ( ) ) ;
} else {
DEBUG_LOG ( SCEIO , " sceIoDevCtl: Memstick FAT callback %i registered " , cbId ) ;
}
2012-12-06 14:16:05 +00:00
return 0 ;
} else {
2016-08-06 03:42:58 +00:00
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
2012-12-06 14:16:05 +00:00
}
2016-08-06 03:42:58 +00:00
} else {
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
2012-12-06 14:16:05 +00:00
}
break ;
2016-08-06 03:42:58 +00:00
case 0x02415822 :
// MScmUnregisterMSInsertEjectCallback
if ( Memory : : IsValidAddress ( argAddr ) & & argLen > = 4 ) {
2016-09-24 21:38:11 +00:00
SceUID cbId = Memory : : Read_U32 ( argAddr ) ;
2016-08-06 03:42:58 +00:00
size_t slot = ( size_t ) - 1 ;
// We want to only remove one at a time.
for ( size_t i = 0 ; i < memStickFatCallbacks . size ( ) ; + + i ) {
if ( memStickFatCallbacks [ i ] = = cbId ) {
slot = i ;
break ;
}
}
if ( slot ! = ( size_t ) - 1 ) {
memStickFatCallbacks . erase ( memStickFatCallbacks . begin ( ) + slot ) ;
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoDevCtl: Unregistered memstick FAT callback %i " , cbId ) ;
2012-12-06 14:16:05 +00:00
return 0 ;
} else {
2016-08-06 03:42:58 +00:00
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
2012-12-06 14:16:05 +00:00
}
}
2013-04-28 11:39:47 +00:00
break ;
case 0x02415823 :
// Set FAT as enabled
2012-12-06 14:16:05 +00:00
if ( Memory : : IsValidAddress ( argAddr ) & & argLen = = 4 ) {
MemoryStick_SetFatState ( ( MemStickFatState ) Memory : : Read_U32 ( argAddr ) ) ;
return 0 ;
} else {
2013-09-07 19:19:21 +00:00
ERROR_LOG ( SCEIO , " Failed 0x02415823 fat " ) ;
2012-12-06 14:16:05 +00:00
return - 1 ;
}
break ;
2013-04-28 11:39:47 +00:00
case 0x02425823 :
// Check if FAT enabled
2013-06-09 00:03:21 +00:00
// If the values added together are >= 0x80000000, or less than outPtr, invalid address.
if ( ( ( int ) outPtr + outLen ) < ( int ) outPtr ) {
2013-09-07 19:19:21 +00:00
ERROR_LOG ( SCEIO , " sceIoDevctl: fatms0: 0x02425823 command, bad address " ) ;
2013-06-09 00:03:21 +00:00
return SCE_KERNEL_ERROR_ILLEGAL_ADDR ;
} else if ( ! Memory : : IsValidAddress ( outPtr ) ) {
// Technically, only checks for NULL, crashes for many bad addresses.
2013-09-07 19:19:21 +00:00
ERROR_LOG ( SCEIO , " sceIoDevctl: fatms0: 0x02425823 command, no output address " ) ;
2013-06-09 00:03:21 +00:00
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
} else {
// Does not care about outLen, even if it's 0.
2016-08-06 03:42:58 +00:00
// Note: writes 1 when inserted, 0 when not inserted.
2012-12-06 14:16:05 +00:00
Memory : : Write_U32 ( MemoryStick_FatState ( ) , outPtr ) ;
2013-11-20 01:48:13 +00:00
return hleDelayResult ( 0 , " check fat state " , cyclesToUs ( 23500 ) ) ;
2012-12-06 14:16:05 +00:00
}
break ;
2013-04-28 11:39:47 +00:00
case 0x02425824 :
// Check if write protected
2016-08-06 03:42:58 +00:00
if ( MemoryStick_State ( ) ! = PSP_MEMORYSTICK_STATE_INSERTED ) {
return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND ;
}
2013-03-11 01:00:15 +00:00
if ( Memory : : IsValidAddress ( outPtr ) & & outLen = = 4 ) {
Memory : : Write_U32 ( 0 , outPtr ) ;
return 0 ;
} else {
2013-09-07 19:19:21 +00:00
ERROR_LOG ( SCEIO , " Failed 0x02425824 fat " ) ;
2013-03-11 01:00:15 +00:00
return - 1 ;
}
break ;
2013-04-28 11:39:47 +00:00
case 0x02425818 :
2016-08-06 03:42:58 +00:00
// Get MS capacity (fatms0).
if ( MemoryStick_State ( ) ! = PSP_MEMORYSTICK_STATE_INSERTED ) {
return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND ;
}
// TODO: Pretend we have a 2GB memory stick? Should we check MemoryStick_FreeSpace?
2013-04-28 11:39:47 +00:00
if ( Memory : : IsValidAddress ( argAddr ) & & argLen > = 4 ) { // NOTE: not outPtr
u32 pointer = Memory : : Read_U32 ( argAddr ) ;
u32 sectorSize = 0x200 ;
u32 memStickSectorSize = 32 * 1024 ;
u32 sectorCount = memStickSectorSize / sectorSize ;
u64 freeSize = 1 * 1024 * 1024 * 1024 ;
2022-09-03 16:13:07 +00:00
auto deviceSize = PSPPointer < DeviceSize > : : Create ( pointer ) ;
if ( deviceSize . IsValid ( ) ) {
deviceSize - > maxClusters = ( u32 ) ( ( freeSize * 95 / 100 ) / ( sectorSize * sectorCount ) ) ;
deviceSize - > freeClusters = deviceSize - > maxClusters ;
deviceSize - > maxSectors = deviceSize - > maxClusters ;
deviceSize - > sectorSize = sectorSize ;
deviceSize - > sectorCount = sectorCount ;
deviceSize . NotifyWrite ( " fatms0:02425818 " ) ;
}
2013-04-28 11:39:47 +00:00
return 0 ;
} else {
return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS ;
2012-12-06 14:16:05 +00:00
}
2013-04-28 11:39:47 +00:00
break ;
2012-12-06 14:16:05 +00:00
}
}
if ( ! strcmp ( name , " kemulator: " ) | | ! strcmp ( name , " emulator: " ) )
{
// Emulator special tricks!
2022-11-26 13:55:45 +00:00
enum
{
EMULATOR_DEVCTL__GET_HAS_DISPLAY = 1 ,
EMULATOR_DEVCTL__SEND_OUTPUT ,
EMULATOR_DEVCTL__IS_EMULATOR ,
EMULATOR_DEVCTL__VERIFY_STATE ,
EMULATOR_DEVCTL__EMIT_SCREENSHOT = 0x20 ,
EMULATOR_DEVCTL__TOGGLE_FASTFORWARD = 0x30 ,
EMULATOR_DEVCTL__GET_ASPECT_RATIO ,
EMULATOR_DEVCTL__GET_SCALE ,
2022-12-05 11:46:00 +00:00
EMULATOR_DEVCTL__GET_AXIS ,
2022-12-04 20:46:01 +00:00
EMULATOR_DEVCTL__GET_VKEY ,
2022-11-26 13:55:45 +00:00
} ;
2013-04-28 11:39:47 +00:00
switch ( cmd ) {
2022-11-26 13:55:45 +00:00
case EMULATOR_DEVCTL__GET_HAS_DISPLAY :
2012-12-06 14:16:05 +00:00
if ( Memory : : IsValidAddress ( outPtr ) )
2021-11-04 00:41:07 +00:00
Memory : : Write_U32 ( PSP_CoreParameter ( ) . headLess ? 0 : 1 , outPtr ) ;
2012-12-06 14:16:05 +00:00
return 0 ;
2022-11-26 13:55:45 +00:00
case EMULATOR_DEVCTL__SEND_OUTPUT :
2012-12-06 14:16:05 +00:00
{
std : : string data ( Memory : : GetCharPointer ( argAddr ) , argLen ) ;
2023-03-26 17:17:34 +00:00
if ( ! System_SendDebugOutput ( data ) )
DEBUG_LOG ( SCEIO , " %s " , data . c_str ( ) ) ;
2023-03-26 17:21:07 +00:00
if ( PSP_CoreParameter ( ) . collectDebugOutput )
* PSP_CoreParameter ( ) . collectDebugOutput + = data ;
2012-12-06 14:16:05 +00:00
return 0 ;
}
2022-11-26 13:55:45 +00:00
case EMULATOR_DEVCTL__IS_EMULATOR :
2012-12-06 14:16:05 +00:00
if ( Memory : : IsValidAddress ( outPtr ) )
2012-12-28 22:13:34 +00:00
Memory : : Write_U32 ( 1 , outPtr ) ;
return 0 ;
2022-11-26 13:55:45 +00:00
case EMULATOR_DEVCTL__VERIFY_STATE :
2012-12-28 22:13:34 +00:00
// Note that this is async, and makes sure the save state matches up.
SaveState : : Verify ( ) ;
// TODO: Maybe save/load to a file just to be sure?
2012-12-06 14:16:05 +00:00
return 0 ;
2013-01-14 00:35:34 +00:00
2022-11-26 13:55:45 +00:00
case EMULATOR_DEVCTL__EMIT_SCREENSHOT :
2017-06-03 21:46:36 +00:00
{
PSPPointer < u8 > topaddr ;
2022-01-24 03:22:32 +00:00
u32 linesize ;
2013-01-14 00:35:34 +00:00
2022-01-24 03:22:32 +00:00
__DisplayGetFramebuf ( & topaddr , & linesize , nullptr , 0 ) ;
2013-01-14 00:35:34 +00:00
// TODO: Convert based on pixel format / mode / something?
2023-03-26 00:34:29 +00:00
System_SendDebugScreenshot ( std : : string ( ( const char * ) & topaddr [ 0 ] , linesize * 272 ) , 272 ) ;
2013-01-14 00:35:34 +00:00
return 0 ;
2012-12-06 14:16:05 +00:00
}
2022-11-26 13:55:45 +00:00
case EMULATOR_DEVCTL__TOGGLE_FASTFORWARD :
if ( argAddr )
PSP_CoreParameter ( ) . fastForward = true ;
else
PSP_CoreParameter ( ) . fastForward = false ;
return 0 ;
case EMULATOR_DEVCTL__GET_ASPECT_RATIO :
2022-11-27 09:04:26 +00:00
if ( Memory : : IsValidAddress ( outPtr ) ) {
2023-04-02 20:27:31 +00:00
// TODO: Share code with CalculateDisplayOutputRect to take a few more things into account.
2022-11-27 20:55:22 +00:00
// I have a planned further refactoring.
float ar ;
if ( g_Config . bDisplayStretch ) {
2023-02-25 12:09:44 +00:00
ar = ( float ) g_display . dp_xres / ( float ) g_display . dp_yres ;
2022-11-27 20:55:22 +00:00
} else {
ar = g_Config . fDisplayAspectRatio * ( 480.0f / 272.0f ) ;
}
Memory : : Write_Float ( ar , outPtr ) ;
2022-11-26 13:55:45 +00:00
}
return 0 ;
case EMULATOR_DEVCTL__GET_SCALE :
2022-11-27 09:04:26 +00:00
if ( Memory : : IsValidAddress ( outPtr ) ) {
// TODO: Maybe do something more sophisticated taking the longest side and screen rotation
// into account, etc.
2023-02-25 12:09:44 +00:00
float scale = ( float ) g_display . dp_xres * g_Config . fDisplayScale / 480.0f ;
2022-11-27 20:55:22 +00:00
Memory : : Write_Float ( scale , outPtr ) ;
2022-11-27 09:04:26 +00:00
}
2022-11-26 13:55:45 +00:00
return 0 ;
2022-12-05 11:46:00 +00:00
case EMULATOR_DEVCTL__GET_AXIS :
2023-02-01 10:26:17 +00:00
if ( Memory : : IsValidAddress ( outPtr ) & & ( argAddr > = 0 & & argAddr < JOYSTICK_AXIS_MAX ) ) {
2022-12-05 11:46:00 +00:00
Memory : : Write_Float ( HLEPlugins : : PluginDataAxis [ argAddr ] , outPtr ) ;
2022-12-04 20:46:01 +00:00
}
return 0 ;
case EMULATOR_DEVCTL__GET_VKEY :
2023-02-01 10:26:17 +00:00
if ( Memory : : IsValidAddress ( outPtr ) & & ( argAddr > = 0 & & argAddr < NKCODE_MAX ) ) {
2023-05-01 10:55:50 +00:00
Memory : : Write_U8 ( HLEPlugins : : GetKey ( argAddr ) , outPtr ) ;
2022-12-04 20:46:01 +00:00
}
2022-11-26 13:55:45 +00:00
return 0 ;
2017-06-03 21:46:36 +00:00
}
2012-12-06 14:16:05 +00:00
2013-09-07 19:19:21 +00:00
ERROR_LOG ( SCEIO , " sceIoDevCtl: UNKNOWN PARAMETERS " ) ;
2012-12-06 14:16:05 +00:00
return 0 ;
}
//089c6d1c weird branch
/*
089 c6bdc ] : HLE : sceKernelCreateCallback ( name = MemoryStick Detection , entry = 089 c7484 ) ( z_un_089c6bc4 )
089 c6c40 ] : HLE : sceKernelCreateCallback ( name = MemoryStick Assignment , entry = 089 c7534 ) ( z_un_089c6bc4 )
*/
2013-12-17 09:55:38 +00:00
ERROR_LOG_REPORT ( SCEIO , " UNIMPL sceIoDevctl( \" %s \" , %08x, %08x, %i, %08x, %i) " , name , cmd , argAddr , argLen , outPtr , outLen ) ;
2012-12-06 14:16:05 +00:00
return SCE_KERNEL_ERROR_UNSUP ;
}
2014-12-08 09:40:08 +00:00
static u32 sceIoRename ( const char * from , const char * to ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoRename(%s, %s) " , from , to ) ;
2013-01-20 23:20:48 +00:00
2013-03-11 02:26:49 +00:00
// TODO: Timing isn't terribly accurate.
if ( ! pspFileSystem . GetFileInfo ( from ) . exists )
2018-05-06 17:15:05 +00:00
return hleDelayResult ( SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND , " file renamed " , 1000 ) ;
2013-01-20 23:20:48 +00:00
2013-06-24 03:09:44 +00:00
int result = pspFileSystem . RenameFile ( from , to ) ;
if ( result < 0 )
2013-09-07 19:19:21 +00:00
WARN_LOG ( SCEIO , " Could not move %s to %s " , from , to ) ;
2013-06-24 03:09:44 +00:00
return hleDelayResult ( result , " file renamed " , 1000 ) ;
2012-11-01 15:19:01 +00:00
}
2014-12-08 09:40:08 +00:00
static u32 sceIoChdir ( const char * dirname ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoChdir(%s) " , dirname ) ;
2013-05-12 23:51:08 +00:00
return pspFileSystem . ChDir ( dirname ) ;
2012-11-01 15:19:01 +00:00
}
2019-07-28 22:43:19 +00:00
static int sceIoChangeAsyncPriority ( int id , int priority ) {
2020-01-03 06:33:32 +00:00
// priority = -1 is valid,means the current thread'priority
2019-07-28 22:43:19 +00:00
if ( priority ! = - 1 & & ( priority < 0x08 | | priority > 0x77 ) ) {
return hleLogError ( SCEIO , SCE_KERNEL_ERROR_ILLEGAL_PRIORITY , " illegal priority %d " , priority ) ;
2013-09-24 13:51:05 +00:00
}
2019-07-28 22:43:19 +00:00
2019-12-26 19:26:31 +00:00
if ( id = = - 1 ) {
asyncDefaultPriority = priority ;
return hleLogSuccessI ( SCEIO , 0 ) ;
}
2019-07-28 22:43:19 +00:00
u32 error ;
FileNode * f = __IoGetFd ( id , error ) ;
if ( ! f ) {
return hleLogError ( SCEIO , error , " bad file descriptor " ) ;
}
2019-10-20 17:26:37 +00:00
if ( asyncThreads [ id ] & & ! asyncThreads [ id ] - > Stopped ( ) ) {
2020-01-03 21:09:58 +00:00
if ( priority = = - 1 ) {
priority = KernelCurThreadPriority ( ) ;
}
2019-10-20 17:26:37 +00:00
asyncThreads [ id ] - > ChangePriority ( priority ) ;
}
2019-07-28 22:43:19 +00:00
asyncParams [ id ] . priority = priority ;
return hleLogSuccessI ( SCEIO , 0 ) ;
2012-11-01 15:19:01 +00:00
}
2021-01-24 19:22:41 +00:00
static int sceIoCloseAsync ( int id ) {
2013-03-10 09:12:03 +00:00
u32 error ;
2013-06-16 17:05:30 +00:00
FileNode * f = __IoGetFd ( id , error ) ;
2021-01-24 19:22:41 +00:00
if ( ! f ) {
2019-07-28 22:55:44 +00:00
return hleLogError ( SCEIO , error , " bad file descriptor " ) ;
2013-03-10 09:12:03 +00:00
}
2021-01-24 19:22:41 +00:00
if ( f - > asyncBusy ( ) ) {
return hleLogWarning ( SCEIO , SCE_KERNEL_ERROR_ASYNC_BUSY , " async busy " ) ;
}
f - > closePending = true ;
auto & params = asyncParams [ id ] ;
params . op = IoAsyncOp : : CLOSE ;
IoStartAsyncThread ( id , f ) ;
return hleLogSuccessI ( SCEIO , 0 ) ;
2012-11-01 15:19:01 +00:00
}
2014-12-08 09:40:08 +00:00
static u32 sceIoSetAsyncCallback ( int id , u32 clbckId , u32 clbckArg )
2012-11-01 15:19:01 +00:00
{
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoSetAsyncCallback(%d, %i, %08x) " , id , clbckId , clbckArg ) ;
2012-11-01 15:19:01 +00:00
2012-12-05 21:19:14 +00:00
u32 error ;
2013-06-16 17:05:30 +00:00
FileNode * f = __IoGetFd ( id , error ) ;
2012-11-01 15:19:01 +00:00
if ( f )
{
2012-12-05 21:19:14 +00:00
f - > callbackID = clbckId ;
f - > callbackArg = clbckArg ;
2012-12-26 10:17:47 +00:00
return 0 ;
2012-11-01 15:19:01 +00:00
}
else
{
2012-12-05 21:19:14 +00:00
return error ;
2012-11-01 15:19:01 +00:00
}
}
2020-05-21 23:34:04 +00:00
static u32 sceIoOpenAsync ( const char * filename , int flags , int mode ) {
hleEatCycles ( 18000 ) ;
2022-12-27 16:33:07 +00:00
// TODO: Use an internal method so as not to pollute the log?
2013-04-06 20:05:29 +00:00
// Intentionally does not work when interrupts disabled.
if ( ! __KernelIsDispatchEnabled ( ) )
sceKernelResumeDispatchThread ( 1 ) ;
2013-08-10 16:54:14 +00:00
int error ;
FileNode * f = __IoOpen ( error , filename , flags , mode ) ;
2013-02-11 01:59:15 +00:00
2012-12-26 07:25:17 +00:00
// We have to return an fd here, which may have been destroyed when we reach Wait if it failed.
2019-07-28 23:53:20 +00:00
if ( f = = nullptr ) {
2020-08-15 22:38:55 +00:00
_assert_ ( error ! = 0 ) ;
2020-05-21 23:34:04 +00:00
if ( error = = SCE_KERNEL_ERROR_NODEV )
return hleLogError ( SCEIO , error , " device not found " ) ;
2013-03-10 10:43:50 +00:00
f = new FileNode ( ) ;
2021-06-06 16:58:46 +00:00
kernelObjects . Create ( f ) ;
f - > handle = - 1 ;
2013-02-11 01:59:15 +00:00
f - > fullpath = filename ;
2013-03-10 10:43:50 +00:00
f - > closePending = true ;
2013-06-16 17:05:30 +00:00
}
2019-07-28 23:53:20 +00:00
// We need an fd even for errors, since it's async.
int fd = __IoAllocFd ( f ) ;
2013-06-16 17:05:30 +00:00
if ( fd < 0 ) {
kernelObjects . Destroy < FileNode > ( f - > GetUID ( ) ) ;
2020-05-21 23:34:04 +00:00
return hleLogError ( SCEIO , hleDelayResult ( fd , " file opened " , 1000 ) , " out of fds " ) ;
2013-02-11 01:59:15 +00:00
}
2019-07-28 23:53:20 +00:00
auto & params = asyncParams [ fd ] ;
params . op = IoAsyncOp : : OPEN ;
2020-01-04 16:48:04 +00:00
params . priority = asyncDefaultPriority ;
2019-07-28 23:53:20 +00:00
params . open . filenameAddr = PARAM ( 0 ) ;
params . open . flags = flags ;
params . open . mode = mode ;
IoStartAsyncThread ( fd , f ) ;
2013-03-10 08:14:50 +00:00
2019-09-01 16:43:01 +00:00
if ( error ! = 0 ) {
2019-07-28 23:53:20 +00:00
f - > asyncResult = ( s64 ) error ;
2019-09-01 16:43:01 +00:00
return hleLogError ( SCEIO , fd , " file not found " ) ;
2019-07-28 23:53:20 +00:00
}
2013-03-10 08:14:50 +00:00
2019-07-28 23:53:20 +00:00
f - > asyncResult = fd ;
return hleLogSuccessI ( SCEIO , fd ) ;
2012-12-05 21:19:14 +00:00
}
2012-11-01 15:19:01 +00:00
2014-12-08 09:40:08 +00:00
static u32 sceIoGetAsyncStat ( int id , u32 poll , u32 address ) {
2012-11-01 15:19:01 +00:00
u32 error ;
2013-06-16 17:05:30 +00:00
FileNode * f = __IoGetFd ( id , error ) ;
2013-08-29 06:15:13 +00:00
if ( f ) {
if ( __IsInInterrupt ( ) ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " %lli = sceIoGetAsyncStat(%i, %i, %08x): illegal context " , f - > asyncResult , id , poll , address ) ;
2013-08-29 06:15:13 +00:00
return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT ;
}
2013-03-10 10:43:50 +00:00
if ( f - > pendingAsyncResult ) {
if ( poll ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " %lli = sceIoGetAsyncStat(%i, %i, %08x): not ready " , f - > asyncResult , id , poll , address ) ;
2013-03-10 10:43:50 +00:00
return 1 ;
} else {
2013-08-28 15:13:44 +00:00
if ( ! __KernelIsDispatchEnabled ( ) ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " %lli = sceIoGetAsyncStat(%i, %i, %08x): dispatch disabled " , f - > asyncResult , id , poll , address ) ;
2013-04-06 20:07:31 +00:00
return SCE_KERNEL_ERROR_CAN_NOT_WAIT ;
2013-08-28 15:13:44 +00:00
}
2013-04-06 20:07:31 +00:00
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " %lli = sceIoGetAsyncStat(%i, %i, %08x): waiting " , f - > asyncResult , id , poll , address ) ;
2013-09-07 17:09:48 +00:00
f - > waitingThreads . push_back ( __KernelGetCurThread ( ) ) ;
2013-09-07 18:22:48 +00:00
__KernelWaitCurThread ( WAITTYPE_ASYNCIO , f - > GetUID ( ) , address , 0 , false , " io waited " ) ;
2013-03-10 10:43:50 +00:00
}
2013-05-13 00:27:38 +00:00
} else if ( f - > hasAsyncResult ) {
2013-08-28 15:13:44 +00:00
if ( ! __KernelIsDispatchEnabled ( ) ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " %lli = sceIoGetAsyncStat(%i, %i, %08x): dispatch disabled " , f - > asyncResult , id , poll , address ) ;
2013-08-28 15:13:44 +00:00
return SCE_KERNEL_ERROR_CAN_NOT_WAIT ;
}
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " %lli = sceIoGetAsyncStat(%i, %i, %08x) " , f - > asyncResult , id , poll , address ) ;
2013-03-10 10:43:50 +00:00
Memory : : Write_U64 ( ( u64 ) f - > asyncResult , address ) ;
2013-05-13 00:27:38 +00:00
f - > hasAsyncResult = false ;
2013-03-10 10:43:50 +00:00
if ( f - > closePending ) {
2013-06-16 17:05:30 +00:00
__IoFreeFd ( id , error ) ;
2013-03-10 10:43:50 +00:00
}
2013-05-13 00:27:38 +00:00
} else {
2013-09-07 19:19:21 +00:00
WARN_LOG ( SCEIO , " SCE_KERNEL_ERROR_NOASYNC = sceIoGetAsyncStat(%i, %i, %08x) " , id , poll , address ) ;
2013-05-13 00:27:38 +00:00
return SCE_KERNEL_ERROR_NOASYNC ;
2013-03-10 10:43:50 +00:00
}
2012-12-05 21:19:14 +00:00
return 0 ; //completed
2012-11-01 15:19:01 +00:00
}
else
{
2013-09-07 19:19:21 +00:00
ERROR_LOG ( SCEIO , " ERROR - sceIoGetAsyncStat with invalid id %i " , id ) ;
2013-05-13 00:27:38 +00:00
return SCE_KERNEL_ERROR_BADF ;
2012-11-01 15:19:01 +00:00
}
}
2014-12-08 09:40:08 +00:00
static int sceIoWaitAsync ( int id , u32 address ) {
2012-11-01 15:19:01 +00:00
u32 error ;
2013-06-16 17:05:30 +00:00
FileNode * f = __IoGetFd ( id , error ) ;
2012-12-06 14:16:05 +00:00
if ( f ) {
2013-08-29 06:15:13 +00:00
if ( __IsInInterrupt ( ) ) {
2019-09-01 16:18:27 +00:00
return hleLogDebug ( SCEIO , SCE_KERNEL_ERROR_ILLEGAL_CONTEXT , " illegal context " ) ;
2013-08-29 06:15:13 +00:00
}
2013-03-10 10:43:50 +00:00
if ( f - > pendingAsyncResult ) {
2013-08-28 15:13:44 +00:00
if ( ! __KernelIsDispatchEnabled ( ) ) {
2019-09-01 16:18:27 +00:00
return hleLogDebug ( SCEIO , SCE_KERNEL_ERROR_CAN_NOT_WAIT , " dispatch disabled " ) ;
2013-08-28 15:13:44 +00:00
}
2013-09-07 17:09:48 +00:00
f - > waitingThreads . push_back ( __KernelGetCurThread ( ) ) ;
2013-09-07 18:22:48 +00:00
__KernelWaitCurThread ( WAITTYPE_ASYNCIO , f - > GetUID ( ) , address , 0 , false , " io waited " ) ;
2019-09-01 16:18:27 +00:00
return hleLogSuccessI ( SCEIO , 0 , " waiting " ) ;
2013-05-13 00:27:38 +00:00
} else if ( f - > hasAsyncResult ) {
2013-08-28 15:13:44 +00:00
if ( ! __KernelIsDispatchEnabled ( ) ) {
2019-09-01 16:18:27 +00:00
return hleLogDebug ( SCEIO , SCE_KERNEL_ERROR_CAN_NOT_WAIT , " dispatch disabled " ) ;
2013-08-28 15:13:44 +00:00
}
2013-03-10 10:43:50 +00:00
Memory : : Write_U64 ( ( u64 ) f - > asyncResult , address ) ;
2013-05-13 00:27:38 +00:00
f - > hasAsyncResult = false ;
2013-03-10 10:43:50 +00:00
if ( f - > closePending ) {
2013-06-16 17:05:30 +00:00
__IoFreeFd ( id , error ) ;
2013-03-10 10:43:50 +00:00
}
2019-09-01 16:18:27 +00:00
return hleLogSuccessI ( SCEIO , 0 , " complete " ) ;
2013-05-13 00:27:38 +00:00
} else {
2019-09-01 16:18:27 +00:00
return hleLogWarning ( SCEIO , SCE_KERNEL_ERROR_NOASYNC , " no async pending " ) ;
2012-11-01 15:19:01 +00:00
}
2012-12-26 07:02:53 +00:00
return 0 ; //completed
2012-12-06 14:16:05 +00:00
} else {
2019-09-01 16:18:27 +00:00
return hleLogError ( SCEIO , SCE_KERNEL_ERROR_BADF , " invalid fd " ) ;
2012-11-01 15:19:01 +00:00
}
}
2014-12-08 09:40:08 +00:00
static int sceIoWaitAsyncCB ( int id , u32 address ) {
2012-11-01 15:19:01 +00:00
// Should process callbacks here
u32 error ;
2013-06-16 17:05:30 +00:00
FileNode * f = __IoGetFd ( id , error ) ;
2012-12-06 14:16:05 +00:00
if ( f ) {
2013-08-29 06:15:13 +00:00
if ( __IsInInterrupt ( ) ) {
2019-09-01 16:18:27 +00:00
return hleLogDebug ( SCEIO , SCE_KERNEL_ERROR_ILLEGAL_CONTEXT , " illegal context " ) ;
2013-08-29 06:15:13 +00:00
}
2012-12-26 07:02:53 +00:00
hleCheckCurrentCallbacks ( ) ;
2013-03-10 10:43:50 +00:00
if ( f - > pendingAsyncResult ) {
2013-08-28 15:13:44 +00:00
// TODO: This seems to re-enable dispatch or something?
2013-09-07 17:09:48 +00:00
f - > waitingThreads . push_back ( __KernelGetCurThread ( ) ) ;
2019-09-01 16:11:09 +00:00
__KernelWaitCurThread ( WAITTYPE_ASYNCIO , f - > GetUID ( ) , address , 0 , true , " io waited " ) ;
2019-09-01 16:18:27 +00:00
return hleLogSuccessI ( SCEIO , 0 , " waiting " ) ;
2013-05-13 00:27:38 +00:00
} else if ( f - > hasAsyncResult ) {
2013-03-10 10:43:50 +00:00
Memory : : Write_U64 ( ( u64 ) f - > asyncResult , address ) ;
2013-05-13 00:27:38 +00:00
f - > hasAsyncResult = false ;
2013-03-10 10:43:50 +00:00
if ( f - > closePending ) {
2013-06-16 17:05:30 +00:00
__IoFreeFd ( id , error ) ;
2013-03-10 10:43:50 +00:00
}
2019-09-01 16:18:27 +00:00
return hleLogSuccessI ( SCEIO , 0 , " complete " ) ;
2013-05-13 00:27:38 +00:00
} else {
2019-09-01 16:18:27 +00:00
return hleLogWarning ( SCEIO , SCE_KERNEL_ERROR_NOASYNC , " no async pending " ) ;
2013-03-10 10:43:50 +00:00
}
2012-12-06 14:16:05 +00:00
} else {
2019-09-01 16:18:27 +00:00
return hleLogError ( SCEIO , SCE_KERNEL_ERROR_BADF , " invalid fd " ) ;
2012-11-01 15:19:01 +00:00
}
}
2014-12-08 09:40:08 +00:00
static u32 sceIoPollAsync ( int id , u32 address ) {
2012-11-01 15:19:01 +00:00
u32 error ;
2013-06-16 17:05:30 +00:00
FileNode * f = __IoGetFd ( id , error ) ;
2012-12-06 14:16:05 +00:00
if ( f ) {
2013-03-10 10:43:50 +00:00
if ( f - > pendingAsyncResult ) {
2019-09-01 16:18:27 +00:00
return hleLogSuccessVerboseI ( SCEIO , 1 , " not ready " ) ;
2013-05-13 00:27:38 +00:00
} else if ( f - > hasAsyncResult ) {
2013-03-10 10:43:50 +00:00
Memory : : Write_U64 ( ( u64 ) f - > asyncResult , address ) ;
2013-05-13 00:27:38 +00:00
f - > hasAsyncResult = false ;
2013-03-10 10:43:50 +00:00
if ( f - > closePending ) {
2013-06-16 17:05:30 +00:00
__IoFreeFd ( id , error ) ;
2013-03-10 10:43:50 +00:00
}
2019-09-01 16:18:27 +00:00
return hleLogSuccessI ( SCEIO , 0 ) ;
2013-05-13 00:27:38 +00:00
} else {
2019-09-01 16:18:27 +00:00
return hleLogDebug ( SCEIO , SCE_KERNEL_ERROR_NOASYNC , " no async pending " ) ;
2012-11-01 15:19:01 +00:00
}
2012-12-06 14:16:05 +00:00
} else {
2019-09-01 16:18:27 +00:00
return hleLogError ( SCEIO , SCE_KERNEL_ERROR_BADF , " invalid fd " ) ;
2012-11-01 15:19:01 +00:00
}
}
2012-12-06 14:16:05 +00:00
class DirListing : public KernelObject {
2012-11-01 15:19:01 +00:00
public :
2014-12-08 20:14:35 +00:00
const char * GetName ( ) override { return name . c_str ( ) ; }
2020-07-20 09:01:52 +00:00
const char * GetTypeName ( ) override { return GetStaticTypeName ( ) ; }
static const char * GetStaticTypeName ( ) { return " DirListing " ; }
2012-11-01 15:19:01 +00:00
static u32 GetMissingErrorCode ( ) { return SCE_KERNEL_ERROR_BADF ; }
2013-06-19 06:54:29 +00:00
static int GetStaticIDType ( ) { return PPSSPP_KERNEL_TMID_DirList ; }
2014-12-08 20:14:35 +00:00
int GetIDType ( ) const override { return PPSSPP_KERNEL_TMID_DirList ; }
2012-12-27 06:45:19 +00:00
2014-12-08 20:14:35 +00:00
void DoState ( PointerWrap & p ) override {
2013-09-15 03:23:03 +00:00
auto s = p . Section ( " DirListing " , 1 ) ;
if ( ! s )
return ;
2020-08-10 04:20:42 +00:00
Do ( p , name ) ;
Do ( p , index ) ;
2012-12-27 06:45:19 +00:00
// TODO: Is this the right way for it to wake up?
2013-01-19 21:48:20 +00:00
int count = ( int ) listing . size ( ) ;
2020-08-10 04:20:42 +00:00
Do ( p , count ) ;
2012-12-28 06:14:31 +00:00
listing . resize ( count ) ;
for ( int i = 0 ; i < count ; + + i ) {
2012-12-27 06:45:19 +00:00
listing [ i ] . DoState ( p ) ;
}
}
2012-11-01 15:19:01 +00:00
std : : string name ;
std : : vector < PSPFileInfo > listing ;
int index ;
} ;
2014-12-08 09:40:08 +00:00
static u32 sceIoDopen ( const char * path ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoDopen( \" %s \" ) " , path ) ;
2012-11-01 15:19:01 +00:00
2022-10-09 16:33:39 +00:00
double startTime = time_now_d ( ) ;
bool listingExists = false ;
auto listing = pspFileSystem . GetDirListing ( path , & listingExists ) ;
if ( ! listingExists ) {
2018-05-06 17:15:05 +00:00
return SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND ;
2013-01-20 23:20:48 +00:00
}
2012-11-01 15:19:01 +00:00
DirListing * dir = new DirListing ( ) ;
SceUID id = kernelObjects . Create ( dir ) ;
2022-10-09 16:33:39 +00:00
dir - > listing = listing ;
2012-11-01 15:19:01 +00:00
dir - > index = 0 ;
dir - > name = std : : string ( path ) ;
2021-09-11 16:06:26 +00:00
double listTime = time_now_d ( ) - startTime ;
if ( listTime > 0.01 ) {
2021-09-11 16:53:46 +00:00
INFO_LOG ( IO , " Dir listing '%s' took %0.3f " , path , listTime ) ;
2021-09-11 16:06:26 +00:00
}
2021-09-11 13:02:52 +00:00
// Blacklist some directories that games should not be able to find out about.
// Speeds up directory iteration on slow Android Scoped Storage implementations :(
// Might also want to filter out PSP/GAME if not a homebrew, maybe also irrelevant directories
// in PSP/SAVEDATA, though iffy to know which ones are irrelevant..
2021-09-11 16:06:26 +00:00
// Also if we're stripping PSP from the path due to setting a directory named PSP as the memstick root,
// these will also show up at ms0: which is not ideal. Should find some other way to deal with that.
if ( ! strcmp ( path , " ms0:/PSP " ) | | ! strcmp ( path , " ms0: " ) ) {
2021-09-11 13:02:52 +00:00
static const char * const pspFolderBlacklist [ ] = {
" CHEATS " ,
" PPSSPP_STATE " ,
" PLUGINS " ,
" SYSTEM " ,
" SCREENSHOT " ,
" TEXTURES " ,
" DUMP " ,
2021-09-11 13:14:44 +00:00
" SHADERS " ,
2021-09-11 13:02:52 +00:00
} ;
std : : vector < PSPFileInfo > filtered ;
for ( const auto & entry : dir - > listing ) {
bool blacklisted = false ;
for ( auto black : pspFolderBlacklist ) {
if ( ! strcasecmp ( entry . name . c_str ( ) , black ) ) {
blacklisted = true ;
break ;
}
}
2021-09-11 16:06:26 +00:00
// Also don't let games see a GAME directory in the root. This confuses Wipeout...
if ( ! strcasecmp ( entry . name . c_str ( ) , " GAME " ) & & ! strcmp ( path , " ms0: " ) ) {
blacklisted = true ;
}
2021-09-11 13:02:52 +00:00
if ( ! blacklisted ) {
filtered . push_back ( entry ) ;
}
}
dir - > listing = filtered ;
}
2013-03-11 02:59:26 +00:00
// TODO: The result is delayed only from the memstick, it seems.
2012-12-05 21:19:14 +00:00
return id ;
2012-11-01 15:19:01 +00:00
}
2014-04-13 21:22:17 +00:00
// For some reason strncpy will fill up the entire output buffer. No reason to do that,
// so we use this trivial replacement.
2014-04-13 21:43:32 +00:00
static void strcpy_limit ( char * dest , const char * src , int limit ) {
2014-04-13 21:22:17 +00:00
int i ;
2014-04-13 21:43:32 +00:00
for ( i = 0 ; i < limit - 1 ; i + + ) {
if ( ! src [ i ] )
2014-04-13 21:22:17 +00:00
break ;
dest [ i ] = src [ i ] ;
}
// Always null terminate.
dest [ i ] = 0 ;
}
2014-12-08 09:40:08 +00:00
static u32 sceIoDread ( int id , u32 dirent_addr ) {
2012-11-01 15:19:01 +00:00
u32 error ;
2012-12-06 18:03:12 +00:00
DirListing * dir = kernelObjects . Get < DirListing > ( id , error ) ;
2012-12-06 14:16:05 +00:00
if ( dir ) {
2013-02-10 04:40:06 +00:00
SceIoDirEnt * entry = ( SceIoDirEnt * ) Memory : : GetPointer ( dirent_addr ) ;
2012-12-06 14:16:05 +00:00
if ( dir - > index = = ( int ) dir - > listing . size ( ) ) {
2021-09-11 13:02:52 +00:00
DEBUG_LOG ( SCEIO , " sceIoDread( %d %08x ) - end " , id , dirent_addr ) ;
2013-02-10 04:40:06 +00:00
entry - > d_name [ 0 ] = ' \0 ' ;
2012-12-05 21:19:14 +00:00
return 0 ;
2012-11-01 15:19:01 +00:00
}
PSPFileInfo & info = dir - > listing [ dir - > index ] ;
__IoGetStat ( & entry - > d_stat , info ) ;
strncpy ( entry - > d_name , info . name . c_str ( ) , 256 ) ;
2013-02-10 04:40:06 +00:00
entry - > d_name [ 255 ] = ' \0 ' ;
2014-04-13 12:10:47 +00:00
2020-05-21 23:10:08 +00:00
bool isFAT = pspFileSystem . FlagsFromFilename ( dir - > name ) & FileSystemFlags : : SIMULATE_FAT32 ;
2014-04-13 12:10:47 +00:00
// Only write d_private for memory stick
2014-04-13 21:22:17 +00:00
if ( isFAT ) {
2014-04-13 12:10:47 +00:00
// write d_private for supporting Custom BGM
// ref JPCSP https://code.google.com/p/jpcsp/source/detail?r=3468
if ( Memory : : IsValidAddress ( entry - > d_private ) ) {
if ( sceKernelGetCompiledSdkVersion ( ) < = 0x0307FFFF ) {
// d_private is pointing to an area of unknown size
// - [0..12] "8.3" file name (null-terminated), could be empty.
// - [13..???] long file name (null-terminated)
2014-04-13 21:22:17 +00:00
// Hm, so currently we don't write the short name at all to d_private? TODO
strcpy_limit ( ( char * ) Memory : : GetPointer ( entry - > d_private + 13 ) , ( const char * ) entry - > d_name , ARRAY_SIZE ( entry - > d_name ) ) ;
2014-04-13 12:10:47 +00:00
}
else {
// d_private is pointing to an area of total size 1044
// - [0..3] size of area
// - [4..19] "8.3" file name (null-terminated), could be empty.
// - [20..???] long file name (null-terminated)
auto size = Memory : : Read_U32 ( entry - > d_private ) ;
2014-04-13 21:22:17 +00:00
// Hm, so currently we don't write the short name at all to d_private? TODO
2014-04-13 12:10:47 +00:00
if ( size > = 1044 ) {
2014-04-13 21:22:17 +00:00
strcpy_limit ( ( char * ) Memory : : GetPointer ( entry - > d_private + 20 ) , ( const char * ) entry - > d_name , ARRAY_SIZE ( entry - > d_name ) ) ;
2014-04-13 12:10:47 +00:00
}
2014-04-10 03:19:03 +00:00
}
}
}
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoDread( %d %08x ) = %s " , id , dirent_addr , entry - > d_name ) ;
2012-11-01 15:19:01 +00:00
2013-03-11 02:59:26 +00:00
// TODO: Improve timing. Only happens on the *first* entry read, ms and umd.
if ( dir - > index + + = = 0 ) {
return hleDelayResult ( 1 , " readdir " , 1000 ) ;
}
2013-02-10 04:40:06 +00:00
return 1 ;
2012-12-06 14:16:05 +00:00
} else {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoDread - invalid listing %i, error %08x " , id , error ) ;
2013-02-10 04:40:06 +00:00
return SCE_KERNEL_ERROR_BADF ;
2012-11-01 15:19:01 +00:00
}
}
2014-12-08 09:40:08 +00:00
static u32 sceIoDclose ( int id ) {
2013-09-07 19:19:21 +00:00
DEBUG_LOG ( SCEIO , " sceIoDclose(%d) " , id ) ;
2012-12-06 18:03:12 +00:00
return kernelObjects . Destroy < DirListing > ( id ) ;
2012-11-01 15:19:01 +00:00
}
2023-05-25 08:01:27 +00:00
int __IoIoctl ( u32 id , u32 cmd , u32 indataPtr , u32 inlen , u32 outdataPtr , u32 outlen , int & usec ) {
2012-12-21 09:18:52 +00:00
u32 error ;
2013-06-16 17:05:30 +00:00
FileNode * f = __IoGetFd ( id , error ) ;
2012-12-21 09:18:52 +00:00
if ( error ) {
2013-10-09 06:49:24 +00:00
ERROR_LOG ( SCEIO , " %08x=sceIoIoctl id: %08x, cmd %08x, bad file " , error , id , cmd ) ;
2012-12-21 09:18:52 +00:00
return error ;
}
2013-10-09 06:49:24 +00:00
if ( f - > asyncBusy ( ) ) {
ERROR_LOG ( SCEIO , " %08x=sceIoIoctl id: %08x, cmd %08x, async busy " , error , id , cmd ) ;
return SCE_KERNEL_ERROR_ASYNC_BUSY ;
}
2012-12-21 09:18:52 +00:00
2013-12-26 21:41:05 +00:00
// TODO: Move this into each command, probably?
2023-05-25 08:01:27 +00:00
usec + = 100 ;
2013-12-26 21:41:05 +00:00
2012-12-21 09:18:52 +00:00
//KD Hearts:
//56:46:434 HLE\sceIo.cpp:886 E[HLE]: UNIMPL 0=sceIoIoctrl id: 0000011f, cmd 04100001, indataPtr 08b313d8, inlen 00000010, outdataPtr 00000000, outLen 0
// 0000000
2013-01-05 23:39:39 +00:00
// TODO: This kind of stuff should be moved to the devices (wherever that would be)
// and does not belong in this file. Same thing with Devctl.
2012-12-21 09:18:52 +00:00
switch ( cmd ) {
2013-01-17 08:44:35 +00:00
// Define decryption key (amctrl.prx DRM)
2013-04-20 05:51:40 +00:00
case 0x04100001 : {
u8 keybuf [ 16 ] ;
u8 * key_ptr ;
u8 pgd_header [ 0x90 ] ;
u8 pgd_magic [ 4 ] = { 0x00 , 0x50 , 0x47 , 0x44 } ;
2012-12-21 09:18:52 +00:00
if ( Memory : : IsValidAddress ( indataPtr ) & & inlen = = 16 ) {
2022-07-24 11:33:03 +00:00
memcpy ( keybuf , Memory : : GetPointerUnchecked ( indataPtr ) , 16 ) ;
2013-04-20 05:51:40 +00:00
key_ptr = keybuf ;
} else {
key_ptr = NULL ;
}
2013-12-04 16:41:59 +00:00
DEBUG_LOG ( SCEIO , " Decrypting PGD DRM files " ) ;
2013-04-20 05:51:40 +00:00
pspFileSystem . SeekFile ( f - > handle , ( s32 ) f - > pgd_offset , FILEMOVE_BEGIN ) ;
pspFileSystem . ReadFile ( f - > handle , pgd_header , 0x90 ) ;
f - > pgdInfo = pgd_open ( pgd_header , 2 , key_ptr ) ;
2023-05-25 08:01:27 +00:00
if ( ! f - > pgdInfo ) {
ERROR_LOG ( SCEIO , " Not a valid PGD file. Examining. " ) ;
2013-04-20 05:51:40 +00:00
f - > npdrm = false ;
pspFileSystem . SeekFile ( f - > handle , ( s32 ) 0 , FILEMOVE_BEGIN ) ;
2023-05-25 08:01:27 +00:00
if ( memcmp ( pgd_header , pgd_magic , 4 ) = = 0 ) {
ERROR_LOG ( SCEIO , " File is PGD file, but there's likely a key mismatch. Returning error. " ) ;
2013-04-20 05:51:40 +00:00
// File is PGD file, but key mismatch
return ERROR_PGD_INVALID_HEADER ;
2023-05-25 08:01:27 +00:00
} else {
WARN_LOG ( SCEIO , " File is not encrypted, proceeding. " ) ;
2013-04-20 05:51:40 +00:00
// File is decrypted.
2013-03-16 08:15:11 +00:00
return 0 ;
2013-02-24 08:28:40 +00:00
}
2023-05-25 08:01:27 +00:00
} else {
// Everything OK.
2013-04-20 05:51:40 +00:00
f - > npdrm = true ;
f - > pgdInfo - > data_offset + = f - > pgd_offset ;
return 0 ;
2012-12-21 09:18:52 +00:00
}
break ;
2013-04-20 05:51:40 +00:00
}
2023-05-25 08:01:27 +00:00
2013-04-20 05:51:40 +00:00
// Set PGD offset. Called from sceNpDrmEdataSetupKey
case 0x04100002 :
f - > pgd_offset = indataPtr ;
break ;
// Get PGD data size. Called from sceNpDrmEdataGetDataSize
case 0x04100010 :
if ( f - > pgdInfo )
return f - > pgdInfo - > data_size ;
else
2022-10-09 15:16:33 +00:00
return ( int ) f - > FileInfo ( ) . size ;
2013-04-20 05:51:40 +00:00
break ;
2012-12-21 09:18:52 +00:00
2013-01-17 08:44:35 +00:00
// Get UMD sector size
case 0x01020003 :
2013-12-16 09:27:22 +00:00
// TODO: Should not work for umd0:/, ms0:/, etc.
// TODO: Should probably move this to something common between ISOFileSystem and VirtualDiscSystem.
INFO_LOG ( SCEIO , " sceIoIoctl: Asked for sector size of file %i " , id ) ;
if ( Memory : : IsValidAddress ( outdataPtr ) & & outlen > = 4 ) {
// ISOs always use 2048 sized sectors.
Memory : : Write_U32 ( 2048 , outdataPtr ) ;
} else {
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
2013-01-17 08:44:35 +00:00
}
break ;
2013-07-08 07:11:58 +00:00
// Get UMD file offset
2013-01-17 08:44:35 +00:00
case 0x01020004 :
2013-12-16 09:27:22 +00:00
// TODO: Should not work for umd0:/, ms0:/, etc.
// TODO: Should probably move this to something common between ISOFileSystem and VirtualDiscSystem.
2017-11-25 11:13:27 +00:00
DEBUG_LOG ( SCEIO , " sceIoIoctl: Asked for file offset of file %d " , id ) ;
2013-12-16 09:27:22 +00:00
if ( Memory : : IsValidAddress ( outdataPtr ) & & outlen > = 4 ) {
2013-07-25 06:58:45 +00:00
u32 offset = ( u32 ) pspFileSystem . GetSeekPos ( f - > handle ) ;
2013-12-16 09:27:22 +00:00
Memory : : Write_U32 ( offset , outdataPtr ) ;
} else {
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
2013-01-17 08:44:35 +00:00
}
break ;
2013-12-17 07:51:04 +00:00
case 0x01010005 :
// TODO: Should not work for umd0:/, ms0:/, etc.
// TODO: Should probably move this to something common between ISOFileSystem and VirtualDiscSystem.
INFO_LOG ( SCEIO , " sceIoIoctl: Seek for file %i " , id ) ;
// Even if the size is 4, it still actually reads a 16 byte struct, it seems.
if ( Memory : : IsValidAddress ( indataPtr ) & & inlen > = 4 ) {
struct SeekInfo {
2021-02-19 06:25:24 +00:00
u64_le offset ;
u32_le unk ;
u32_le whence ;
2013-12-17 07:51:04 +00:00
} ;
const auto seekInfo = PSPPointer < SeekInfo > : : Create ( indataPtr ) ;
FileMove seek ;
s64 newPos = __IoLseekDest ( f , seekInfo - > offset , seekInfo - > whence , seek ) ;
2022-10-09 15:16:33 +00:00
if ( newPos < 0 | | newPos > f - > FileInfo ( ) . size ) {
2013-12-17 07:51:04 +00:00
// Not allowed to seek past the end of the file with this API.
return ERROR_ERRNO_IO_ERROR ;
}
pspFileSystem . SeekFile ( f - > handle , ( s32 ) seekInfo - > offset , seek ) ;
} else {
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
}
break ;
2013-12-16 09:27:22 +00:00
2013-04-13 07:36:32 +00:00
// Get UMD file start sector.
2013-01-05 23:39:39 +00:00
case 0x01020006 :
2013-12-16 09:27:22 +00:00
// TODO: Should not work for umd0:/, ms0:/, etc.
// TODO: Should probably move this to something common between ISOFileSystem and VirtualDiscSystem.
INFO_LOG ( SCEIO , " sceIoIoctl: Asked for start sector of file %i " , id ) ;
2013-01-05 23:39:39 +00:00
if ( Memory : : IsValidAddress ( outdataPtr ) & & outlen > = 4 ) {
2022-10-09 15:16:33 +00:00
Memory : : Write_U32 ( f - > FileInfo ( ) . startSector , outdataPtr ) ;
2013-12-16 09:27:22 +00:00
} else {
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
2013-01-05 23:39:39 +00:00
}
break ;
2013-01-17 08:44:35 +00:00
// Get UMD file size in bytes.
2013-01-05 23:39:39 +00:00
case 0x01020007 :
2013-12-16 09:27:22 +00:00
// TODO: Should not work for umd0:/, ms0:/, etc.
// TODO: Should probably move this to something common between ISOFileSystem and VirtualDiscSystem.
INFO_LOG ( SCEIO , " sceIoIoctl: Asked for size of file %i " , id ) ;
2013-01-05 23:39:39 +00:00
if ( Memory : : IsValidAddress ( outdataPtr ) & & outlen > = 8 ) {
2022-10-09 15:16:33 +00:00
Memory : : Write_U64 ( f - > FileInfo ( ) . size , outdataPtr ) ;
2013-12-16 09:27:22 +00:00
} else {
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
2013-01-05 23:39:39 +00:00
}
break ;
2013-12-17 08:17:34 +00:00
// Read from UMD file.
case 0x01030008 :
// TODO: Should not work for umd0:/, ms0:/, etc.
// TODO: Should probably move this to something common between ISOFileSystem and VirtualDiscSystem.
INFO_LOG ( SCEIO , " sceIoIoctl: Read from file %i " , id ) ;
if ( Memory : : IsValidAddress ( indataPtr ) & & inlen > = 4 ) {
u32 size = Memory : : Read_U32 ( indataPtr ) ;
if ( Memory : : IsValidAddress ( outdataPtr ) & & size < = outlen ) {
2013-12-26 21:41:05 +00:00
// sceIoRead does its own delaying (and deferring.)
usec = 0 ;
2013-12-17 08:17:34 +00:00
return sceIoRead ( id , outdataPtr , size ) ;
} else {
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
}
} else {
2013-12-26 21:53:34 +00:00
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
}
break ;
// Get current sector seek pos from UMD device file.
case 0x01d20001 :
// TODO: Should work only for umd0:/, etc. not for ms0:/ or disc0:/.
// TODO: Should probably move this to something common between ISOFileSystem and VirtualDiscSystem.
INFO_LOG ( SCEIO , " sceIoIoctl: Sector tell from file %i " , id ) ;
if ( Memory : : IsValidAddress ( outdataPtr ) & & outlen > = 4 ) {
Memory : : Write_U32 ( ( u32 ) pspFileSystem . GetSeekPos ( f - > handle ) , outdataPtr ) ;
} else {
2013-12-17 08:17:34 +00:00
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
}
break ;
2013-12-20 16:52:50 +00:00
// Read raw sectors from UMD device file.
2013-08-23 06:23:48 +00:00
case 0x01f30003 :
2013-12-20 16:52:50 +00:00
// TODO: Should work only for umd0:/, etc. not for ms0:/ or disc0:/.
// TODO: Should probably move this to something common between ISOFileSystem and VirtualDiscSystem.
INFO_LOG ( SCEIO , " sceIoIoctl: Sector read from file %i " , id ) ;
if ( Memory : : IsValidAddress ( indataPtr ) & & inlen > = 4 ) {
u32 size = Memory : : Read_U32 ( indataPtr ) ;
// Note that size is specified in sectors, not bytes.
if ( size > 0 & & Memory : : IsValidAddress ( outdataPtr ) & & size < = outlen ) {
2013-12-26 21:41:05 +00:00
// sceIoRead does its own delaying (and deferring.)
usec = 0 ;
2013-12-20 16:52:50 +00:00
return sceIoRead ( id , outdataPtr , size ) ;
} else {
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
}
} else {
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
2013-05-20 20:49:26 +00:00
}
2013-12-20 16:52:50 +00:00
break ;
2013-05-20 20:49:26 +00:00
2013-12-26 20:45:30 +00:00
// Seek by sector in UMD device file.
case 0x01f100a6 :
// TODO: Should work only for umd0:/, etc. not for ms0:/ or disc0:/.
// TODO: Should probably move this to something common between ISOFileSystem and VirtualDiscSystem.
INFO_LOG ( SCEIO , " sceIoIoctl: Sector seek for file %i " , id ) ;
// Even if the size is 4, it still actually reads a 16 byte struct, it seems.
2019-12-13 00:19:17 +00:00
2019-12-13 09:59:55 +00:00
//if (GetIOTimingMethod() == IOTIMING_REALISTIC) // Need a check for io timing method?
2023-05-25 08:01:27 +00:00
usec + = 15000 ; // Fantasy Golf Pangya Portable(KS) needs a delay over 15000us.
2019-12-13 00:19:17 +00:00
2013-12-26 20:45:30 +00:00
if ( Memory : : IsValidAddress ( indataPtr ) & & inlen > = 4 ) {
struct SeekInfo {
2021-02-19 06:25:24 +00:00
u64_le offset ;
u32_le unk ;
u32_le whence ;
2013-12-26 20:45:30 +00:00
} ;
const auto seekInfo = PSPPointer < SeekInfo > : : Create ( indataPtr ) ;
FileMove seek ;
s64 newPos = __IoLseekDest ( f , seekInfo - > offset , seekInfo - > whence , seek ) ;
// Position is in sectors, don't forget.
2022-10-09 15:16:33 +00:00
if ( newPos < 0 | | newPos > f - > FileInfo ( ) . size ) {
2013-12-26 20:45:30 +00:00
// Not allowed to seek past the end of the file with this API.
return SCE_KERNEL_ERROR_ERRNO_INVALID_FILE_SIZE ;
}
pspFileSystem . SeekFile ( f - > handle , ( s32 ) seekInfo - > offset , seek ) ;
} else {
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT ;
}
break ;
2013-01-05 23:39:39 +00:00
default :
2013-03-27 05:20:45 +00:00
{
2013-12-26 22:47:17 +00:00
int result = pspFileSystem . Ioctl ( f - > handle , cmd , indataPtr , inlen , outdataPtr , outlen , usec ) ;
2013-12-31 05:37:19 +00:00
if ( result = = ( int ) SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED ) {
2013-12-26 22:47:17 +00:00
char temp [ 256 ] ;
// We want the reported message to include the cmd, so it's unique.
2023-04-28 19:04:05 +00:00
snprintf ( temp , sizeof ( temp ) , " sceIoIoctl(%%s, %08x, %%08x, %%x, %%08x, %%x) " , cmd ) ;
2013-12-26 22:47:17 +00:00
Reporting : : ReportMessage ( temp , f - > fullpath . c_str ( ) , indataPtr , inlen , outdataPtr , outlen ) ;
ERROR_LOG ( SCEIO , " UNIMPL 0=sceIoIoctl id: %08x, cmd %08x, indataPtr %08x, inlen %08x, outdataPtr %08x, outLen %08x " , id , cmd , indataPtr , inlen , outdataPtr , outlen ) ;
}
return result ;
2013-03-27 05:20:45 +00:00
}
2013-01-05 23:39:39 +00:00
break ;
2012-12-21 09:18:52 +00:00
}
2013-03-10 22:03:24 +00:00
return 0 ;
}
2014-12-08 09:40:08 +00:00
u32 sceIoIoctl ( u32 id , u32 cmd , u32 indataPtr , u32 inlen , u32 outdataPtr , u32 outlen )
2013-03-10 22:03:24 +00:00
{
2013-12-26 21:41:05 +00:00
int usec = 0 ;
int result = __IoIoctl ( id , cmd , indataPtr , inlen , outdataPtr , outlen , usec ) ;
if ( usec ! = 0 ) {
return hleDelayResult ( result , " io ctrl command " , usec ) ;
2013-10-09 06:49:24 +00:00
}
return result ;
2012-12-08 16:30:21 +00:00
}
2014-12-08 09:40:08 +00:00
static u32 sceIoIoctlAsync ( u32 id , u32 cmd , u32 indataPtr , u32 inlen , u32 outdataPtr , u32 outlen )
2013-01-17 08:44:35 +00:00
{
2013-03-10 22:03:24 +00:00
u32 error ;
2013-06-16 17:05:30 +00:00
FileNode * f = __IoGetFd ( id , error ) ;
2013-03-10 22:03:24 +00:00
if ( f ) {
2013-10-09 06:49:24 +00:00
if ( f - > asyncBusy ( ) ) {
2019-07-28 22:55:44 +00:00
return hleLogWarning ( SCEIO , SCE_KERNEL_ERROR_ASYNC_BUSY , " async busy " ) ;
2013-10-09 06:49:24 +00:00
}
2019-07-28 23:46:56 +00:00
auto & params = asyncParams [ id ] ;
params . op = IoAsyncOp : : IOCTL ;
params . ioctl . cmd = cmd ;
params . ioctl . inAddr = indataPtr ;
params . ioctl . inSize = inlen ;
params . ioctl . outAddr = outdataPtr ;
params . ioctl . outSize = outlen ;
IoStartAsyncThread ( id , f ) ;
return hleLogSuccessI ( SCEIO , 0 ) ;
2013-03-10 22:03:24 +00:00
} else {
2019-07-28 22:55:44 +00:00
return hleLogError ( SCEIO , error , " bad file descriptor " ) ;
2013-03-10 22:03:24 +00:00
}
2013-01-17 08:44:35 +00:00
}
2014-12-08 09:40:08 +00:00
static u32 sceIoGetFdList ( u32 outAddr , int outSize , u32 fdNumAddr ) {
2013-09-07 19:19:21 +00:00
WARN_LOG ( SCEIO , " sceIoGetFdList(%08x, %i, %08x) " , outAddr , outSize , fdNumAddr ) ;
2013-06-16 17:05:30 +00:00
2013-08-19 11:55:28 +00:00
auto out = PSPPointer < SceUID_le > : : Create ( outAddr ) ;
2013-06-16 17:05:30 +00:00
int count = 0 ;
2013-06-16 16:11:19 +00:00
2013-06-16 17:05:30 +00:00
// Always have the first three.
for ( int i = 0 ; i < PSP_MIN_FD ; + + i ) {
// TODO: Technically it seems like these are fixed ids > PSP_COUNT_FDS.
2013-06-25 13:51:39 +00:00
if ( count < outSize & & out . IsValid ( ) ) {
2013-06-16 17:05:30 +00:00
out [ count ] = i ;
}
+ + count ;
2013-06-16 16:11:19 +00:00
}
2013-06-16 17:05:30 +00:00
for ( int i = PSP_MIN_FD ; i < PSP_COUNT_FDS ; + + i ) {
if ( fds [ i ] = = 0 ) {
continue ;
}
2013-06-25 13:51:39 +00:00
if ( count < outSize & & out . IsValid ( ) ) {
2013-06-16 17:05:30 +00:00
out [ count ] = i ;
}
+ + count ;
}
2013-06-16 16:11:19 +00:00
if ( Memory : : IsValidAddress ( fdNumAddr ) )
2013-06-16 17:05:30 +00:00
Memory : : Write_U32 ( count , fdNumAddr ) ;
if ( count > = outSize ) {
return outSize ;
} else {
return count ;
}
2013-03-24 21:06:18 +00:00
}
// Presumably lets you hook up stderr to a MsgPipe.
2014-12-08 09:40:08 +00:00
static u32 sceKernelRegisterStderrPipe ( u32 msgPipeUID ) {
2013-09-07 19:19:21 +00:00
ERROR_LOG_REPORT ( SCEIO , " UNIMPL sceKernelRegisterStderrPipe(%08x) " , msgPipeUID ) ;
2013-03-24 21:06:18 +00:00
return 0 ;
}
2014-12-08 09:40:08 +00:00
static u32 sceKernelRegisterStdoutPipe ( u32 msgPipeUID ) {
2013-09-07 19:19:21 +00:00
ERROR_LOG_REPORT ( SCEIO , " UNIMPL sceKernelRegisterStdoutPipe(%08x) " , msgPipeUID ) ;
2013-03-24 21:06:18 +00:00
return 0 ;
}
2019-07-28 22:43:19 +00:00
static int IoAsyncFinish ( int id ) {
2019-07-28 22:55:44 +00:00
u32 error ;
FileNode * f = __IoGetFd ( id , error ) ;
if ( f ) {
2019-07-28 23:46:56 +00:00
// Reset this so the Io funcs don't reject the request.
f - > pendingAsyncResult = false ;
2020-01-03 21:09:58 +00:00
// Reset the PC back so we will run again on resume.
currentMIPS - > pc = asyncThreads [ id ] - > Entry ( ) ;
2019-07-28 23:46:56 +00:00
auto & params = asyncParams [ id ] ;
int result ;
int us ;
bool complete ;
switch ( params . op ) {
case IoAsyncOp : : READ :
complete = __IoRead ( result , id , params . std . addr , params . std . size , us ) ;
if ( complete ) {
f - > asyncResult = ( s64 ) result ;
DEBUG_LOG ( SCEIO , " ASYNC %llx=sceIoReadAsync(%d, %08x, %x) " , f - > asyncResult , id , params . std . addr , params . std . size ) ;
} else {
DEBUG_LOG ( SCEIO , " ASYNC sceIoReadAsync(%d, %08x, %x): deferring result " , id , params . std . addr , params . std . size ) ;
}
break ;
case IoAsyncOp : : WRITE :
complete = __IoWrite ( result , id , params . std . addr , params . std . size , us ) ;
if ( complete ) {
f - > asyncResult = ( s64 ) result ;
DEBUG_LOG ( SCEIO , " ASYNC %llx=sceIoWriteAsync(%d, %08x, %x) " , f - > asyncResult , id , params . std . addr , params . std . size ) ;
} else {
DEBUG_LOG ( SCEIO , " ASYNC sceIoWriteAsync(%d, %08x, %x): deferring result " , id , params . std . addr , params . std . size ) ;
}
break ;
case IoAsyncOp : : SEEK :
f - > asyncResult = __IoLseek ( id , params . seek . pos , params . seek . whence ) ;
// Educated guess at timing.
us = 100 ;
DEBUG_LOG ( SCEIO , " ASYNC %lli = sceIoLseekAsync(%d, %llx, %i) " , f - > asyncResult , id , params . seek . pos , params . seek . whence ) ;
break ;
case IoAsyncOp : : OPEN :
2020-05-21 23:58:19 +00:00
{
// See notes on timing in sceIoOpen.
2020-05-22 00:57:41 +00:00
const std : : string filename = Memory : : GetCharPointer ( params . open . filenameAddr ) ;
IFileSystem * sys = pspFileSystem . GetSystemFromFilename ( filename ) ;
if ( sys ) {
if ( f - > asyncResult = = ( int ) SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND ) {
us = sys - > Flags ( ) & FileSystemFlags : : UMD ? 6000 : 10000 ;
} else if ( sys - > DevType ( f - > handle ) & ( PSPDevType : : BLOCK | PSPDevType : : EMU_LBN ) ) {
// These are fast to open, no delay or even rescheduling happens.
us = 80 ;
} else {
us = sys - > Flags ( ) & FileSystemFlags : : UMD ? 4000 : 10000 ;
}
} else {
us = 80 ;
}
2019-07-28 23:46:56 +00:00
break ;
2020-05-21 23:58:19 +00:00
}
2019-07-28 23:46:56 +00:00
case IoAsyncOp : : CLOSE :
f - > asyncResult = 0 ;
2020-01-12 17:41:46 +00:00
// CLOSE shouldn't have a delay. See #12549.
us = 0 ;
2019-07-28 23:46:56 +00:00
DEBUG_LOG ( SCEIO , " ASYNC %lli = sceIoCloseAsync(%d) " , f - > asyncResult , id ) ;
break ;
case IoAsyncOp : : IOCTL :
2023-05-25 08:01:27 +00:00
us = 0 ; // __IoIoctl will add 100.
2019-07-28 23:46:56 +00:00
f - > asyncResult = __IoIoctl ( id , params . ioctl . cmd , params . ioctl . inAddr , params . ioctl . inSize , params . ioctl . outAddr , params . ioctl . outSize , us ) ;
DEBUG_LOG ( SCEIO , " ASYNC sceIoIoctlAsync(%08x, %08x, %08x, %08x, %08x, %08x) " , id , params . ioctl . cmd , params . ioctl . inAddr , params . ioctl . inSize , params . ioctl . outAddr , params . ioctl . outSize ) ;
break ;
default :
2020-03-11 04:00:30 +00:00
ERROR_LOG_REPORT ( SCEIO , " Unknown async op %d " , ( int ) params . op ) ;
2020-03-08 21:58:42 +00:00
us = 0 ;
2019-07-28 23:46:56 +00:00
break ;
}
__IoSchedAsync ( f , id , us ) ;
2019-09-01 16:27:45 +00:00
__KernelWaitCurThread ( WAITTYPE_ASYNCIO , id , 0 , 0 , false , " async io " ) ;
2020-01-03 21:09:58 +00:00
hleSkipDeadbeef ( ) ;
2019-07-28 23:46:56 +00:00
params . op = IoAsyncOp : : NONE ;
2019-07-28 22:55:44 +00:00
return 0 ;
} else {
return hleLogError ( SCEIO , error , " bad file descriptor " ) ;
}
2019-07-28 22:43:19 +00:00
}
2012-12-27 06:45:19 +00:00
KernelObject * __KernelFileNodeObject ( ) {
return new FileNode ;
}
KernelObject * __KernelDirListingObject ( ) {
return new DirListing ;
}
2012-12-06 14:16:05 +00:00
const HLEFunction IoFileMgrForUser [ ] = {
2016-08-04 16:42:45 +00:00
{ 0xB29DDF9C , & WrapU_C < sceIoDopen > , " sceIoDopen " , ' i ' , " s " } ,
{ 0xE3EB004C , & WrapU_IU < sceIoDread > , " sceIoDread " , ' i ' , " ix " } ,
{ 0xEB092469 , & WrapU_I < sceIoDclose > , " sceIoDclose " , ' i ' , " i " } ,
{ 0xE95A012B , & WrapU_UUUUUU < sceIoIoctlAsync > , " sceIoIoctlAsync " , ' i ' , " ixpipi " } ,
{ 0x63632449 , & WrapU_UUUUUU < sceIoIoctl > , " sceIoIoctl " , ' i ' , " ixpipi " } ,
{ 0xACE946E8 , & WrapU_CU < sceIoGetstat > , " sceIoGetstat " , ' i ' , " sx " } ,
{ 0xB8A740F4 , & WrapU_CUU < sceIoChstat > , " sceIoChstat " , ' i ' , " sxx " } ,
{ 0x55F4717D , & WrapU_C < sceIoChdir > , " sceIoChdir " , ' i ' , " s " } ,
2015-03-22 23:57:56 +00:00
{ 0X08BD7374 , & WrapU_I < sceIoGetDevType > , " sceIoGetDevType " , ' x ' , " i " } ,
2016-08-04 16:42:45 +00:00
{ 0xB2A628C1 , & WrapU_UUUIUI < sceIoAssign > , " sceIoAssign " , ' i ' , " sssixi " } ,
2016-01-25 06:52:19 +00:00
{ 0XE8BC6571 , & WrapU_I < sceIoCancel > , " sceIoCancel " , ' i ' , " i " } ,
{ 0XB293727F , & WrapI_II < sceIoChangeAsyncPriority > , " sceIoChangeAsyncPriority " , ' i ' , " ix " } ,
{ 0X810C4BC3 , & WrapU_I < sceIoClose > , " sceIoClose " , ' i ' , " i " } ,
2015-03-22 23:57:56 +00:00
{ 0XFF5940B6 , & WrapI_I < sceIoCloseAsync > , " sceIoCloseAsync " , ' i ' , " i " } ,
2016-08-04 16:42:45 +00:00
{ 0x54F5FB11 , & WrapU_CIUIUI < sceIoDevctl > , " sceIoDevctl " , ' i ' , " sxpipi " } ,
2016-01-25 06:52:19 +00:00
{ 0XCB05F8D6 , & WrapU_IUU < sceIoGetAsyncStat > , " sceIoGetAsyncStat " , ' i ' , " iiP " } ,
2016-08-04 16:42:45 +00:00
{ 0x27EB27B8 , & WrapI64_II64I < sceIoLseek > , " sceIoLseek " , ' I ' , " iIi " } ,
{ 0x68963324 , & WrapU_III < sceIoLseek32 > , " sceIoLseek32 " , ' i ' , " iii " } ,
2016-01-25 06:52:19 +00:00
{ 0X1B385D8F , & WrapU_III < sceIoLseek32Async > , " sceIoLseek32Async " , ' i ' , " iii " } ,
{ 0X71B19E77 , & WrapU_II64I < sceIoLseekAsync > , " sceIoLseekAsync " , ' i ' , " iIi " } ,
2016-08-04 16:42:45 +00:00
{ 0x109F50BC , & WrapU_CII < sceIoOpen > , " sceIoOpen " , ' i ' , " sii " } ,
{ 0x89AA9906 , & WrapU_CII < sceIoOpenAsync > , " sceIoOpenAsync " , ' i ' , " sii " } ,
{ 0x06A70004 , & WrapU_CI < sceIoMkdir > , " sceIoMkdir " , ' i ' , " si " } ,
{ 0x3251EA56 , & WrapU_IU < sceIoPollAsync > , " sceIoPollAsync " , ' i ' , " iP " } ,
{ 0x6A638D83 , & WrapU_IUI < sceIoRead > , " sceIoRead " , ' i ' , " ixi " } ,
{ 0xA0B5A7C2 , & WrapU_IUI < sceIoReadAsync > , " sceIoReadAsync " , ' i ' , " ixi " } ,
{ 0xF27A9C51 , & WrapU_C < sceIoRemove > , " sceIoRemove " , ' i ' , " s " } ,
{ 0x779103A0 , & WrapU_CC < sceIoRename > , " sceIoRename " , ' i ' , " ss " } ,
{ 0x1117C65F , & WrapU_C < sceIoRmdir > , " sceIoRmdir " , ' i ' , " s " } ,
2016-01-25 06:52:19 +00:00
{ 0XA12A0514 , & WrapU_IUU < sceIoSetAsyncCallback > , " sceIoSetAsyncCallback " , ' i ' , " ixx " } ,
2016-08-04 16:42:45 +00:00
{ 0xAB96437F , & WrapU_CI < sceIoSync > , " sceIoSync " , ' i ' , " si " } ,
{ 0x6D08A871 , & WrapU_C < sceIoUnassign > , " sceIoUnassign " , ' i ' , " s " } ,
{ 0x42EC03AC , & WrapU_IUI < sceIoWrite > , " sceIoWrite " , ' i ' , " ixi " } ,
{ 0x0FACAB19 , & WrapU_IUI < sceIoWriteAsync > , " sceIoWriteAsync " , ' i ' , " ixi " } ,
{ 0x35DBD746 , & WrapI_IU < sceIoWaitAsyncCB > , " sceIoWaitAsyncCB " , ' i ' , " iP " } ,
{ 0xE23EEC33 , & WrapI_IU < sceIoWaitAsync > , " sceIoWaitAsync " , ' i ' , " iP " } ,
2016-01-25 06:52:19 +00:00
{ 0X5C2BE2CC , & WrapU_UIU < sceIoGetFdList > , " sceIoGetFdList " , ' i ' , " xip " } ,
2019-07-28 22:43:19 +00:00
{ 0x13370001 , & WrapI_I < IoAsyncFinish > , " __IoAsyncFinish " , ' i ' , " i " } ,
2012-11-01 15:19:01 +00:00
} ;
2012-12-06 14:16:05 +00:00
void Register_IoFileMgrForUser ( ) {
2012-11-01 15:19:01 +00:00
RegisterModule ( " IoFileMgrForUser " , ARRAY_SIZE ( IoFileMgrForUser ) , IoFileMgrForUser ) ;
}
2016-08-04 16:33:18 +00:00
const HLEFunction IoFileMgrForKernel [ ] = {
2016-08-04 16:42:45 +00:00
{ 0XA905B705 , nullptr , " sceIoCloseAll " , ' ? ' , " " } ,
{ 0X411106BA , nullptr , " sceIoGetThreadCwd " , ' ? ' , " " } ,
{ 0XCB0A151F , nullptr , " sceIoChangeThreadCwd " , ' ? ' , " " } ,
{ 0X8E982A74 , nullptr , " sceIoAddDrv " , ' ? ' , " " } ,
{ 0XC7F35804 , nullptr , " sceIoDelDrv " , ' ? ' , " " } ,
{ 0X3C54E908 , nullptr , " sceIoReopen " , ' ? ' , " " } ,
{ 0xB29DDF9C , & WrapU_C < sceIoDopen > , " sceIoDopen " , ' i ' , " s " , HLE_KERNEL_SYSCALL } ,
{ 0xE3EB004C , & WrapU_IU < sceIoDread > , " sceIoDread " , ' i ' , " ix " , HLE_KERNEL_SYSCALL } ,
{ 0xEB092469 , & WrapU_I < sceIoDclose > , " sceIoDclose " , ' i ' , " i " , HLE_KERNEL_SYSCALL } ,
{ 0X109F50BC , & WrapU_CII < sceIoOpen > , " sceIoOpen " , ' i ' , " sii " , HLE_KERNEL_SYSCALL } ,
{ 0x6A638D83 , & WrapU_IUI < sceIoRead > , " sceIoRead " , ' i ' , " ixi " , HLE_KERNEL_SYSCALL } ,
{ 0x42EC03AC , & WrapU_IUI < sceIoWrite > , " sceIoWrite " , ' i ' , " ixi " , HLE_KERNEL_SYSCALL } ,
{ 0x68963324 , & WrapU_III < sceIoLseek32 > , " sceIoLseek32 " , ' i ' , " iii " , HLE_KERNEL_SYSCALL } ,
{ 0x27EB27B8 , & WrapI64_II64I < sceIoLseek > , " sceIoLseek " , ' I ' , " iIi " , HLE_KERNEL_SYSCALL } ,
{ 0x810C4BC3 , & WrapU_I < sceIoClose > , " sceIoClose " , ' i ' , " i " , HLE_KERNEL_SYSCALL } ,
{ 0x779103A0 , & WrapU_CC < sceIoRename > , " sceIoRename " , ' i ' , " ss " , HLE_KERNEL_SYSCALL } ,
{ 0xF27A9C51 , & WrapU_C < sceIoRemove > , " sceIoRemove " , ' i ' , " s " , HLE_KERNEL_SYSCALL } ,
{ 0x55F4717D , & WrapU_C < sceIoChdir > , " sceIoChdir " , ' i ' , " s " , HLE_KERNEL_SYSCALL } ,
{ 0x06A70004 , & WrapU_CI < sceIoMkdir > , " sceIoMkdir " , ' i ' , " si " , HLE_KERNEL_SYSCALL } ,
{ 0x1117C65F , & WrapU_C < sceIoRmdir > , " sceIoRmdir " , ' i ' , " s " , HLE_KERNEL_SYSCALL } ,
{ 0x54F5FB11 , & WrapU_CIUIUI < sceIoDevctl > , " sceIoDevctl " , ' i ' , " sxpipi " , HLE_KERNEL_SYSCALL } ,
{ 0x63632449 , & WrapU_UUUUUU < sceIoIoctl > , " sceIoIoctl " , ' i ' , " ixpipi " , HLE_KERNEL_SYSCALL } ,
{ 0xAB96437F , & WrapU_CI < sceIoSync > , " sceIoSync " , ' i ' , " si " , HLE_KERNEL_SYSCALL } ,
{ 0xB2A628C1 , & WrapU_UUUIUI < sceIoAssign > , " sceIoAssign " , ' i ' , " sssixi " , HLE_KERNEL_SYSCALL } ,
{ 0x6D08A871 , & WrapU_C < sceIoUnassign > , " sceIoUnassign " , ' i ' , " s " , HLE_KERNEL_SYSCALL } ,
{ 0xACE946E8 , & WrapU_CU < sceIoGetstat > , " sceIoGetstat " , ' i ' , " sx " , HLE_KERNEL_SYSCALL } ,
{ 0xB8A740F4 , & WrapU_CUU < sceIoChstat > , " sceIoChstat " , ' i ' , " sxx " , HLE_KERNEL_SYSCALL } ,
{ 0xA0B5A7C2 , & WrapU_IUI < sceIoReadAsync > , " sceIoReadAsync " , ' i ' , " ixi " , HLE_KERNEL_SYSCALL } ,
{ 0x3251EA56 , & WrapU_IU < sceIoPollAsync > , " sceIoPollAsync " , ' i ' , " iP " , HLE_KERNEL_SYSCALL } ,
{ 0xE23EEC33 , & WrapI_IU < sceIoWaitAsync > , " sceIoWaitAsync " , ' i ' , " iP " , HLE_KERNEL_SYSCALL } ,
{ 0x35DBD746 , & WrapI_IU < sceIoWaitAsyncCB > , " sceIoWaitAsyncCB " , ' i ' , " iP " , HLE_KERNEL_SYSCALL } ,
2023-06-07 06:50:49 +00:00
{ 0xBD17474F , nullptr , " sceIoGetIobUserLevel " , ' ? ' , " " } ,
2016-08-04 16:42:45 +00:00
{ 0x76DA16E3 , nullptr , " IoFileMgrForKernel_76DA16E3 " , ' ? ' , " " } ,
2016-08-04 16:33:18 +00:00
} ;
void Register_IoFileMgrForKernel ( ) {
RegisterModule ( " IoFileMgrForKernel " , ARRAY_SIZE ( IoFileMgrForKernel ) , IoFileMgrForKernel ) ;
}
2012-11-01 15:19:01 +00:00
2012-12-06 14:16:05 +00:00
const HLEFunction StdioForUser [ ] = {
2016-01-25 06:52:19 +00:00
{ 0X172D316E , & WrapU_V < sceKernelStdin > , " sceKernelStdin " , ' i ' , " " } ,
{ 0XA6BAB2E9 , & WrapU_V < sceKernelStdout > , " sceKernelStdout " , ' i ' , " " } ,
{ 0XF78BA90A , & WrapU_V < sceKernelStderr > , " sceKernelStderr " , ' i ' , " " } ,
{ 0X432D8F5C , & WrapU_U < sceKernelRegisterStdoutPipe > , " sceKernelRegisterStdoutPipe " , ' i ' , " x " } ,
{ 0X6F797E03 , & WrapU_U < sceKernelRegisterStderrPipe > , " sceKernelRegisterStderrPipe " , ' i ' , " x " } ,
2015-03-22 23:57:56 +00:00
{ 0XA46785C9 , nullptr , " sceKernelStdioSendChar " , ' ? ' , " " } ,
{ 0X0CBB0571 , nullptr , " sceKernelStdioLseek " , ' ? ' , " " } ,
{ 0X3054D478 , nullptr , " sceKernelStdioRead " , ' ? ' , " " } ,
{ 0XA3B931DB , nullptr , " sceKernelStdioWrite " , ' ? ' , " " } ,
{ 0X924ABA61 , nullptr , " sceKernelStdioOpen " , ' ? ' , " " } ,
{ 0X9D061C19 , nullptr , " sceKernelStdioClose " , ' ? ' , " " } ,
2012-11-01 15:19:01 +00:00
} ;
2012-12-06 14:16:05 +00:00
void Register_StdioForUser ( ) {
2012-11-01 15:19:01 +00:00
RegisterModule ( " StdioForUser " , ARRAY_SIZE ( StdioForUser ) , StdioForUser ) ;
}
2016-08-04 16:33:18 +00:00
const HLEFunction StdioForKernel [ ] = {
{ 0X98220F3E , nullptr , " sceKernelStdoutReopen " , ' ? ' , " " } ,
{ 0XFB5380C5 , nullptr , " sceKernelStderrReopen " , ' ? ' , " " } ,
{ 0XCAB439DF , nullptr , " printf " , ' ? ' , " " } ,
{ 0X2CCF071A , nullptr , " fdprintf " , ' ? ' , " " } ,
{ 0XD97C8CB9 , nullptr , " puts " , ' ? ' , " " } ,
{ 0X172D316E , nullptr , " sceKernelStdin " , ' ? ' , " " } ,
2020-06-07 11:36:12 +00:00
{ 0XA6BAB2E9 , & WrapU_V < sceKernelStdout > , " sceKernelStdout " , ' i ' , " " , HLE_KERNEL_SYSCALL } ,
2016-08-04 16:33:18 +00:00
{ 0XF78BA90A , nullptr , " sceKernelStderr " , ' ? ' , " " } ,
} ;
void Register_StdioForKernel ( ) {
RegisterModule ( " StdioForKernel " , ARRAY_SIZE ( StdioForKernel ) , StdioForKernel ) ;
}