Merge pull request #1854 from JosJuice/read-disc-after-delay
DVDInterface: Read disc after delay, not before
This commit is contained in:
commit
8cc6e5cff9
|
@ -234,8 +234,14 @@ void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata
|
|||
tsQueue.Push(ne);
|
||||
}
|
||||
|
||||
// Executes an event immediately, then returns.
|
||||
void ScheduleEvent_Immediate(int event_type, u64 userdata)
|
||||
{
|
||||
event_types[event_type].callback(userdata, 0);
|
||||
}
|
||||
|
||||
// Same as ScheduleEvent_Threadsafe(0, ...) EXCEPT if we are already on the CPU thread
|
||||
// in which case the event will get handled immediately, before returning.
|
||||
// in which case this is the same as ScheduleEvent_Immediate.
|
||||
void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata)
|
||||
{
|
||||
if (Core::IsCPUThread())
|
||||
|
|
|
@ -43,11 +43,11 @@ void DoState(PointerWrap &p);
|
|||
int RegisterEvent(const std::string& name, TimedCallback callback);
|
||||
void UnregisterAllEvents();
|
||||
|
||||
// userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from disk,
|
||||
// when we implement state saves.
|
||||
void ScheduleEvent(int cyclesIntoFuture, int event_type, u64 userdata=0);
|
||||
void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata=0);
|
||||
void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata=0);
|
||||
// userdata MAY NOT CONTAIN POINTERS. userdata might get written and reloaded from savestates.
|
||||
void ScheduleEvent(int cyclesIntoFuture, int event_type, u64 userdata = 0);
|
||||
void ScheduleEvent_Immediate(int event_type, u64 userdata = 0);
|
||||
void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata = 0);
|
||||
void ScheduleEvent_Threadsafe_Immediate(int event_type, u64 userdata = 0);
|
||||
|
||||
// We only permit one event of each type in the queue at a time.
|
||||
void RemoveEvent(int event_type);
|
||||
|
|
|
@ -213,6 +213,22 @@ union UDICFG
|
|||
UDICFG(u32 _hex) {Hex = _hex;}
|
||||
};
|
||||
|
||||
struct DVDReadCommand
|
||||
{
|
||||
bool is_valid;
|
||||
|
||||
u64 DVD_offset;
|
||||
u32 output_address;
|
||||
u32 length;
|
||||
bool decrypt;
|
||||
|
||||
DIInterruptType interrupt_type;
|
||||
|
||||
// Used to notify emulated software after executing command.
|
||||
// Pointers don't work with savestates, so CoreTiming events are used instead
|
||||
int callback_event_type;
|
||||
};
|
||||
|
||||
|
||||
// STATE_TO_SAVE
|
||||
// hardware registers
|
||||
|
@ -225,6 +241,8 @@ static UDICR m_DICR;
|
|||
static UDIIMMBUF m_DIIMMBUF;
|
||||
static UDICFG m_DICFG;
|
||||
|
||||
static DVDReadCommand current_read_command;
|
||||
|
||||
static u32 AudioPos;
|
||||
static u32 CurrentStart;
|
||||
static u32 CurrentLength;
|
||||
|
@ -236,7 +254,8 @@ static u32 g_ErrorCode = 0;
|
|||
static bool g_bDiscInside = false;
|
||||
bool g_bStream = false;
|
||||
static bool g_bStopAtTrackEnd = false;
|
||||
static int tc = 0;
|
||||
static int finish_execute_command = 0;
|
||||
static int finish_execute_read_command = 0;
|
||||
static int dtk = 0;
|
||||
|
||||
static u64 g_last_read_offset;
|
||||
|
@ -257,8 +276,8 @@ void UpdateInterrupts();
|
|||
void GenerateDIInterrupt(DIInterruptType _DVDInterrupt);
|
||||
|
||||
void WriteImmediate(u32 value, u32 output_address, bool write_to_DIIMMBUF);
|
||||
DVDCommandResult ExecuteReadCommand(u64 DVD_offset, u32 output_address,
|
||||
u32 DVD_length, u32 output_length, bool decrypt);
|
||||
DVDReadCommand ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length,
|
||||
bool decrypt, DIInterruptType* interrupt_type, u64* ticks_until_completion);
|
||||
|
||||
u64 SimulateDiscReadTime(u64 offset, u32 length);
|
||||
s64 CalculateRawDiscReadTime(u64 offset, s64 length);
|
||||
|
@ -274,6 +293,8 @@ void DoState(PointerWrap &p)
|
|||
p.Do(m_DIIMMBUF);
|
||||
p.DoPOD(m_DICFG);
|
||||
|
||||
p.Do(current_read_command);
|
||||
|
||||
p.Do(NextStart);
|
||||
p.Do(AudioPos);
|
||||
p.Do(NextLength);
|
||||
|
@ -291,7 +312,7 @@ void DoState(PointerWrap &p)
|
|||
p.Do(g_bStopAtTrackEnd);
|
||||
}
|
||||
|
||||
static void TransferComplete(u64 userdata, int cyclesLate)
|
||||
static void FinishExecuteCommand(u64 userdata, int cyclesLate)
|
||||
{
|
||||
if (m_DICR.TSTART)
|
||||
{
|
||||
|
@ -301,6 +322,30 @@ static void TransferComplete(u64 userdata, int cyclesLate)
|
|||
}
|
||||
}
|
||||
|
||||
static void FinishExecuteReadCommand(u64 userdata, int cyclesLate)
|
||||
{
|
||||
if (!current_read_command.is_valid)
|
||||
{
|
||||
PanicAlertT("DVDInterface tried to execute non-existing command");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Here is the actual disc reading
|
||||
if (!DVDRead(current_read_command.DVD_offset, current_read_command.output_address,
|
||||
current_read_command.length, current_read_command.decrypt))
|
||||
{
|
||||
PanicAlertT("Can't read from DVD_Plugin - DVD-Interface: Fatal Error");
|
||||
}
|
||||
}
|
||||
|
||||
// The command is marked as invalid because it shouldn't be used again
|
||||
current_read_command.is_valid = false;
|
||||
|
||||
// The final step is to notify the emulated software that the command has been executed
|
||||
CoreTiming::ScheduleEvent_Immediate(current_read_command.callback_event_type,
|
||||
current_read_command.interrupt_type);
|
||||
}
|
||||
|
||||
static u32 ProcessDTKSamples(short *tempPCM, u32 num_samples)
|
||||
{
|
||||
u32 samples_processed = 0;
|
||||
|
@ -376,6 +421,8 @@ void Init()
|
|||
m_DICFG.Hex = 0;
|
||||
m_DICFG.CONFIG = 1; // Disable bootrom descrambler
|
||||
|
||||
current_read_command.is_valid = false;
|
||||
|
||||
AudioPos = 0;
|
||||
NextStart = 0;
|
||||
NextLength = 0;
|
||||
|
@ -393,7 +440,8 @@ void Init()
|
|||
ejectDisc = CoreTiming::RegisterEvent("EjectDisc", EjectDiscCallback);
|
||||
insertDisc = CoreTiming::RegisterEvent("InsertDisc", InsertDiscCallback);
|
||||
|
||||
tc = CoreTiming::RegisterEvent("TransferComplete", TransferComplete);
|
||||
finish_execute_command = CoreTiming::RegisterEvent("FinishExecuteCommand", FinishExecuteCommand);
|
||||
finish_execute_read_command = CoreTiming::RegisterEvent("FinishExecuteReadCommand", FinishExecuteReadCommand);
|
||||
dtk = CoreTiming::RegisterEvent("StreamingTimer", DTKStreamingCallback);
|
||||
|
||||
CoreTiming::ScheduleEvent(0, dtk);
|
||||
|
@ -542,21 +590,8 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
|||
m_DICR.Hex = val & 7;
|
||||
if (m_DICR.TSTART)
|
||||
{
|
||||
DVDCommandResult result = ExecuteCommand(
|
||||
m_DICMDBUF[0].Hex, m_DICMDBUF[1].Hex, m_DICMDBUF[2].Hex,
|
||||
m_DIMAR.Hex, m_DILENGTH.Hex, true);
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed)
|
||||
{
|
||||
// Make sure fast disc speed performs "instant" reads; in addition
|
||||
// to being used to speed up games, fast disc speed is used as a
|
||||
// workaround for crashes in Star Wars Rogue Leader.
|
||||
TransferComplete(result.interrupt_type, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The transfer is finished after a delay
|
||||
CoreTiming::ScheduleEvent((int)result.ticks_until_completion, tc, result.interrupt_type);
|
||||
}
|
||||
ExecuteCommand(m_DICMDBUF[0].Hex, m_DICMDBUF[1].Hex, m_DICMDBUF[2].Hex,
|
||||
m_DIMAR.Hex, m_DILENGTH.Hex, true, finish_execute_command);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
@ -612,38 +647,51 @@ void WriteImmediate(u32 value, u32 output_address, bool write_to_DIIMMBUF)
|
|||
Memory::Write_U32(value, output_address);
|
||||
}
|
||||
|
||||
DVDCommandResult ExecuteReadCommand(u64 DVD_offset, u32 output_address,
|
||||
u32 DVD_length, u32 output_length, bool decrypt)
|
||||
// If the returned DVDReadCommand has is_valid set to true,
|
||||
// FinishExecuteReadCommand must be used to finish executing it
|
||||
DVDReadCommand ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 output_length,
|
||||
bool decrypt, DIInterruptType* interrupt_type, u64* ticks_until_completion)
|
||||
{
|
||||
DVDReadCommand command;
|
||||
|
||||
if (!g_bDiscInside)
|
||||
{
|
||||
g_ErrorCode = ERROR_NO_DISK | ERROR_COVER_H;
|
||||
*interrupt_type = INT_DEINT;
|
||||
command.is_valid = false;
|
||||
return command;
|
||||
}
|
||||
|
||||
if (DVD_length > output_length)
|
||||
{
|
||||
WARN_LOG(DVDINTERFACE, "Detected attempt to read more data from the DVD than fit inside the out buffer. Clamp.");
|
||||
DVD_length = output_length;
|
||||
}
|
||||
|
||||
DVDCommandResult result;
|
||||
result.ticks_until_completion = SimulateDiscReadTime(DVD_offset, DVD_length);
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed)
|
||||
*ticks_until_completion = 0; // An optional hack to speed up loading times
|
||||
else
|
||||
*ticks_until_completion = SimulateDiscReadTime(DVD_offset, DVD_length);
|
||||
|
||||
if (!g_bDiscInside)
|
||||
{
|
||||
g_ErrorCode = ERROR_NO_DISK | ERROR_COVER_H;
|
||||
result.interrupt_type = INT_DEINT;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!DVDRead(DVD_offset, output_address, DVD_length, decrypt))
|
||||
PanicAlertT("Can't read from DVD_Plugin - DVD-Interface: Fatal Error");
|
||||
|
||||
result.interrupt_type = INT_TCINT;
|
||||
return result;
|
||||
*interrupt_type = INT_TCINT;
|
||||
command.is_valid = true;
|
||||
command.DVD_offset = DVD_offset;
|
||||
command.output_address = output_address;
|
||||
command.length = DVD_length;
|
||||
command.decrypt = decrypt;
|
||||
return command;
|
||||
}
|
||||
|
||||
DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2,
|
||||
u32 output_address, u32 output_length, bool write_to_DIIMMBUF)
|
||||
// When the command has finished executing, callback_event_type
|
||||
// will be called using CoreTiming::ScheduleEvent,
|
||||
// with the userdata set to the interrupt type.
|
||||
void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_address, u32 output_length,
|
||||
bool write_to_DIIMMBUF, int callback_event_type)
|
||||
{
|
||||
DVDCommandResult result;
|
||||
result.interrupt_type = INT_TCINT;
|
||||
result.ticks_until_completion = SystemTimers::GetTicksPerSecond() / 15000;
|
||||
DIInterruptType interrupt_type = INT_TCINT;
|
||||
u64 ticks_until_completion = SystemTimers::GetTicksPerSecond() / 15000;
|
||||
DVDReadCommand read_command;
|
||||
read_command.is_valid = false;
|
||||
|
||||
bool GCAM = (SConfig::GetInstance().m_SIDevice[0] == SIDEVICE_AM_BASEBOARD) &&
|
||||
(SConfig::GetInstance().m_EXIDevice[2] == EXIDEVICE_AM_BASEBOARD);
|
||||
|
@ -688,19 +736,21 @@ DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2,
|
|||
// Only seems to be used from WII_IPC, not through direct access
|
||||
case DVDLowReadDiskID:
|
||||
INFO_LOG(DVDINTERFACE, "DVDLowReadDiskID");
|
||||
result = ExecuteReadCommand(0, output_address, 0x20, output_length, false);
|
||||
read_command = ExecuteReadCommand(0, output_address, 0x20, output_length,
|
||||
false, &interrupt_type, &ticks_until_completion);
|
||||
break;
|
||||
|
||||
// Only used from WII_IPC. This is the only read command that decrypts data
|
||||
case DVDLowRead:
|
||||
INFO_LOG(DVDINTERFACE, "DVDLowRead: DVDAddr: 0x%09" PRIx64 ", Size: 0x%x", (u64)command_2 << 2, command_1);
|
||||
result = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, true);
|
||||
read_command = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length,
|
||||
true, &interrupt_type, &ticks_until_completion);
|
||||
break;
|
||||
|
||||
// Probably only used by Wii
|
||||
case DVDLowWaitForCoverClose:
|
||||
INFO_LOG(DVDINTERFACE, "DVDLowWaitForCoverClose");
|
||||
result.interrupt_type = (DIInterruptType)4; // ???
|
||||
interrupt_type = (DIInterruptType)4; // ???
|
||||
break;
|
||||
|
||||
// "Set Extension"...not sure what it does. GC only?
|
||||
|
@ -773,14 +823,15 @@ DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2,
|
|||
(command_2 > 0x7ed40000 && command_2 < 0x7ed40008) ||
|
||||
(((command_2 + command_1) > 0x7ed40000) && (command_2 + command_1) < 0x7ed40008)))
|
||||
{
|
||||
result = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length, false);
|
||||
read_command = ExecuteReadCommand((u64)command_2 << 2, output_address, command_1, output_length,
|
||||
false, &interrupt_type, &ticks_until_completion);
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG(DVDINTERFACE, "DVDLowUnencryptedRead: trying to read out of bounds @ %09" PRIx64, (u64)command_2 << 2);
|
||||
g_ErrorCode = ERROR_READY | ERROR_BLOCK_OOB;
|
||||
// Should cause software to call DVDLowRequestError
|
||||
result.interrupt_type = INT_BRKINT;
|
||||
interrupt_type = INT_BRKINT;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -805,7 +856,7 @@ DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2,
|
|||
// Does not work on retail discs/drives
|
||||
// Retail games send this command to see if they are running on real retail hw
|
||||
g_ErrorCode = ERROR_READY | ERROR_INV_CMD;
|
||||
result.interrupt_type = INT_BRKINT;
|
||||
interrupt_type = INT_BRKINT;
|
||||
break;
|
||||
|
||||
// DMA Read from Disc. Only seems to be used through direct access, not WII_IPC
|
||||
|
@ -869,13 +920,15 @@ DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2,
|
|||
}
|
||||
}
|
||||
|
||||
result = ExecuteReadCommand(iDVDOffset, output_address, command_2, output_length, false);
|
||||
read_command = ExecuteReadCommand(iDVDOffset, output_address, command_2, output_length,
|
||||
false, &interrupt_type, &ticks_until_completion);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x40: // Read DiscID
|
||||
INFO_LOG(DVDINTERFACE, "Read DiscID %08x", Memory::Read_U32(output_address));
|
||||
result = ExecuteReadCommand(0, output_address, 0x20, output_length, false);
|
||||
read_command = ExecuteReadCommand(0, output_address, 0x20, output_length,
|
||||
false, &interrupt_type, &ticks_until_completion);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1210,7 +1263,23 @@ DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2,
|
|||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
// The command will finish executing after a delay,
|
||||
// to simulate the speed of a real disc drive
|
||||
if (read_command.is_valid)
|
||||
{
|
||||
// We schedule a FinishExecuteReadCommand (which will call the actual callback
|
||||
// once it's done) so that the data transfer isn't completed too early.
|
||||
// Most games don't care about it, but if it's done wrong, Resident Evil 3
|
||||
// plays some extra noise when playing the menu selection sound effect.
|
||||
read_command.callback_event_type = callback_event_type;
|
||||
read_command.interrupt_type = interrupt_type;
|
||||
current_read_command = read_command;
|
||||
CoreTiming::ScheduleEvent((int)ticks_until_completion, finish_execute_read_command);
|
||||
}
|
||||
else
|
||||
{
|
||||
CoreTiming::ScheduleEvent((int)ticks_until_completion, callback_event_type, interrupt_type);
|
||||
}
|
||||
}
|
||||
|
||||
// Simulates the timing aspects of reading data from a disc.
|
||||
|
|
|
@ -85,12 +85,6 @@ enum DIInterruptType
|
|||
INT_CVRINT = 3,
|
||||
};
|
||||
|
||||
struct DVDCommandResult
|
||||
{
|
||||
DIInterruptType interrupt_type;
|
||||
u64 ticks_until_completion;
|
||||
};
|
||||
|
||||
void Init();
|
||||
void Shutdown();
|
||||
void DoState(PointerWrap &p);
|
||||
|
@ -105,7 +99,7 @@ void ChangeDisc(const std::string& fileName);
|
|||
// DVD Access Functions
|
||||
bool DVDRead(u64 _iDVDOffset, u32 _iRamAddress, u32 _iLength, bool decrypt);
|
||||
extern bool g_bStream;
|
||||
DVDCommandResult ExecuteCommand(u32 command_0, u32 command_1, u32 command_2,
|
||||
u32 output_address, u32 output_length, bool write_to_DIIMMBUF);
|
||||
void ExecuteCommand(u32 command_0, u32 command_1, u32 command_2, u32 output_address, u32 output_length,
|
||||
bool write_to_DIIMMBUF, int callback_event_type);
|
||||
|
||||
} // end of namespace DVDInterface
|
||||
|
|
|
@ -85,7 +85,7 @@ static u64 last_reply_time;
|
|||
|
||||
static const u64 ENQUEUE_REQUEST_FLAG = 0x100000000ULL;
|
||||
static const u64 ENQUEUE_ACKNOWLEDGEMENT_FLAG = 0x200000000ULL;
|
||||
static void EnqueueEventCallback(u64 userdata, int)
|
||||
static void EnqueueEvent(u64 userdata, int cycles_late = 0)
|
||||
{
|
||||
if (userdata & ENQUEUE_ACKNOWLEDGEMENT_FLAG)
|
||||
{
|
||||
|
@ -144,7 +144,7 @@ void Init()
|
|||
g_DeviceMap[i] = new CWII_IPC_HLE_Device_stub(i, "/dev/usb/oh1"); i++;
|
||||
g_DeviceMap[i] = new IWII_IPC_HLE_Device(i, "_Unimplemented_Device_"); i++;
|
||||
|
||||
event_enqueue = CoreTiming::RegisterEvent("IPCEvent", EnqueueEventCallback);
|
||||
event_enqueue = CoreTiming::RegisterEvent("IPCEvent", EnqueueEvent);
|
||||
}
|
||||
|
||||
void Reset(bool _bHard)
|
||||
|
@ -563,6 +563,11 @@ void EnqueueReply_Threadsafe(u32 address, int cycles_in_future)
|
|||
CoreTiming::ScheduleEvent_Threadsafe(cycles_in_future, event_enqueue, address);
|
||||
}
|
||||
|
||||
void EnqueueReply_Immediate(u32 address)
|
||||
{
|
||||
EnqueueEvent(address);
|
||||
}
|
||||
|
||||
void EnqueueCommandAcknowledgement(u32 address, int cycles_in_future)
|
||||
{
|
||||
CoreTiming::ScheduleEvent(cycles_in_future, event_enqueue,
|
||||
|
|
|
@ -80,6 +80,7 @@ void ExecuteCommand(u32 _Address);
|
|||
void EnqueueRequest(u32 address);
|
||||
void EnqueueReply(u32 address, int cycles_in_future = 0);
|
||||
void EnqueueReply_Threadsafe(u32 address, int cycles_in_future = 0);
|
||||
void EnqueueReply_Immediate(u32 address);
|
||||
void EnqueueCommandAcknowledgement(u32 _Address, int cycles_in_future = 0);
|
||||
|
||||
} // end of namespace WII_IPC_HLE_Interface
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "Common/Logging/LogManager.h"
|
||||
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/VolumeHandler.h"
|
||||
#include "Core/HW/DVDInterface.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
|
@ -17,14 +18,38 @@
|
|||
#include "Core/IPC_HLE/WII_IPC_HLE.h"
|
||||
#include "Core/IPC_HLE/WII_IPC_HLE_Device_DI.h"
|
||||
|
||||
using namespace DVDInterface;
|
||||
static CWII_IPC_HLE_Device_di* g_di_pointer;
|
||||
static int ioctl_callback;
|
||||
|
||||
CWII_IPC_HLE_Device_di::CWII_IPC_HLE_Device_di(u32 _DeviceID, const std::string& _rDeviceName )
|
||||
static void IOCtlCallback(u64 userdata, int cycles_late)
|
||||
{
|
||||
if (g_di_pointer != nullptr)
|
||||
g_di_pointer->FinishIOCtl((DVDInterface::DIInterruptType)userdata);
|
||||
|
||||
// If g_di_pointer == nullptr, IOS was probably shut down,
|
||||
// so the command shouldn't be completed
|
||||
}
|
||||
|
||||
CWII_IPC_HLE_Device_di::CWII_IPC_HLE_Device_di(u32 _DeviceID, const std::string& _rDeviceName)
|
||||
: IWII_IPC_HLE_Device(_DeviceID, _rDeviceName)
|
||||
{}
|
||||
{
|
||||
if (g_di_pointer == nullptr)
|
||||
ERROR_LOG(WII_IPC_DVD, "Trying to run two DI devices at once. IOCtl may not behave as expected.");
|
||||
|
||||
g_di_pointer = this;
|
||||
ioctl_callback = CoreTiming::RegisterEvent("IOCtlCallbackDI", IOCtlCallback);
|
||||
}
|
||||
|
||||
CWII_IPC_HLE_Device_di::~CWII_IPC_HLE_Device_di()
|
||||
{}
|
||||
{
|
||||
g_di_pointer = nullptr;
|
||||
}
|
||||
|
||||
void CWII_IPC_HLE_Device_di::DoState(PointerWrap& p)
|
||||
{
|
||||
DoStateShared(p);
|
||||
p.Do(m_commands_to_execute);
|
||||
}
|
||||
|
||||
IPCCommandResult CWII_IPC_HLE_Device_di::Open(u32 _CommandAddress, u32 _Mode)
|
||||
{
|
||||
|
@ -43,10 +68,29 @@ IPCCommandResult CWII_IPC_HLE_Device_di::Close(u32 _CommandAddress, bool _bForce
|
|||
|
||||
IPCCommandResult CWII_IPC_HLE_Device_di::IOCtl(u32 _CommandAddress)
|
||||
{
|
||||
u32 BufferIn = Memory::Read_U32(_CommandAddress + 0x10);
|
||||
u32 BufferInSize = Memory::Read_U32(_CommandAddress + 0x14);
|
||||
u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18);
|
||||
u32 BufferOutSize = Memory::Read_U32(_CommandAddress + 0x1C);
|
||||
// DI IOCtls are handled in a special way by Dolphin
|
||||
// compared to other WII_IPC_HLE functions.
|
||||
// This is a wrapper around DVDInterface's ExecuteCommand,
|
||||
// which will execute commands more or less asynchronously.
|
||||
// Only one command can be executed at a time, so commands
|
||||
// are queued until DVDInterface is ready to handle them.
|
||||
|
||||
bool ready_to_execute = m_commands_to_execute.empty();
|
||||
m_commands_to_execute.push_back(_CommandAddress);
|
||||
if (ready_to_execute)
|
||||
StartIOCtl(_CommandAddress);
|
||||
|
||||
// DVDInterface handles the timing, and we handle the reply,
|
||||
// so WII_IPC_HLE shouldn't do any of that.
|
||||
return IPC_NO_REPLY;
|
||||
}
|
||||
|
||||
void CWII_IPC_HLE_Device_di::StartIOCtl(u32 command_address)
|
||||
{
|
||||
u32 BufferIn = Memory::Read_U32(command_address + 0x10);
|
||||
u32 BufferInSize = Memory::Read_U32(command_address + 0x14);
|
||||
u32 BufferOut = Memory::Read_U32(command_address + 0x18);
|
||||
u32 BufferOutSize = Memory::Read_U32(command_address + 0x1C);
|
||||
|
||||
u32 command_0 = Memory::Read_U32(BufferIn);
|
||||
u32 command_1 = Memory::Read_U32(BufferIn + 4);
|
||||
|
@ -63,14 +107,38 @@ IPCCommandResult CWII_IPC_HLE_Device_di::IOCtl(u32 _CommandAddress)
|
|||
Memory::Memset(BufferOut, 0, BufferOutSize);
|
||||
}
|
||||
|
||||
DVDCommandResult result = ExecuteCommand(command_0, command_1, command_2,
|
||||
BufferOut, BufferOutSize, false);
|
||||
Memory::Write_U32(result.interrupt_type, _CommandAddress + 0x4);
|
||||
// DVDInterface's ExecuteCommand handles most of the work.
|
||||
// The IOCtl callback is used to generate a reply afterwards.
|
||||
DVDInterface::ExecuteCommand(command_0, command_1, command_2, BufferOut, BufferOutSize,
|
||||
false, ioctl_callback);
|
||||
}
|
||||
|
||||
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bFastDiscSpeed)
|
||||
result.ticks_until_completion = 0; // An optional hack to speed up loading times
|
||||
void CWII_IPC_HLE_Device_di::FinishIOCtl(DVDInterface::DIInterruptType interrupt_type)
|
||||
{
|
||||
if (m_commands_to_execute.empty())
|
||||
{
|
||||
PanicAlertT("WII_IPC_HLE_Device_DI tried to reply to non-existing command");
|
||||
return;
|
||||
}
|
||||
|
||||
return { true, result.ticks_until_completion };
|
||||
// This command has been executed, so it's removed from the queue
|
||||
u32 command_address = m_commands_to_execute.front();
|
||||
m_commands_to_execute.pop_front();
|
||||
|
||||
// The DI interrupt type is used as a return value
|
||||
Memory::Write_U32(interrupt_type, command_address + 4);
|
||||
|
||||
// The original hardware overwrites the command type with the async reply type.
|
||||
Memory::Write_U32(IPC_REP_ASYNC, command_address);
|
||||
// IOS also seems to write back the command that was responded to in the FD field.
|
||||
Memory::Write_U32(Memory::Read_U32(command_address), command_address + 8);
|
||||
// Generate a reply to the IPC command
|
||||
WII_IPC_HLE_Interface::EnqueueReply_Immediate(command_address);
|
||||
|
||||
// DVDInterface is now ready to execute another command,
|
||||
// so we start executing a command from the queue if there is one
|
||||
if (!m_commands_to_execute.empty())
|
||||
StartIOCtl(m_commands_to_execute.front());
|
||||
}
|
||||
|
||||
IPCCommandResult CWII_IPC_HLE_Device_di::IOCtlV(u32 _CommandAddress)
|
||||
|
@ -88,7 +156,7 @@ IPCCommandResult CWII_IPC_HLE_Device_di::IOCtlV(u32 _CommandAddress)
|
|||
u32 ReturnValue = 0;
|
||||
switch (CommandBuffer.Parameter)
|
||||
{
|
||||
case DVDLowOpenPartition:
|
||||
case DVDInterface::DVDLowOpenPartition:
|
||||
{
|
||||
_dbg_assert_msg_(WII_IPC_DVD, CommandBuffer.InBuffer[1].m_Address == 0, "DVDLowOpenPartition with ticket");
|
||||
_dbg_assert_msg_(WII_IPC_DVD, CommandBuffer.InBuffer[2].m_Address == 0, "DVDLowOpenPartition with cert chain");
|
||||
|
|
|
@ -4,14 +4,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include "Core/HW/DVDInterface.h"
|
||||
#include "Core/IPC_HLE/WII_IPC_HLE_Device.h"
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
class IVolume;
|
||||
class IFileSystem;
|
||||
}
|
||||
|
||||
class CWII_IPC_HLE_Device_di : public IWII_IPC_HLE_Device
|
||||
{
|
||||
public:
|
||||
|
@ -20,9 +16,18 @@ public:
|
|||
|
||||
virtual ~CWII_IPC_HLE_Device_di();
|
||||
|
||||
void DoState(PointerWrap& p) override;
|
||||
|
||||
IPCCommandResult Open(u32 _CommandAddress, u32 _Mode) override;
|
||||
IPCCommandResult Close(u32 _CommandAddress, bool _bForce) override;
|
||||
|
||||
IPCCommandResult IOCtl(u32 _CommandAddress) override;
|
||||
IPCCommandResult IOCtlV(u32 _CommandAddress) override;
|
||||
|
||||
void FinishIOCtl(DVDInterface::DIInterruptType interrupt_type);
|
||||
private:
|
||||
|
||||
void StartIOCtl(u32 command_address);
|
||||
|
||||
std::deque<u32> m_commands_to_execute;
|
||||
};
|
||||
|
|
|
@ -64,7 +64,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
|
|||
static std::thread g_save_thread;
|
||||
|
||||
// Don't forget to increase this after doing changes on the savestate system
|
||||
static const u32 STATE_VERSION = 39;
|
||||
static const u32 STATE_VERSION = 40;
|
||||
|
||||
enum
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue