2012-11-01 15:19:01 +00:00
|
|
|
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
2012-11-04 22:01:49 +00:00
|
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
2012-11-01 15:19:01 +00:00
|
|
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
|
|
|
|
// Official git repository and contact information can be found at
|
|
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
|
2013-12-29 23:11:29 +00:00
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "file/file_util.h"
|
|
|
|
|
2013-08-29 06:15:13 +00:00
|
|
|
#include "Common/ChunkFile.h"
|
2014-11-23 21:59:56 +00:00
|
|
|
#include "Core/Loaders.h"
|
2014-03-15 18:22:19 +00:00
|
|
|
#include "Core/MemMap.h"
|
2013-12-29 23:11:29 +00:00
|
|
|
#include "Core/System.h"
|
2017-01-29 10:41:31 +00:00
|
|
|
#include "Core/Host.h"
|
2013-02-04 04:31:46 +00:00
|
|
|
#include "Core/CoreTiming.h"
|
2013-05-27 21:09:38 +00:00
|
|
|
#include "Core/Reporting.h"
|
2013-12-29 23:11:29 +00:00
|
|
|
#include "Core/MIPS/MIPS.h"
|
|
|
|
#include "Core/HLE/HLE.h"
|
2014-03-15 18:22:19 +00:00
|
|
|
#include "Core/HLE/FunctionWrappers.h"
|
2013-08-29 06:15:13 +00:00
|
|
|
#include "Core/HLE/sceUmd.h"
|
|
|
|
#include "Core/HLE/sceKernelThread.h"
|
|
|
|
#include "Core/HLE/sceKernelInterrupt.h"
|
2014-01-31 07:10:26 +00:00
|
|
|
#include "Core/HLE/sceKernelMemory.h"
|
2013-09-08 18:24:21 +00:00
|
|
|
#include "Core/HLE/KernelWaitHelpers.h"
|
2013-11-24 04:52:55 +00:00
|
|
|
|
2013-11-24 03:02:36 +00:00
|
|
|
#include "Core/FileSystems/BlockDevices.h"
|
2013-12-29 22:28:31 +00:00
|
|
|
#include "Core/FileSystems/MetaFileSystem.h"
|
2013-11-24 03:02:36 +00:00
|
|
|
#include "Core/FileSystems/ISOFileSystem.h"
|
|
|
|
#include "Core/FileSystems/VirtualDiscFileSystem.h"
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2013-02-25 10:19:08 +00:00
|
|
|
const u64 MICRO_DELAY_ACTIVATE = 4000;
|
|
|
|
|
2012-12-28 07:00:04 +00:00
|
|
|
static u8 umdActivated = 1;
|
|
|
|
static u32 umdStatus = 0;
|
|
|
|
static u32 umdErrorStat = 0;
|
2014-01-31 07:10:26 +00:00
|
|
|
static int driveCBId = 0;
|
2013-02-25 10:19:08 +00:00
|
|
|
static int umdStatTimeoutEvent = -1;
|
|
|
|
static int umdStatChangeEvent = -1;
|
2017-01-28 13:09:37 +00:00
|
|
|
static int umdInsertChangeEvent = -1;
|
2013-05-27 21:09:38 +00:00
|
|
|
static std::vector<SceUID> umdWaitingThreads;
|
2013-09-08 18:24:21 +00:00
|
|
|
static std::map<SceUID, u64> umdPausedWaits;
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2013-11-24 03:02:36 +00:00
|
|
|
bool UMDReplacePermit = false;
|
2017-01-28 10:39:20 +00:00
|
|
|
bool UMDInserted = true;
|
2013-11-24 03:02:36 +00:00
|
|
|
|
2012-11-08 15:28:45 +00:00
|
|
|
struct PspUmdInfo {
|
2013-07-27 22:45:07 +00:00
|
|
|
u32_le size;
|
|
|
|
u32_le type;
|
2012-11-08 15:28:45 +00:00
|
|
|
};
|
|
|
|
|
2012-12-24 05:27:26 +00:00
|
|
|
void __UmdStatTimeout(u64 userdata, int cyclesLate);
|
2013-02-25 10:19:08 +00:00
|
|
|
void __UmdStatChange(u64 userdata, int cyclesLate);
|
2017-01-28 13:09:37 +00:00
|
|
|
void __UmdInsertChange(u64 userdata, int cyclesLate);
|
2013-05-27 21:09:38 +00:00
|
|
|
void __UmdBeginCallback(SceUID threadID, SceUID prevCallbackId);
|
|
|
|
void __UmdEndCallback(SceUID threadID, SceUID prevCallbackId);
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2012-12-24 05:27:26 +00:00
|
|
|
void __UmdInit()
|
|
|
|
{
|
2013-02-25 10:19:08 +00:00
|
|
|
umdStatTimeoutEvent = CoreTiming::RegisterEvent("UmdTimeout", __UmdStatTimeout);
|
|
|
|
umdStatChangeEvent = CoreTiming::RegisterEvent("UmdChange", __UmdStatChange);
|
2017-01-28 13:09:37 +00:00
|
|
|
umdInsertChangeEvent = CoreTiming::RegisterEvent("UmdInsertChange", __UmdInsertChange);
|
2012-11-01 15:19:01 +00:00
|
|
|
umdActivated = 1;
|
|
|
|
umdStatus = 0;
|
2012-11-07 19:59:56 +00:00
|
|
|
umdErrorStat = 0;
|
2014-01-31 07:10:26 +00:00
|
|
|
driveCBId = 0;
|
2014-03-04 15:55:08 +00:00
|
|
|
umdWaitingThreads.clear();
|
|
|
|
umdPausedWaits.clear();
|
2013-05-27 21:09:38 +00:00
|
|
|
|
|
|
|
__KernelRegisterWaitTypeFuncs(WAITTYPE_UMD, __UmdBeginCallback, __UmdEndCallback);
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2012-12-28 07:00:04 +00:00
|
|
|
void __UmdDoState(PointerWrap &p)
|
|
|
|
{
|
2017-01-28 19:38:28 +00:00
|
|
|
auto s = p.Section("sceUmd", 1, 3);
|
2013-09-15 03:23:03 +00:00
|
|
|
if (!s)
|
|
|
|
return;
|
|
|
|
|
2012-12-28 07:00:04 +00:00
|
|
|
p.Do(umdActivated);
|
|
|
|
p.Do(umdStatus);
|
|
|
|
p.Do(umdErrorStat);
|
|
|
|
p.Do(driveCBId);
|
2013-02-25 10:19:08 +00:00
|
|
|
p.Do(umdStatTimeoutEvent);
|
|
|
|
CoreTiming::RestoreRegisterEvent(umdStatTimeoutEvent, "UmdTimeout", __UmdStatTimeout);
|
|
|
|
p.Do(umdStatChangeEvent);
|
|
|
|
CoreTiming::RestoreRegisterEvent(umdStatChangeEvent, "UmdChange", __UmdStatChange);
|
|
|
|
p.Do(umdWaitingThreads);
|
2013-09-08 18:24:21 +00:00
|
|
|
p.Do(umdPausedWaits);
|
2013-11-24 08:46:06 +00:00
|
|
|
|
2017-01-28 19:38:28 +00:00
|
|
|
if (s > 1) {
|
2013-11-24 08:46:06 +00:00
|
|
|
p.Do(UMDReplacePermit);
|
2017-01-28 19:38:28 +00:00
|
|
|
if (UMDReplacePermit)
|
2017-01-29 10:41:31 +00:00
|
|
|
host->UpdateUI();
|
2017-01-28 19:38:28 +00:00
|
|
|
}
|
|
|
|
if (s > 2) {
|
|
|
|
p.Do(umdInsertChangeEvent);
|
|
|
|
CoreTiming::RestoreRegisterEvent(umdInsertChangeEvent, "UmdInsertChange", __UmdInsertChange);
|
|
|
|
p.Do(UMDInserted);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
UMDInserted = true;
|
2012-12-28 07:00:04 +00:00
|
|
|
}
|
|
|
|
|
2014-12-08 09:40:08 +00:00
|
|
|
static u8 __KernelUmdGetState()
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2014-01-31 07:10:26 +00:00
|
|
|
// Most games seem to expect the disc to be ready early on, active or not.
|
|
|
|
// It seems like the PSP sets this state when the disc is "ready".
|
|
|
|
u8 state = PSP_UMD_PRESENT | PSP_UMD_READY;
|
2012-11-01 15:19:01 +00:00
|
|
|
if (umdActivated) {
|
2013-05-19 06:41:43 +00:00
|
|
|
state |= PSP_UMD_READABLE;
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
2017-01-28 13:09:37 +00:00
|
|
|
void __UmdInsertChange(u64 userdata, int cyclesLate)
|
|
|
|
{
|
|
|
|
UMDInserted = true;
|
|
|
|
}
|
|
|
|
|
2013-02-25 10:19:08 +00:00
|
|
|
void __UmdStatChange(u64 userdata, int cyclesLate)
|
|
|
|
{
|
|
|
|
// TODO: Why not a bool anyway?
|
|
|
|
umdActivated = userdata & 0xFF;
|
|
|
|
|
|
|
|
// Wake anyone waiting on this.
|
2013-05-19 06:41:43 +00:00
|
|
|
for (size_t i = 0; i < umdWaitingThreads.size(); ++i) {
|
2013-09-08 18:24:21 +00:00
|
|
|
const SceUID threadID = umdWaitingThreads[i];
|
2013-02-25 10:19:08 +00:00
|
|
|
|
|
|
|
u32 error;
|
2013-05-27 21:09:38 +00:00
|
|
|
u32 stat = __KernelGetWaitValue(threadID, error);
|
2013-02-25 10:19:08 +00:00
|
|
|
bool keep = false;
|
2013-09-08 18:24:21 +00:00
|
|
|
if (HLEKernel::VerifyWait(threadID, WAITTYPE_UMD, 1)) {
|
2013-05-27 21:09:38 +00:00
|
|
|
if ((stat & __KernelUmdGetState()) != 0)
|
|
|
|
__KernelResumeThreadFromWait(threadID, 0);
|
2013-02-25 10:19:08 +00:00
|
|
|
// Only if they are still waiting do we keep them in the list.
|
|
|
|
else
|
|
|
|
keep = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!keep)
|
|
|
|
umdWaitingThreads.erase(umdWaitingThreads.begin() + i--);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-08 09:40:08 +00:00
|
|
|
static void __KernelUmdActivate()
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2013-05-19 06:41:43 +00:00
|
|
|
u32 notifyArg = PSP_UMD_PRESENT | PSP_UMD_READABLE;
|
2014-02-02 15:53:37 +00:00
|
|
|
// PSP_UMD_READY will be returned when sceKernelGetCompiledSdkVersion() != 0
|
|
|
|
if (sceKernelGetCompiledSdkVersion() != 0) {
|
|
|
|
notifyArg |= PSP_UMD_READY;
|
|
|
|
}
|
2014-01-31 07:10:26 +00:00
|
|
|
if (driveCBId != 0)
|
2013-09-02 10:02:48 +00:00
|
|
|
__KernelNotifyCallback(driveCBId, notifyArg);
|
2013-02-25 23:34:20 +00:00
|
|
|
|
2013-02-25 10:19:08 +00:00
|
|
|
// Don't activate immediately, take time to "spin up."
|
|
|
|
CoreTiming::RemoveAllEvents(umdStatChangeEvent);
|
|
|
|
CoreTiming::ScheduleEvent(usToCycles(MICRO_DELAY_ACTIVATE), umdStatChangeEvent, 1);
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2014-12-08 09:40:08 +00:00
|
|
|
static void __KernelUmdDeactivate()
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2013-05-19 06:41:43 +00:00
|
|
|
u32 notifyArg = PSP_UMD_PRESENT | PSP_UMD_READY;
|
2014-01-31 07:10:26 +00:00
|
|
|
if (driveCBId != 0)
|
2013-09-02 10:02:48 +00:00
|
|
|
__KernelNotifyCallback(driveCBId, notifyArg);
|
2013-02-25 23:34:20 +00:00
|
|
|
|
2013-02-25 10:19:08 +00:00
|
|
|
CoreTiming::RemoveAllEvents(umdStatChangeEvent);
|
|
|
|
__UmdStatChange(0, 0);
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2013-05-27 21:09:38 +00:00
|
|
|
void __UmdBeginCallback(SceUID threadID, SceUID prevCallbackId)
|
|
|
|
{
|
|
|
|
SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId;
|
|
|
|
|
2013-09-08 18:24:21 +00:00
|
|
|
if (HLEKernel::VerifyWait(threadID, WAITTYPE_UMD, 1))
|
2013-05-27 21:09:38 +00:00
|
|
|
{
|
|
|
|
// This means two callbacks in a row. PSP crashes if the same callback runs inside itself.
|
|
|
|
// TODO: Handle this better?
|
2013-09-08 18:24:21 +00:00
|
|
|
if (umdPausedWaits.find(pauseKey) != umdPausedWaits.end())
|
2013-05-27 21:09:38 +00:00
|
|
|
return;
|
|
|
|
|
2013-09-07 20:02:55 +00:00
|
|
|
_dbg_assert_msg_(SCEIO, umdStatTimeoutEvent != -1, "Must have a umd timer");
|
2013-05-27 21:09:38 +00:00
|
|
|
s64 cyclesLeft = CoreTiming::UnscheduleEvent(umdStatTimeoutEvent, threadID);
|
|
|
|
if (cyclesLeft != 0)
|
2013-09-08 18:24:21 +00:00
|
|
|
umdPausedWaits[pauseKey] = CoreTiming::GetTicks() + cyclesLeft;
|
2013-05-27 21:09:38 +00:00
|
|
|
else
|
2013-09-08 18:24:21 +00:00
|
|
|
umdPausedWaits[pauseKey] = 0;
|
2013-05-27 21:09:38 +00:00
|
|
|
|
2013-09-09 04:41:54 +00:00
|
|
|
HLEKernel::RemoveWaitingThread(umdWaitingThreads, threadID);
|
2013-05-27 21:09:38 +00:00
|
|
|
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatCB: Suspending lock wait for callback");
|
2013-05-27 21:09:38 +00:00
|
|
|
}
|
|
|
|
else
|
2013-09-07 20:02:55 +00:00
|
|
|
WARN_LOG_REPORT(SCEIO, "sceUmdWaitDriveStatCB: beginning callback with bad wait id?");
|
2013-05-27 21:09:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void __UmdEndCallback(SceUID threadID, SceUID prevCallbackId)
|
|
|
|
{
|
|
|
|
SceUID pauseKey = prevCallbackId == 0 ? threadID : prevCallbackId;
|
|
|
|
|
|
|
|
u32 error;
|
|
|
|
u32 stat = __KernelGetWaitValue(threadID, error);
|
2013-09-08 18:24:21 +00:00
|
|
|
if (umdPausedWaits.find(pauseKey) == umdPausedWaits.end())
|
2013-05-27 21:09:38 +00:00
|
|
|
{
|
2013-09-07 20:02:55 +00:00
|
|
|
WARN_LOG_REPORT(SCEIO, "__UmdEndCallback(): UMD paused wait missing");
|
2013-05-27 21:09:38 +00:00
|
|
|
|
|
|
|
__KernelResumeThreadFromWait(threadID, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-09-08 18:24:21 +00:00
|
|
|
u64 waitDeadline = umdPausedWaits[pauseKey];
|
|
|
|
umdPausedWaits.erase(pauseKey);
|
2013-05-27 21:09:38 +00:00
|
|
|
|
|
|
|
// TODO: Don't wake up if __KernelCurHasReadyCallbacks()?
|
|
|
|
|
|
|
|
if ((stat & __KernelUmdGetState()) != 0)
|
|
|
|
{
|
|
|
|
__KernelResumeThreadFromWait(threadID, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
s64 cyclesLeft = waitDeadline - CoreTiming::GetTicks();
|
|
|
|
if (cyclesLeft < 0 && waitDeadline != 0)
|
|
|
|
__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT);
|
|
|
|
else
|
|
|
|
{
|
2013-09-07 20:02:55 +00:00
|
|
|
_dbg_assert_msg_(SCEIO, umdStatTimeoutEvent != -1, "Must have a umd timer");
|
2013-05-27 21:09:38 +00:00
|
|
|
CoreTiming::ScheduleEvent(cyclesLeft, umdStatTimeoutEvent, __KernelGetCurThread());
|
|
|
|
|
|
|
|
umdWaitingThreads.push_back(threadID);
|
|
|
|
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatCB: Resuming lock wait for callback");
|
2013-05-27 21:09:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-08 09:40:08 +00:00
|
|
|
static int sceUmdCheckMedium()
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2017-01-28 10:39:20 +00:00
|
|
|
if (UMDInserted) {
|
2017-01-28 03:51:20 +00:00
|
|
|
DEBUG_LOG(SCEIO, "1=sceUmdCheckMedium()");
|
|
|
|
return 1; //non-zero: disc in drive
|
|
|
|
}
|
|
|
|
DEBUG_LOG(SCEIO, "0=sceUmdCheckMedium()");
|
|
|
|
return 0;
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2014-12-08 09:40:08 +00:00
|
|
|
static u32 sceUmdGetDiscInfo(u32 infoAddr)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "sceUmdGetDiscInfo(%08x)", infoAddr);
|
2012-12-02 08:33:39 +00:00
|
|
|
|
2013-05-19 06:41:43 +00:00
|
|
|
if (Memory::IsValidAddress(infoAddr)) {
|
2013-12-17 07:47:34 +00:00
|
|
|
auto info = PSPPointer<PspUmdInfo>::Create(infoAddr);
|
2013-07-27 22:45:07 +00:00
|
|
|
if (info->size != 8)
|
2012-12-02 08:33:39 +00:00
|
|
|
return PSP_ERROR_UMD_INVALID_PARAM;
|
|
|
|
|
2013-07-27 22:45:07 +00:00
|
|
|
info->type = PSP_UMD_TYPE_GAME;
|
2012-12-02 08:33:39 +00:00
|
|
|
return 0;
|
2013-05-19 06:41:43 +00:00
|
|
|
} else
|
2012-12-02 08:33:39 +00:00
|
|
|
return PSP_ERROR_UMD_INVALID_PARAM;
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2014-12-08 09:40:08 +00:00
|
|
|
static int sceUmdActivate(u32 mode, const char *name)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2013-05-19 06:41:43 +00:00
|
|
|
if (mode < 1 || mode > 2)
|
2012-12-09 08:09:25 +00:00
|
|
|
return PSP_ERROR_UMD_INVALID_PARAM;
|
2012-12-02 00:40:13 +00:00
|
|
|
|
2012-11-01 15:19:01 +00:00
|
|
|
__KernelUmdActivate();
|
2012-12-02 00:40:13 +00:00
|
|
|
|
2013-05-19 06:41:43 +00:00
|
|
|
if (mode == 1) {
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "0=sceUmdActivate(%d, %s)", mode, name);
|
2013-05-19 06:41:43 +00:00
|
|
|
} else {
|
2013-09-07 20:02:55 +00:00
|
|
|
ERROR_LOG(SCEIO, "UNTESTED 0=sceUmdActivate(%d, %s)", mode, name);
|
2012-12-02 00:40:13 +00:00
|
|
|
}
|
|
|
|
|
2012-12-09 08:09:25 +00:00
|
|
|
return 0;
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2014-12-08 09:40:08 +00:00
|
|
|
static int sceUmdDeactivate(u32 mode, const char *name)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2012-12-02 00:40:13 +00:00
|
|
|
// Why 18? No idea.
|
2013-05-19 06:41:43 +00:00
|
|
|
if (mode > 18)
|
2012-12-09 08:09:25 +00:00
|
|
|
return PSP_ERROR_UMD_INVALID_PARAM;
|
2012-12-02 00:40:13 +00:00
|
|
|
|
2012-12-02 06:05:35 +00:00
|
|
|
__KernelUmdDeactivate();
|
|
|
|
|
2013-05-19 06:41:43 +00:00
|
|
|
if (mode == 1) {
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "0=sceUmdDeactivate(%d, %s)", mode, name);
|
2013-05-19 06:41:43 +00:00
|
|
|
} else {
|
2013-09-07 20:02:55 +00:00
|
|
|
ERROR_LOG(SCEIO, "UNTESTED 0=sceUmdDeactivate(%d, %s)", mode, name);
|
2012-12-02 00:40:13 +00:00
|
|
|
}
|
|
|
|
|
2012-12-09 08:09:25 +00:00
|
|
|
return 0;
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2014-12-08 09:40:08 +00:00
|
|
|
static u32 sceUmdRegisterUMDCallBack(u32 cbId)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2013-09-02 10:02:48 +00:00
|
|
|
int retVal = 0;
|
2012-12-02 08:20:28 +00:00
|
|
|
|
|
|
|
// TODO: If the callback is invalid, return PSP_ERROR_UMD_INVALID_PARAM.
|
2013-09-02 08:25:41 +00:00
|
|
|
if (!kernelObjects.IsValid(cbId)) {
|
2012-12-02 08:20:28 +00:00
|
|
|
retVal = PSP_ERROR_UMD_INVALID_PARAM;
|
2013-09-02 08:25:41 +00:00
|
|
|
} else {
|
|
|
|
// There's only ever one.
|
2012-12-02 07:48:11 +00:00
|
|
|
driveCBId = cbId;
|
2012-11-18 21:28:26 +00:00
|
|
|
}
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "%d=sceUmdRegisterUMDCallback(id=%08x)", retVal, cbId);
|
2012-12-02 08:20:28 +00:00
|
|
|
return retVal;
|
2012-11-07 14:44:13 +00:00
|
|
|
}
|
|
|
|
|
2014-12-08 09:40:08 +00:00
|
|
|
static int sceUmdUnRegisterUMDCallBack(int cbId)
|
2012-11-07 14:44:13 +00:00
|
|
|
{
|
2013-03-02 21:50:38 +00:00
|
|
|
int retVal;
|
2012-12-02 08:20:28 +00:00
|
|
|
|
2014-01-31 07:10:26 +00:00
|
|
|
if (cbId != driveCBId) {
|
2012-12-02 08:20:28 +00:00
|
|
|
retVal = PSP_ERROR_UMD_INVALID_PARAM;
|
2014-01-31 07:10:26 +00:00
|
|
|
} else {
|
|
|
|
if (sceKernelGetCompiledSdkVersion() > 0x3000000) {
|
|
|
|
retVal = 0;
|
|
|
|
} else {
|
|
|
|
retVal = cbId;
|
|
|
|
}
|
|
|
|
driveCBId = 0;
|
2012-12-02 08:20:28 +00:00
|
|
|
}
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "%08x=sceUmdUnRegisterUMDCallBack(id=%08x)", retVal, cbId);
|
2012-12-02 08:20:28 +00:00
|
|
|
return retVal;
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2014-12-08 09:40:08 +00:00
|
|
|
static u32 sceUmdGetDriveStat()
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
|
|
|
//u32 retVal = PSP_UMD_INITED | PSP_UMD_READY | PSP_UMD_PRESENT;
|
|
|
|
u32 retVal = __KernelUmdGetState();
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO,"0x%02x=sceUmdGetDriveStat()", retVal);
|
2012-11-11 22:47:28 +00:00
|
|
|
return retVal;
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2012-12-02 07:05:03 +00:00
|
|
|
void __UmdStatTimeout(u64 userdata, int cyclesLate)
|
|
|
|
{
|
|
|
|
SceUID threadID = (SceUID)userdata;
|
|
|
|
|
|
|
|
u32 error;
|
|
|
|
SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_UMD, error);
|
|
|
|
// Assuming it's still waiting.
|
|
|
|
if (waitID == 1)
|
|
|
|
__KernelResumeThreadFromWait(threadID, SCE_KERNEL_ERROR_WAIT_TIMEOUT);
|
2013-02-25 10:19:08 +00:00
|
|
|
|
2013-09-09 04:41:54 +00:00
|
|
|
HLEKernel::RemoveWaitingThread(umdWaitingThreads, threadID);
|
2012-12-02 07:05:03 +00:00
|
|
|
}
|
|
|
|
|
2014-12-08 09:40:08 +00:00
|
|
|
static void __UmdWaitStat(u32 timeout)
|
2012-12-02 07:05:03 +00:00
|
|
|
{
|
|
|
|
// This happens to be how the hardware seems to time things.
|
|
|
|
if (timeout <= 4)
|
|
|
|
timeout = 15;
|
|
|
|
else if (timeout <= 215)
|
|
|
|
timeout = 250;
|
|
|
|
|
2013-02-25 10:19:08 +00:00
|
|
|
CoreTiming::ScheduleEvent(usToCycles((int) timeout), umdStatTimeoutEvent, __KernelGetCurThread());
|
2012-12-02 07:05:03 +00:00
|
|
|
}
|
|
|
|
|
2012-11-01 15:19:01 +00:00
|
|
|
/**
|
|
|
|
* Wait for a drive to reach a certain state
|
|
|
|
*
|
|
|
|
* @param stat - The drive stat to wait for.
|
|
|
|
* @return < 0 on error
|
|
|
|
*
|
|
|
|
*/
|
2014-12-08 09:40:08 +00:00
|
|
|
static int sceUmdWaitDriveStat(u32 stat)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2013-08-28 15:13:44 +00:00
|
|
|
if (stat == 0) {
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStat(stat = %08x): bad status", stat);
|
2013-08-28 15:13:44 +00:00
|
|
|
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!__KernelIsDispatchEnabled()) {
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStat(stat = %08x): dispatch disabled", stat);
|
2013-08-28 15:13:44 +00:00
|
|
|
return SCE_KERNEL_ERROR_CAN_NOT_WAIT;
|
|
|
|
}
|
2013-08-29 06:15:13 +00:00
|
|
|
if (__IsInInterrupt()) {
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStat(stat = %08x): inside interrupt", stat);
|
2013-08-29 06:15:13 +00:00
|
|
|
return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;
|
|
|
|
}
|
2012-12-02 02:43:45 +00:00
|
|
|
|
2013-05-19 06:41:43 +00:00
|
|
|
if ((stat & __KernelUmdGetState()) == 0) {
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStat(stat = %08x): waiting", stat);
|
2013-05-27 21:09:38 +00:00
|
|
|
umdWaitingThreads.push_back(__KernelGetCurThread());
|
2013-01-26 18:44:04 +00:00
|
|
|
__KernelWaitCurThread(WAITTYPE_UMD, 1, stat, 0, 0, "umd stat waited");
|
2013-08-28 15:13:44 +00:00
|
|
|
return 0;
|
2013-02-25 10:19:08 +00:00
|
|
|
}
|
2012-12-10 05:38:05 +00:00
|
|
|
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "0=sceUmdWaitDriveStat(stat = %08x)", stat);
|
2012-12-10 05:38:05 +00:00
|
|
|
return 0;
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2014-12-08 09:40:08 +00:00
|
|
|
static int sceUmdWaitDriveStatWithTimer(u32 stat, u32 timeout)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2013-08-28 15:13:44 +00:00
|
|
|
if (stat == 0) {
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatWithTimer(stat = %08x, timeout = %d): bad status", stat, timeout);
|
2013-08-28 15:13:44 +00:00
|
|
|
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!__KernelIsDispatchEnabled()) {
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatWithTimer(stat = %08x, timeout = %d): dispatch disabled", stat, timeout);
|
2013-08-28 15:13:44 +00:00
|
|
|
return SCE_KERNEL_ERROR_CAN_NOT_WAIT;
|
|
|
|
}
|
2013-08-29 06:15:13 +00:00
|
|
|
if (__IsInInterrupt()) {
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatWithTimer(stat = %08x, timeout = %d): inside interrupt", stat, timeout);
|
2013-08-29 06:15:13 +00:00
|
|
|
return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;
|
|
|
|
}
|
2012-12-02 02:43:45 +00:00
|
|
|
|
2013-05-19 06:41:43 +00:00
|
|
|
if ((stat & __KernelUmdGetState()) == 0) {
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatWithTimer(stat = %08x, timeout = %d): waiting", stat, timeout);
|
2012-12-02 07:05:03 +00:00
|
|
|
__UmdWaitStat(timeout);
|
2013-05-27 21:09:38 +00:00
|
|
|
umdWaitingThreads.push_back(__KernelGetCurThread());
|
2013-02-25 10:19:08 +00:00
|
|
|
__KernelWaitCurThread(WAITTYPE_UMD, 1, stat, 0, 0, "umd stat waited with timer");
|
2013-08-28 15:13:44 +00:00
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
hleReSchedule("umd stat checked");
|
|
|
|
}
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "0=sceUmdWaitDriveStatWithTimer(stat = %08x, timeout = %d)", stat, timeout);
|
2012-12-10 05:38:05 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2012-11-18 19:34:10 +00:00
|
|
|
|
2014-12-08 09:40:08 +00:00
|
|
|
static int sceUmdWaitDriveStatCB(u32 stat, u32 timeout)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2013-08-28 15:13:44 +00:00
|
|
|
if (stat == 0) {
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatCB(stat = %08x, timeout = %d): bad status", stat, timeout);
|
2013-08-28 15:13:44 +00:00
|
|
|
return SCE_KERNEL_ERROR_ERRNO_INVALID_ARGUMENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!__KernelIsDispatchEnabled()) {
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatCB(stat = %08x, timeout = %d): dispatch disabled", stat, timeout);
|
2013-08-28 15:13:44 +00:00
|
|
|
return SCE_KERNEL_ERROR_CAN_NOT_WAIT;
|
|
|
|
}
|
2013-08-29 06:15:13 +00:00
|
|
|
if (__IsInInterrupt()) {
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "sceUmdWaitDriveStatCB(stat = %08x, timeout = %d): inside interrupt", stat, timeout);
|
2013-08-29 06:15:13 +00:00
|
|
|
return SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;
|
|
|
|
}
|
2012-12-02 07:05:03 +00:00
|
|
|
|
2012-12-10 05:38:05 +00:00
|
|
|
hleCheckCurrentCallbacks();
|
2013-05-19 06:41:43 +00:00
|
|
|
if ((stat & __KernelUmdGetState()) == 0) {
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "0=sceUmdWaitDriveStatCB(stat = %08x, timeout = %d): waiting", stat, timeout);
|
2013-08-28 15:13:44 +00:00
|
|
|
if (timeout == 0) {
|
2012-12-02 07:05:03 +00:00
|
|
|
timeout = 8000;
|
2013-08-28 15:13:44 +00:00
|
|
|
}
|
2012-12-02 07:05:03 +00:00
|
|
|
|
|
|
|
__UmdWaitStat(timeout);
|
2013-05-27 21:09:38 +00:00
|
|
|
umdWaitingThreads.push_back(__KernelGetCurThread());
|
2013-01-26 18:44:04 +00:00
|
|
|
__KernelWaitCurThread(WAITTYPE_UMD, 1, stat, 0, true, "umd stat waited");
|
2013-08-28 15:13:44 +00:00
|
|
|
} else {
|
2012-12-09 06:39:36 +00:00
|
|
|
hleReSchedule("umd stat waited");
|
2013-08-28 15:13:44 +00:00
|
|
|
}
|
2012-12-09 08:09:25 +00:00
|
|
|
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "0=sceUmdWaitDriveStatCB(stat = %08x, timeout = %d)", stat, timeout);
|
2012-12-09 08:09:25 +00:00
|
|
|
return 0;
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2014-12-08 09:40:08 +00:00
|
|
|
static u32 sceUmdCancelWaitDriveStat()
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO, "0=sceUmdCancelWaitDriveStat()");
|
2012-12-02 01:09:03 +00:00
|
|
|
|
2013-09-08 18:24:21 +00:00
|
|
|
for (size_t i = 0; i < umdWaitingThreads.size(); ++i) {
|
|
|
|
const SceUID threadID = umdWaitingThreads[i];
|
|
|
|
CoreTiming::UnscheduleEvent(umdStatTimeoutEvent, threadID);
|
2013-09-15 15:18:20 +00:00
|
|
|
HLEKernel::ResumeFromWait(threadID, WAITTYPE_UMD, 1, (int)SCE_KERNEL_ERROR_WAIT_CANCEL);
|
2013-09-08 18:24:21 +00:00
|
|
|
}
|
|
|
|
umdWaitingThreads.clear();
|
|
|
|
|
2012-12-17 19:15:23 +00:00
|
|
|
return 0;
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
|
2014-12-08 09:40:08 +00:00
|
|
|
static u32 sceUmdGetErrorStat()
|
2012-11-07 19:59:56 +00:00
|
|
|
{
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO,"%i=sceUmdGetErrorStat()", umdErrorStat);
|
2012-11-11 22:47:28 +00:00
|
|
|
return umdErrorStat;
|
2012-11-07 19:59:56 +00:00
|
|
|
}
|
|
|
|
|
2013-11-24 04:52:55 +00:00
|
|
|
void __UmdReplace(std::string filepath) {
|
2014-11-23 21:59:56 +00:00
|
|
|
// TODO: This should really go through Loaders, no? What if it's an invalid file?
|
|
|
|
|
2013-11-24 08:08:05 +00:00
|
|
|
// Only get system from disc0 seems have been enough.
|
|
|
|
IFileSystem* currentUMD = pspFileSystem.GetSystem("disc0:");
|
2014-08-02 10:35:27 +00:00
|
|
|
IFileSystem* currentISOBlock = pspFileSystem.GetSystem("umd0:");
|
2013-11-24 08:08:05 +00:00
|
|
|
if (!currentUMD)
|
|
|
|
return;
|
2013-11-24 03:02:36 +00:00
|
|
|
|
2014-11-23 22:02:35 +00:00
|
|
|
FileLoader *loadedFile = ConstructFileLoader(filepath);
|
2014-11-23 21:59:56 +00:00
|
|
|
|
2013-11-24 03:02:36 +00:00
|
|
|
IFileSystem* umd2;
|
2014-11-23 21:59:56 +00:00
|
|
|
if (!loadedFile->Exists()) {
|
|
|
|
delete loadedFile;
|
2013-11-24 04:26:39 +00:00
|
|
|
return;
|
2014-11-23 21:59:56 +00:00
|
|
|
}
|
|
|
|
UpdateLoadedFile(loadedFile);
|
|
|
|
|
2014-11-25 20:02:19 +00:00
|
|
|
if (loadedFile->IsDirectory()) {
|
2013-11-24 04:52:55 +00:00
|
|
|
umd2 = new VirtualDiscFileSystem(&pspFileSystem, filepath);
|
2013-11-24 03:02:36 +00:00
|
|
|
} else {
|
2014-11-23 21:59:56 +00:00
|
|
|
auto bd = constructBlockDevice(loadedFile);
|
2013-11-24 03:02:36 +00:00
|
|
|
if (!bd)
|
|
|
|
return;
|
|
|
|
umd2 = new ISOFileSystem(&pspFileSystem, bd);
|
2013-11-24 10:46:24 +00:00
|
|
|
pspFileSystem.Remount(currentUMD, umd2);
|
2014-08-02 10:35:27 +00:00
|
|
|
|
|
|
|
if (currentUMD != currentISOBlock) {
|
|
|
|
// We mounted an ISO block system separately.
|
|
|
|
IFileSystem *iso = new ISOBlockSystem(static_cast<ISOFileSystem *>(umd2));
|
|
|
|
pspFileSystem.Remount(currentISOBlock, iso);
|
|
|
|
delete currentISOBlock;
|
|
|
|
}
|
2013-11-24 03:02:36 +00:00
|
|
|
}
|
2013-11-24 08:08:05 +00:00
|
|
|
delete currentUMD;
|
2017-01-28 10:39:20 +00:00
|
|
|
UMDInserted = false;
|
2017-01-28 13:09:37 +00:00
|
|
|
CoreTiming::ScheduleEvent(usToCycles(200*1000), umdInsertChangeEvent, 0); // Wait sceUmdCheckMedium call
|
2013-11-24 08:08:05 +00:00
|
|
|
// TODO Is this always correct if UMD was not activated?
|
2013-11-24 03:02:36 +00:00
|
|
|
u32 notifyArg = PSP_UMD_PRESENT | PSP_UMD_READABLE | PSP_UMD_CHANGED;
|
|
|
|
if (driveCBId != -1)
|
|
|
|
__KernelNotifyCallback(driveCBId, notifyArg);
|
|
|
|
}
|
|
|
|
|
2013-11-24 08:08:05 +00:00
|
|
|
bool getUMDReplacePermit() {
|
|
|
|
return UMDReplacePermit;
|
|
|
|
}
|
|
|
|
|
2014-12-08 09:40:08 +00:00
|
|
|
static u32 sceUmdReplaceProhibit()
|
2012-12-17 21:52:31 +00:00
|
|
|
{
|
2013-11-24 03:02:36 +00:00
|
|
|
UMDReplacePermit = false;
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO,"sceUmdReplaceProhibit()");
|
2017-01-29 10:41:31 +00:00
|
|
|
host->UpdateUI();
|
2012-12-17 21:52:31 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-12-08 09:40:08 +00:00
|
|
|
static u32 sceUmdReplacePermit()
|
2012-12-17 21:52:31 +00:00
|
|
|
{
|
2013-11-24 03:02:36 +00:00
|
|
|
UMDReplacePermit = true;
|
2013-09-07 20:02:55 +00:00
|
|
|
DEBUG_LOG(SCEIO,"sceUmdReplacePermit()");
|
2017-01-29 10:41:31 +00:00
|
|
|
host->UpdateUI();
|
2012-12-17 21:52:31 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-01 15:19:01 +00:00
|
|
|
const HLEFunction sceUmdUser[] =
|
|
|
|
{
|
2015-03-22 23:57:56 +00:00
|
|
|
{0XC6183D47, &WrapI_UC<sceUmdActivate>, "sceUmdActivate", 'i', "xs"},
|
|
|
|
{0X6B4A146C, &WrapU_V<sceUmdGetDriveStat>, "sceUmdGetDriveStat", 'x', "" },
|
|
|
|
{0X46EBB729, &WrapI_V<sceUmdCheckMedium>, "sceUmdCheckMedium", 'i', "" },
|
|
|
|
{0XE83742BA, &WrapI_UC<sceUmdDeactivate>, "sceUmdDeactivate", 'i', "xs"},
|
|
|
|
{0X8EF08FCE, &WrapI_U<sceUmdWaitDriveStat>, "sceUmdWaitDriveStat", 'i', "x" },
|
|
|
|
{0X56202973, &WrapI_UU<sceUmdWaitDriveStatWithTimer>, "sceUmdWaitDriveStatWithTimer", 'i', "xx"},
|
|
|
|
{0X4A9E5E29, &WrapI_UU<sceUmdWaitDriveStatCB>, "sceUmdWaitDriveStatCB", 'i', "xx"},
|
|
|
|
{0X6AF9B50A, &WrapU_V<sceUmdCancelWaitDriveStat>, "sceUmdCancelWaitDriveStat", 'x', "" },
|
|
|
|
{0X20628E6F, &WrapU_V<sceUmdGetErrorStat>, "sceUmdGetErrorStat", 'x', "" },
|
|
|
|
{0X340B7686, &WrapU_U<sceUmdGetDiscInfo>, "sceUmdGetDiscInfo", 'x', "x" },
|
|
|
|
{0XAEE7404D, &WrapU_U<sceUmdRegisterUMDCallBack>, "sceUmdRegisterUMDCallBack", 'x', "x" },
|
|
|
|
{0XBD2BDE07, &WrapI_I<sceUmdUnRegisterUMDCallBack>, "sceUmdUnRegisterUMDCallBack", 'i', "i" },
|
|
|
|
{0X87533940, &WrapU_V<sceUmdReplaceProhibit>, "sceUmdReplaceProhibit", 'x', "" },
|
|
|
|
{0XCBE9F02A, &WrapU_V<sceUmdReplacePermit>, "sceUmdReplacePermit", 'x', "" },
|
|
|
|
{0X14C6C45C, nullptr, "sceUmdUnuseUMDInMsUsbWlan", '?', "" },
|
|
|
|
{0XB103FA38, nullptr, "sceUmdUseUMDInMsUsbWlan", '?', "" },
|
2012-11-01 15:19:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
void Register_sceUmdUser()
|
|
|
|
{
|
|
|
|
RegisterModule("sceUmdUser", ARRAY_SIZE(sceUmdUser), sceUmdUser);
|
|
|
|
}
|