1 // Copyright (c) 2012- PPSSPP Project.
2
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
6
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
11
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
14
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18 #include <cstdlib>
19 #include <set>
20 #include <thread>
21 #include <memory>
22
23 #include "Common/Thread/ThreadUtil.h"
24 #include "Common/Profiler/Profiler.h"
25 #include "Common/TimeUtil.h"
26
27 #include "Common/File/FileUtil.h"
28 #include "Common/Serialize/SerializeFuncs.h"
29 #include "Common/Serialize/SerializeMap.h"
30 #include "Common/Serialize/SerializeSet.h"
31 #include "Common/StringUtils.h"
32 #include "Core/Core.h"
33 #include "Core/Config.h"
34 #include "Core/ConfigValues.h"
35 #include "Core/Debugger/MemBlockInfo.h"
36 #include "Core/ELF/ParamSFO.h"
37 #include "Core/MemMapHelpers.h"
38 #include "Core/System.h"
39 #include "Core/HDRemaster.h"
40 #include "Core/Host.h"
41 #include "Core/SaveState.h"
42 #include "Core/HLE/HLE.h"
43 #include "Core/HLE/HLEHelperThread.h"
44 #include "Core/HLE/FunctionWrappers.h"
45 #include "Core/HLE/sceDisplay.h"
46 #include "Core/HLE/sceKernel.h"
47 #include "Core/HLE/sceUmd.h"
48 #include "Core/MIPS/MIPS.h"
49 #include "Core/HW/MemoryStick.h"
50 #include "Core/HW/AsyncIOManager.h"
51 #include "Core/CoreTiming.h"
52 #include "Core/Reporting.h"
53
54 #include "Core/FileSystems/FileSystem.h"
55 #include "Core/FileSystems/MetaFileSystem.h"
56 #include "Core/FileSystems/ISOFileSystem.h"
57 #include "Core/FileSystems/DirectoryFileSystem.h"
58
59 extern "C" {
60 #include "ext/libkirk/amctrl.h"
61 };
62
63 #include "Core/HLE/sceIo.h"
64 #include "Core/HLE/sceRtc.h"
65 #include "Core/HLE/sceKernel.h"
66 #include "Core/HLE/sceKernelMemory.h"
67 #include "Core/HLE/sceKernelThread.h"
68 #include "Core/HLE/sceKernelInterrupt.h"
69 #include "Core/HLE/KernelWaitHelpers.h"
70
71 // For headless screenshots.
72 #include "Core/HLE/sceDisplay.h"
73
74 static const int ERROR_ERRNO_IO_ERROR = 0x80010005;
75 static const int ERROR_MEMSTICK_DEVCTL_BAD_PARAMS = 0x80220081;
76 static const int ERROR_MEMSTICK_DEVCTL_TOO_MANY_CALLBACKS = 0x80220082;
77 static const int ERROR_PGD_INVALID_HEADER = 0x80510204;
78 /*
79
80 TODO: async io is missing features!
81
82 flash0: - fat access - system file volume
83 flash1: - fat access - configuration file volume
84 flashfat#: this too
85
86 lflash: - block access - entire flash
87 fatms: memstick
88 isofs: fat access - umd
89 disc0: fat access - umd
90 ms0: - fat access - memcard
91 umd: - block access - umd
92 irda?: - (?=0..9) block access - infra-red port (doesnt support seeking, maybe send/recieve data from port tho)
93 mscm0: - block access - memstick cm??
94 umd00: block access - umd
95 umd01: block access - umd
96 */
97
98 #define PSP_O_RDONLY 0x0001
99 #define PSP_O_WRONLY 0x0002
100 #define PSP_O_RDWR 0x0003
101 #define PSP_O_NBLOCK 0x0010
102 #define PSP_O_APPEND 0x0100
103 #define PSP_O_CREAT 0x0200
104 #define PSP_O_TRUNC 0x0400
105 #define PSP_O_EXCL 0x0800
106 #define PSP_O_NOWAIT 0x8000
107 #define PSP_O_NPDRM 0x40000000
108
109 // chstat
110 #define SCE_CST_MODE 0x0001
111 #define SCE_CST_ATTR 0x0002
112 #define SCE_CST_SIZE 0x0004
113 #define SCE_CST_CT 0x0008
114 #define SCE_CST_AT 0x0010
115 #define SCE_CST_MT 0x0020
116 #define SCE_CST_PRVT 0x0040
117
118 typedef s32 SceMode;
119 typedef s64 SceOff;
120 typedef u64 SceIores;
121
122 const int PSP_COUNT_FDS = 64;
123 // TODO: Should be 3, and stdin/stdout/stderr are special values aliased to 0?
124 const int PSP_MIN_FD = 4;
125 const int PSP_STDOUT = 1;
126 const int PSP_STDERR = 2;
127 const int PSP_STDIN = 3;
128 static int asyncNotifyEvent = -1;
129 static int syncNotifyEvent = -1;
130 static SceUID fds[PSP_COUNT_FDS];
131
132 static std::vector<SceUID> memStickCallbacks;
133 static std::vector<SceUID> memStickFatCallbacks;
134 static MemStickState lastMemStickState;
135 static MemStickFatState lastMemStickFatState;
136
137 static AsyncIOManager ioManager;
138 static bool ioManagerThreadEnabled = false;
139 static std::thread *ioManagerThread;
140
141 // TODO: Is it better to just put all on the thread?
142 // Let's try. (was 256)
143 const int IO_THREAD_MIN_DATA_SIZE = 0;
144
145 #define SCE_STM_FDIR 0x1000
146 #define SCE_STM_FREG 0x2000
147 #define SCE_STM_FLNK 0x4000
148
149 enum {
150 TYPE_DIR = 0x10,
151 TYPE_FILE = 0x20
152 };
153
154 struct SceIoStat {
155 SceMode_le st_mode;
156 u32_le st_attr;
157 SceOff_le st_size;
158 ScePspDateTime st_c_time;
159 ScePspDateTime st_a_time;
160 ScePspDateTime st_m_time;
161 u32_le st_private[6];
162 };
163
164 struct SceIoDirEnt {
165 SceIoStat d_stat;
166 char d_name[256];
167 u32_le d_private;
168 };
169
170 enum class IoAsyncOp {
171 NONE,
172 OPEN,
173 CLOSE,
174 READ,
175 WRITE,
176 SEEK,
177 IOCTL,
178 };
179
180 struct IoAsyncParams {
181 IoAsyncOp op = IoAsyncOp::NONE;
182 int priority = -1;
183 union {
184 struct {
185 u32 filenameAddr;
186 int flags;
187 int mode;
188 } open;
189 struct {
190 u32 addr;
191 u32 size;
192 } std;
193 struct {
194 s64 pos;
195 int whence;
196 } seek;
197 struct {
198 u32 cmd;
199 u32 inAddr;
200 u32 inSize;
201 u32 outAddr;
202 u32 outSize;
203 } ioctl;
204 };
205 };
206
207 static IoAsyncParams asyncParams[PSP_COUNT_FDS];
208 static HLEHelperThread *asyncThreads[PSP_COUNT_FDS]{};
209 static int asyncDefaultPriority = -1;
210
211 class FileNode : public KernelObject {
212 public:
FileNode()213 FileNode() {}
~FileNode()214 ~FileNode() {
215 if (handle != -1)
216 pspFileSystem.CloseFile(handle);
217 pgd_close(pgdInfo);
218 }
GetName()219 const char *GetName() override { return fullpath.c_str(); }
GetTypeName()220 const char *GetTypeName() override { return GetStaticTypeName(); }
GetStaticTypeName()221 static const char *GetStaticTypeName() { return "OpenFile"; }
GetQuickInfo(char * ptr,int size)222 void GetQuickInfo(char *ptr, int size) override {
223 sprintf(ptr, "Seekpos: %08x", (u32)pspFileSystem.GetSeekPos(handle));
224 }
GetMissingErrorCode()225 static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_BADF; }
GetStaticIDType()226 static int GetStaticIDType() { return PPSSPP_KERNEL_TMID_File; }
GetIDType() const227 int GetIDType() const override { return PPSSPP_KERNEL_TMID_File; }
228
asyncBusy()229 bool asyncBusy() {
230 return pendingAsyncResult || hasAsyncResult;
231 }
232
DoState(PointerWrap & p)233 void DoState(PointerWrap &p) override {
234 auto s = p.Section("FileNode", 1, 3);
235 if (!s)
236 return;
237
238 Do(p, fullpath);
239 Do(p, handle);
240 Do(p, callbackID);
241 Do(p, callbackArg);
242 Do(p, asyncResult);
243 Do(p, hasAsyncResult);
244 Do(p, pendingAsyncResult);
245 Do(p, sectorBlockMode);
246 Do(p, closePending);
247 Do(p, info);
248 Do(p, openMode);
249
250 Do(p, npdrm);
251 Do(p, pgd_offset);
252 bool hasPGD = pgdInfo != NULL;
253 Do(p, hasPGD);
254 if (hasPGD) {
255 if (p.mode == p.MODE_READ) {
256 pgdInfo = (PGD_DESC*) malloc(sizeof(PGD_DESC));
257 }
258 p.DoVoid(pgdInfo, sizeof(PGD_DESC));
259 if (p.mode == p.MODE_READ) {
260 pgdInfo->block_buf = (u8 *)malloc(pgdInfo->block_size * 2);
261 }
262 }
263
264 Do(p, waitingThreads);
265 if (s >= 2) {
266 Do(p, waitingSyncThreads);
267 }
268 if (s >= 3) {
269 Do(p, isTTY);
270 }
271 Do(p, pausedWaits);
272 }
273
274 std::string fullpath;
275 u32 handle;
276
277 u32 callbackID = 0;
278 u32 callbackArg = 0;
279
280 s64 asyncResult = 0;
281 bool hasAsyncResult = false;
282 bool pendingAsyncResult = false;
283
284 bool sectorBlockMode = false;
285 // TODO: Use an enum instead?
286 bool closePending = false;
287
288 PSPFileInfo info;
289 u32 openMode = 0;
290
291 u32 npdrm = 0;
292 u32 pgd_offset = 0;
293 PGD_DESC *pgdInfo = nullptr;
294
295 std::vector<SceUID> waitingThreads;
296 std::vector<SceUID> waitingSyncThreads;
297 // Key is the callback id it was for, or if no callback, the thread id.
298 // Value is actually meaningless but kept for consistency with other wait types.
299 std::map<SceUID, u64> pausedWaits;
300
301 bool isTTY = false;
302 };
303
304 /******************************************************************************/
305
306
307
308 /******************************************************************************/
309
310 u64 __IoCompleteAsyncIO(FileNode *f);
311
IoAsyncCleanupThread(int fd)312 static void IoAsyncCleanupThread(int fd) {
313 if (asyncThreads[fd]) {
314 if (!asyncThreads[fd]->Stopped()) {
315 asyncThreads[fd]->Terminate();
316 }
317 delete asyncThreads[fd];
318 asyncThreads[fd] = nullptr;
319 }
320 }
321
GetIOTimingMethod()322 static int GetIOTimingMethod() {
323 if (PSP_CoreParameter().compat.flags().ForceUMDDelay) {
324 return IOTIMING_REALISTIC;
325 } else {
326 return g_Config.iIOTimingMethod;
327 }
328 }
329
TellFsThreadEnded(SceUID threadID)330 static void TellFsThreadEnded (SceUID threadID) {
331 pspFileSystem.ThreadEnded(threadID);
332 }
333
__IoGetFd(int fd,u32 & error)334 static FileNode *__IoGetFd(int fd, u32 &error) {
335 if (fd < 0 || fd >= PSP_COUNT_FDS) {
336 error = SCE_KERNEL_ERROR_BADF;
337 return NULL;
338 }
339
340 return kernelObjects.Get<FileNode>(fds[fd], error);
341 }
342
__IoAllocFd(FileNode * f)343 static int __IoAllocFd(FileNode *f) {
344 // The PSP takes the lowest available id after stderr/etc.
345 for (int possible = PSP_MIN_FD; possible < PSP_COUNT_FDS; ++possible) {
346 if (fds[possible] == 0) {
347 fds[possible] = f->GetUID();
348 return possible;
349 }
350 }
351
352 // Bugger, out of fds...
353 return SCE_KERNEL_ERROR_MFILE;
354 }
355
__IoFreeFd(int fd,u32 & error)356 static void __IoFreeFd(int fd, u32 &error) {
357 if (fd == PSP_STDIN || fd == PSP_STDERR || fd == PSP_STDOUT) {
358 error = SCE_KERNEL_ERROR_ILLEGAL_PERM;
359 } else if (fd < PSP_MIN_FD || fd >= PSP_COUNT_FDS) {
360 error = SCE_KERNEL_ERROR_BADF;
361 } else {
362 FileNode *f = __IoGetFd(fd, error);
363 if (f) {
364 // If there are pending results, don't allow closing.
365 if (ioManager.HasOperation(f->handle)) {
366 error = SCE_KERNEL_ERROR_ASYNC_BUSY;
367 return;
368 }
369
370 // Wake anyone waiting on the file before closing it.
371 for (size_t i = 0; i < f->waitingThreads.size(); ++i) {
372 HLEKernel::ResumeFromWait(f->waitingThreads[i], WAITTYPE_ASYNCIO, f->GetUID(), (int)SCE_KERNEL_ERROR_WAIT_DELETE);
373 }
374
375 CoreTiming::UnscheduleEvent(asyncNotifyEvent, fd);
376 for (size_t i = 0; i < f->waitingSyncThreads.size(); ++i) {
377 CoreTiming::UnscheduleEvent(syncNotifyEvent, ((u64)f->waitingSyncThreads[i] << 32) | fd);
378 }
379
380 PROFILE_THIS_SCOPE("io_rw");
381
382 // Discard any pending results.
383 AsyncIOResult managerResult;
384 ioManager.WaitResult(f->handle, managerResult);
385
386 IoAsyncCleanupThread(fd);
387 }
388 error = kernelObjects.Destroy<FileNode>(fds[fd]);
389 fds[fd] = 0;
390 }
391 }
392
393 // Async IO seems to work roughly like this:
394 // 1. Game calls SceIo*Async() to start the process.
395 // 2. This runs a thread with a customizable priority.
396 // 3. The operation runs, which takes an inconsistent amount of time from UMD.
397 // 4. Once done (regardless of other syscalls), the fd-registered callback is notified.
398 // 5. The game can find out via *CB() or sceKernelCheckCallback().
399 // 6. At this point, the fd is STILL not usable.
400 // 7. One must call sceIoWaitAsync / sceIoWaitAsyncCB / sceIoPollAsync / possibly sceIoGetAsyncStat.
401 // 8. Finally, the fd is usable (or closed via sceIoCloseAsync.) Presumably the io thread has joined now.
402
403 // TODO: Closed files are a bit special: until the fd is reused (?), the async result is still available.
404 // Clearly a buffer is used, it doesn't seem like they are actually kernel objects.
405
406 // For now, let's at least delay the callback notification.
__IoAsyncNotify(u64 userdata,int cyclesLate)407 static void __IoAsyncNotify(u64 userdata, int cyclesLate) {
408 int fd = (int) userdata;
409
410 u32 error;
411 FileNode *f = __IoGetFd(fd, error);
412 if (!f) {
413 ERROR_LOG_REPORT(SCEIO, "__IoAsyncNotify: file no longer exists?");
414 return;
415 }
416
417 int ioTimingMethod = GetIOTimingMethod();
418 if (ioTimingMethod == IOTIMING_HOST) {
419 // Not all async operations actually queue up. Maybe should separate them?
420 if (!ioManager.HasResult(f->handle) && ioManager.HasOperation(f->handle)) {
421 // Try again in another 0.5ms until the IO completes on the host.
422 CoreTiming::ScheduleEvent(usToCycles(500) - cyclesLate, asyncNotifyEvent, userdata);
423 return;
424 }
425 __IoCompleteAsyncIO(f);
426 } else if (ioTimingMethod == IOTIMING_REALISTIC) {
427 u64 finishTicks = __IoCompleteAsyncIO(f);
428 if (finishTicks > CoreTiming::GetTicks()) {
429 // Reschedule for later, since we now know how long it ought to take.
430 CoreTiming::ScheduleEvent(finishTicks - CoreTiming::GetTicks(), asyncNotifyEvent, userdata);
431 return;
432 }
433 } else {
434 __IoCompleteAsyncIO(f);
435 }
436
437 if (f->waitingThreads.empty()) {
438 return;
439 }
440
441 SceUID threadID = f->waitingThreads.front();
442 f->waitingThreads.erase(f->waitingThreads.begin());
443
444 u32 address = __KernelGetWaitValue(threadID, error);
445 if (HLEKernel::VerifyWait(threadID, WAITTYPE_ASYNCIO, f->GetUID())) {
446 HLEKernel::ResumeFromWait(threadID, WAITTYPE_ASYNCIO, f->GetUID(), 0);
447 // Someone woke up, so it's no longer got one.
448 f->hasAsyncResult = false;
449
450 if (Memory::IsValidAddress(address)) {
451 Memory::Write_U64((u64) f->asyncResult, address);
452 }
453
454 // If this was a sceIoCloseAsync, we should close it at this point.
455 if (f->closePending) {
456 __IoFreeFd(fd, error);
457 }
458 }
459 }
460
__IoSyncNotify(u64 userdata,int cyclesLate)461 static void __IoSyncNotify(u64 userdata, int cyclesLate) {
462 PROFILE_THIS_SCOPE("io_rw");
463
464 SceUID threadID = userdata >> 32;
465 int fd = (int) (userdata & 0xFFFFFFFF);
466
467 s64 result = -1;
468 u32 error;
469 FileNode *f = __IoGetFd(fd, error);
470 if (!f) {
471 ERROR_LOG_REPORT(SCEIO, "__IoSyncNotify: file no longer exists?");
472 return;
473 }
474
475 int ioTimingMethod = GetIOTimingMethod();
476 if (ioTimingMethod == IOTIMING_HOST) {
477 if (!ioManager.HasResult(f->handle)) {
478 // Try again in another 0.5ms until the IO completes on the host.
479 CoreTiming::ScheduleEvent(usToCycles(500) - cyclesLate, syncNotifyEvent, userdata);
480 return;
481 }
482 } else if (ioTimingMethod == IOTIMING_REALISTIC) {
483 u64 finishTicks = ioManager.ResultFinishTicks(f->handle);
484 if (finishTicks > CoreTiming::GetTicks()) {
485 // Reschedule for later when the result should finish.
486 CoreTiming::ScheduleEvent(finishTicks - CoreTiming::GetTicks(), syncNotifyEvent, userdata);
487 return;
488 }
489 }
490
491 f->pendingAsyncResult = false;
492 f->hasAsyncResult = false;
493
494 AsyncIOResult managerResult;
495 if (ioManager.WaitResult(f->handle, managerResult)) {
496 result = managerResult.result;
497 } else {
498 ERROR_LOG(SCEIO, "Unable to complete IO operation on %s", f->GetName());
499 }
500
501 f->pendingAsyncResult = false;
502 f->hasAsyncResult = false;
503
504 HLEKernel::ResumeFromWait(threadID, WAITTYPE_IO, fd, result);
505 f->waitingSyncThreads.erase(std::remove(f->waitingSyncThreads.begin(), f->waitingSyncThreads.end(), threadID), f->waitingSyncThreads.end());
506 }
507
__IoAsyncBeginCallback(SceUID threadID,SceUID prevCallbackId)508 static void __IoAsyncBeginCallback(SceUID threadID, SceUID prevCallbackId) {
509 auto result = HLEKernel::WaitBeginCallback<FileNode, WAITTYPE_ASYNCIO, SceUID>(threadID, prevCallbackId, -1);
510 if (result == HLEKernel::WAIT_CB_SUCCESS) {
511 DEBUG_LOG(SCEIO, "sceIoWaitAsync: Suspending wait for callback");
512 } else if (result == HLEKernel::WAIT_CB_BAD_WAIT_ID) {
513 WARN_LOG_REPORT(SCEIO, "sceIoWaitAsync: beginning callback with bad wait id?");
514 }
515 }
516
__IoCheckAsyncWait(FileNode * f,SceUID threadID,u32 & error,int result,bool & wokeThreads)517 static bool __IoCheckAsyncWait(FileNode *f, SceUID threadID, u32 &error, int result, bool &wokeThreads)
518 {
519 int fd = -1;
520 for (int i = 0; i < (int)ARRAY_SIZE(fds); ++i) {
521 if (fds[i] == f->GetUID()) {
522 fd = i;
523 break;
524 }
525 }
526 if (fd == -1) {
527 ERROR_LOG_REPORT(SCEIO, "__IoCheckAsyncWait: could not find io handle");
528 return true;
529 }
530
531 if (!HLEKernel::VerifyWait(threadID, WAITTYPE_ASYNCIO, f->GetUID())) {
532 return true;
533 }
534
535 // If result is an error code, we're just letting it go.
536 if (result == 0) {
537 if (f->pendingAsyncResult || !f->hasAsyncResult) {
538 return false;
539 }
540
541 u32 address = __KernelGetWaitValue(threadID, error);
542 Memory::Write_U64((u64) f->asyncResult, address);
543 f->hasAsyncResult = false;
544
545 if (f->closePending) {
546 __IoFreeFd(fd, error);
547 }
548 }
549
550 __KernelResumeThreadFromWait(threadID, result);
551 wokeThreads = true;
552 return true;
553 }
554
__IoAsyncEndCallback(SceUID threadID,SceUID prevCallbackId)555 static void __IoAsyncEndCallback(SceUID threadID, SceUID prevCallbackId) {
556 auto result = HLEKernel::WaitEndCallback<FileNode, WAITTYPE_ASYNCIO, SceUID>(threadID, prevCallbackId, -1, __IoCheckAsyncWait);
557 if (result == HLEKernel::WAIT_CB_RESUMED_WAIT) {
558 DEBUG_LOG(SCEIO, "sceKernelWaitEventFlagCB: Resuming lock wait for callback");
559 }
560 }
561
__IoManagerThread()562 static void __IoManagerThread() {
563 SetCurrentThreadName("IO");
564 while (ioManagerThreadEnabled && coreState != CORE_BOOT_ERROR && coreState != CORE_RUNTIME_ERROR && coreState != CORE_POWERDOWN) {
565 ioManager.RunEventsUntil(CoreTiming::GetTicks() + msToCycles(1000));
566 }
567 }
568
__IoWakeManager(CoreLifecycle stage)569 static void __IoWakeManager(CoreLifecycle stage) {
570 // Ping the thread so that it knows to check coreState.
571 if (stage == CoreLifecycle::STOPPING) {
572 ioManagerThreadEnabled = false;
573 ioManager.FinishEventLoop();
574 }
575 }
576
__IoVblank()577 static void __IoVblank() {
578 // We update memstick status here just to avoid possible thread safety issues.
579 // It doesn't actually need to be on a vblank.
580
581 // This will only change status if g_Config was changed.
582 MemoryStick_SetState(g_Config.bMemStickInserted ? PSP_MEMORYSTICK_STATE_INSERTED : PSP_MEMORYSTICK_STATE_NOT_INSERTED);
583
584 MemStickState newState = MemoryStick_State();
585 MemStickFatState newFatState = MemoryStick_FatState();
586
587 // First, the fat callbacks, these are easy.
588 if (lastMemStickFatState != newFatState) {
589 int notifyMsg = 0;
590 if (newFatState == PSP_FAT_MEMORYSTICK_STATE_ASSIGNED) {
591 notifyMsg = 1;
592 } else if (newFatState == PSP_FAT_MEMORYSTICK_STATE_UNASSIGNED) {
593 notifyMsg = 2;
594 }
595 if (notifyMsg != 0) {
596 for (SceUID cbId : memStickFatCallbacks) {
597 __KernelNotifyCallback(cbId, notifyMsg);
598 }
599 }
600 }
601
602 // Next, the controller notifies mounting (fat) too.
603 if (lastMemStickState != newState || lastMemStickFatState != newFatState) {
604 int notifyMsg = 0;
605 if (newState == PSP_MEMORYSTICK_STATE_INSERTED && newFatState == PSP_FAT_MEMORYSTICK_STATE_ASSIGNED) {
606 notifyMsg = 1;
607 } else if (newState == PSP_MEMORYSTICK_STATE_INSERTED && newFatState == PSP_FAT_MEMORYSTICK_STATE_UNASSIGNED) {
608 // Still mounting (1 will come later.)
609 notifyMsg = 4;
610 } else if (newState == PSP_MEMORYSTICK_STATE_NOT_INSERTED) {
611 notifyMsg = 2;
612 }
613 if (notifyMsg != 0) {
614 for (SceUID cbId : memStickCallbacks) {
615 __KernelNotifyCallback(cbId, notifyMsg);
616 }
617 }
618 }
619
620 lastMemStickState = newState;
621 lastMemStickFatState = newFatState;
622 }
623
__IoInit()624 void __IoInit() {
625 asyncNotifyEvent = CoreTiming::RegisterEvent("IoAsyncNotify", __IoAsyncNotify);
626 syncNotifyEvent = CoreTiming::RegisterEvent("IoSyncNotify", __IoSyncNotify);
627
628 // TODO(scoped): This won't work if memStickDirectory points at the contents of /PSP...
629 #if defined(USING_WIN_UI) || defined(APPLE)
630 auto flash0System = std::shared_ptr<IFileSystem>(new DirectoryFileSystem(&pspFileSystem, g_Config.flash0Directory, FileSystemFlags::FLASH));
631 #else
632 auto flash0System = std::shared_ptr<IFileSystem>(new VFSFileSystem(&pspFileSystem, "flash0"));
633 #endif
634 FileSystemFlags memstickFlags = FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD;
635
636 Path pspDir = GetSysDirectory(DIRECTORY_PSP);
637 if (pspDir == g_Config.memStickDirectory) {
638 // Initially tried to do this with dual mounts, but failed due to save state compatibility issues.
639 INFO_LOG(SCEIO, "Enabling /PSP compatibility mode");
640 memstickFlags |= FileSystemFlags::STRIP_PSP;
641 }
642
643 auto memstickSystem = std::shared_ptr<IFileSystem>(new DirectoryFileSystem(&pspFileSystem, g_Config.memStickDirectory, memstickFlags));
644
645 pspFileSystem.Mount("ms0:", memstickSystem);
646 pspFileSystem.Mount("fatms0:", memstickSystem);
647 pspFileSystem.Mount("fatms:", memstickSystem);
648 pspFileSystem.Mount("pfat0:", memstickSystem);
649
650 pspFileSystem.Mount("flash0:", flash0System);
651
652 if (g_RemasterMode) {
653 const std::string gameId = g_paramSFO.GetDiscID();
654 const Path exdataPath = GetSysDirectory(DIRECTORY_EXDATA) / gameId;
655 if (File::Exists(exdataPath)) {
656 auto exdataSystem = std::shared_ptr<IFileSystem>(new DirectoryFileSystem(&pspFileSystem, exdataPath, FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD));
657 pspFileSystem.Mount("exdata0:", exdataSystem);
658 INFO_LOG(SCEIO, "Mounted exdata/%s/ under memstick for exdata0:/", gameId.c_str());
659 } else {
660 INFO_LOG(SCEIO, "Did not find exdata/%s/ under memstick for exdata0:/", gameId.c_str());
661 }
662 }
663
664 __KernelListenThreadEnd(&TellFsThreadEnded);
665
666 memset(fds, 0, sizeof(fds));
667
668 ioManagerThreadEnabled = g_Config.bSeparateIOThread;
669 ioManager.SetThreadEnabled(ioManagerThreadEnabled);
670 if (ioManagerThreadEnabled) {
671 Core_ListenLifecycle(&__IoWakeManager);
672 ioManagerThread = new std::thread(&__IoManagerThread);
673 }
674
675 __KernelRegisterWaitTypeFuncs(WAITTYPE_ASYNCIO, __IoAsyncBeginCallback, __IoAsyncEndCallback);
676
677 MemoryStick_Init();
678 lastMemStickState = MemoryStick_State();
679 lastMemStickFatState = MemoryStick_FatState();
680 __DisplayListenVblank(__IoVblank);
681 }
682
__IoDoState(PointerWrap & p)683 void __IoDoState(PointerWrap &p) {
684 auto s = p.Section("sceIo", 1, 5);
685 if (!s)
686 return;
687
688 ioManager.DoState(p);
689 DoArray(p, fds, ARRAY_SIZE(fds));
690 Do(p, asyncNotifyEvent);
691 CoreTiming::RestoreRegisterEvent(asyncNotifyEvent, "IoAsyncNotify", __IoAsyncNotify);
692 Do(p, syncNotifyEvent);
693 CoreTiming::RestoreRegisterEvent(syncNotifyEvent, "IoSyncNotify", __IoSyncNotify);
694 if (s < 2) {
695 std::set<SceUID> legacy;
696 memStickCallbacks.clear();
697 memStickFatCallbacks.clear();
698
699 // Convert from set to vector.
700 Do(p, legacy);
701 for (SceUID id : legacy) {
702 memStickCallbacks.push_back(id);
703 }
704 Do(p, legacy);
705 for (SceUID id : legacy) {
706 memStickFatCallbacks.push_back(id);
707 }
708 } else {
709 Do(p, memStickCallbacks);
710 Do(p, memStickFatCallbacks);
711 }
712
713 if (s >= 3) {
714 Do(p, lastMemStickState);
715 Do(p, lastMemStickFatState);
716 }
717
718 for (int i = 0; i < PSP_COUNT_FDS; ++i) {
719 auto clearThread = [&]() {
720 if (asyncThreads[i])
721 asyncThreads[i]->Forget();
722 delete asyncThreads[i];
723 asyncThreads[i] = nullptr;
724 };
725
726 if (s >= 4) {
727 p.DoVoid(&asyncParams[i], (int)sizeof(IoAsyncParams));
728 bool hasThread = asyncThreads[i] != nullptr;
729 Do(p, hasThread);
730 if (hasThread) {
731 if (p.GetMode() == p.MODE_READ)
732 clearThread();
733 DoClass(p, asyncThreads[i]);
734 } else if (!hasThread) {
735 clearThread();
736 }
737 } else {
738 asyncParams[i].op = IoAsyncOp::NONE;
739 asyncParams[i].priority = -1;
740 clearThread();
741 }
742 }
743
744 if (s >= 5) {
745 Do(p, asyncDefaultPriority);
746 } else {
747 asyncDefaultPriority = -1;
748 }
749 }
750
__IoShutdown()751 void __IoShutdown() {
752 ioManagerThreadEnabled = false;
753 ioManager.SyncThread();
754 ioManager.FinishEventLoop();
755 if (ioManagerThread != nullptr) {
756 ioManagerThread->join();
757 delete ioManagerThread;
758 ioManagerThread = nullptr;
759 ioManager.Shutdown();
760 }
761
762 for (int i = 0; i < PSP_COUNT_FDS; ++i) {
763 asyncParams[i].op = IoAsyncOp::NONE;
764 asyncParams[i].priority = -1;
765 if (asyncThreads[i])
766 asyncThreads[i]->Forget();
767 delete asyncThreads[i];
768 asyncThreads[i] = nullptr;
769 }
770 asyncDefaultPriority = -1;
771
772 pspFileSystem.Unmount("ms0:");
773 pspFileSystem.Unmount("fatms0:");
774 pspFileSystem.Unmount("fatms:");
775 pspFileSystem.Unmount("pfat0:");
776 pspFileSystem.Unmount("flash0:");
777 pspFileSystem.Unmount("exdata0:");
778
779 MemoryStick_Shutdown();
780 memStickCallbacks.clear();
781 memStickFatCallbacks.clear();
782 }
783
IODetermineFilename(FileNode * f)784 static std::string IODetermineFilename(FileNode *f) {
785 uint64_t offset = pspFileSystem.GetSeekPos(f->handle);
786 if ((pspFileSystem.DevType(f->handle) & PSPDevType::BLOCK) != 0) {
787 return StringFromFormat("%s offset 0x%08llx", f->fullpath.c_str(), offset * 2048);
788 }
789 return StringFromFormat("%s offset 0x%08llx", f->fullpath.c_str(), offset);
790 }
791
__IoGetFileHandleFromId(u32 id,u32 & outError)792 u32 __IoGetFileHandleFromId(u32 id, u32 &outError)
793 {
794 FileNode *f = __IoGetFd(id, outError);
795 if (!f) {
796 return (u32)-1;
797 }
798 return f->handle;
799 }
800
IoStartAsyncThread(int id,FileNode * f)801 static void IoStartAsyncThread(int id, FileNode *f) {
802 if (asyncThreads[id] && !asyncThreads[id]->Stopped()) {
803 // Wake the thread up.
804 if (asyncParams[id].priority == -1 && sceKernelGetCompiledSdkVersion() >= 0x04020000) {
805 asyncThreads[id]->ChangePriority(KernelCurThreadPriority());
806 }
807 asyncThreads[id]->Resume(WAITTYPE_ASYNCIO, id, 0);
808 } else {
809 IoAsyncCleanupThread(id);
810 int priority = asyncParams[id].priority;
811 if (priority == -1)
812 priority = KernelCurThreadPriority();
813 asyncThreads[id] = new HLEHelperThread("SceIoAsync", "IoFileMgrForUser", "__IoAsyncFinish", priority, 0x200);
814 asyncThreads[id]->Start(id, 0);
815 }
816 f->pendingAsyncResult = true;
817 }
818
sceIoAssign(u32 alias_addr,u32 physical_addr,u32 filesystem_addr,int mode,u32 arg_addr,int argSize)819 static u32 sceIoAssign(u32 alias_addr, u32 physical_addr, u32 filesystem_addr, int mode, u32 arg_addr, int argSize)
820 {
821 std::string alias = Memory::GetCharPointer(alias_addr);
822 std::string physical_dev = Memory::GetCharPointer(physical_addr);
823 std::string filesystem_dev = Memory::GetCharPointer(filesystem_addr);
824 std::string perm;
825
826 switch (mode) {
827 case 0:
828 perm = "IOASSIGN_RDWR";
829 break;
830 case 1:
831 perm = "IOASSIGN_RDONLY";
832 break;
833 default:
834 perm = "unhandled";
835 break;
836 }
837 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);
838 return 0;
839 }
840
sceIoUnassign(const char * alias)841 static u32 sceIoUnassign(const char *alias)
842 {
843 WARN_LOG_REPORT(SCEIO, "sceIoUnassign(%s)", alias);
844 return 0;
845 }
846
sceKernelStdin()847 static u32 sceKernelStdin() {
848 DEBUG_LOG(SCEIO, "%d=sceKernelStdin()", PSP_STDIN);
849 return PSP_STDIN;
850 }
851
sceKernelStdout()852 static u32 sceKernelStdout() {
853 DEBUG_LOG(SCEIO, "%d=sceKernelStdout()", PSP_STDOUT);
854 return PSP_STDOUT;
855 }
856
sceKernelStderr()857 static u32 sceKernelStderr() {
858 DEBUG_LOG(SCEIO, "%d=sceKernelStderr()", PSP_STDERR);
859 return PSP_STDERR;
860 }
861
__IoCompleteAsyncIO(FileNode * f)862 u64 __IoCompleteAsyncIO(FileNode *f) {
863 PROFILE_THIS_SCOPE("io_rw");
864
865 int ioTimingMethod = GetIOTimingMethod();
866 if (ioTimingMethod == IOTIMING_REALISTIC) {
867 u64 finishTicks = ioManager.ResultFinishTicks(f->handle);
868 if (finishTicks > CoreTiming::GetTicks()) {
869 return finishTicks;
870 }
871 }
872 AsyncIOResult managerResult;
873 if (ioManager.WaitResult(f->handle, managerResult)) {
874 f->asyncResult = managerResult.result;
875 } else {
876 // It's okay, not all operations are deferred.
877 }
878 if (f->callbackID) {
879 __KernelNotifyCallback(f->callbackID, f->callbackArg);
880 }
881 f->pendingAsyncResult = false;
882 f->hasAsyncResult = true;
883
884 return 0;
885 }
886
__IoCopyDate(ScePspDateTime & date_out,const tm & date_in)887 void __IoCopyDate(ScePspDateTime& date_out, const tm& date_in)
888 {
889 date_out.year = date_in.tm_year+1900;
890 date_out.month = date_in.tm_mon+1;
891 date_out.day = date_in.tm_mday;
892 date_out.hour = date_in.tm_hour;
893 date_out.minute = date_in.tm_min;
894 date_out.second = date_in.tm_sec;
895 date_out.microsecond = 0;
896 }
897
__IoGetStat(SceIoStat * stat,PSPFileInfo & info)898 static void __IoGetStat(SceIoStat *stat, PSPFileInfo &info) {
899 memset(stat, 0xfe, sizeof(SceIoStat));
900 stat->st_size = (s64) info.size;
901
902 int type, attr;
903 if (info.type & FILETYPE_DIRECTORY)
904 type = SCE_STM_FDIR, attr = TYPE_DIR;
905 else
906 type = SCE_STM_FREG, attr = TYPE_FILE;
907
908 stat->st_mode = type | info.access;
909 stat->st_attr = attr;
910 stat->st_size = info.size;
911 __IoCopyDate(stat->st_a_time, info.atime);
912 __IoCopyDate(stat->st_c_time, info.ctime);
913 __IoCopyDate(stat->st_m_time, info.mtime);
914 stat->st_private[0] = info.startSector;
915 }
916
__IoSchedAsync(FileNode * f,int fd,int usec)917 static void __IoSchedAsync(FileNode *f, int fd, int usec) {
918 CoreTiming::ScheduleEvent(usToCycles(usec), asyncNotifyEvent, fd);
919
920 f->pendingAsyncResult = true;
921 f->hasAsyncResult = false;
922 }
923
__IoSchedSync(FileNode * f,int fd,int usec)924 static void __IoSchedSync(FileNode *f, int fd, int usec) {
925 u64 param = ((u64)__KernelGetCurThread()) << 32 | fd;
926 CoreTiming::ScheduleEvent(usToCycles(usec), syncNotifyEvent, param);
927
928 f->pendingAsyncResult = false;
929 f->hasAsyncResult = false;
930 }
931
sceIoGetstat(const char * filename,u32 addr)932 static u32 sceIoGetstat(const char *filename, u32 addr) {
933 // TODO: Improve timing (although this seems normally slow..)
934 int usec = 1000;
935
936 SceIoStat stat;
937 PSPFileInfo info = pspFileSystem.GetFileInfo(filename);
938 if (info.exists) {
939 __IoGetStat(&stat, info);
940 if (Memory::IsValidAddress(addr)) {
941 Memory::WriteStruct(addr, &stat);
942 DEBUG_LOG(SCEIO, "sceIoGetstat(%s, %08x) : sector = %08x", filename, addr, info.startSector);
943 return hleDelayResult(0, "io getstat", usec);
944 } else {
945 ERROR_LOG(SCEIO, "sceIoGetstat(%s, %08x) : bad address", filename, addr);
946 return hleDelayResult(-1, "io getstat", usec);
947 }
948 } else {
949 DEBUG_LOG(SCEIO, "sceIoGetstat(%s, %08x) : FILE NOT FOUND", filename, addr);
950 return hleDelayResult(SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND, "io getstat", usec);
951 }
952 }
953
sceIoChstat(const char * filename,u32 iostatptr,u32 changebits)954 static u32 sceIoChstat(const char *filename, u32 iostatptr, u32 changebits) {
955 auto iostat = PSPPointer<SceIoStat>::Create(iostatptr);
956 if (!iostat.IsValid())
957 return hleReportError(SCEIO, SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT, "bad address");
958
959 ERROR_LOG_REPORT(SCEIO, "UNIMPL sceIoChstat(%s, %08x, %08x)", filename, iostatptr, changebits);
960 if (changebits & SCE_CST_MODE)
961 ERROR_LOG_REPORT(SCEIO, "sceIoChstat: change mode to %03o requested", iostat->st_mode);
962 if (changebits & SCE_CST_ATTR)
963 ERROR_LOG_REPORT(SCEIO, "sceIoChstat: change attr to %04x requested", iostat->st_attr);
964 if (changebits & SCE_CST_SIZE)
965 ERROR_LOG(SCEIO, "sceIoChstat: change size requested");
966 if (changebits & SCE_CST_CT)
967 ERROR_LOG(SCEIO, "sceIoChstat: change creation time requested");
968 if (changebits & SCE_CST_AT)
969 ERROR_LOG(SCEIO, "sceIoChstat: change access time requested");
970 if (changebits & SCE_CST_MT)
971 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);
972 if (changebits & SCE_CST_PRVT)
973 ERROR_LOG(SCEIO, "sceIoChstat: change private data requested");
974 return 0;
975 }
976
npdrmRead(FileNode * f,u8 * data,int size)977 static u32 npdrmRead(FileNode *f, u8 *data, int size) {
978 PGD_DESC *pgd = f->pgdInfo;
979 u32 block, offset, blockPos;
980 u32 remain_size, copy_size;
981
982 block = pgd->file_offset/pgd->block_size;
983 offset = pgd->file_offset%pgd->block_size;
984
985 if (size > (int)pgd->data_size)
986 size = (int)pgd->data_size;
987 remain_size = size;
988
989 while(remain_size){
990
991 if(pgd->current_block!=block){
992 blockPos = block*pgd->block_size;
993 pspFileSystem.SeekFile(f->handle, (s32)pgd->data_offset+blockPos, FILEMOVE_BEGIN);
994 pspFileSystem.ReadFile(f->handle, pgd->block_buf, pgd->block_size);
995 pgd_decrypt_block(pgd, block);
996 pgd->current_block = block;
997 }
998
999 if(offset+remain_size>pgd->block_size){
1000 copy_size = pgd->block_size-offset;
1001 memcpy(data, pgd->block_buf+offset, copy_size);
1002 block += 1;
1003 offset = 0;
1004 } else {
1005 copy_size = remain_size;
1006 memcpy(data, pgd->block_buf+offset, copy_size);
1007 }
1008
1009 data += copy_size;
1010 remain_size -= copy_size;
1011 pgd->file_offset += copy_size;
1012 }
1013
1014 return size;
1015 }
1016
__IoRead(int & result,int id,u32 data_addr,int size,int & us)1017 static bool __IoRead(int &result, int id, u32 data_addr, int size, int &us) {
1018 PROFILE_THIS_SCOPE("io_rw");
1019 // Low estimate, may be improved later from the ReadFile result.
1020 us = size / 100;
1021 if (us < 100) {
1022 us = 100;
1023 }
1024
1025 if (id == PSP_STDIN) {
1026 DEBUG_LOG(SCEIO, "sceIoRead STDIN");
1027 result = 0; //stdin
1028 return true;
1029 }
1030
1031 u32 error;
1032 FileNode *f = __IoGetFd(id, error);
1033 if (f) {
1034 if (f->asyncBusy()) {
1035 result = SCE_KERNEL_ERROR_ASYNC_BUSY;
1036 return true;
1037 }
1038 if (!(f->openMode & FILEACCESS_READ)) {
1039 result = SCE_KERNEL_ERROR_BADF;
1040 return true;
1041 } else if (size < 0) {
1042 result = SCE_KERNEL_ERROR_ILLEGAL_ADDR;
1043 return true;
1044 } else if (Memory::IsValidAddress(data_addr)) {
1045 const std::string tag = "IoRead/" + IODetermineFilename(f);
1046 NotifyMemInfo(MemBlockFlags::WRITE, data_addr, size, tag.c_str(), tag.size());
1047 u8 *data = (u8 *)Memory::GetPointer(data_addr);
1048 u32 validSize = Memory::ValidSize(data_addr, size);
1049 if (f->npdrm) {
1050 result = npdrmRead(f, data, validSize);
1051 currentMIPS->InvalidateICache(data_addr, validSize);
1052 return true;
1053 }
1054
1055 bool useThread = __KernelIsDispatchEnabled() && ioManagerThreadEnabled && size > IO_THREAD_MIN_DATA_SIZE;
1056 if (useThread) {
1057 // If there's a pending operation on this file, wait for it to finish and don't overwrite it.
1058 useThread = !ioManager.HasOperation(f->handle);
1059 if (!useThread) {
1060 ioManager.SyncThread();
1061 }
1062 }
1063 if (useThread) {
1064 AsyncIOEvent ev = IO_EVENT_READ;
1065 ev.handle = f->handle;
1066 ev.buf = data;
1067 ev.bytes = validSize;
1068 ev.invalidateAddr = data_addr;
1069 ioManager.ScheduleOperation(ev);
1070 return false;
1071 } else {
1072 if (GetIOTimingMethod() != IOTIMING_REALISTIC) {
1073 result = (int)pspFileSystem.ReadFile(f->handle, data, validSize);
1074 } else {
1075 result = (int)pspFileSystem.ReadFile(f->handle, data, validSize, us);
1076 }
1077 currentMIPS->InvalidateICache(data_addr, validSize);
1078 return true;
1079 }
1080 } else {
1081 if (size != 0) {
1082 // TODO: For some combinations of bad pointer + size, SCE_KERNEL_ERROR_ILLEGAL_ADDR.
1083 // Seems like only for kernel RAM. For most cases, it really is -1.
1084 result = -1;
1085 } else {
1086 result = 0;
1087 }
1088 return true;
1089 }
1090 } else {
1091 result = error;
1092 return true;
1093 }
1094 }
1095
sceIoRead(int id,u32 data_addr,int size)1096 static u32 sceIoRead(int id, u32 data_addr, int size) {
1097 u32 error;
1098 FileNode *f = __IoGetFd(id, error);
1099 if (id > 2 && f != NULL) {
1100 if (!__KernelIsDispatchEnabled()) {
1101 DEBUG_LOG(SCEIO, "sceIoRead(%d, %08x, %x): dispatch disabled", id, data_addr, size);
1102 return SCE_KERNEL_ERROR_CAN_NOT_WAIT;
1103 }
1104 if (__IsInInterrupt()) {
1105 DEBUG_LOG(SCEIO, "sceIoRead(%d, %08x, %x): inside interrupt", id, data_addr, size);
1106 return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;
1107 }
1108 }
1109
1110 int result;
1111 int us;
1112 bool complete = __IoRead(result, id, data_addr, size, us);
1113 if (!complete) {
1114 DEBUG_LOG(SCEIO, "sceIoRead(%d, %08x, %x): deferring result", id, data_addr, size);
1115
1116 __IoSchedSync(f, id, us);
1117 __KernelWaitCurThread(WAITTYPE_IO, id, 0, 0, false, "io read");
1118 f->waitingSyncThreads.push_back(__KernelGetCurThread());
1119 return 0;
1120 } else if (result >= 0) {
1121 DEBUG_LOG(SCEIO, "%x=sceIoRead(%d, %08x, %x)", result, id, data_addr, size);
1122 return hleDelayResult(result, "io read", us);
1123 } else {
1124 WARN_LOG(SCEIO, "sceIoRead(%d, %08x, %x): error %08x", id, data_addr, size, result);
1125 return result;
1126 }
1127 }
1128
sceIoReadAsync(int id,u32 data_addr,int size)1129 static u32 sceIoReadAsync(int id, u32 data_addr, int size) {
1130 u32 error;
1131 FileNode *f = __IoGetFd(id, error);
1132 if (f) {
1133 if (f->asyncBusy()) {
1134 return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_ASYNC_BUSY, "async busy");
1135 }
1136
1137 auto ¶ms = asyncParams[id];
1138 params.op = IoAsyncOp::READ;
1139 params.std.addr = data_addr;
1140 params.std.size = size;
1141 IoStartAsyncThread(id, f);
1142 return hleLogSuccessI(SCEIO, 0);
1143 } else {
1144 return hleLogError(SCEIO, error, "bad file descriptor");
1145 }
1146 }
1147
__IoWrite(int & result,int id,u32 data_addr,int size,int & us)1148 static bool __IoWrite(int &result, int id, u32 data_addr, int size, int &us) {
1149 PROFILE_THIS_SCOPE("io_rw");
1150 // Low estimate, may be improved later from the WriteFile result.
1151 us = size / 100;
1152 if (us < 100) {
1153 us = 100;
1154 }
1155
1156 const void *data_ptr = Memory::GetPointer(data_addr);
1157 const u32 validSize = Memory::ValidSize(data_addr, size);
1158 // Let's handle stdout/stderr specially.
1159 if (id == PSP_STDOUT || id == PSP_STDERR) {
1160 const char *str = (const char *) data_ptr;
1161 const int str_size = size <= 0 ? 0 : (str[validSize - 1] == '\n' ? validSize - 1 : validSize);
1162 INFO_LOG(SCEIO, "%s: %.*s", id == 1 ? "stdout" : "stderr", str_size, str);
1163 result = validSize;
1164 return true;
1165 }
1166 u32 error;
1167 FileNode *f = __IoGetFd(id, error);
1168 if (f) {
1169 if (f->asyncBusy()) {
1170 result = SCE_KERNEL_ERROR_ASYNC_BUSY;
1171 return true;
1172 }
1173 if (!(f->openMode & FILEACCESS_WRITE)) {
1174 result = SCE_KERNEL_ERROR_BADF;
1175 return true;
1176 }
1177 if (size < 0) {
1178 result = SCE_KERNEL_ERROR_ILLEGAL_ADDR;
1179 return true;
1180 }
1181
1182 const std::string tag = "IoWrite/" + IODetermineFilename(f);
1183 NotifyMemInfo(MemBlockFlags::READ, data_addr, size, tag.c_str(), tag.size());
1184
1185 if (f->isTTY) {
1186 const char *str = (const char *)data_ptr;
1187 const int str_size = size <= 0 ? 0 : (str[validSize - 1] == '\n' ? validSize - 1 : validSize);
1188 INFO_LOG(SCEIO, "%s: %.*s", "tty", str_size, str);
1189 result = validSize;
1190 return true;
1191 }
1192
1193 bool useThread = __KernelIsDispatchEnabled() && ioManagerThreadEnabled && size > IO_THREAD_MIN_DATA_SIZE;
1194 if (useThread) {
1195 // If there's a pending operation on this file, wait for it to finish and don't overwrite it.
1196 useThread = !ioManager.HasOperation(f->handle);
1197 if (!useThread) {
1198 ioManager.SyncThread();
1199 }
1200 }
1201 if (useThread) {
1202 AsyncIOEvent ev = IO_EVENT_WRITE;
1203 ev.handle = f->handle;
1204 ev.buf = (u8 *) data_ptr;
1205 ev.bytes = validSize;
1206 ev.invalidateAddr = 0;
1207 ioManager.ScheduleOperation(ev);
1208 return false;
1209 } else {
1210 if (GetIOTimingMethod() != IOTIMING_REALISTIC) {
1211 result = (int)pspFileSystem.WriteFile(f->handle, (u8 *) data_ptr, validSize);
1212 } else {
1213 result = (int)pspFileSystem.WriteFile(f->handle, (u8 *) data_ptr, validSize, us);
1214 }
1215 }
1216 return true;
1217 } else {
1218 ERROR_LOG(SCEIO, "sceIoWrite ERROR: no file open");
1219 result = (s32) error;
1220 return true;
1221 }
1222 }
1223
sceIoWrite(int id,u32 data_addr,int size)1224 static u32 sceIoWrite(int id, u32 data_addr, int size) {
1225 u32 error;
1226 FileNode *f = __IoGetFd(id, error);
1227 if (id > 2 && f != NULL) {
1228 if (!__KernelIsDispatchEnabled()) {
1229 DEBUG_LOG(SCEIO, "sceIoWrite(%d, %08x, %x): dispatch disabled", id, data_addr, size);
1230 return SCE_KERNEL_ERROR_CAN_NOT_WAIT;
1231 }
1232 if (__IsInInterrupt()) {
1233 DEBUG_LOG(SCEIO, "sceIoWrite(%d, %08x, %x): inside interrupt", id, data_addr, size);
1234 return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;
1235 }
1236 }
1237
1238 int result;
1239 int us;
1240 bool complete = __IoWrite(result, id, data_addr, size, us);
1241 if (!complete) {
1242 DEBUG_LOG(SCEIO, "sceIoWrite(%d, %08x, %x): deferring result", id, data_addr, size);
1243
1244 __IoSchedSync(f, id, us);
1245 __KernelWaitCurThread(WAITTYPE_IO, id, 0, 0, false, "io write");
1246 f->waitingSyncThreads.push_back(__KernelGetCurThread());
1247 return 0;
1248 } else if (result >= 0) {
1249 DEBUG_LOG(SCEIO, "%x=sceIoWrite(%d, %08x, %x)", result, id, data_addr, size);
1250 if (__KernelIsDispatchEnabled()) {
1251 // If we wrote to stdout, return an error (even though we did log it) rather than delaying.
1252 // On actual hardware, it would just return this... we just want the log output.
1253 if (__IsInInterrupt()) {
1254 return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;
1255 }
1256 return hleDelayResult(result, "io write", us);
1257 } else {
1258 return result;
1259 }
1260 } else {
1261 WARN_LOG(SCEIO, "sceIoWrite(%d, %08x, %x): error %08x", id, data_addr, size, result);
1262 return result;
1263 }
1264 }
1265
sceIoWriteAsync(int id,u32 data_addr,int size)1266 static u32 sceIoWriteAsync(int id, u32 data_addr, int size) {
1267 u32 error;
1268 FileNode *f = __IoGetFd(id, error);
1269 if (f) {
1270 if (f->asyncBusy()) {
1271 return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_ASYNC_BUSY, "async busy");
1272 }
1273
1274 auto ¶ms = asyncParams[id];
1275 params.op = IoAsyncOp::WRITE;
1276 params.std.addr = data_addr;
1277 params.std.size = size;
1278 IoStartAsyncThread(id, f);
1279 return hleLogSuccessI(SCEIO, 0);
1280 } else {
1281 return hleLogError(SCEIO, error, "bad file descriptor");
1282 }
1283 }
1284
sceIoGetDevType(int id)1285 static u32 sceIoGetDevType(int id) {
1286 if (id == PSP_STDOUT || id == PSP_STDERR || id == PSP_STDIN) {
1287 DEBUG_LOG(SCEIO, "sceIoGetDevType(%d)", id);
1288 return (u32)PSPDevType::FILE;
1289 }
1290
1291 u32 error;
1292 FileNode *f = __IoGetFd(id, error);
1293 int result;
1294 if (f) {
1295 // TODO: When would this return PSP_DEV_TYPE_ALIAS?
1296 WARN_LOG(SCEIO, "sceIoGetDevType(%d - %s)", id, f->fullpath.c_str());
1297 if (f->isTTY)
1298 result = (u32)PSPDevType::FILE;
1299 result = (u32)pspFileSystem.DevType(f->handle) & (u32)PSPDevType::EMU_MASK;
1300 } else {
1301 ERROR_LOG(SCEIO, "sceIoGetDevType: unknown id %d", id);
1302 result = SCE_KERNEL_ERROR_BADF;
1303 }
1304
1305 return result;
1306 }
1307
sceIoCancel(int id)1308 static u32 sceIoCancel(int id)
1309 {
1310 u32 error;
1311 FileNode *f = __IoGetFd(id, error);
1312 if (f) {
1313 // It seems like this is unsupported for UMDs and memory sticks, based on tests.
1314 return hleReportError(SCEIO, SCE_KERNEL_ERROR_UNSUP, "unimplemented or unsupported");
1315 } else {
1316 return hleLogError(SCEIO, SCE_KERNEL_ERROR_BADF, "invalid fd");
1317 }
1318 }
1319
npdrmLseek(FileNode * f,s32 where,FileMove whence)1320 static u32 npdrmLseek(FileNode *f, s32 where, FileMove whence)
1321 {
1322 u32 newPos, blockPos;
1323
1324 if(whence==FILEMOVE_BEGIN){
1325 newPos = where;
1326 }else if(whence==FILEMOVE_CURRENT){
1327 newPos = f->pgdInfo->file_offset+where;
1328 }else{
1329 newPos = f->pgdInfo->data_size+where;
1330 }
1331
1332 if (newPos > f->pgdInfo->data_size)
1333 return -EINVAL;
1334
1335 f->pgdInfo->file_offset = newPos;
1336 blockPos = newPos&~(f->pgdInfo->block_size-1);
1337 pspFileSystem.SeekFile(f->handle, (s32)f->pgdInfo->data_offset+blockPos, whence);
1338
1339 return newPos;
1340 }
1341
__IoLseekDest(FileNode * f,s64 offset,int whence,FileMove & seek)1342 static s64 __IoLseekDest(FileNode *f, s64 offset, int whence, FileMove &seek) {
1343 PROFILE_THIS_SCOPE("io_rw");
1344 seek = FILEMOVE_BEGIN;
1345
1346 // Let's make sure this isn't incorrect mid-operation.
1347 if (ioManager.HasOperation(f->handle)) {
1348 ioManager.SyncThread();
1349 }
1350
1351 s64 newPos = 0;
1352 switch (whence) {
1353 case 0:
1354 newPos = offset;
1355 break;
1356 case 1:
1357 newPos = pspFileSystem.GetSeekPos(f->handle) + offset;
1358 seek = FILEMOVE_CURRENT;
1359 break;
1360 case 2:
1361 newPos = f->info.size + offset;
1362 seek = FILEMOVE_END;
1363 break;
1364 default:
1365 return (s32)SCE_KERNEL_ERROR_INVAL;
1366 }
1367
1368 // Yes, -1 is the correct return code for this case.
1369 if (newPos < 0)
1370 return -1;
1371 return newPos;
1372 }
1373
__IoLseek(SceUID id,s64 offset,int whence)1374 static s64 __IoLseek(SceUID id, s64 offset, int whence) {
1375 u32 error;
1376 FileNode *f = __IoGetFd(id, error);
1377 if (f) {
1378 if (f->asyncBusy()) {
1379 WARN_LOG(SCEIO, "sceIoLseek*(%d, %llx, %i): async busy", id, offset, whence);
1380 return SCE_KERNEL_ERROR_ASYNC_BUSY;
1381 }
1382 FileMove seek;
1383 s64 newPos = __IoLseekDest(f, offset, whence, seek);
1384
1385 if(f->npdrm)
1386 return npdrmLseek(f, (s32)offset, seek);
1387
1388 if (newPos < 0)
1389 return newPos;
1390 return pspFileSystem.SeekFile(f->handle, (s32) offset, seek);
1391 } else {
1392 return (s32) error;
1393 }
1394 }
1395
sceIoLseek(int id,s64 offset,int whence)1396 static s64 sceIoLseek(int id, s64 offset, int whence) {
1397 s64 result = __IoLseek(id, offset, whence);
1398 if (result >= 0 || result == -1) {
1399 DEBUG_LOG(SCEIO, "%lli = sceIoLseek(%d, %llx, %i)", result, id, offset, whence);
1400 // Educated guess at timing.
1401 hleEatCycles(1400);
1402 hleReSchedule("io seek");
1403 return result;
1404 } else {
1405 return hleLogError(SCEIO, result, "bad file descriptor");
1406 }
1407 }
1408
sceIoLseek32(int id,int offset,int whence)1409 static u32 sceIoLseek32(int id, int offset, int whence) {
1410 s32 result = (s32) __IoLseek(id, offset, whence);
1411 if (result >= 0 || result == -1) {
1412 DEBUG_LOG(SCEIO, "%i = sceIoLseek32(%d, %x, %i)", result, id, offset, whence);
1413 // Educated guess at timing.
1414 hleEatCycles(1400);
1415 hleReSchedule("io seek");
1416 return result;
1417 } else {
1418 return hleLogError(SCEIO, result, "bad file descriptor");
1419 }
1420 }
1421
sceIoLseekAsync(int id,s64 offset,int whence)1422 static u32 sceIoLseekAsync(int id, s64 offset, int whence) {
1423 u32 error;
1424 FileNode *f = __IoGetFd(id, error);
1425 if (f) {
1426 if (whence < 0 || whence > 2) {
1427 return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_INVAL, "invalid whence");
1428 }
1429 if (f->asyncBusy()) {
1430 return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_ASYNC_BUSY, "async busy");
1431 }
1432
1433 auto ¶ms = asyncParams[id];
1434 params.op = IoAsyncOp::SEEK;
1435 params.seek.pos = offset;
1436 params.seek.whence = whence;
1437 IoStartAsyncThread(id, f);
1438 return hleLogSuccessI(SCEIO, 0);
1439 } else {
1440 return hleLogError(SCEIO, error, "bad file descriptor");
1441 }
1442 return 0;
1443 }
1444
sceIoLseek32Async(int id,int offset,int whence)1445 static u32 sceIoLseek32Async(int id, int offset, int whence) {
1446 u32 error;
1447 FileNode *f = __IoGetFd(id, error);
1448 if (f) {
1449 if (whence < 0 || whence > 2) {
1450 return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_INVAL, "invalid whence");
1451 }
1452 if (f->asyncBusy()) {
1453 return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_ASYNC_BUSY, "async busy");
1454 }
1455
1456 auto ¶ms = asyncParams[id];
1457 params.op = IoAsyncOp::SEEK;
1458 params.seek.pos = offset;
1459 params.seek.whence = whence;
1460 IoStartAsyncThread(id, f);
1461 return hleLogSuccessI(SCEIO, 0);
1462 } else {
1463 return hleLogError(SCEIO, error, "bad file descriptor");
1464 }
1465 return 0;
1466 }
1467
__IoOpen(int & error,const char * filename,int flags,int mode)1468 static FileNode *__IoOpen(int &error, const char *filename, int flags, int mode) {
1469 int access = FILEACCESS_NONE;
1470 if (flags & PSP_O_RDONLY)
1471 access |= FILEACCESS_READ;
1472 if (flags & PSP_O_WRONLY)
1473 access |= FILEACCESS_WRITE;
1474 if (flags & PSP_O_APPEND)
1475 access |= FILEACCESS_APPEND;
1476 if (flags & PSP_O_CREAT)
1477 access |= FILEACCESS_CREATE;
1478 if (flags & PSP_O_TRUNC)
1479 access |= FILEACCESS_TRUNCATE;
1480 if (flags & PSP_O_EXCL)
1481 access |= FILEACCESS_EXCL;
1482
1483 PSPFileInfo info;
1484 int h = -1;
1485 bool isTTY = false;
1486 // TODO: Technically, tty1, etc. too and space doesn't matter.
1487 if (startsWithNoCase(filename, "tty0:")) {
1488 info.name = filename;
1489 info.access = 0777;
1490 info.exists = true;
1491
1492 isTTY = true;
1493 } else {
1494 info = pspFileSystem.GetFileInfo(filename);
1495
1496 h = pspFileSystem.OpenFile(filename, (FileAccess)access);
1497 if (h < 0) {
1498 error = h;
1499 return nullptr;
1500 }
1501 }
1502 error = 0;
1503
1504 FileNode *f = new FileNode();
1505 kernelObjects.Create(f);
1506 f->handle = h;
1507 f->fullpath = filename;
1508 f->asyncResult = h;
1509 f->info = info;
1510 f->openMode = access;
1511 f->isTTY = isTTY;
1512
1513 f->npdrm = (flags & PSP_O_NPDRM)? true: false;
1514 f->pgd_offset = 0;
1515
1516 return f;
1517 }
1518
sceIoOpen(const char * filename,int flags,int mode)1519 static u32 sceIoOpen(const char *filename, int flags, int mode) {
1520 hleEatCycles(18000);
1521
1522 if (!__KernelIsDispatchEnabled()) {
1523 hleEatCycles(48000);
1524 return hleLogError(SCEIO, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled");
1525 }
1526
1527 int error;
1528 FileNode *f = __IoOpen(error, filename, flags, mode);
1529 if (!f) {
1530 _assert_(error != 0);
1531 if (error == (int)SCE_KERNEL_ERROR_NOCWD) {
1532 // TODO: Timing is not accurate.
1533 return hleLogError(SCEIO, hleDelayResult(error, "file opened", 10000), "no current working directory");
1534 } else if (error == (int)SCE_KERNEL_ERROR_NODEV) {
1535 return hleLogError(SCEIO, error, "device not found");
1536 } else if (error == (int)SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND) {
1537 // UMD: Varies between 5-10ms, could take longer if disc spins up.
1538 // TODO: Bad filename at root (disc0:/no.exist) should take ~200us.
1539 // Card: Path depth matters, but typically between 10-13ms on a standard Pro Duo.
1540 // TODO: If a UMD and spun down, this can easily take 1s+.
1541 int delay = pspFileSystem.FlagsFromFilename(filename) & FileSystemFlags::UMD ? 6000 : 10000;
1542 return hleLogWarning(SCEIO, hleDelayResult(error, "file opened", delay), "file not found");
1543 } else {
1544 return hleLogError(SCEIO, hleDelayResult(error, "file opened", 10000));
1545 }
1546 }
1547
1548 int id = __IoAllocFd(f);
1549 if (id < 0) {
1550 kernelObjects.Destroy<FileNode>(f->GetUID());
1551 return hleLogError(SCEIO, hleDelayResult(id, "file opened", 1000), "out of fds");
1552 } else {
1553 asyncParams[id].priority = asyncDefaultPriority;
1554 IFileSystem *sys = pspFileSystem.GetSystemFromFilename(filename);
1555 if (sys && !f->isTTY && (sys->DevType(f->handle) & (PSPDevType::BLOCK | PSPDevType::EMU_LBN))) {
1556 // These are fast to open, no delay or even rescheduling happens.
1557 return hleLogSuccessI(SCEIO, id);
1558 }
1559 // UMD: Speed varies from 1-6ms.
1560 // Card: Path depth matters, but typically between 10-13ms on a standard Pro Duo.
1561 int delay = pspFileSystem.FlagsFromFilename(filename) & FileSystemFlags::UMD ? 4000 : 10000;
1562 return hleLogSuccessI(SCEIO, hleDelayResult(id, "file opened", delay));
1563 }
1564 }
1565
sceIoClose(int id)1566 static u32 sceIoClose(int id) {
1567 u32 error;
1568 DEBUG_LOG(SCEIO, "sceIoClose(%d)", id);
1569 __IoFreeFd(id, error);
1570 // Timing is not accurate, aiming low for now.
1571 return hleDelayResult(error, "file closed", 100);
1572 }
1573
sceIoRemove(const char * filename)1574 static u32 sceIoRemove(const char *filename) {
1575 DEBUG_LOG(SCEIO, "sceIoRemove(%s)", filename);
1576
1577 // TODO: This timing isn't necessarily accurate, low end for now.
1578 if(!pspFileSystem.GetFileInfo(filename).exists)
1579 return hleDelayResult(SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND, "file removed", 100);
1580
1581 pspFileSystem.RemoveFile(filename);
1582 return hleDelayResult(0, "file removed", 100);
1583 }
1584
sceIoMkdir(const char * dirname,int mode)1585 static u32 sceIoMkdir(const char *dirname, int mode) {
1586 DEBUG_LOG(SCEIO, "sceIoMkdir(%s, %i)", dirname, mode);
1587 // TODO: Improve timing.
1588 if (pspFileSystem.MkDir(dirname))
1589 return hleDelayResult(0, "mkdir", 1000);
1590 else
1591 return hleDelayResult(SCE_KERNEL_ERROR_ERRNO_FILE_ALREADY_EXISTS, "mkdir", 1000);
1592 }
1593
sceIoRmdir(const char * dirname)1594 static u32 sceIoRmdir(const char *dirname) {
1595 DEBUG_LOG(SCEIO, "sceIoRmdir(%s)", dirname);
1596 // TODO: Improve timing.
1597 if (pspFileSystem.RmDir(dirname))
1598 return hleDelayResult(0, "rmdir", 1000);
1599 else
1600 return hleDelayResult(SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND, "rmdir", 1000);
1601 }
1602
sceIoSync(const char * devicename,int flag)1603 static u32 sceIoSync(const char *devicename, int flag) {
1604 DEBUG_LOG(SCEIO, "UNIMPL sceIoSync(%s, %i)", devicename, flag);
1605 return 0;
1606 }
1607
1608 struct DeviceSize {
1609 u32_le maxClusters;
1610 u32_le freeClusters;
1611 u32_le maxSectors;
1612 u32_le sectorSize;
1613 u32_le sectorCount;
1614 };
1615
sceIoDevctl(const char * name,int cmd,u32 argAddr,int argLen,u32 outPtr,int outLen)1616 static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 outPtr, int outLen) {
1617 if (strcmp(name, "emulator:")) {
1618 DEBUG_LOG(SCEIO,"sceIoDevctl(\"%s\", %08x, %08x, %i, %08x, %i)", name, cmd, argAddr, argLen, outPtr, outLen);
1619 }
1620
1621 // UMD checks
1622 switch (cmd) {
1623 case 0x01F20001:
1624 // Get UMD disc type
1625 if (Memory::IsValidAddress(outPtr) && outLen >= 8) {
1626 Memory::Write_U32(0x10, outPtr + 4); // Always return game disc (if present)
1627 return 0;
1628 } else {
1629 return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
1630 }
1631 break;
1632 case 0x01F20002:
1633 // Get UMD current LBA
1634 if (Memory::IsValidAddress(outPtr) && outLen >= 4) {
1635 Memory::Write_U32(0x10, outPtr); // Assume first sector
1636 return 0;
1637 } else {
1638 return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
1639 }
1640 break;
1641 case 0x01F20003:
1642 if (Memory::IsValidAddress(argAddr) && argLen >= 4) {
1643 PSPFileInfo info = pspFileSystem.GetFileInfo("umd1:");
1644 Memory::Write_U32((u32) (info.size) - 1, outPtr);
1645 return 0;
1646 } else {
1647 return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
1648 }
1649 break;
1650 case 0x01F100A3:
1651 // Seek UMD disc (raw)
1652 if (Memory::IsValidAddress(argAddr) && argLen >= 4) {
1653 return hleDelayResult(0, "dev seek", 100);
1654 } else {
1655 return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
1656 }
1657 break;
1658 case 0x01F100A4:
1659 // Prepare UMD data into cache.
1660 if (Memory::IsValidAddress(argAddr) && argLen >= 4) {
1661 return 0;
1662 } else {
1663 return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
1664 }
1665 break;
1666 case 0x01F300A5:
1667 // Prepare UMD data into cache and get status
1668 if (Memory::IsValidAddress(argAddr) && argLen >= 4) {
1669 Memory::Write_U32(1, outPtr); // Status (unitary index of the requested read, greater or equal to 1)
1670 return 0;
1671 } else {
1672 return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
1673 }
1674 break;
1675 case 0x01F300A7:
1676 // Wait for the UMD data cache thread
1677 if (Memory::IsValidAddress(argAddr) && argLen >= 4) {
1678 // TODO :
1679 // Place the calling thread in wait state
1680 return 0;
1681 } else {
1682 return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
1683 }
1684 break;
1685 case 0x01F300A8:
1686 // Poll the UMD data cache thread
1687 if (Memory::IsValidAddress(argAddr) && argLen >= 4) {
1688 // 0 - UMD data cache thread has finished
1689 // 0x10 - UMD data cache thread is waiting
1690 // 0x20 - UMD data cache thread is running
1691 return 0; // Return finished
1692 } else {
1693 return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
1694 }
1695 break;
1696 case 0x01F300A9:
1697 // Cancel the UMD data cache thread
1698 if (Memory::IsValidAddress(argAddr) && argLen >= 4) {
1699 // TODO :
1700 // Wake up the thread waiting for the UMD data cache handling.
1701 return 0;
1702 } else {
1703 return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
1704 }
1705 break;
1706 // TODO: What do these do? Seem to require a u32 in, no output.
1707 case 0x01F100A6:
1708 case 0x01F100A8:
1709 case 0x01F100A9:
1710 ERROR_LOG_REPORT(SCEIO, "UNIMPL sceIoDevctl(\"%s\", %08x, %08x, %i, %08x, %i)", name, cmd, argAddr, argLen, outPtr, outLen);
1711 return 0;
1712 }
1713
1714 // This should really send it on to a FileSystem implementation instead.
1715
1716 if (!strcmp(name, "mscmhc0:") || !strcmp(name, "ms0:") || !strcmp(name, "memstick:"))
1717 {
1718 // MemoryStick checks
1719 switch (cmd) {
1720 case 0x02025801:
1721 // Check the MemoryStick's driver status (mscmhc0: only.)
1722 if (Memory::IsValidAddress(outPtr) && outLen >= 4) {
1723 if (MemoryStick_State() == PSP_MEMORYSTICK_STATE_INSERTED) {
1724 // 1 = not inserted (ready), 4 = inserted
1725 Memory::Write_U32(PSP_MEMORYSTICK_STATE_DEVICE_INSERTED, outPtr);
1726 } else {
1727 Memory::Write_U32(PSP_MEMORYSTICK_STATE_DRIVER_READY, outPtr);
1728 }
1729 return 0;
1730 } else {
1731 return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
1732 }
1733 break;
1734 case 0x02015804:
1735 // Register MemoryStick's insert/eject callback (mscmhc0)
1736 if (Memory::IsValidAddress(argAddr) && outPtr == 0 && argLen >= 4) {
1737 u32 cbId = Memory::Read_U32(argAddr);
1738 int type = -1;
1739 kernelObjects.GetIDType(cbId, &type);
1740
1741 if (memStickCallbacks.size() < 32 && type == SCE_KERNEL_TMID_Callback) {
1742 memStickCallbacks.push_back(cbId);
1743 if (MemoryStick_State() == PSP_MEMORYSTICK_STATE_INSERTED) {
1744 // Only fired immediately if the card is currently inserted.
1745 // Values observed:
1746 // * 1 = Memory stick inserted
1747 // * 2 = Memory stick removed
1748 // * 4 = Memory stick mounting? (followed by a 1 about 500ms later)
1749 DEBUG_LOG(SCEIO, "sceIoDevctl: Memstick callback %i registered, notifying immediately", cbId);
1750 __KernelNotifyCallback(cbId, MemoryStick_State());
1751 } else {
1752 DEBUG_LOG(SCEIO, "sceIoDevctl: Memstick callback %i registered", cbId);
1753 }
1754 return 0;
1755 } else {
1756 return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
1757 }
1758 } else {
1759 return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
1760 }
1761 break;
1762 case 0x02015805:
1763 // Unregister MemoryStick's insert/eject callback (mscmhc0)
1764 if (Memory::IsValidAddress(argAddr) && argLen >= 4) {
1765 SceUID cbId = Memory::Read_U32(argAddr);
1766 size_t slot = (size_t)-1;
1767 // We want to only remove one at a time.
1768 for (size_t i = 0; i < memStickCallbacks.size(); ++i) {
1769 if (memStickCallbacks[i] == cbId) {
1770 slot = i;
1771 break;
1772 }
1773 }
1774
1775 if (slot != (size_t)-1) {
1776 memStickCallbacks.erase(memStickCallbacks.begin() + slot);
1777 DEBUG_LOG(SCEIO, "sceIoDevctl: Unregistered memstick callback %i", cbId);
1778 return 0;
1779 } else {
1780 return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
1781 }
1782 } else {
1783 return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
1784 }
1785 break;
1786 case 0x02025806:
1787 // Check if the device is inserted (mscmhc0)
1788 if (Memory::IsValidAddress(outPtr) && outLen >= 4) {
1789 // 1 = Inserted.
1790 // 2 = Not inserted.
1791 Memory::Write_U32(MemoryStick_State(), outPtr);
1792 return 0;
1793 } else {
1794 return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
1795 }
1796 break;
1797 case 0x02425818:
1798 // Get MS capacity (fatms0).
1799 if (MemoryStick_State() != PSP_MEMORYSTICK_STATE_INSERTED) {
1800 return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND;
1801 }
1802 // TODO: Pretend we have a 2GB memory stick? Should we check MemoryStick_FreeSpace?
1803 if (Memory::IsValidAddress(argAddr) && argLen >= 4) { // NOTE: not outPtr
1804 u32 pointer = Memory::Read_U32(argAddr);
1805 u32 sectorSize = 0x200;
1806 u32 memStickSectorSize = 32 * 1024;
1807 u32 sectorCount = memStickSectorSize / sectorSize;
1808 u64 freeSize = 1 * 1024 * 1024 * 1024;
1809 DeviceSize deviceSize;
1810 deviceSize.maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount));
1811 deviceSize.freeClusters = deviceSize.maxClusters;
1812 deviceSize.maxSectors = deviceSize.maxClusters;
1813 deviceSize.sectorSize = sectorSize;
1814 deviceSize.sectorCount = sectorCount;
1815 Memory::WriteStruct(pointer, &deviceSize);
1816 return 0;
1817 } else {
1818 return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
1819 }
1820 break;
1821 case 0x02425824:
1822 // Check if write protected
1823 if (MemoryStick_State() != PSP_MEMORYSTICK_STATE_INSERTED) {
1824 return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND;
1825 }
1826 if (Memory::IsValidAddress(outPtr) && outLen == 4) {
1827 Memory::Write_U32(0, outPtr);
1828 return 0;
1829 } else {
1830 ERROR_LOG(SCEIO, "Failed 0x02425824 fat");
1831 return -1;
1832 }
1833 break;
1834 }
1835 }
1836
1837 if (!strcmp(name, "fatms0:"))
1838 {
1839 switch (cmd) {
1840 case 0x0240d81e:
1841 // TODO: Invalidate MS driver file table cache (nop)
1842 break;
1843 case 0x02415821:
1844 // MScmRegisterMSInsertEjectCallback
1845 if (Memory::IsValidAddress(argAddr) && argLen >= 4) {
1846 u32 cbId = Memory::Read_U32(argAddr);
1847 int type = -1;
1848 kernelObjects.GetIDType(cbId, &type);
1849
1850 if (memStickFatCallbacks.size() < 32 && type == SCE_KERNEL_TMID_Callback) {
1851 memStickFatCallbacks.push_back(cbId);
1852 if (MemoryStick_State() == PSP_MEMORYSTICK_STATE_INSERTED) {
1853 // Only fired immediately if the card is currently inserted.
1854 // Values observed:
1855 // * 1 = Memory stick inserted
1856 // * 2 = Memory stick removed
1857 DEBUG_LOG(SCEIO, "sceIoDevCtl: Memstick FAT callback %i registered, notifying immediately", cbId);
1858 __KernelNotifyCallback(cbId, MemoryStick_FatState());
1859 } else {
1860 DEBUG_LOG(SCEIO, "sceIoDevCtl: Memstick FAT callback %i registered", cbId);
1861 }
1862 return 0;
1863 } else {
1864 return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
1865 }
1866 } else {
1867 return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
1868 }
1869 break;
1870 case 0x02415822:
1871 // MScmUnregisterMSInsertEjectCallback
1872 if (Memory::IsValidAddress(argAddr) && argLen >= 4) {
1873 SceUID cbId = Memory::Read_U32(argAddr);
1874 size_t slot = (size_t)-1;
1875 // We want to only remove one at a time.
1876 for (size_t i = 0; i < memStickFatCallbacks.size(); ++i) {
1877 if (memStickFatCallbacks[i] == cbId) {
1878 slot = i;
1879 break;
1880 }
1881 }
1882
1883 if (slot != (size_t)-1) {
1884 memStickFatCallbacks.erase(memStickFatCallbacks.begin() + slot);
1885 DEBUG_LOG(SCEIO, "sceIoDevCtl: Unregistered memstick FAT callback %i", cbId);
1886 return 0;
1887 } else {
1888 return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
1889 }
1890 }
1891 break;
1892 case 0x02415823:
1893 // Set FAT as enabled
1894 if (Memory::IsValidAddress(argAddr) && argLen == 4) {
1895 MemoryStick_SetFatState((MemStickFatState)Memory::Read_U32(argAddr));
1896 return 0;
1897 } else {
1898 ERROR_LOG(SCEIO, "Failed 0x02415823 fat");
1899 return -1;
1900 }
1901 break;
1902 case 0x02425823:
1903 // Check if FAT enabled
1904 // If the values added together are >= 0x80000000, or less than outPtr, invalid address.
1905 if (((int)outPtr + outLen) < (int)outPtr) {
1906 ERROR_LOG(SCEIO, "sceIoDevctl: fatms0: 0x02425823 command, bad address");
1907 return SCE_KERNEL_ERROR_ILLEGAL_ADDR;
1908 } else if (!Memory::IsValidAddress(outPtr)) {
1909 // Technically, only checks for NULL, crashes for many bad addresses.
1910 ERROR_LOG(SCEIO, "sceIoDevctl: fatms0: 0x02425823 command, no output address");
1911 return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
1912 } else {
1913 // Does not care about outLen, even if it's 0.
1914 // Note: writes 1 when inserted, 0 when not inserted.
1915 Memory::Write_U32(MemoryStick_FatState(), outPtr);
1916 return hleDelayResult(0, "check fat state", cyclesToUs(23500));
1917 }
1918 break;
1919 case 0x02425824:
1920 // Check if write protected
1921 if (MemoryStick_State() != PSP_MEMORYSTICK_STATE_INSERTED) {
1922 return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND;
1923 }
1924 if (Memory::IsValidAddress(outPtr) && outLen == 4) {
1925 Memory::Write_U32(0, outPtr);
1926 return 0;
1927 } else {
1928 ERROR_LOG(SCEIO, "Failed 0x02425824 fat");
1929 return -1;
1930 }
1931 break;
1932 case 0x02425818:
1933 // Get MS capacity (fatms0).
1934 if (MemoryStick_State() != PSP_MEMORYSTICK_STATE_INSERTED) {
1935 return SCE_KERNEL_ERROR_ERRNO_DEVICE_NOT_FOUND;
1936 }
1937 // TODO: Pretend we have a 2GB memory stick? Should we check MemoryStick_FreeSpace?
1938 if (Memory::IsValidAddress(argAddr) && argLen >= 4) { // NOTE: not outPtr
1939 u32 pointer = Memory::Read_U32(argAddr);
1940 u32 sectorSize = 0x200;
1941 u32 memStickSectorSize = 32 * 1024;
1942 u32 sectorCount = memStickSectorSize / sectorSize;
1943 u64 freeSize = 1 * 1024 * 1024 * 1024;
1944 DeviceSize deviceSize;
1945 deviceSize.maxClusters = (u32)((freeSize * 95 / 100) / (sectorSize * sectorCount));
1946 deviceSize.freeClusters = deviceSize.maxClusters;
1947 deviceSize.maxSectors = deviceSize.maxClusters;
1948 deviceSize.sectorSize = sectorSize;
1949 deviceSize.sectorCount = sectorCount;
1950 Memory::WriteStruct(pointer, &deviceSize);
1951 return 0;
1952 } else {
1953 return ERROR_MEMSTICK_DEVCTL_BAD_PARAMS;
1954 }
1955 break;
1956 }
1957 }
1958
1959 if (!strcmp(name, "kemulator:") || !strcmp(name, "emulator:"))
1960 {
1961 // Emulator special tricks!
1962 switch (cmd) {
1963 case 1: // EMULATOR_DEVCTL__GET_HAS_DISPLAY
1964 if (Memory::IsValidAddress(outPtr))
1965 Memory::Write_U32(0, outPtr); // TODO: Make a headless mode for running tests!
1966 return 0;
1967 case 2: // EMULATOR_DEVCTL__SEND_OUTPUT
1968 {
1969 std::string data(Memory::GetCharPointer(argAddr), argLen);
1970 if (PSP_CoreParameter().printfEmuLog) {
1971 host->SendDebugOutput(data);
1972 } else {
1973 if (PSP_CoreParameter().collectEmuLog) {
1974 *PSP_CoreParameter().collectEmuLog += data;
1975 } else {
1976 DEBUG_LOG(SCEIO, "%s", data.c_str());
1977 }
1978 }
1979 return 0;
1980 }
1981 case 3: // EMULATOR_DEVCTL__IS_EMULATOR
1982 if (Memory::IsValidAddress(outPtr))
1983 Memory::Write_U32(1, outPtr);
1984 return 0;
1985 case 4: // EMULATOR_DEVCTL__VERIFY_STATE
1986 // Note that this is async, and makes sure the save state matches up.
1987 SaveState::Verify();
1988 // TODO: Maybe save/load to a file just to be sure?
1989 return 0;
1990
1991 case 0x20: // EMULATOR_DEVCTL__EMIT_SCREENSHOT
1992 {
1993 PSPPointer<u8> topaddr;
1994 u32 linesize, pixelFormat;
1995
1996 __DisplayGetFramebuf(&topaddr, &linesize, &pixelFormat, 0);
1997 // TODO: Convert based on pixel format / mode / something?
1998 host->SendDebugScreenshot(topaddr, linesize, 272);
1999 return 0;
2000 }
2001 }
2002
2003 ERROR_LOG(SCEIO, "sceIoDevCtl: UNKNOWN PARAMETERS");
2004
2005 return 0;
2006 }
2007
2008 //089c6d1c weird branch
2009 /*
2010 089c6bdc ]: HLE: sceKernelCreateCallback(name= MemoryStick Detection ,entry= 089c7484 ) (z_un_089c6bc4)
2011 089c6c40 ]: HLE: sceKernelCreateCallback(name= MemoryStick Assignment ,entry= 089c7534 ) (z_un_089c6bc4)
2012 */
2013 ERROR_LOG_REPORT(SCEIO, "UNIMPL sceIoDevctl(\"%s\", %08x, %08x, %i, %08x, %i)", name, cmd, argAddr, argLen, outPtr, outLen);
2014 return SCE_KERNEL_ERROR_UNSUP;
2015 }
2016
sceIoRename(const char * from,const char * to)2017 static u32 sceIoRename(const char *from, const char *to) {
2018 DEBUG_LOG(SCEIO, "sceIoRename(%s, %s)", from, to);
2019
2020 // TODO: Timing isn't terribly accurate.
2021 if (!pspFileSystem.GetFileInfo(from).exists)
2022 return hleDelayResult(SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND, "file renamed", 1000);
2023
2024 int result = pspFileSystem.RenameFile(from, to);
2025 if (result < 0)
2026 WARN_LOG(SCEIO, "Could not move %s to %s", from, to);
2027 return hleDelayResult(result, "file renamed", 1000);
2028 }
2029
sceIoChdir(const char * dirname)2030 static u32 sceIoChdir(const char *dirname) {
2031 DEBUG_LOG(SCEIO, "sceIoChdir(%s)", dirname);
2032 return pspFileSystem.ChDir(dirname);
2033 }
2034
sceIoChangeAsyncPriority(int id,int priority)2035 static int sceIoChangeAsyncPriority(int id, int priority) {
2036 // priority = -1 is valid,means the current thread'priority
2037 if (priority != -1 && (priority < 0x08 || priority > 0x77)) {
2038 return hleLogError(SCEIO, SCE_KERNEL_ERROR_ILLEGAL_PRIORITY, "illegal priority %d", priority);
2039 }
2040
2041 if (id == -1) {
2042 asyncDefaultPriority = priority;
2043 return hleLogSuccessI(SCEIO, 0);
2044 }
2045
2046 u32 error;
2047 FileNode *f = __IoGetFd(id, error);
2048 if (!f) {
2049 return hleLogError(SCEIO, error, "bad file descriptor");
2050 }
2051
2052 if (asyncThreads[id] && !asyncThreads[id]->Stopped()) {
2053 if (priority == -1) {
2054 priority = KernelCurThreadPriority();
2055 }
2056 asyncThreads[id]->ChangePriority(priority);
2057 }
2058
2059 asyncParams[id].priority = priority;
2060 return hleLogSuccessI(SCEIO, 0);
2061 }
2062
sceIoCloseAsync(int id)2063 static int sceIoCloseAsync(int id) {
2064 u32 error;
2065 FileNode *f = __IoGetFd(id, error);
2066 if (!f) {
2067 return hleLogError(SCEIO, error, "bad file descriptor");
2068 }
2069 if (f->asyncBusy()) {
2070 return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_ASYNC_BUSY, "async busy");
2071 }
2072
2073 f->closePending = true;
2074
2075 auto ¶ms = asyncParams[id];
2076 params.op = IoAsyncOp::CLOSE;
2077 IoStartAsyncThread(id, f);
2078 return hleLogSuccessI(SCEIO, 0);
2079 }
2080
sceIoSetAsyncCallback(int id,u32 clbckId,u32 clbckArg)2081 static u32 sceIoSetAsyncCallback(int id, u32 clbckId, u32 clbckArg)
2082 {
2083 DEBUG_LOG(SCEIO, "sceIoSetAsyncCallback(%d, %i, %08x)", id, clbckId, clbckArg);
2084
2085 u32 error;
2086 FileNode *f = __IoGetFd(id, error);
2087 if (f)
2088 {
2089 f->callbackID = clbckId;
2090 f->callbackArg = clbckArg;
2091 return 0;
2092 }
2093 else
2094 {
2095 return error;
2096 }
2097 }
2098
sceIoOpenAsync(const char * filename,int flags,int mode)2099 static u32 sceIoOpenAsync(const char *filename, int flags, int mode) {
2100 hleEatCycles(18000);
2101
2102 // TOOD: Use an internal method so as not to pollute the log?
2103 // Intentionally does not work when interrupts disabled.
2104 if (!__KernelIsDispatchEnabled())
2105 sceKernelResumeDispatchThread(1);
2106
2107 int error;
2108 FileNode *f = __IoOpen(error, filename, flags, mode);
2109
2110 // We have to return an fd here, which may have been destroyed when we reach Wait if it failed.
2111 if (f == nullptr) {
2112 _assert_(error != 0);
2113 if (error == SCE_KERNEL_ERROR_NODEV)
2114 return hleLogError(SCEIO, error, "device not found");
2115
2116 f = new FileNode();
2117 kernelObjects.Create(f);
2118 f->handle = -1;
2119 f->fullpath = filename;
2120 f->closePending = true;
2121 }
2122
2123 // We need an fd even for errors, since it's async.
2124 int fd = __IoAllocFd(f);
2125 if (fd < 0) {
2126 kernelObjects.Destroy<FileNode>(f->GetUID());
2127 return hleLogError(SCEIO, hleDelayResult(fd, "file opened", 1000), "out of fds");
2128 }
2129
2130 auto ¶ms = asyncParams[fd];
2131 params.op = IoAsyncOp::OPEN;
2132 params.priority = asyncDefaultPriority;
2133 params.open.filenameAddr = PARAM(0);
2134 params.open.flags = flags;
2135 params.open.mode = mode;
2136 IoStartAsyncThread(fd, f);
2137
2138 if (error != 0) {
2139 f->asyncResult = (s64)error;
2140 return hleLogError(SCEIO, fd, "file not found");
2141 }
2142
2143 f->asyncResult = fd;
2144 return hleLogSuccessI(SCEIO, fd);
2145 }
2146
sceIoGetAsyncStat(int id,u32 poll,u32 address)2147 static u32 sceIoGetAsyncStat(int id, u32 poll, u32 address) {
2148 u32 error;
2149 FileNode *f = __IoGetFd(id, error);
2150 if (f) {
2151 if (__IsInInterrupt()) {
2152 DEBUG_LOG(SCEIO, "%lli = sceIoGetAsyncStat(%i, %i, %08x): illegal context", f->asyncResult, id, poll, address);
2153 return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;
2154 }
2155 if (f->pendingAsyncResult) {
2156 if (poll) {
2157 DEBUG_LOG(SCEIO, "%lli = sceIoGetAsyncStat(%i, %i, %08x): not ready", f->asyncResult, id, poll, address);
2158 return 1;
2159 } else {
2160 if (!__KernelIsDispatchEnabled()) {
2161 DEBUG_LOG(SCEIO, "%lli = sceIoGetAsyncStat(%i, %i, %08x): dispatch disabled", f->asyncResult, id, poll, address);
2162 return SCE_KERNEL_ERROR_CAN_NOT_WAIT;
2163 }
2164
2165 DEBUG_LOG(SCEIO, "%lli = sceIoGetAsyncStat(%i, %i, %08x): waiting", f->asyncResult, id, poll, address);
2166 f->waitingThreads.push_back(__KernelGetCurThread());
2167 __KernelWaitCurThread(WAITTYPE_ASYNCIO, f->GetUID(), address, 0, false, "io waited");
2168 }
2169 } else if (f->hasAsyncResult) {
2170 if (!__KernelIsDispatchEnabled()) {
2171 DEBUG_LOG(SCEIO, "%lli = sceIoGetAsyncStat(%i, %i, %08x): dispatch disabled", f->asyncResult, id, poll, address);
2172 return SCE_KERNEL_ERROR_CAN_NOT_WAIT;
2173 }
2174
2175 DEBUG_LOG(SCEIO, "%lli = sceIoGetAsyncStat(%i, %i, %08x)", f->asyncResult, id, poll, address);
2176 Memory::Write_U64((u64) f->asyncResult, address);
2177 f->hasAsyncResult = false;
2178
2179 if (f->closePending) {
2180 __IoFreeFd(id, error);
2181 }
2182 } else {
2183 WARN_LOG(SCEIO, "SCE_KERNEL_ERROR_NOASYNC = sceIoGetAsyncStat(%i, %i, %08x)", id, poll, address);
2184 return SCE_KERNEL_ERROR_NOASYNC;
2185 }
2186 return 0; //completed
2187 }
2188 else
2189 {
2190 ERROR_LOG(SCEIO, "ERROR - sceIoGetAsyncStat with invalid id %i", id);
2191 return SCE_KERNEL_ERROR_BADF;
2192 }
2193 }
2194
sceIoWaitAsync(int id,u32 address)2195 static int sceIoWaitAsync(int id, u32 address) {
2196 u32 error;
2197 FileNode *f = __IoGetFd(id, error);
2198 if (f) {
2199 if (__IsInInterrupt()) {
2200 return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_ILLEGAL_CONTEXT, "illegal context");
2201 }
2202 if (f->pendingAsyncResult) {
2203 if (!__KernelIsDispatchEnabled()) {
2204 return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled");
2205 }
2206 f->waitingThreads.push_back(__KernelGetCurThread());
2207 __KernelWaitCurThread(WAITTYPE_ASYNCIO, f->GetUID(), address, 0, false, "io waited");
2208 return hleLogSuccessI(SCEIO, 0, "waiting");
2209 } else if (f->hasAsyncResult) {
2210 if (!__KernelIsDispatchEnabled()) {
2211 return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_CAN_NOT_WAIT, "dispatch disabled");
2212 }
2213 Memory::Write_U64((u64) f->asyncResult, address);
2214 f->hasAsyncResult = false;
2215
2216 if (f->closePending) {
2217 __IoFreeFd(id, error);
2218 }
2219
2220 return hleLogSuccessI(SCEIO, 0, "complete");
2221 } else {
2222 return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_NOASYNC, "no async pending");
2223 }
2224 return 0; //completed
2225 } else {
2226 return hleLogError(SCEIO, SCE_KERNEL_ERROR_BADF, "invalid fd");
2227 }
2228 }
2229
sceIoWaitAsyncCB(int id,u32 address)2230 static int sceIoWaitAsyncCB(int id, u32 address) {
2231 // Should process callbacks here
2232 u32 error;
2233 FileNode *f = __IoGetFd(id, error);
2234 if (f) {
2235 if (__IsInInterrupt()) {
2236 return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_ILLEGAL_CONTEXT, "illegal context");
2237 }
2238
2239 hleCheckCurrentCallbacks();
2240 if (f->pendingAsyncResult) {
2241 // TODO: This seems to re-enable dispatch or something?
2242 f->waitingThreads.push_back(__KernelGetCurThread());
2243 __KernelWaitCurThread(WAITTYPE_ASYNCIO, f->GetUID(), address, 0, true, "io waited");
2244 return hleLogSuccessI(SCEIO, 0, "waiting");
2245 } else if (f->hasAsyncResult) {
2246 Memory::Write_U64((u64) f->asyncResult, address);
2247 f->hasAsyncResult = false;
2248
2249 if (f->closePending) {
2250 __IoFreeFd(id, error);
2251 }
2252
2253 return hleLogSuccessI(SCEIO, 0, "complete");
2254 } else {
2255 return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_NOASYNC, "no async pending");
2256 }
2257 } else {
2258 return hleLogError(SCEIO, SCE_KERNEL_ERROR_BADF, "invalid fd");
2259 }
2260 }
2261
sceIoPollAsync(int id,u32 address)2262 static u32 sceIoPollAsync(int id, u32 address) {
2263 u32 error;
2264 FileNode *f = __IoGetFd(id, error);
2265 if (f) {
2266 if (f->pendingAsyncResult) {
2267 return hleLogSuccessVerboseI(SCEIO, 1, "not ready");
2268 } else if (f->hasAsyncResult) {
2269 Memory::Write_U64((u64) f->asyncResult, address);
2270 f->hasAsyncResult = false;
2271
2272 if (f->closePending) {
2273 __IoFreeFd(id, error);
2274 }
2275 return hleLogSuccessI(SCEIO, 0);
2276 } else {
2277 return hleLogDebug(SCEIO, SCE_KERNEL_ERROR_NOASYNC, "no async pending");
2278 }
2279 } else {
2280 return hleLogError(SCEIO, SCE_KERNEL_ERROR_BADF, "invalid fd");
2281 }
2282 }
2283
2284 class DirListing : public KernelObject {
2285 public:
GetName()2286 const char *GetName() override { return name.c_str(); }
GetTypeName()2287 const char *GetTypeName() override { return GetStaticTypeName(); }
GetStaticTypeName()2288 static const char *GetStaticTypeName() { return "DirListing"; }
GetMissingErrorCode()2289 static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_BADF; }
GetStaticIDType()2290 static int GetStaticIDType() { return PPSSPP_KERNEL_TMID_DirList; }
GetIDType() const2291 int GetIDType() const override { return PPSSPP_KERNEL_TMID_DirList; }
2292
DoState(PointerWrap & p)2293 void DoState(PointerWrap &p) override {
2294 auto s = p.Section("DirListing", 1);
2295 if (!s)
2296 return;
2297
2298 Do(p, name);
2299 Do(p, index);
2300
2301 // TODO: Is this the right way for it to wake up?
2302 int count = (int) listing.size();
2303 Do(p, count);
2304 listing.resize(count);
2305 for (int i = 0; i < count; ++i) {
2306 listing[i].DoState(p);
2307 }
2308 }
2309
2310 std::string name;
2311 std::vector<PSPFileInfo> listing;
2312 int index;
2313 };
2314
sceIoDopen(const char * path)2315 static u32 sceIoDopen(const char *path) {
2316 DEBUG_LOG(SCEIO, "sceIoDopen(\"%s\")", path);
2317
2318 if (!pspFileSystem.GetFileInfo(path).exists) {
2319 return SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND;
2320 }
2321
2322 DirListing *dir = new DirListing();
2323 SceUID id = kernelObjects.Create(dir);
2324
2325 double startTime = time_now_d();
2326
2327 dir->listing = pspFileSystem.GetDirListing(path);
2328 dir->index = 0;
2329 dir->name = std::string(path);
2330
2331 double listTime = time_now_d() - startTime;
2332
2333 if (listTime > 0.01) {
2334 INFO_LOG(IO, "Dir listing '%s' took %0.3f", path, listTime);
2335 }
2336
2337 // Blacklist some directories that games should not be able to find out about.
2338 // Speeds up directory iteration on slow Android Scoped Storage implementations :(
2339 // Might also want to filter out PSP/GAME if not a homebrew, maybe also irrelevant directories
2340 // in PSP/SAVEDATA, though iffy to know which ones are irrelevant..
2341 // Also if we're stripping PSP from the path due to setting a directory named PSP as the memstick root,
2342 // these will also show up at ms0: which is not ideal. Should find some other way to deal with that.
2343 if (!strcmp(path, "ms0:/PSP") || !strcmp(path, "ms0:")) {
2344 static const char *const pspFolderBlacklist[] = {
2345 "CHEATS",
2346 "PPSSPP_STATE",
2347 "PLUGINS",
2348 "SYSTEM",
2349 "SCREENSHOT",
2350 "TEXTURES",
2351 "DUMP",
2352 "SHADERS",
2353 };
2354 std::vector<PSPFileInfo> filtered;
2355 for (const auto &entry : dir->listing) {
2356 bool blacklisted = false;
2357 for (auto black : pspFolderBlacklist) {
2358 if (!strcasecmp(entry.name.c_str(), black)) {
2359 blacklisted = true;
2360 break;
2361 }
2362 }
2363 // Also don't let games see a GAME directory in the root. This confuses Wipeout...
2364 if (!strcasecmp(entry.name.c_str(), "GAME") && !strcmp(path, "ms0:")) {
2365 blacklisted = true;
2366 }
2367
2368 if (!blacklisted) {
2369 filtered.push_back(entry);
2370 }
2371 }
2372
2373 dir->listing = filtered;
2374 }
2375
2376 // TODO: The result is delayed only from the memstick, it seems.
2377 return id;
2378 }
2379
2380 // For some reason strncpy will fill up the entire output buffer. No reason to do that,
2381 // so we use this trivial replacement.
strcpy_limit(char * dest,const char * src,int limit)2382 static void strcpy_limit(char *dest, const char *src, int limit) {
2383 int i;
2384 for (i = 0; i < limit - 1; i++) {
2385 if (!src[i])
2386 break;
2387 dest[i] = src[i];
2388 }
2389 // Always null terminate.
2390 dest[i] = 0;
2391 }
2392
sceIoDread(int id,u32 dirent_addr)2393 static u32 sceIoDread(int id, u32 dirent_addr) {
2394 u32 error;
2395 DirListing *dir = kernelObjects.Get<DirListing>(id, error);
2396 if (dir) {
2397 SceIoDirEnt *entry = (SceIoDirEnt*) Memory::GetPointer(dirent_addr);
2398
2399 if (dir->index == (int) dir->listing.size()) {
2400 DEBUG_LOG(SCEIO, "sceIoDread( %d %08x ) - end", id, dirent_addr);
2401 entry->d_name[0] = '\0';
2402 return 0;
2403 }
2404
2405 PSPFileInfo &info = dir->listing[dir->index];
2406 __IoGetStat(&entry->d_stat, info);
2407
2408 strncpy(entry->d_name, info.name.c_str(), 256);
2409 entry->d_name[255] = '\0';
2410
2411 bool isFAT = pspFileSystem.FlagsFromFilename(dir->name) & FileSystemFlags::SIMULATE_FAT32;
2412 // Only write d_private for memory stick
2413 if (isFAT) {
2414 // write d_private for supporting Custom BGM
2415 // ref JPCSP https://code.google.com/p/jpcsp/source/detail?r=3468
2416 if (Memory::IsValidAddress(entry->d_private)){
2417 if (sceKernelGetCompiledSdkVersion() <= 0x0307FFFF){
2418 // d_private is pointing to an area of unknown size
2419 // - [0..12] "8.3" file name (null-terminated), could be empty.
2420 // - [13..???] long file name (null-terminated)
2421
2422 // Hm, so currently we don't write the short name at all to d_private? TODO
2423 strcpy_limit((char*)Memory::GetPointer(entry->d_private + 13), (const char*)entry->d_name, ARRAY_SIZE(entry->d_name));
2424 }
2425 else {
2426 // d_private is pointing to an area of total size 1044
2427 // - [0..3] size of area
2428 // - [4..19] "8.3" file name (null-terminated), could be empty.
2429 // - [20..???] long file name (null-terminated)
2430 auto size = Memory::Read_U32(entry->d_private);
2431 // Hm, so currently we don't write the short name at all to d_private? TODO
2432 if (size >= 1044) {
2433 strcpy_limit((char*)Memory::GetPointer(entry->d_private + 20), (const char*)entry->d_name, ARRAY_SIZE(entry->d_name));
2434 }
2435 }
2436 }
2437 }
2438 DEBUG_LOG(SCEIO, "sceIoDread( %d %08x ) = %s", id, dirent_addr, entry->d_name);
2439
2440 // TODO: Improve timing. Only happens on the *first* entry read, ms and umd.
2441 if (dir->index++ == 0) {
2442 return hleDelayResult(1, "readdir", 1000);
2443 }
2444 return 1;
2445 } else {
2446 DEBUG_LOG(SCEIO, "sceIoDread - invalid listing %i, error %08x", id, error);
2447 return SCE_KERNEL_ERROR_BADF;
2448 }
2449 }
2450
sceIoDclose(int id)2451 static u32 sceIoDclose(int id) {
2452 DEBUG_LOG(SCEIO, "sceIoDclose(%d)", id);
2453 return kernelObjects.Destroy<DirListing>(id);
2454 }
2455
__IoIoctl(u32 id,u32 cmd,u32 indataPtr,u32 inlen,u32 outdataPtr,u32 outlen,int & usec)2456 static int __IoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen, int &usec) {
2457 u32 error;
2458 FileNode *f = __IoGetFd(id, error);
2459 if (error) {
2460 ERROR_LOG(SCEIO, "%08x=sceIoIoctl id: %08x, cmd %08x, bad file", error, id, cmd);
2461 return error;
2462 }
2463 if (f->asyncBusy()) {
2464 ERROR_LOG(SCEIO, "%08x=sceIoIoctl id: %08x, cmd %08x, async busy", error, id, cmd);
2465 return SCE_KERNEL_ERROR_ASYNC_BUSY;
2466 }
2467
2468 // TODO: Move this into each command, probably?
2469 usec = 100;
2470
2471 //KD Hearts:
2472 //56:46:434 HLE\sceIo.cpp:886 E[HLE]: UNIMPL 0=sceIoIoctrl id: 0000011f, cmd 04100001, indataPtr 08b313d8, inlen 00000010, outdataPtr 00000000, outLen 0
2473 // 0000000
2474
2475 // TODO: This kind of stuff should be moved to the devices (wherever that would be)
2476 // and does not belong in this file. Same thing with Devctl.
2477
2478 switch (cmd) {
2479 // Define decryption key (amctrl.prx DRM)
2480 case 0x04100001: {
2481 u8 keybuf[16];
2482 u8 *key_ptr;
2483 u8 pgd_header[0x90];
2484 u8 pgd_magic[4] = {0x00, 0x50, 0x47, 0x44};
2485
2486 if (Memory::IsValidAddress(indataPtr) && inlen == 16) {
2487 memcpy(keybuf, Memory::GetPointer(indataPtr), 16);
2488 key_ptr = keybuf;
2489 }else{
2490 key_ptr = NULL;
2491 }
2492
2493 DEBUG_LOG(SCEIO, "Decrypting PGD DRM files");
2494 pspFileSystem.SeekFile(f->handle, (s32)f->pgd_offset, FILEMOVE_BEGIN);
2495 pspFileSystem.ReadFile(f->handle, pgd_header, 0x90);
2496 f->pgdInfo = pgd_open(pgd_header, 2, key_ptr);
2497 if(f->pgdInfo==NULL){
2498 ERROR_LOG(SCEIO, "Not a valid PGD file. Open as normal file.");
2499 f->npdrm = false;
2500 pspFileSystem.SeekFile(f->handle, (s32)0, FILEMOVE_BEGIN);
2501 if(memcmp(pgd_header, pgd_magic, 4)==0){
2502 // File is PGD file, but key mismatch
2503 return ERROR_PGD_INVALID_HEADER;
2504 }else{
2505 // File is decrypted.
2506 return 0;
2507 }
2508 }else{
2509 // Everthing OK.
2510 f->npdrm = true;
2511 f->pgdInfo->data_offset += f->pgd_offset;
2512 return 0;
2513 }
2514 break;
2515 }
2516 // Set PGD offset. Called from sceNpDrmEdataSetupKey
2517 case 0x04100002:
2518 f->pgd_offset = indataPtr;
2519 break;
2520
2521 // Get PGD data size. Called from sceNpDrmEdataGetDataSize
2522 case 0x04100010:
2523 if(f->pgdInfo)
2524 return f->pgdInfo->data_size;
2525 else
2526 return (int)f->info.size;
2527 break;
2528
2529 // Get UMD sector size
2530 case 0x01020003:
2531 // TODO: Should not work for umd0:/, ms0:/, etc.
2532 // TODO: Should probably move this to something common between ISOFileSystem and VirtualDiscSystem.
2533 INFO_LOG(SCEIO, "sceIoIoctl: Asked for sector size of file %i", id);
2534 if (Memory::IsValidAddress(outdataPtr) && outlen >= 4) {
2535 // ISOs always use 2048 sized sectors.
2536 Memory::Write_U32(2048, outdataPtr);
2537 } else {
2538 return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
2539 }
2540 break;
2541
2542 // Get UMD file offset
2543 case 0x01020004:
2544 // TODO: Should not work for umd0:/, ms0:/, etc.
2545 // TODO: Should probably move this to something common between ISOFileSystem and VirtualDiscSystem.
2546 DEBUG_LOG(SCEIO, "sceIoIoctl: Asked for file offset of file %d", id);
2547 if (Memory::IsValidAddress(outdataPtr) && outlen >= 4) {
2548 u32 offset = (u32)pspFileSystem.GetSeekPos(f->handle);
2549 Memory::Write_U32(offset, outdataPtr);
2550 } else {
2551 return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
2552 }
2553 break;
2554
2555 case 0x01010005:
2556 // TODO: Should not work for umd0:/, ms0:/, etc.
2557 // TODO: Should probably move this to something common between ISOFileSystem and VirtualDiscSystem.
2558 INFO_LOG(SCEIO, "sceIoIoctl: Seek for file %i", id);
2559 // Even if the size is 4, it still actually reads a 16 byte struct, it seems.
2560 if (Memory::IsValidAddress(indataPtr) && inlen >= 4) {
2561 struct SeekInfo {
2562 u64_le offset;
2563 u32_le unk;
2564 u32_le whence;
2565 };
2566 const auto seekInfo = PSPPointer<SeekInfo>::Create(indataPtr);
2567 FileMove seek;
2568 s64 newPos = __IoLseekDest(f, seekInfo->offset, seekInfo->whence, seek);
2569 if (newPos < 0 || newPos > f->info.size) {
2570 // Not allowed to seek past the end of the file with this API.
2571 return ERROR_ERRNO_IO_ERROR;
2572 }
2573 pspFileSystem.SeekFile(f->handle, (s32)seekInfo->offset, seek);
2574 } else {
2575 return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
2576 }
2577 break;
2578
2579 // Get UMD file start sector.
2580 case 0x01020006:
2581 // TODO: Should not work for umd0:/, ms0:/, etc.
2582 // TODO: Should probably move this to something common between ISOFileSystem and VirtualDiscSystem.
2583 INFO_LOG(SCEIO, "sceIoIoctl: Asked for start sector of file %i", id);
2584 if (Memory::IsValidAddress(outdataPtr) && outlen >= 4) {
2585 Memory::Write_U32(f->info.startSector, outdataPtr);
2586 } else {
2587 return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
2588 }
2589 break;
2590
2591 // Get UMD file size in bytes.
2592 case 0x01020007:
2593 // TODO: Should not work for umd0:/, ms0:/, etc.
2594 // TODO: Should probably move this to something common between ISOFileSystem and VirtualDiscSystem.
2595 INFO_LOG(SCEIO, "sceIoIoctl: Asked for size of file %i", id);
2596 if (Memory::IsValidAddress(outdataPtr) && outlen >= 8) {
2597 Memory::Write_U64(f->info.size, outdataPtr);
2598 } else {
2599 return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
2600 }
2601 break;
2602
2603 // Read from UMD file.
2604 case 0x01030008:
2605 // TODO: Should not work for umd0:/, ms0:/, etc.
2606 // TODO: Should probably move this to something common between ISOFileSystem and VirtualDiscSystem.
2607 INFO_LOG(SCEIO, "sceIoIoctl: Read from file %i", id);
2608 if (Memory::IsValidAddress(indataPtr) && inlen >= 4) {
2609 u32 size = Memory::Read_U32(indataPtr);
2610 if (Memory::IsValidAddress(outdataPtr) && size <= outlen) {
2611 // sceIoRead does its own delaying (and deferring.)
2612 usec = 0;
2613 return sceIoRead(id, outdataPtr, size);
2614 } else {
2615 return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
2616 }
2617 } else {
2618 return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
2619 }
2620 break;
2621
2622 // Get current sector seek pos from UMD device file.
2623 case 0x01d20001:
2624 // TODO: Should work only for umd0:/, etc. not for ms0:/ or disc0:/.
2625 // TODO: Should probably move this to something common between ISOFileSystem and VirtualDiscSystem.
2626 INFO_LOG(SCEIO, "sceIoIoctl: Sector tell from file %i", id);
2627 if (Memory::IsValidAddress(outdataPtr) && outlen >= 4) {
2628 Memory::Write_U32((u32)pspFileSystem.GetSeekPos(f->handle), outdataPtr);
2629 } else {
2630 return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
2631 }
2632 break;
2633
2634 // Read raw sectors from UMD device file.
2635 case 0x01f30003:
2636 // TODO: Should work only for umd0:/, etc. not for ms0:/ or disc0:/.
2637 // TODO: Should probably move this to something common between ISOFileSystem and VirtualDiscSystem.
2638 INFO_LOG(SCEIO, "sceIoIoctl: Sector read from file %i", id);
2639 if (Memory::IsValidAddress(indataPtr) && inlen >= 4) {
2640 u32 size = Memory::Read_U32(indataPtr);
2641 // Note that size is specified in sectors, not bytes.
2642 if (size > 0 && Memory::IsValidAddress(outdataPtr) && size <= outlen) {
2643 // sceIoRead does its own delaying (and deferring.)
2644 usec = 0;
2645 return sceIoRead(id, outdataPtr, size);
2646 } else {
2647 return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
2648 }
2649 } else {
2650 return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
2651 }
2652 break;
2653
2654 // Seek by sector in UMD device file.
2655 case 0x01f100a6:
2656 // TODO: Should work only for umd0:/, etc. not for ms0:/ or disc0:/.
2657 // TODO: Should probably move this to something common between ISOFileSystem and VirtualDiscSystem.
2658 INFO_LOG(SCEIO, "sceIoIoctl: Sector seek for file %i", id);
2659 // Even if the size is 4, it still actually reads a 16 byte struct, it seems.
2660
2661 //if (GetIOTimingMethod() == IOTIMING_REALISTIC) // Need a check for io timing method?
2662 usec = 15000;// Fantasy Golf Pangya Portable(KS) needs a delay over 15000us.
2663
2664 if (Memory::IsValidAddress(indataPtr) && inlen >= 4) {
2665 struct SeekInfo {
2666 u64_le offset;
2667 u32_le unk;
2668 u32_le whence;
2669 };
2670 const auto seekInfo = PSPPointer<SeekInfo>::Create(indataPtr);
2671 FileMove seek;
2672 s64 newPos = __IoLseekDest(f, seekInfo->offset, seekInfo->whence, seek);
2673 // Position is in sectors, don't forget.
2674 if (newPos < 0 || newPos > f->info.size) {
2675 // Not allowed to seek past the end of the file with this API.
2676 return SCE_KERNEL_ERROR_ERRNO_INVALID_FILE_SIZE;
2677 }
2678 pspFileSystem.SeekFile(f->handle, (s32)seekInfo->offset, seek);
2679 } else {
2680 return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
2681 }
2682 break;
2683
2684 default:
2685 {
2686 int result = pspFileSystem.Ioctl(f->handle, cmd, indataPtr, inlen, outdataPtr, outlen, usec);
2687 if (result == (int)SCE_KERNEL_ERROR_ERRNO_FUNCTION_NOT_SUPPORTED) {
2688 char temp[256];
2689 // We want the reported message to include the cmd, so it's unique.
2690 sprintf(temp, "sceIoIoctl(%%s, %08x, %%08x, %%x, %%08x, %%x)", cmd);
2691 Reporting::ReportMessage(temp, f->fullpath.c_str(), indataPtr, inlen, outdataPtr, outlen);
2692 ERROR_LOG(SCEIO, "UNIMPL 0=sceIoIoctl id: %08x, cmd %08x, indataPtr %08x, inlen %08x, outdataPtr %08x, outLen %08x", id,cmd,indataPtr,inlen,outdataPtr,outlen);
2693 }
2694 return result;
2695 }
2696 break;
2697 }
2698
2699 return 0;
2700 }
2701
sceIoIoctl(u32 id,u32 cmd,u32 indataPtr,u32 inlen,u32 outdataPtr,u32 outlen)2702 u32 sceIoIoctl(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen)
2703 {
2704 int usec = 0;
2705 int result = __IoIoctl(id, cmd, indataPtr, inlen, outdataPtr, outlen, usec);
2706 if (usec != 0) {
2707 return hleDelayResult(result, "io ctrl command", usec);
2708 }
2709 return result;
2710 }
2711
sceIoIoctlAsync(u32 id,u32 cmd,u32 indataPtr,u32 inlen,u32 outdataPtr,u32 outlen)2712 static u32 sceIoIoctlAsync(u32 id, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen)
2713 {
2714 u32 error;
2715 FileNode *f = __IoGetFd(id, error);
2716 if (f) {
2717 if (f->asyncBusy()) {
2718 return hleLogWarning(SCEIO, SCE_KERNEL_ERROR_ASYNC_BUSY, "async busy");
2719 }
2720
2721 auto ¶ms = asyncParams[id];
2722 params.op = IoAsyncOp::IOCTL;
2723 params.ioctl.cmd = cmd;
2724 params.ioctl.inAddr = indataPtr;
2725 params.ioctl.inSize = inlen;
2726 params.ioctl.outAddr = outdataPtr;
2727 params.ioctl.outSize = outlen;
2728 IoStartAsyncThread(id, f);
2729 return hleLogSuccessI(SCEIO, 0);
2730 } else {
2731 return hleLogError(SCEIO, error, "bad file descriptor");
2732 }
2733 }
2734
sceIoGetFdList(u32 outAddr,int outSize,u32 fdNumAddr)2735 static u32 sceIoGetFdList(u32 outAddr, int outSize, u32 fdNumAddr) {
2736 WARN_LOG(SCEIO, "sceIoGetFdList(%08x, %i, %08x)", outAddr, outSize, fdNumAddr);
2737
2738 auto out = PSPPointer<SceUID_le>::Create(outAddr);
2739 int count = 0;
2740
2741 // Always have the first three.
2742 for (int i = 0; i < PSP_MIN_FD; ++i) {
2743 // TODO: Technically it seems like these are fixed ids > PSP_COUNT_FDS.
2744 if (count < outSize && out.IsValid()) {
2745 out[count] = i;
2746 }
2747 ++count;
2748 }
2749
2750 for (int i = PSP_MIN_FD; i < PSP_COUNT_FDS; ++i) {
2751 if (fds[i] == 0) {
2752 continue;
2753 }
2754 if (count < outSize && out.IsValid()) {
2755 out[count] = i;
2756 }
2757 ++count;
2758 }
2759
2760 if (Memory::IsValidAddress(fdNumAddr))
2761 Memory::Write_U32(count, fdNumAddr);
2762 if (count >= outSize) {
2763 return outSize;
2764 } else {
2765 return count;
2766 }
2767 }
2768
2769 // Presumably lets you hook up stderr to a MsgPipe.
sceKernelRegisterStderrPipe(u32 msgPipeUID)2770 static u32 sceKernelRegisterStderrPipe(u32 msgPipeUID) {
2771 ERROR_LOG_REPORT(SCEIO, "UNIMPL sceKernelRegisterStderrPipe(%08x)", msgPipeUID);
2772 return 0;
2773 }
2774
sceKernelRegisterStdoutPipe(u32 msgPipeUID)2775 static u32 sceKernelRegisterStdoutPipe(u32 msgPipeUID) {
2776 ERROR_LOG_REPORT(SCEIO, "UNIMPL sceKernelRegisterStdoutPipe(%08x)", msgPipeUID);
2777 return 0;
2778 }
2779
IoAsyncFinish(int id)2780 static int IoAsyncFinish(int id) {
2781 u32 error;
2782 FileNode *f = __IoGetFd(id, error);
2783 if (f) {
2784 // Reset this so the Io funcs don't reject the request.
2785 f->pendingAsyncResult = false;
2786 // Reset the PC back so we will run again on resume.
2787 currentMIPS->pc = asyncThreads[id]->Entry();
2788
2789 auto ¶ms = asyncParams[id];
2790
2791 int result;
2792 int us;
2793 bool complete;
2794
2795 switch (params.op) {
2796 case IoAsyncOp::READ:
2797 complete = __IoRead(result, id, params.std.addr, params.std.size, us);
2798 if (complete) {
2799 f->asyncResult = (s64)result;
2800 DEBUG_LOG(SCEIO, "ASYNC %llx=sceIoReadAsync(%d, %08x, %x)", f->asyncResult, id, params.std.addr, params.std.size);
2801 } else {
2802 DEBUG_LOG(SCEIO, "ASYNC sceIoReadAsync(%d, %08x, %x): deferring result", id, params.std.addr, params.std.size);
2803 }
2804 break;
2805
2806 case IoAsyncOp::WRITE:
2807 complete = __IoWrite(result, id, params.std.addr, params.std.size, us);
2808 if (complete) {
2809 f->asyncResult = (s64)result;
2810 DEBUG_LOG(SCEIO, "ASYNC %llx=sceIoWriteAsync(%d, %08x, %x)", f->asyncResult, id, params.std.addr, params.std.size);
2811 } else {
2812 DEBUG_LOG(SCEIO, "ASYNC sceIoWriteAsync(%d, %08x, %x): deferring result", id, params.std.addr, params.std.size);
2813 }
2814 break;
2815
2816 case IoAsyncOp::SEEK:
2817 f->asyncResult = __IoLseek(id, params.seek.pos, params.seek.whence);
2818 // Educated guess at timing.
2819 us = 100;
2820 DEBUG_LOG(SCEIO, "ASYNC %lli = sceIoLseekAsync(%d, %llx, %i)", f->asyncResult, id, params.seek.pos, params.seek.whence);
2821 break;
2822
2823 case IoAsyncOp::OPEN:
2824 {
2825 // See notes on timing in sceIoOpen.
2826 const std::string filename = Memory::GetCharPointer(params.open.filenameAddr);
2827 IFileSystem *sys = pspFileSystem.GetSystemFromFilename(filename);
2828 if (sys) {
2829 if (f->asyncResult == (int)SCE_KERNEL_ERROR_ERRNO_FILE_NOT_FOUND) {
2830 us = sys->Flags() & FileSystemFlags::UMD ? 6000 : 10000;
2831 } else if (sys->DevType(f->handle) & (PSPDevType::BLOCK | PSPDevType::EMU_LBN)) {
2832 // These are fast to open, no delay or even rescheduling happens.
2833 us = 80;
2834 } else {
2835 us = sys->Flags() & FileSystemFlags::UMD ? 4000 : 10000;
2836 }
2837 } else {
2838 us = 80;
2839 }
2840 break;
2841 }
2842
2843 case IoAsyncOp::CLOSE:
2844 f->asyncResult = 0;
2845 // CLOSE shouldn't have a delay. See #12549.
2846 us = 0;
2847 DEBUG_LOG(SCEIO, "ASYNC %lli = sceIoCloseAsync(%d)", f->asyncResult, id);
2848 break;
2849
2850 case IoAsyncOp::IOCTL:
2851 us = 100;
2852 f->asyncResult = __IoIoctl(id, params.ioctl.cmd, params.ioctl.inAddr, params.ioctl.inSize, params.ioctl.outAddr, params.ioctl.outSize, us);
2853 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);
2854 break;
2855
2856 default:
2857 ERROR_LOG_REPORT(SCEIO, "Unknown async op %d", (int)params.op);
2858 us = 0;
2859 break;
2860 }
2861
2862 __IoSchedAsync(f, id, us);
2863 __KernelWaitCurThread(WAITTYPE_ASYNCIO, id, 0, 0, false, "async io");
2864 hleSkipDeadbeef();
2865
2866 params.op = IoAsyncOp::NONE;
2867 return 0;
2868 } else {
2869 return hleLogError(SCEIO, error, "bad file descriptor");
2870 }
2871 }
2872
__KernelFileNodeObject()2873 KernelObject *__KernelFileNodeObject() {
2874 return new FileNode;
2875 }
2876
__KernelDirListingObject()2877 KernelObject *__KernelDirListingObject() {
2878 return new DirListing;
2879 }
2880
2881 const HLEFunction IoFileMgrForUser[] = {
2882 {0xB29DDF9C, &WrapU_C<sceIoDopen>, "sceIoDopen", 'i', "s" },
2883 {0xE3EB004C, &WrapU_IU<sceIoDread>, "sceIoDread", 'i', "ix" },
2884 {0xEB092469, &WrapU_I<sceIoDclose>, "sceIoDclose", 'i', "i" },
2885 {0xE95A012B, &WrapU_UUUUUU<sceIoIoctlAsync>, "sceIoIoctlAsync", 'i', "ixpipi"},
2886 {0x63632449, &WrapU_UUUUUU<sceIoIoctl>, "sceIoIoctl", 'i', "ixpipi"},
2887 {0xACE946E8, &WrapU_CU<sceIoGetstat>, "sceIoGetstat", 'i', "sx" },
2888 {0xB8A740F4, &WrapU_CUU<sceIoChstat>, "sceIoChstat", 'i', "sxx" },
2889 {0x55F4717D, &WrapU_C<sceIoChdir>, "sceIoChdir", 'i', "s" },
2890 {0X08BD7374, &WrapU_I<sceIoGetDevType>, "sceIoGetDevType", 'x', "i" },
2891 {0xB2A628C1, &WrapU_UUUIUI<sceIoAssign>, "sceIoAssign", 'i', "sssixi"},
2892 {0XE8BC6571, &WrapU_I<sceIoCancel>, "sceIoCancel", 'i', "i" },
2893 {0XB293727F, &WrapI_II<sceIoChangeAsyncPriority>, "sceIoChangeAsyncPriority", 'i', "ix" },
2894 {0X810C4BC3, &WrapU_I<sceIoClose>, "sceIoClose", 'i', "i" },
2895 {0XFF5940B6, &WrapI_I<sceIoCloseAsync>, "sceIoCloseAsync", 'i', "i" },
2896 {0x54F5FB11, &WrapU_CIUIUI<sceIoDevctl>, "sceIoDevctl", 'i', "sxpipi"},
2897 {0XCB05F8D6, &WrapU_IUU<sceIoGetAsyncStat>, "sceIoGetAsyncStat", 'i', "iiP" },
2898 {0x27EB27B8, &WrapI64_II64I<sceIoLseek>, "sceIoLseek", 'I', "iIi" },
2899 {0x68963324, &WrapU_III<sceIoLseek32>, "sceIoLseek32", 'i', "iii" },
2900 {0X1B385D8F, &WrapU_III<sceIoLseek32Async>, "sceIoLseek32Async", 'i', "iii" },
2901 {0X71B19E77, &WrapU_II64I<sceIoLseekAsync>, "sceIoLseekAsync", 'i', "iIi" },
2902 {0x109F50BC, &WrapU_CII<sceIoOpen>, "sceIoOpen", 'i', "sii" },
2903 {0x89AA9906, &WrapU_CII<sceIoOpenAsync>, "sceIoOpenAsync", 'i', "sii" },
2904 {0x06A70004, &WrapU_CI<sceIoMkdir>, "sceIoMkdir", 'i', "si" },
2905 {0x3251EA56, &WrapU_IU<sceIoPollAsync>, "sceIoPollAsync", 'i', "iP" },
2906 {0x6A638D83, &WrapU_IUI<sceIoRead>, "sceIoRead", 'i', "ixi" },
2907 {0xA0B5A7C2, &WrapU_IUI<sceIoReadAsync>, "sceIoReadAsync", 'i', "ixi" },
2908 {0xF27A9C51, &WrapU_C<sceIoRemove>, "sceIoRemove", 'i', "s" },
2909 {0x779103A0, &WrapU_CC<sceIoRename>, "sceIoRename", 'i', "ss" },
2910 {0x1117C65F, &WrapU_C<sceIoRmdir>, "sceIoRmdir", 'i', "s" },
2911 {0XA12A0514, &WrapU_IUU<sceIoSetAsyncCallback>, "sceIoSetAsyncCallback", 'i', "ixx" },
2912 {0xAB96437F, &WrapU_CI<sceIoSync>, "sceIoSync", 'i', "si" },
2913 {0x6D08A871, &WrapU_C<sceIoUnassign>, "sceIoUnassign", 'i', "s" },
2914 {0x42EC03AC, &WrapU_IUI<sceIoWrite>, "sceIoWrite", 'i', "ixi" },
2915 {0x0FACAB19, &WrapU_IUI<sceIoWriteAsync>, "sceIoWriteAsync", 'i', "ixi" },
2916 {0x35DBD746, &WrapI_IU<sceIoWaitAsyncCB>, "sceIoWaitAsyncCB", 'i', "iP" },
2917 {0xE23EEC33, &WrapI_IU<sceIoWaitAsync>, "sceIoWaitAsync", 'i', "iP" },
2918 {0X5C2BE2CC, &WrapU_UIU<sceIoGetFdList>, "sceIoGetFdList", 'i', "xip" },
2919 {0x13370001, &WrapI_I<IoAsyncFinish>, "__IoAsyncFinish", 'i', "i" },
2920 };
2921
Register_IoFileMgrForUser()2922 void Register_IoFileMgrForUser() {
2923 RegisterModule("IoFileMgrForUser", ARRAY_SIZE(IoFileMgrForUser), IoFileMgrForUser);
2924 }
2925
2926 const HLEFunction IoFileMgrForKernel[] = {
2927 {0XA905B705, nullptr, "sceIoCloseAll", '?', "" },
2928 {0X411106BA, nullptr, "sceIoGetThreadCwd", '?', "" },
2929 {0XCB0A151F, nullptr, "sceIoChangeThreadCwd", '?', "" },
2930 {0X8E982A74, nullptr, "sceIoAddDrv", '?', "" },
2931 {0XC7F35804, nullptr, "sceIoDelDrv", '?', "" },
2932 {0X3C54E908, nullptr, "sceIoReopen", '?', "" },
2933 {0xB29DDF9C, &WrapU_C<sceIoDopen>, "sceIoDopen", 'i', "s", HLE_KERNEL_SYSCALL },
2934 {0xE3EB004C, &WrapU_IU<sceIoDread>, "sceIoDread", 'i', "ix", HLE_KERNEL_SYSCALL },
2935 {0xEB092469, &WrapU_I<sceIoDclose>, "sceIoDclose", 'i', "i", HLE_KERNEL_SYSCALL },
2936 {0X109F50BC, &WrapU_CII<sceIoOpen>, "sceIoOpen", 'i', "sii", HLE_KERNEL_SYSCALL },
2937 {0x6A638D83, &WrapU_IUI<sceIoRead>, "sceIoRead", 'i', "ixi", HLE_KERNEL_SYSCALL },
2938 {0x42EC03AC, &WrapU_IUI<sceIoWrite>, "sceIoWrite", 'i', "ixi", HLE_KERNEL_SYSCALL },
2939 {0x68963324, &WrapU_III<sceIoLseek32>, "sceIoLseek32", 'i', "iii", HLE_KERNEL_SYSCALL },
2940 {0x27EB27B8, &WrapI64_II64I<sceIoLseek>, "sceIoLseek", 'I', "iIi", HLE_KERNEL_SYSCALL },
2941 {0x810C4BC3, &WrapU_I<sceIoClose>, "sceIoClose", 'i', "i", HLE_KERNEL_SYSCALL },
2942 {0x779103A0, &WrapU_CC<sceIoRename>, "sceIoRename", 'i', "ss", HLE_KERNEL_SYSCALL },
2943 {0xF27A9C51, &WrapU_C<sceIoRemove>, "sceIoRemove", 'i', "s", HLE_KERNEL_SYSCALL },
2944 {0x55F4717D, &WrapU_C<sceIoChdir>, "sceIoChdir", 'i', "s", HLE_KERNEL_SYSCALL },
2945 {0x06A70004, &WrapU_CI<sceIoMkdir>, "sceIoMkdir", 'i', "si", HLE_KERNEL_SYSCALL },
2946 {0x1117C65F, &WrapU_C<sceIoRmdir>, "sceIoRmdir", 'i', "s", HLE_KERNEL_SYSCALL },
2947 {0x54F5FB11, &WrapU_CIUIUI<sceIoDevctl>, "sceIoDevctl", 'i', "sxpipi", HLE_KERNEL_SYSCALL },
2948 {0x63632449, &WrapU_UUUUUU<sceIoIoctl>, "sceIoIoctl", 'i', "ixpipi", HLE_KERNEL_SYSCALL },
2949 {0xAB96437F, &WrapU_CI<sceIoSync>, "sceIoSync", 'i', "si", HLE_KERNEL_SYSCALL },
2950 {0xB2A628C1, &WrapU_UUUIUI<sceIoAssign>, "sceIoAssign", 'i', "sssixi", HLE_KERNEL_SYSCALL },
2951 {0x6D08A871, &WrapU_C<sceIoUnassign>, "sceIoUnassign", 'i', "s", HLE_KERNEL_SYSCALL },
2952 {0xACE946E8, &WrapU_CU<sceIoGetstat>, "sceIoGetstat", 'i', "sx", HLE_KERNEL_SYSCALL },
2953 {0xB8A740F4, &WrapU_CUU<sceIoChstat>, "sceIoChstat", 'i', "sxx", HLE_KERNEL_SYSCALL },
2954 {0xA0B5A7C2, &WrapU_IUI<sceIoReadAsync>, "sceIoReadAsync", 'i', "ixi", HLE_KERNEL_SYSCALL },
2955 {0x3251EA56, &WrapU_IU<sceIoPollAsync>, "sceIoPollAsync", 'i', "iP", HLE_KERNEL_SYSCALL },
2956 {0xE23EEC33, &WrapI_IU<sceIoWaitAsync>, "sceIoWaitAsync", 'i', "iP", HLE_KERNEL_SYSCALL },
2957 {0x35DBD746, &WrapI_IU<sceIoWaitAsyncCB>, "sceIoWaitAsyncCB", 'i', "iP", HLE_KERNEL_SYSCALL },
2958 {0xBD17474F, nullptr, "IoFileMgrForKernel_BD17474F", '?', "" },
2959 {0x76DA16E3, nullptr, "IoFileMgrForKernel_76DA16E3", '?', "" },
2960 };
2961
Register_IoFileMgrForKernel()2962 void Register_IoFileMgrForKernel() {
2963 RegisterModule("IoFileMgrForKernel", ARRAY_SIZE(IoFileMgrForKernel), IoFileMgrForKernel);
2964 }
2965
2966 const HLEFunction StdioForUser[] = {
2967 {0X172D316E, &WrapU_V<sceKernelStdin>, "sceKernelStdin", 'i', "" },
2968 {0XA6BAB2E9, &WrapU_V<sceKernelStdout>, "sceKernelStdout", 'i', "" },
2969 {0XF78BA90A, &WrapU_V<sceKernelStderr>, "sceKernelStderr", 'i', "" },
2970 {0X432D8F5C, &WrapU_U<sceKernelRegisterStdoutPipe>, "sceKernelRegisterStdoutPipe", 'i', "x" },
2971 {0X6F797E03, &WrapU_U<sceKernelRegisterStderrPipe>, "sceKernelRegisterStderrPipe", 'i', "x" },
2972 {0XA46785C9, nullptr, "sceKernelStdioSendChar", '?', "" },
2973 {0X0CBB0571, nullptr, "sceKernelStdioLseek", '?', "" },
2974 {0X3054D478, nullptr, "sceKernelStdioRead", '?', "" },
2975 {0XA3B931DB, nullptr, "sceKernelStdioWrite", '?', "" },
2976 {0X924ABA61, nullptr, "sceKernelStdioOpen", '?', "" },
2977 {0X9D061C19, nullptr, "sceKernelStdioClose", '?', "" },
2978 };
2979
Register_StdioForUser()2980 void Register_StdioForUser() {
2981 RegisterModule("StdioForUser", ARRAY_SIZE(StdioForUser), StdioForUser);
2982 }
2983
2984 const HLEFunction StdioForKernel[] = {
2985 {0X98220F3E, nullptr, "sceKernelStdoutReopen", '?', "" },
2986 {0XFB5380C5, nullptr, "sceKernelStderrReopen", '?', "" },
2987 {0XCAB439DF, nullptr, "printf", '?', "" },
2988 {0X2CCF071A, nullptr, "fdprintf", '?', "" },
2989 {0XD97C8CB9, nullptr, "puts", '?', "" },
2990 {0X172D316E, nullptr, "sceKernelStdin", '?', "" },
2991 {0XA6BAB2E9, &WrapU_V<sceKernelStdout>, "sceKernelStdout", 'i', "" ,HLE_KERNEL_SYSCALL },
2992 {0XF78BA90A, nullptr, "sceKernelStderr", '?', "" },
2993 };
2994
Register_StdioForKernel()2995 void Register_StdioForKernel() {
2996 RegisterModule("StdioForKernel", ARRAY_SIZE(StdioForKernel), StdioForKernel);
2997 }
2998