diff --git a/Source/Core/Core/CoreTiming.cpp b/Source/Core/Core/CoreTiming.cpp index c8ff6ce675..5e75c27b05 100644 --- a/Source/Core/Core/CoreTiming.cpp +++ b/Source/Core/Core/CoreTiming.cpp @@ -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()) diff --git a/Source/Core/Core/CoreTiming.h b/Source/Core/Core/CoreTiming.h index 1de5b92cec..fdd5fe14eb 100644 --- a/Source/Core/Core/CoreTiming.h +++ b/Source/Core/Core/CoreTiming.h @@ -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); diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index cbf2307da5..278c1d1ee5 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -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. diff --git a/Source/Core/Core/HW/DVDInterface.h b/Source/Core/Core/HW/DVDInterface.h index 5b8a249fee..3f9337a9e6 100644 --- a/Source/Core/Core/HW/DVDInterface.h +++ b/Source/Core/Core/HW/DVDInterface.h @@ -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 diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp b/Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp index 2f5de7e188..08b5ed2ed9 100644 --- a/Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp +++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE.cpp @@ -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, diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE.h b/Source/Core/Core/IPC_HLE/WII_IPC_HLE.h index 1175462dc3..3383642b74 100644 --- a/Source/Core/Core/IPC_HLE/WII_IPC_HLE.h +++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE.h @@ -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 diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp index 9628afde9b..07a22b79a2 100644 --- a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp +++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.cpp @@ -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"); diff --git a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.h b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.h index 6c10e8f258..4ca874ed1d 100644 --- a/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.h +++ b/Source/Core/Core/IPC_HLE/WII_IPC_HLE_Device_DI.h @@ -4,14 +4,10 @@ #pragma once +#include +#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 m_commands_to_execute; }; diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index 23dad1e098..16a86a9c15 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -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 {