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 &params = 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 &params = 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 &params = 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 &params = 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 &params = 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 &params = 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 &params = 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 &params = 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