Merge pull request #11637 from AdmiralCurtiss/dvd-class
HW/DVD: Refactor DVDInterface and DVDThread to classes.
This commit is contained in:
commit
00a6f8c82c
|
@ -44,6 +44,7 @@
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
#include "Core/PowerPC/Profiler.h"
|
#include "Core/PowerPC/Profiler.h"
|
||||||
#include "Core/State.h"
|
#include "Core/State.h"
|
||||||
|
#include "Core/System.h"
|
||||||
|
|
||||||
#include "DiscIO/Blob.h"
|
#include "DiscIO/Blob.h"
|
||||||
#include "DiscIO/Enums.h"
|
#include "DiscIO/Enums.h"
|
||||||
|
@ -648,7 +649,7 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_ChangeDisc(J
|
||||||
{
|
{
|
||||||
const std::string path = GetJString(env, jFile);
|
const std::string path = GetJString(env, jFile);
|
||||||
__android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Change Disc: %s", path.c_str());
|
__android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Change Disc: %s", path.c_str());
|
||||||
Core::RunAsCPUThread([&path] { DVDInterface::ChangeDisc(path); });
|
Core::RunAsCPUThread([&path] { Core::System::GetInstance().GetDVDInterface().ChangeDisc(path); });
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jobject JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetLogTypeNames(JNIEnv* env,
|
JNIEXPORT jobject JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetLogTypeNames(JNIEnv* env,
|
||||||
|
|
|
@ -317,7 +317,7 @@ static const DiscIO::VolumeDisc* SetDisc(std::unique_ptr<DiscIO::VolumeDisc> dis
|
||||||
std::vector<std::string> auto_disc_change_paths = {})
|
std::vector<std::string> auto_disc_change_paths = {})
|
||||||
{
|
{
|
||||||
const DiscIO::VolumeDisc* pointer = disc.get();
|
const DiscIO::VolumeDisc* pointer = disc.get();
|
||||||
DVDInterface::SetDisc(std::move(disc), auto_disc_change_paths);
|
Core::System::GetInstance().GetDVDInterface().SetDisc(std::move(disc), auto_disc_change_paths);
|
||||||
return pointer;
|
return pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,7 +344,7 @@ bool CBoot::DVDReadDiscID(Core::System& system, const DiscIO::VolumeDisc& disc,
|
||||||
|
|
||||||
// Transition out of the DiscIdNotRead state (which the drive should be in at this point,
|
// Transition out of the DiscIdNotRead state (which the drive should be in at this point,
|
||||||
// on the assumption that this is only used for the first read)
|
// on the assumption that this is only used for the first read)
|
||||||
DVDInterface::SetDriveState(DVDInterface::DriveState::ReadyNoReadsMade);
|
system.GetDVDInterface().SetDriveState(DVD::DriveState::ReadyNoReadsMade);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -295,11 +295,11 @@ bool CBoot::EmulatedBS2_GC(Core::System& system, const Core::CPUThreadGuard& gua
|
||||||
// No known game uses a size other than the default.
|
// No known game uses a size other than the default.
|
||||||
if (streaming_size == 0)
|
if (streaming_size == 0)
|
||||||
streaming_size = 10;
|
streaming_size = 10;
|
||||||
DVDInterface::AudioBufferConfig(true, streaming_size);
|
system.GetDVDInterface().AudioBufferConfig(true, streaming_size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DVDInterface::AudioBufferConfig(false, 0);
|
system.GetDVDInterface().AudioBufferConfig(false, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool ntsc = DiscIO::IsNTSC(SConfig::GetInstance().m_region);
|
const bool ntsc = DiscIO::IsNTSC(SConfig::GetInstance().m_region);
|
||||||
|
|
|
@ -126,7 +126,7 @@ void SConfig::SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Plat
|
||||||
// (IOS HLE ES calls us with a TMDReader rather than a volume when launching
|
// (IOS HLE ES calls us with a TMDReader rather than a volume when launching
|
||||||
// a disc game, because ES has no reason to be accessing the disc directly.)
|
// a disc game, because ES has no reason to be accessing the disc directly.)
|
||||||
if (platform == DiscIO::Platform::WiiWAD ||
|
if (platform == DiscIO::Platform::WiiWAD ||
|
||||||
!DVDInterface::UpdateRunningGameMetadata(tmd_title_id))
|
!Core::System::GetInstance().GetDVDInterface().UpdateRunningGameMetadata(tmd_title_id))
|
||||||
{
|
{
|
||||||
// If not launching a disc game, just read everything from the TMD.
|
// If not launching a disc game, just read everything from the TMD.
|
||||||
SetRunningGameMetadata(tmd.GetGameID(), tmd.GetGameTDBID(), tmd_title_id, tmd.GetTitleVersion(),
|
SetRunningGameMetadata(tmd.GetGameID(), tmd.GetGameTDBID(), tmd_title_id, tmd.GetTitleVersion(),
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,14 +3,26 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Common/BitField.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
|
||||||
|
#include "Core/HW/StreamADPCM.h"
|
||||||
|
|
||||||
class PointerWrap;
|
class PointerWrap;
|
||||||
|
namespace Core
|
||||||
|
{
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
namespace CoreTiming
|
||||||
|
{
|
||||||
|
struct EventType;
|
||||||
|
}
|
||||||
namespace DiscIO
|
namespace DiscIO
|
||||||
{
|
{
|
||||||
class VolumeDisc;
|
class VolumeDisc;
|
||||||
|
@ -21,25 +33,8 @@ namespace MMIO
|
||||||
class Mapping;
|
class Mapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace DVDInterface
|
namespace DVD
|
||||||
{
|
{
|
||||||
class DVDInterfaceState
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DVDInterfaceState();
|
|
||||||
DVDInterfaceState(const DVDInterfaceState&) = delete;
|
|
||||||
DVDInterfaceState(DVDInterfaceState&&) = delete;
|
|
||||||
DVDInterfaceState& operator=(const DVDInterfaceState&) = delete;
|
|
||||||
DVDInterfaceState& operator=(DVDInterfaceState&&) = delete;
|
|
||||||
~DVDInterfaceState();
|
|
||||||
|
|
||||||
struct Data;
|
|
||||||
Data& GetData() { return *m_data; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<Data> m_data;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class DICommand : u8
|
enum class DICommand : u8
|
||||||
{
|
{
|
||||||
Inquiry = 0x12,
|
Inquiry = 0x12,
|
||||||
|
@ -125,48 +120,185 @@ enum class EjectCause
|
||||||
Software,
|
Software,
|
||||||
};
|
};
|
||||||
|
|
||||||
void Init();
|
class DVDInterface
|
||||||
void ResetDrive(bool spinup);
|
{
|
||||||
void Shutdown();
|
public:
|
||||||
void DoState(PointerWrap& p);
|
explicit DVDInterface(Core::System& system);
|
||||||
|
DVDInterface(const DVDInterface&) = delete;
|
||||||
|
DVDInterface(DVDInterface&&) = delete;
|
||||||
|
DVDInterface& operator=(const DVDInterface&) = delete;
|
||||||
|
DVDInterface& operator=(DVDInterface&&) = delete;
|
||||||
|
~DVDInterface();
|
||||||
|
|
||||||
void RegisterMMIO(MMIO::Mapping* mmio, u32 base, bool is_wii);
|
void Init();
|
||||||
|
void ResetDrive(bool spinup);
|
||||||
|
void Shutdown();
|
||||||
|
void DoState(PointerWrap& p);
|
||||||
|
|
||||||
void SetDisc(std::unique_ptr<DiscIO::VolumeDisc> disc,
|
void RegisterMMIO(MMIO::Mapping* mmio, u32 base, bool is_wii);
|
||||||
std::optional<std::vector<std::string>> auto_disc_change_paths);
|
|
||||||
bool IsDiscInside();
|
|
||||||
void EjectDisc(EjectCause cause); // Must only be called on the CPU thread
|
|
||||||
void ChangeDisc(const std::vector<std::string>& paths); // Must only be called on the CPU thread
|
|
||||||
void ChangeDisc(const std::string& new_path); // Must only be called on the CPU thread
|
|
||||||
bool AutoChangeDisc(); // Must only be called on the CPU thread
|
|
||||||
|
|
||||||
// This function returns true and calls SConfig::SetRunningGameMetadata(Volume&, Partition&)
|
void SetDisc(std::unique_ptr<DiscIO::VolumeDisc> disc,
|
||||||
// if both of the following conditions are true:
|
std::optional<std::vector<std::string>> auto_disc_change_paths);
|
||||||
// - A disc is inserted
|
bool IsDiscInside() const;
|
||||||
// - The title_id argument doesn't contain a value, or its value matches the disc's title ID
|
void EjectDisc(EjectCause cause); // Must only be called on the CPU thread
|
||||||
bool UpdateRunningGameMetadata(std::optional<u64> title_id = {});
|
void ChangeDisc(const std::vector<std::string>& paths); // Must only be called on the CPU thread
|
||||||
|
void ChangeDisc(const std::string& new_path); // Must only be called on the CPU thread
|
||||||
|
bool AutoChangeDisc(); // Must only be called on the CPU thread
|
||||||
|
|
||||||
// Direct access to DI for IOS HLE (simpler to implement than how real IOS accesses DI,
|
// This function returns true and calls SConfig::SetRunningGameMetadata(Volume&, Partition&)
|
||||||
// and lets us skip encrypting/decrypting in some cases)
|
// if both of the following conditions are true:
|
||||||
void ExecuteCommand(ReplyType reply_type);
|
// - A disc is inserted
|
||||||
void PerformDecryptingRead(u32 position, u32 length, u32 output_address,
|
// - The title_id argument doesn't contain a value, or its value matches the disc's title ID
|
||||||
const DiscIO::Partition& partition, ReplyType reply_type);
|
bool UpdateRunningGameMetadata(std::optional<u64> title_id = {});
|
||||||
|
|
||||||
// For circumventing Error #001 in DirectoryBlobs, which may have data in the offsets being checked.
|
// Direct access to DI for IOS HLE (simpler to implement than how real IOS accesses DI,
|
||||||
void ForceOutOfBoundsRead(ReplyType reply_type);
|
// and lets us skip encrypting/decrypting in some cases)
|
||||||
|
void ExecuteCommand(ReplyType reply_type);
|
||||||
|
void PerformDecryptingRead(u32 position, u32 length, u32 output_address,
|
||||||
|
const DiscIO::Partition& partition, ReplyType reply_type);
|
||||||
|
|
||||||
// Exposed for use by emulated BS2; does not perform any checks on drive state
|
// For circumventing Error #001 in DirectoryBlobs, which may have data in the offsets being
|
||||||
void AudioBufferConfig(bool enable_dtk, u8 dtk_buffer_length);
|
// checked.
|
||||||
|
void ForceOutOfBoundsRead(ReplyType reply_type);
|
||||||
|
|
||||||
void SetDriveState(DriveState state);
|
// Exposed for use by emulated BS2; does not perform any checks on drive state
|
||||||
void SetDriveError(DriveError error);
|
void AudioBufferConfig(bool enable_dtk, u8 dtk_buffer_length);
|
||||||
|
|
||||||
// Used by DVDThread
|
void SetDriveState(DriveState state);
|
||||||
void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type, s64 cycles_late,
|
void SetDriveError(DriveError error);
|
||||||
const std::vector<u8>& data = std::vector<u8>());
|
|
||||||
|
|
||||||
// Used by IOS HLE
|
// Used by DVDThread
|
||||||
void SetInterruptEnabled(DIInterruptType interrupt, bool enabled);
|
void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type, s64 cycles_late,
|
||||||
void ClearInterrupt(DIInterruptType interrupt);
|
const std::vector<u8>& data = std::vector<u8>());
|
||||||
|
|
||||||
} // namespace DVDInterface
|
// Used by IOS HLE
|
||||||
|
void SetInterruptEnabled(DIInterruptType interrupt, bool enabled);
|
||||||
|
void ClearInterrupt(DIInterruptType interrupt);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vector<u8>& audio_data,
|
||||||
|
s64 cycles_late);
|
||||||
|
size_t ProcessDTKSamples(std::vector<s16>* temp_pcm, const std::vector<u8>& audio_data);
|
||||||
|
u32 AdvanceDTK(u32 maximum_samples, u32* samples_to_process);
|
||||||
|
|
||||||
|
void SetLidOpen();
|
||||||
|
void UpdateInterrupts();
|
||||||
|
void GenerateDIInterrupt(DIInterruptType dvd_interrupt);
|
||||||
|
|
||||||
|
bool CheckReadPreconditions();
|
||||||
|
bool ExecuteReadCommand(u64 dvd_offset, u32 output_address, u32 dvd_length, u32 output_length,
|
||||||
|
const DiscIO::Partition& partition, ReplyType reply_type,
|
||||||
|
DIInterruptType* interrupt_type);
|
||||||
|
void ScheduleReads(u64 offset, u32 length, const DiscIO::Partition& partition, u32 output_address,
|
||||||
|
ReplyType reply_type);
|
||||||
|
|
||||||
|
static void AutoChangeDiscCallback(Core::System& system, u64 userdata, s64 cyclesLate);
|
||||||
|
static void EjectDiscCallback(Core::System& system, u64 userdata, s64 cyclesLate);
|
||||||
|
static void InsertDiscCallback(Core::System& system, u64 userdata, s64 cyclesLate);
|
||||||
|
static void FinishExecutingCommandCallback(Core::System& system, u64 userdata, s64 cycles_late);
|
||||||
|
|
||||||
|
// DI Status Register
|
||||||
|
union UDISR
|
||||||
|
{
|
||||||
|
u32 Hex = 0;
|
||||||
|
|
||||||
|
BitField<0, 1, u32> BREAK; // Stop the Device + Interrupt
|
||||||
|
BitField<1, 1, u32> DEINTMASK; // Access Device Error Int Mask
|
||||||
|
BitField<2, 1, u32> DEINT; // Access Device Error Int
|
||||||
|
BitField<3, 1, u32> TCINTMASK; // Transfer Complete Int Mask
|
||||||
|
BitField<4, 1, u32> TCINT; // Transfer Complete Int
|
||||||
|
BitField<5, 1, u32> BRKINTMASK;
|
||||||
|
BitField<6, 1, u32> BRKINT; // w 1: clear brkint
|
||||||
|
BitField<7, 25, u32> reserved;
|
||||||
|
|
||||||
|
UDISR() = default;
|
||||||
|
explicit UDISR(u32 hex) : Hex{hex} {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// DI Cover Register
|
||||||
|
union UDICVR
|
||||||
|
{
|
||||||
|
u32 Hex = 0;
|
||||||
|
|
||||||
|
BitField<0, 1, u32> CVR; // 0: Cover closed 1: Cover open
|
||||||
|
BitField<1, 1, u32> CVRINTMASK; // 1: Interrupt enabled
|
||||||
|
BitField<2, 1, u32> CVRINT; // r 1: Interrupt requested w 1: Interrupt clear
|
||||||
|
BitField<3, 29, u32> reserved;
|
||||||
|
|
||||||
|
UDICVR() = default;
|
||||||
|
explicit UDICVR(u32 hex) : Hex{hex} {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// DI DMA Control Register
|
||||||
|
union UDICR
|
||||||
|
{
|
||||||
|
u32 Hex = 0;
|
||||||
|
|
||||||
|
BitField<0, 1, u32> TSTART; // w:1 start r:0 ready
|
||||||
|
BitField<1, 1, u32> DMA; // 1: DMA Mode
|
||||||
|
// 0: Immediate Mode (can only do Access Register Command)
|
||||||
|
BitField<2, 1, u32> RW; // 0: Read Command (DVD to Memory) 1: Write Command (Memory to DVD)
|
||||||
|
BitField<3, 29, u32> reserved;
|
||||||
|
};
|
||||||
|
|
||||||
|
// DI Config Register
|
||||||
|
union UDICFG
|
||||||
|
{
|
||||||
|
u32 Hex = 0;
|
||||||
|
|
||||||
|
BitField<0, 8, u32> CONFIG;
|
||||||
|
BitField<8, 24, u32> reserved;
|
||||||
|
|
||||||
|
UDICFG() = default;
|
||||||
|
explicit UDICFG(u32 hex) : Hex{hex} {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hardware registers
|
||||||
|
UDISR m_DISR;
|
||||||
|
UDICVR m_DICVR;
|
||||||
|
std::array<u32, 3> m_DICMDBUF{};
|
||||||
|
u32 m_DIMAR = 0;
|
||||||
|
u32 m_DILENGTH = 0;
|
||||||
|
UDICR m_DICR;
|
||||||
|
u32 m_DIIMMBUF = 0;
|
||||||
|
UDICFG m_DICFG;
|
||||||
|
|
||||||
|
StreamADPCM::ADPCMDecoder m_adpcm_decoder;
|
||||||
|
|
||||||
|
// DTK
|
||||||
|
bool m_stream = false;
|
||||||
|
bool m_stop_at_track_end = false;
|
||||||
|
u64 m_audio_position = 0;
|
||||||
|
u64 m_current_start = 0;
|
||||||
|
u32 m_current_length = 0;
|
||||||
|
u64 m_next_start = 0;
|
||||||
|
u32 m_next_length = 0;
|
||||||
|
u32 m_pending_samples = 0;
|
||||||
|
bool m_enable_dtk = false;
|
||||||
|
u8 m_dtk_buffer_length = 0; // TODO: figure out how this affects the regular buffer
|
||||||
|
|
||||||
|
// Disc drive state
|
||||||
|
DriveState m_drive_state = DriveState::Ready;
|
||||||
|
DriveError m_error_code = DriveError::None;
|
||||||
|
u64 m_disc_end_offset = 0;
|
||||||
|
|
||||||
|
// Disc drive timing
|
||||||
|
u64 m_read_buffer_start_time = 0;
|
||||||
|
u64 m_read_buffer_end_time = 0;
|
||||||
|
u64 m_read_buffer_start_offset = 0;
|
||||||
|
u64 m_read_buffer_end_offset = 0;
|
||||||
|
|
||||||
|
// Disc changing
|
||||||
|
std::string m_disc_path_to_insert;
|
||||||
|
std::vector<std::string> m_auto_disc_change_paths;
|
||||||
|
size_t m_auto_disc_change_index = 0;
|
||||||
|
|
||||||
|
// Events
|
||||||
|
CoreTiming::EventType* m_finish_executing_command = nullptr;
|
||||||
|
CoreTiming::EventType* m_auto_change_disc = nullptr;
|
||||||
|
CoreTiming::EventType* m_eject_disc = nullptr;
|
||||||
|
CoreTiming::EventType* m_insert_disc = nullptr;
|
||||||
|
|
||||||
|
Core::System& m_system;
|
||||||
|
};
|
||||||
|
} // namespace DVD
|
||||||
|
|
|
@ -34,122 +34,58 @@
|
||||||
#include "DiscIO/Enums.h"
|
#include "DiscIO/Enums.h"
|
||||||
#include "DiscIO/Volume.h"
|
#include "DiscIO/Volume.h"
|
||||||
|
|
||||||
namespace DVDThread
|
namespace DVD
|
||||||
{
|
{
|
||||||
struct ReadRequest
|
DVDThread::DVDThread(Core::System& system) : m_system(system)
|
||||||
{
|
|
||||||
bool copy_to_ram = false;
|
|
||||||
u32 output_address = 0;
|
|
||||||
u64 dvd_offset = 0;
|
|
||||||
u32 length = 0;
|
|
||||||
DiscIO::Partition partition{};
|
|
||||||
|
|
||||||
// This determines which code DVDInterface will run to reply
|
|
||||||
// to the emulated software. We can't use callbacks,
|
|
||||||
// because function pointers can't be stored in savestates.
|
|
||||||
DVDInterface::ReplyType reply_type = DVDInterface::ReplyType::NoReply;
|
|
||||||
|
|
||||||
// IDs are used to uniquely identify a request. They must not be
|
|
||||||
// identical to IDs of any other requests that currently exist, but
|
|
||||||
// it's fine to re-use IDs of requests that have existed in the past.
|
|
||||||
u64 id = 0;
|
|
||||||
|
|
||||||
// Only used for logging
|
|
||||||
u64 time_started_ticks = 0;
|
|
||||||
u64 realtime_started_us = 0;
|
|
||||||
u64 realtime_done_us = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
using ReadResult = std::pair<ReadRequest, std::vector<u8>>;
|
|
||||||
|
|
||||||
static void StartDVDThread(DVDThreadState::Data& state);
|
|
||||||
static void StopDVDThread(DVDThreadState::Data& state);
|
|
||||||
|
|
||||||
static void DVDThread();
|
|
||||||
static void WaitUntilIdle();
|
|
||||||
|
|
||||||
static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length,
|
|
||||||
const DiscIO::Partition& partition,
|
|
||||||
DVDInterface::ReplyType reply_type, s64 ticks_until_completion);
|
|
||||||
|
|
||||||
static void FinishRead(Core::System& system, u64 id, s64 cycles_late);
|
|
||||||
|
|
||||||
struct DVDThreadState::Data
|
|
||||||
{
|
|
||||||
CoreTiming::EventType* finish_read = nullptr;
|
|
||||||
|
|
||||||
u64 next_id = 0;
|
|
||||||
|
|
||||||
std::thread dvd_thread;
|
|
||||||
Common::Event request_queue_expanded; // Is set by CPU thread
|
|
||||||
Common::Event result_queue_expanded; // Is set by DVD thread
|
|
||||||
Common::Flag dvd_thread_exiting = Common::Flag(false); // Is set by CPU thread
|
|
||||||
|
|
||||||
Common::SPSCQueue<ReadRequest, false> request_queue;
|
|
||||||
Common::SPSCQueue<ReadResult, false> result_queue;
|
|
||||||
std::map<u64, ReadResult> result_map;
|
|
||||||
|
|
||||||
std::unique_ptr<DiscIO::Volume> disc;
|
|
||||||
|
|
||||||
FileMonitor::FileLogger file_logger;
|
|
||||||
};
|
|
||||||
|
|
||||||
DVDThreadState::DVDThreadState() : m_data(std::make_unique<Data>())
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
DVDThreadState::~DVDThreadState() = default;
|
DVDThread::~DVDThread() = default;
|
||||||
|
|
||||||
void Start()
|
void DVDThread::Start()
|
||||||
{
|
{
|
||||||
auto& system = Core::System::GetInstance();
|
m_finish_read = m_system.GetCoreTiming().RegisterEvent("FinishReadDVDThread", GlobalFinishRead);
|
||||||
auto& state = system.GetDVDThreadState().GetData();
|
|
||||||
|
|
||||||
state.finish_read = system.GetCoreTiming().RegisterEvent("FinishReadDVDThread", FinishRead);
|
m_request_queue_expanded.Reset();
|
||||||
|
m_result_queue_expanded.Reset();
|
||||||
state.request_queue_expanded.Reset();
|
m_request_queue.Clear();
|
||||||
state.result_queue_expanded.Reset();
|
m_result_queue.Clear();
|
||||||
state.request_queue.Clear();
|
|
||||||
state.result_queue.Clear();
|
|
||||||
|
|
||||||
// This is reset on every launch for determinism, but it doesn't matter
|
// This is reset on every launch for determinism, but it doesn't matter
|
||||||
// much, because this will never get exposed to the emulated game.
|
// much, because this will never get exposed to the emulated game.
|
||||||
state.next_id = 0;
|
m_next_id = 0;
|
||||||
|
|
||||||
StartDVDThread(state);
|
StartDVDThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void StartDVDThread(DVDThreadState::Data& state)
|
void DVDThread::StartDVDThread()
|
||||||
{
|
{
|
||||||
ASSERT(!state.dvd_thread.joinable());
|
ASSERT(!m_dvd_thread.joinable());
|
||||||
state.dvd_thread_exiting.Clear();
|
m_dvd_thread_exiting.Clear();
|
||||||
state.dvd_thread = std::thread(DVDThread);
|
m_dvd_thread = std::thread(&DVDThread::DVDThreadMain, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stop()
|
void DVDThread::Stop()
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
|
StopDVDThread();
|
||||||
StopDVDThread(state);
|
m_disc.reset();
|
||||||
state.disc.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void StopDVDThread(DVDThreadState::Data& state)
|
void DVDThread::StopDVDThread()
|
||||||
{
|
{
|
||||||
ASSERT(state.dvd_thread.joinable());
|
ASSERT(m_dvd_thread.joinable());
|
||||||
|
|
||||||
// By setting dvd_thread_exiting, we ask the DVD thread to cleanly exit.
|
// By setting dvd_thread_exiting, we ask the DVD thread to cleanly exit.
|
||||||
// In case the request queue is empty, we need to set request_queue_expanded
|
// In case the request queue is empty, we need to set request_queue_expanded
|
||||||
// so that the DVD thread will wake up and check dvd_thread_exiting.
|
// so that the DVD thread will wake up and check dvd_thread_exiting.
|
||||||
state.dvd_thread_exiting.Set();
|
m_dvd_thread_exiting.Set();
|
||||||
state.request_queue_expanded.Set();
|
m_request_queue_expanded.Set();
|
||||||
|
|
||||||
state.dvd_thread.join();
|
m_dvd_thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DoState(PointerWrap& p)
|
void DVDThread::DoState(PointerWrap& p)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
|
|
||||||
|
|
||||||
// By waiting for the DVD thread to be done working, we ensure
|
// By waiting for the DVD thread to be done working, we ensure
|
||||||
// that request_queue will be empty and that the DVD thread
|
// that request_queue will be empty and that the DVD thread
|
||||||
// won't be touching anything while this function runs.
|
// won't be touching anything while this function runs.
|
||||||
|
@ -159,14 +95,14 @@ void DoState(PointerWrap& p)
|
||||||
// PointerWrap::Do supports std::map but not Common::SPSCQueue.
|
// PointerWrap::Do supports std::map but not Common::SPSCQueue.
|
||||||
// This won't affect the behavior of FinishRead.
|
// This won't affect the behavior of FinishRead.
|
||||||
ReadResult result;
|
ReadResult result;
|
||||||
while (state.result_queue.Pop(result))
|
while (m_result_queue.Pop(result))
|
||||||
state.result_map.emplace(result.first.id, std::move(result));
|
m_result_map.emplace(result.first.id, std::move(result));
|
||||||
|
|
||||||
// Both queues are now empty, so we don't need to savestate them.
|
// Both queues are now empty, so we don't need to savestate them.
|
||||||
p.Do(state.result_map);
|
p.Do(m_result_map);
|
||||||
p.Do(state.next_id);
|
p.Do(m_next_id);
|
||||||
|
|
||||||
// state.disc isn't savestated (because it points to files on the
|
// m_disc isn't savestated (because it points to files on the
|
||||||
// local system). Instead, we check that the status of the disc
|
// local system). Instead, we check that the status of the disc
|
||||||
// is the same as when the savestate was made. This won't catch
|
// is the same as when the savestate was made. This won't catch
|
||||||
// cases of having the wrong disc inserted, though.
|
// cases of having the wrong disc inserted, though.
|
||||||
|
@ -178,7 +114,7 @@ void DoState(PointerWrap& p)
|
||||||
if (had_disc)
|
if (had_disc)
|
||||||
PanicAlertFmtT("An inserted disc was expected but not found.");
|
PanicAlertFmtT("An inserted disc was expected but not found.");
|
||||||
else
|
else
|
||||||
state.disc.reset();
|
m_disc.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Savestates can be smaller if the buffers of results aren't saved,
|
// TODO: Savestates can be smaller if the buffers of results aren't saved,
|
||||||
|
@ -192,123 +128,108 @@ void DoState(PointerWrap& p)
|
||||||
// was made. Handling that properly may be more effort than it's worth.
|
// was made. Handling that properly may be more effort than it's worth.
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetDisc(std::unique_ptr<DiscIO::Volume> disc)
|
void DVDThread::SetDisc(std::unique_ptr<DiscIO::Volume> disc)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
|
|
||||||
|
|
||||||
WaitUntilIdle();
|
WaitUntilIdle();
|
||||||
state.disc = std::move(disc);
|
m_disc = std::move(disc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HasDisc()
|
bool DVDThread::HasDisc() const
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
|
return m_disc != nullptr;
|
||||||
return state.disc != nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HasWiiHashes()
|
bool DVDThread::HasWiiHashes() const
|
||||||
{
|
{
|
||||||
// HasWiiHashes is thread-safe, so calling WaitUntilIdle isn't necessary.
|
// HasWiiHashes is thread-safe, so calling WaitUntilIdle isn't necessary.
|
||||||
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
|
return m_disc->HasWiiHashes();
|
||||||
return state.disc->HasWiiHashes();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DiscIO::Platform GetDiscType()
|
DiscIO::Platform DVDThread::GetDiscType() const
|
||||||
{
|
{
|
||||||
// GetVolumeType is thread-safe, so calling WaitUntilIdle isn't necessary.
|
// GetVolumeType is thread-safe, so calling WaitUntilIdle isn't necessary.
|
||||||
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
|
return m_disc->GetVolumeType();
|
||||||
return state.disc->GetVolumeType();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 PartitionOffsetToRawOffset(u64 offset, const DiscIO::Partition& partition)
|
u64 DVDThread::PartitionOffsetToRawOffset(u64 offset, const DiscIO::Partition& partition)
|
||||||
{
|
{
|
||||||
// PartitionOffsetToRawOffset is thread-safe, so calling WaitUntilIdle isn't necessary.
|
// PartitionOffsetToRawOffset is thread-safe, so calling WaitUntilIdle isn't necessary.
|
||||||
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
|
return m_disc->PartitionOffsetToRawOffset(offset, partition);
|
||||||
return state.disc->PartitionOffsetToRawOffset(offset, partition);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IOS::ES::TMDReader GetTMD(const DiscIO::Partition& partition)
|
IOS::ES::TMDReader DVDThread::GetTMD(const DiscIO::Partition& partition)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
|
|
||||||
WaitUntilIdle();
|
WaitUntilIdle();
|
||||||
return state.disc->GetTMD(partition);
|
return m_disc->GetTMD(partition);
|
||||||
}
|
}
|
||||||
|
|
||||||
IOS::ES::TicketReader GetTicket(const DiscIO::Partition& partition)
|
IOS::ES::TicketReader DVDThread::GetTicket(const DiscIO::Partition& partition)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
|
|
||||||
WaitUntilIdle();
|
WaitUntilIdle();
|
||||||
return state.disc->GetTicket(partition);
|
return m_disc->GetTicket(partition);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsInsertedDiscRunning()
|
bool DVDThread::IsInsertedDiscRunning()
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
|
if (!m_disc)
|
||||||
|
|
||||||
if (!state.disc)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
WaitUntilIdle();
|
WaitUntilIdle();
|
||||||
|
|
||||||
return SConfig::GetInstance().GetGameID() == state.disc->GetGameID();
|
return SConfig::GetInstance().GetGameID() == m_disc->GetGameID();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UpdateRunningGameMetadata(const DiscIO::Partition& partition, std::optional<u64> title_id)
|
bool DVDThread::UpdateRunningGameMetadata(const DiscIO::Partition& partition,
|
||||||
|
std::optional<u64> title_id)
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
|
if (!m_disc)
|
||||||
|
|
||||||
if (!state.disc)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
WaitUntilIdle();
|
WaitUntilIdle();
|
||||||
|
|
||||||
if (title_id)
|
if (title_id)
|
||||||
{
|
{
|
||||||
const std::optional<u64> volume_title_id = state.disc->GetTitleID(partition);
|
const std::optional<u64> volume_title_id = m_disc->GetTitleID(partition);
|
||||||
if (!volume_title_id || *volume_title_id != *title_id)
|
if (!volume_title_id || *volume_title_id != *title_id)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SConfig::GetInstance().SetRunningGameMetadata(*state.disc, partition);
|
SConfig::GetInstance().SetRunningGameMetadata(*m_disc, partition);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaitUntilIdle()
|
void DVDThread::WaitUntilIdle()
|
||||||
{
|
{
|
||||||
ASSERT(Core::IsCPUThread());
|
ASSERT(Core::IsCPUThread());
|
||||||
|
|
||||||
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
|
while (!m_request_queue.Empty())
|
||||||
|
m_result_queue_expanded.Wait();
|
||||||
|
|
||||||
while (!state.request_queue.Empty())
|
StopDVDThread();
|
||||||
state.result_queue_expanded.Wait();
|
StartDVDThread();
|
||||||
|
|
||||||
StopDVDThread(state);
|
|
||||||
StartDVDThread(state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StartRead(u64 dvd_offset, u32 length, const DiscIO::Partition& partition,
|
void DVDThread::StartRead(u64 dvd_offset, u32 length, const DiscIO::Partition& partition,
|
||||||
DVDInterface::ReplyType reply_type, s64 ticks_until_completion)
|
DVD::ReplyType reply_type, s64 ticks_until_completion)
|
||||||
{
|
{
|
||||||
StartReadInternal(false, 0, dvd_offset, length, partition, reply_type, ticks_until_completion);
|
StartReadInternal(false, 0, dvd_offset, length, partition, reply_type, ticks_until_completion);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length,
|
void DVDThread::StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length,
|
||||||
const DiscIO::Partition& partition, DVDInterface::ReplyType reply_type,
|
const DiscIO::Partition& partition,
|
||||||
s64 ticks_until_completion)
|
DVD::ReplyType reply_type, s64 ticks_until_completion)
|
||||||
{
|
{
|
||||||
StartReadInternal(true, output_address, dvd_offset, length, partition, reply_type,
|
StartReadInternal(true, output_address, dvd_offset, length, partition, reply_type,
|
||||||
ticks_until_completion);
|
ticks_until_completion);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length,
|
void DVDThread::StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length,
|
||||||
const DiscIO::Partition& partition,
|
const DiscIO::Partition& partition, DVD::ReplyType reply_type,
|
||||||
DVDInterface::ReplyType reply_type, s64 ticks_until_completion)
|
s64 ticks_until_completion)
|
||||||
{
|
{
|
||||||
ASSERT(Core::IsCPUThread());
|
ASSERT(Core::IsCPUThread());
|
||||||
|
|
||||||
auto& system = Core::System::GetInstance();
|
auto& core_timing = m_system.GetCoreTiming();
|
||||||
auto& core_timing = system.GetCoreTiming();
|
|
||||||
auto& state = system.GetDVDThreadState().GetData();
|
|
||||||
|
|
||||||
ReadRequest request;
|
ReadRequest request;
|
||||||
|
|
||||||
|
@ -319,22 +240,25 @@ static void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offs
|
||||||
request.partition = partition;
|
request.partition = partition;
|
||||||
request.reply_type = reply_type;
|
request.reply_type = reply_type;
|
||||||
|
|
||||||
u64 id = state.next_id++;
|
u64 id = m_next_id++;
|
||||||
request.id = id;
|
request.id = id;
|
||||||
|
|
||||||
request.time_started_ticks = core_timing.GetTicks();
|
request.time_started_ticks = core_timing.GetTicks();
|
||||||
request.realtime_started_us = Common::Timer::NowUs();
|
request.realtime_started_us = Common::Timer::NowUs();
|
||||||
|
|
||||||
state.request_queue.Push(std::move(request));
|
m_request_queue.Push(std::move(request));
|
||||||
state.request_queue_expanded.Set();
|
m_request_queue_expanded.Set();
|
||||||
|
|
||||||
core_timing.ScheduleEvent(ticks_until_completion, state.finish_read, id);
|
core_timing.ScheduleEvent(ticks_until_completion, m_finish_read, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void FinishRead(Core::System& system, u64 id, s64 cycles_late)
|
void DVDThread::GlobalFinishRead(Core::System& system, u64 id, s64 cycles_late)
|
||||||
{
|
{
|
||||||
auto& state = system.GetDVDThreadState().GetData();
|
system.GetDVDThread().FinishRead(id, cycles_late);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DVDThread::FinishRead(u64 id, s64 cycles_late)
|
||||||
|
{
|
||||||
// We can't simply pop result_queue and always get the ReadResult
|
// We can't simply pop result_queue and always get the ReadResult
|
||||||
// we want, because the DVD thread may add ReadResults to the queue
|
// we want, because the DVD thread may add ReadResults to the queue
|
||||||
// in a different order than we want to get them. What we do instead
|
// in a different order than we want to get them. What we do instead
|
||||||
|
@ -346,23 +270,23 @@ static void FinishRead(Core::System& system, u64 id, s64 cycles_late)
|
||||||
// When this function is called again later, it will check the map for
|
// When this function is called again later, it will check the map for
|
||||||
// the wanted ReadResult before it starts searching through the queue.
|
// the wanted ReadResult before it starts searching through the queue.
|
||||||
ReadResult result;
|
ReadResult result;
|
||||||
auto it = state.result_map.find(id);
|
auto it = m_result_map.find(id);
|
||||||
if (it != state.result_map.end())
|
if (it != m_result_map.end())
|
||||||
{
|
{
|
||||||
result = std::move(it->second);
|
result = std::move(it->second);
|
||||||
state.result_map.erase(it);
|
m_result_map.erase(it);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
while (!state.result_queue.Pop(result))
|
while (!m_result_queue.Pop(result))
|
||||||
state.result_queue_expanded.Wait();
|
m_result_queue_expanded.Wait();
|
||||||
|
|
||||||
if (result.first.id == id)
|
if (result.first.id == id)
|
||||||
break;
|
break;
|
||||||
else
|
else
|
||||||
state.result_map.emplace(result.first.id, std::move(result));
|
m_result_map.emplace(result.first.id, std::move(result));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We have now obtained the right ReadResult.
|
// We have now obtained the right ReadResult.
|
||||||
|
@ -376,63 +300,62 @@ static void FinishRead(Core::System& system, u64 id, s64 cycles_late)
|
||||||
"Emulated time including delay: {} us.",
|
"Emulated time including delay: {} us.",
|
||||||
request.realtime_done_us - request.realtime_started_us,
|
request.realtime_done_us - request.realtime_started_us,
|
||||||
Common::Timer::NowUs() - request.realtime_started_us,
|
Common::Timer::NowUs() - request.realtime_started_us,
|
||||||
(system.GetCoreTiming().GetTicks() - request.time_started_ticks) /
|
(m_system.GetCoreTiming().GetTicks() - request.time_started_ticks) /
|
||||||
(SystemTimers::GetTicksPerSecond() / 1000000));
|
(SystemTimers::GetTicksPerSecond() / 1000000));
|
||||||
|
|
||||||
DVDInterface::DIInterruptType interrupt;
|
auto& dvd_interface = m_system.GetDVDInterface();
|
||||||
|
DVD::DIInterruptType interrupt;
|
||||||
if (buffer.size() != request.length)
|
if (buffer.size() != request.length)
|
||||||
{
|
{
|
||||||
PanicAlertFmtT("The disc could not be read (at {0:#x} - {1:#x}).", request.dvd_offset,
|
PanicAlertFmtT("The disc could not be read (at {0:#x} - {1:#x}).", request.dvd_offset,
|
||||||
request.dvd_offset + request.length);
|
request.dvd_offset + request.length);
|
||||||
|
|
||||||
DVDInterface::SetDriveError(DVDInterface::DriveError::ReadError);
|
dvd_interface.SetDriveError(DVD::DriveError::ReadError);
|
||||||
interrupt = DVDInterface::DIInterruptType::DEINT;
|
interrupt = DVD::DIInterruptType::DEINT;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (request.copy_to_ram)
|
if (request.copy_to_ram)
|
||||||
{
|
{
|
||||||
auto& memory = system.GetMemory();
|
auto& memory = m_system.GetMemory();
|
||||||
memory.CopyToEmu(request.output_address, buffer.data(), request.length);
|
memory.CopyToEmu(request.output_address, buffer.data(), request.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
interrupt = DVDInterface::DIInterruptType::TCINT;
|
interrupt = DVD::DIInterruptType::TCINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify the emulated software that the command has been executed
|
// Notify the emulated software that the command has been executed
|
||||||
DVDInterface::FinishExecutingCommand(request.reply_type, interrupt, cycles_late, buffer);
|
dvd_interface.FinishExecutingCommand(request.reply_type, interrupt, cycles_late, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DVDThread()
|
void DVDThread::DVDThreadMain()
|
||||||
{
|
{
|
||||||
auto& state = Core::System::GetInstance().GetDVDThreadState().GetData();
|
|
||||||
|
|
||||||
Common::SetCurrentThreadName("DVD thread");
|
Common::SetCurrentThreadName("DVD thread");
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
state.request_queue_expanded.Wait();
|
m_request_queue_expanded.Wait();
|
||||||
|
|
||||||
if (state.dvd_thread_exiting.IsSet())
|
if (m_dvd_thread_exiting.IsSet())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ReadRequest request;
|
ReadRequest request;
|
||||||
while (state.request_queue.Pop(request))
|
while (m_request_queue.Pop(request))
|
||||||
{
|
{
|
||||||
state.file_logger.Log(*state.disc, request.partition, request.dvd_offset);
|
m_file_logger.Log(*m_disc, request.partition, request.dvd_offset);
|
||||||
|
|
||||||
std::vector<u8> buffer(request.length);
|
std::vector<u8> buffer(request.length);
|
||||||
if (!state.disc->Read(request.dvd_offset, request.length, buffer.data(), request.partition))
|
if (!m_disc->Read(request.dvd_offset, request.length, buffer.data(), request.partition))
|
||||||
buffer.resize(0);
|
buffer.resize(0);
|
||||||
|
|
||||||
request.realtime_done_us = Common::Timer::NowUs();
|
request.realtime_done_us = Common::Timer::NowUs();
|
||||||
|
|
||||||
state.result_queue.Push(ReadResult(std::move(request), std::move(buffer)));
|
m_result_queue.Push(ReadResult(std::move(request), std::move(buffer)));
|
||||||
state.result_queue_expanded.Set();
|
m_result_queue_expanded.Set();
|
||||||
|
|
||||||
if (state.dvd_thread_exiting.IsSet())
|
if (m_dvd_thread_exiting.IsSet())
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace DVDThread
|
} // namespace DVD
|
||||||
|
|
|
@ -3,23 +3,37 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/Event.h"
|
||||||
|
#include "Common/Flag.h"
|
||||||
|
#include "Common/SPSCQueue.h"
|
||||||
|
|
||||||
|
#include "Core/HW/DVD/DVDInterface.h"
|
||||||
|
#include "Core/HW/DVD/FileMonitor.h"
|
||||||
|
|
||||||
|
#include "DiscIO/Volume.h"
|
||||||
|
|
||||||
class PointerWrap;
|
class PointerWrap;
|
||||||
|
namespace Core
|
||||||
|
{
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
namespace CoreTiming
|
||||||
|
{
|
||||||
|
struct EventType;
|
||||||
|
}
|
||||||
namespace DiscIO
|
namespace DiscIO
|
||||||
{
|
{
|
||||||
struct Partition;
|
struct Partition;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace DVDInterface
|
|
||||||
{
|
|
||||||
enum class ReplyType : u32;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace DiscIO
|
namespace DiscIO
|
||||||
{
|
{
|
||||||
enum class Platform;
|
enum class Platform;
|
||||||
|
@ -32,48 +46,103 @@ class TMDReader;
|
||||||
class TicketReader;
|
class TicketReader;
|
||||||
} // namespace IOS::ES
|
} // namespace IOS::ES
|
||||||
|
|
||||||
namespace DVDThread
|
namespace DVD
|
||||||
{
|
{
|
||||||
class DVDThreadState
|
enum class ReplyType : u32;
|
||||||
|
|
||||||
|
class DVDThread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DVDThreadState();
|
explicit DVDThread(Core::System& system);
|
||||||
DVDThreadState(const DVDThreadState&) = delete;
|
DVDThread(const DVDThread&) = delete;
|
||||||
DVDThreadState(DVDThreadState&&) = delete;
|
DVDThread(DVDThread&&) = delete;
|
||||||
DVDThreadState& operator=(const DVDThreadState&) = delete;
|
DVDThread& operator=(const DVDThread&) = delete;
|
||||||
DVDThreadState& operator=(DVDThreadState&&) = delete;
|
DVDThread& operator=(DVDThread&&) = delete;
|
||||||
~DVDThreadState();
|
~DVDThread();
|
||||||
|
|
||||||
struct Data;
|
void Start();
|
||||||
Data& GetData() { return *m_data; }
|
void Stop();
|
||||||
|
void DoState(PointerWrap& p);
|
||||||
|
|
||||||
|
void SetDisc(std::unique_ptr<DiscIO::Volume> disc);
|
||||||
|
bool HasDisc() const;
|
||||||
|
|
||||||
|
bool HasWiiHashes() const;
|
||||||
|
DiscIO::Platform GetDiscType() const;
|
||||||
|
u64 PartitionOffsetToRawOffset(u64 offset, const DiscIO::Partition& partition);
|
||||||
|
IOS::ES::TMDReader GetTMD(const DiscIO::Partition& partition);
|
||||||
|
IOS::ES::TicketReader GetTicket(const DiscIO::Partition& partition);
|
||||||
|
bool IsInsertedDiscRunning();
|
||||||
|
// This function returns true and calls SConfig::SetRunningGameMetadata(Volume&, Partition&)
|
||||||
|
// if both of the following conditions are true:
|
||||||
|
// - A disc is inserted
|
||||||
|
// - The title_id argument doesn't contain a value, or its value matches the disc's title ID
|
||||||
|
bool UpdateRunningGameMetadata(const DiscIO::Partition& partition,
|
||||||
|
std::optional<u64> title_id = {});
|
||||||
|
|
||||||
|
void StartRead(u64 dvd_offset, u32 length, const DiscIO::Partition& partition,
|
||||||
|
DVD::ReplyType reply_type, s64 ticks_until_completion);
|
||||||
|
void StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length,
|
||||||
|
const DiscIO::Partition& partition, DVD::ReplyType reply_type,
|
||||||
|
s64 ticks_until_completion);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Data> m_data;
|
void StartDVDThread();
|
||||||
|
void StopDVDThread();
|
||||||
|
void WaitUntilIdle();
|
||||||
|
|
||||||
|
void StartReadInternal(bool copy_to_ram, u32 output_address, u64 dvd_offset, u32 length,
|
||||||
|
const DiscIO::Partition& partition, DVD::ReplyType reply_type,
|
||||||
|
s64 ticks_until_completion);
|
||||||
|
|
||||||
|
static void GlobalFinishRead(Core::System& system, u64 id, s64 cycles_late);
|
||||||
|
void FinishRead(u64 id, s64 cycles_late);
|
||||||
|
|
||||||
|
void DVDThreadMain();
|
||||||
|
|
||||||
|
struct ReadRequest
|
||||||
|
{
|
||||||
|
bool copy_to_ram = false;
|
||||||
|
u32 output_address = 0;
|
||||||
|
u64 dvd_offset = 0;
|
||||||
|
u32 length = 0;
|
||||||
|
DiscIO::Partition partition{};
|
||||||
|
|
||||||
|
// This determines which code DVDInterface will run to reply
|
||||||
|
// to the emulated software. We can't use callbacks,
|
||||||
|
// because function pointers can't be stored in savestates.
|
||||||
|
DVD::ReplyType reply_type = DVD::ReplyType::NoReply;
|
||||||
|
|
||||||
|
// IDs are used to uniquely identify a request. They must not be
|
||||||
|
// identical to IDs of any other requests that currently exist, but
|
||||||
|
// it's fine to re-use IDs of requests that have existed in the past.
|
||||||
|
u64 id = 0;
|
||||||
|
|
||||||
|
// Only used for logging
|
||||||
|
u64 time_started_ticks = 0;
|
||||||
|
u64 realtime_started_us = 0;
|
||||||
|
u64 realtime_done_us = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ReadResult = std::pair<ReadRequest, std::vector<u8>>;
|
||||||
|
|
||||||
|
CoreTiming::EventType* m_finish_read = nullptr;
|
||||||
|
|
||||||
|
u64 m_next_id = 0;
|
||||||
|
|
||||||
|
std::thread m_dvd_thread;
|
||||||
|
Common::Event m_request_queue_expanded; // Is set by CPU thread
|
||||||
|
Common::Event m_result_queue_expanded; // Is set by DVD thread
|
||||||
|
Common::Flag m_dvd_thread_exiting = Common::Flag(false); // Is set by CPU thread
|
||||||
|
|
||||||
|
Common::SPSCQueue<ReadRequest, false> m_request_queue;
|
||||||
|
Common::SPSCQueue<ReadResult, false> m_result_queue;
|
||||||
|
std::map<u64, ReadResult> m_result_map;
|
||||||
|
|
||||||
|
std::unique_ptr<DiscIO::Volume> m_disc;
|
||||||
|
|
||||||
|
FileMonitor::FileLogger m_file_logger;
|
||||||
|
|
||||||
|
Core::System& m_system;
|
||||||
};
|
};
|
||||||
|
} // namespace DVD
|
||||||
void Start();
|
|
||||||
void Stop();
|
|
||||||
void DoState(PointerWrap& p);
|
|
||||||
|
|
||||||
void SetDisc(std::unique_ptr<DiscIO::Volume> disc);
|
|
||||||
bool HasDisc();
|
|
||||||
|
|
||||||
bool HasWiiHashes();
|
|
||||||
DiscIO::Platform GetDiscType();
|
|
||||||
u64 PartitionOffsetToRawOffset(u64 offset, const DiscIO::Partition& partition);
|
|
||||||
IOS::ES::TMDReader GetTMD(const DiscIO::Partition& partition);
|
|
||||||
IOS::ES::TicketReader GetTicket(const DiscIO::Partition& partition);
|
|
||||||
bool IsInsertedDiscRunning();
|
|
||||||
// This function returns true and calls SConfig::SetRunningGameMetadata(Volume&, Partition&)
|
|
||||||
// if both of the following conditions are true:
|
|
||||||
// - A disc is inserted
|
|
||||||
// - The title_id argument doesn't contain a value, or its value matches the disc's title ID
|
|
||||||
bool UpdateRunningGameMetadata(const DiscIO::Partition& partition,
|
|
||||||
std::optional<u64> title_id = {});
|
|
||||||
|
|
||||||
void StartRead(u64 dvd_offset, u32 length, const DiscIO::Partition& partition,
|
|
||||||
DVDInterface::ReplyType reply_type, s64 ticks_until_completion);
|
|
||||||
void StartReadToEmulatedRAM(u32 output_address, u64 dvd_offset, u32 length,
|
|
||||||
const DiscIO::Partition& partition, DVDInterface::ReplyType reply_type,
|
|
||||||
s64 ticks_until_completion);
|
|
||||||
} // namespace DVDThread
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ void Init(const Sram* override_sram)
|
||||||
AddressSpace::Init();
|
AddressSpace::Init();
|
||||||
MemoryInterface::Init();
|
MemoryInterface::Init();
|
||||||
system.GetDSP().Init(Config::Get(Config::MAIN_DSP_HLE));
|
system.GetDSP().Init(Config::Get(Config::MAIN_DSP_HLE));
|
||||||
DVDInterface::Init();
|
system.GetDVDInterface().Init();
|
||||||
system.GetGPFifo().Init();
|
system.GetGPFifo().Init();
|
||||||
system.GetCPU().Init(Config::Get(Config::MAIN_CPU_CORE));
|
system.GetCPU().Init(Config::Get(Config::MAIN_CPU_CORE));
|
||||||
SystemTimers::Init();
|
SystemTimers::Init();
|
||||||
|
@ -72,7 +72,7 @@ void Shutdown()
|
||||||
|
|
||||||
SystemTimers::Shutdown();
|
SystemTimers::Shutdown();
|
||||||
system.GetCPU().Shutdown();
|
system.GetCPU().Shutdown();
|
||||||
DVDInterface::Shutdown();
|
system.GetDVDInterface().Shutdown();
|
||||||
system.GetDSP().Shutdown();
|
system.GetDSP().Shutdown();
|
||||||
MemoryInterface::Shutdown();
|
MemoryInterface::Shutdown();
|
||||||
AddressSpace::Shutdown();
|
AddressSpace::Shutdown();
|
||||||
|
@ -101,7 +101,7 @@ void DoState(PointerWrap& p)
|
||||||
p.DoMarker("ProcessorInterface");
|
p.DoMarker("ProcessorInterface");
|
||||||
system.GetDSP().DoState(p);
|
system.GetDSP().DoState(p);
|
||||||
p.DoMarker("DSP");
|
p.DoMarker("DSP");
|
||||||
DVDInterface::DoState(p);
|
system.GetDVDInterface().DoState(p);
|
||||||
p.DoMarker("DVDInterface");
|
p.DoMarker("DVDInterface");
|
||||||
system.GetGPFifo().DoState(p);
|
system.GetGPFifo().DoState(p);
|
||||||
p.DoMarker("GPFifo");
|
p.DoMarker("GPFifo");
|
||||||
|
|
|
@ -55,14 +55,14 @@ void MemoryManager::InitMMIO(bool is_wii)
|
||||||
system.GetProcessorInterface().RegisterMMIO(m_mmio_mapping.get(), 0x0C003000);
|
system.GetProcessorInterface().RegisterMMIO(m_mmio_mapping.get(), 0x0C003000);
|
||||||
MemoryInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0C004000);
|
MemoryInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0C004000);
|
||||||
system.GetDSP().RegisterMMIO(m_mmio_mapping.get(), 0x0C005000);
|
system.GetDSP().RegisterMMIO(m_mmio_mapping.get(), 0x0C005000);
|
||||||
DVDInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0C006000, false);
|
system.GetDVDInterface().RegisterMMIO(m_mmio_mapping.get(), 0x0C006000, false);
|
||||||
SerialInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0C006400);
|
SerialInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0C006400);
|
||||||
ExpansionInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0C006800);
|
ExpansionInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0C006800);
|
||||||
system.GetAudioInterface().RegisterMMIO(m_mmio_mapping.get(), 0x0C006C00);
|
system.GetAudioInterface().RegisterMMIO(m_mmio_mapping.get(), 0x0C006C00);
|
||||||
if (is_wii)
|
if (is_wii)
|
||||||
{
|
{
|
||||||
IOS::RegisterMMIO(m_mmio_mapping.get(), 0x0D000000);
|
IOS::RegisterMMIO(m_mmio_mapping.get(), 0x0D000000);
|
||||||
DVDInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0D006000, true);
|
system.GetDVDInterface().RegisterMMIO(m_mmio_mapping.get(), 0x0D006000, true);
|
||||||
SerialInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0D006400);
|
SerialInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0D006400);
|
||||||
ExpansionInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0D006800);
|
ExpansionInterface::RegisterMMIO(m_mmio_mapping.get(), 0x0D006800);
|
||||||
system.GetAudioInterface().RegisterMMIO(m_mmio_mapping.get(), 0x0D006C00);
|
system.GetAudioInterface().RegisterMMIO(m_mmio_mapping.get(), 0x0D006C00);
|
||||||
|
|
|
@ -120,7 +120,7 @@ void ProcessorInterfaceManager::RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
processor_interface.m_reset_code);
|
processor_interface.m_reset_code);
|
||||||
if (!SConfig::GetInstance().bWii && ~processor_interface.m_reset_code & 0x4)
|
if (!SConfig::GetInstance().bWii && ~processor_interface.m_reset_code & 0x4)
|
||||||
{
|
{
|
||||||
DVDInterface::ResetDrive(true);
|
system.GetDVDInterface().ResetDrive(true);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -208,13 +208,13 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
mmio->Register(base | GPIOB_OUT, MMIO::DirectRead<u32>(&g_gpio_out.m_hex),
|
mmio->Register(base | GPIOB_OUT, MMIO::DirectRead<u32>(&g_gpio_out.m_hex),
|
||||||
MMIO::ComplexWrite<u32>([](Core::System&, u32, u32 val) {
|
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||||
g_gpio_out.m_hex =
|
g_gpio_out.m_hex =
|
||||||
(val & gpio_owner.m_hex) | (g_gpio_out.m_hex & ~gpio_owner.m_hex);
|
(val & gpio_owner.m_hex) | (g_gpio_out.m_hex & ~gpio_owner.m_hex);
|
||||||
if (g_gpio_out[GPIO::DO_EJECT])
|
if (g_gpio_out[GPIO::DO_EJECT])
|
||||||
{
|
{
|
||||||
INFO_LOG_FMT(WII_IPC, "Ejecting disc due to GPIO write");
|
INFO_LOG_FMT(WII_IPC, "Ejecting disc due to GPIO write");
|
||||||
DVDInterface::EjectDisc(DVDInterface::EjectCause::Software);
|
system.GetDVDInterface().EjectDisc(DVD::EjectCause::Software);
|
||||||
}
|
}
|
||||||
// SENSOR_BAR is checked by WiimoteEmu::CameraLogic
|
// SENSOR_BAR is checked by WiimoteEmu::CameraLogic
|
||||||
// TODO: AVE, SLOT_LED
|
// TODO: AVE, SLOT_LED
|
||||||
|
@ -223,9 +223,9 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
MMIO::ComplexWrite<u32>([](Core::System&, u32, u32 val) {
|
MMIO::ComplexWrite<u32>([](Core::System&, u32, u32 val) {
|
||||||
gpio_dir.m_hex = (val & gpio_owner.m_hex) | (gpio_dir.m_hex & ~gpio_owner.m_hex);
|
gpio_dir.m_hex = (val & gpio_owner.m_hex) | (gpio_dir.m_hex & ~gpio_owner.m_hex);
|
||||||
}));
|
}));
|
||||||
mmio->Register(base | GPIOB_IN, MMIO::ComplexRead<u32>([](Core::System&, u32) {
|
mmio->Register(base | GPIOB_IN, MMIO::ComplexRead<u32>([](Core::System& system, u32) {
|
||||||
Common::Flags<GPIO> gpio_in;
|
Common::Flags<GPIO> gpio_in;
|
||||||
gpio_in[GPIO::SLOT_IN] = DVDInterface::IsDiscInside();
|
gpio_in[GPIO::SLOT_IN] = system.GetDVDInterface().IsDiscInside();
|
||||||
return gpio_in.m_hex;
|
return gpio_in.m_hex;
|
||||||
}),
|
}),
|
||||||
MMIO::Nop<u32>());
|
MMIO::Nop<u32>());
|
||||||
|
@ -241,13 +241,13 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
// go through the HW_GPIOB registers if the corresponding bit is set in the HW_GPIO_OWNER
|
// go through the HW_GPIOB registers if the corresponding bit is set in the HW_GPIO_OWNER
|
||||||
// register.
|
// register.
|
||||||
mmio->Register(base | GPIO_OUT, MMIO::DirectRead<u32>(&g_gpio_out.m_hex),
|
mmio->Register(base | GPIO_OUT, MMIO::DirectRead<u32>(&g_gpio_out.m_hex),
|
||||||
MMIO::ComplexWrite<u32>([](Core::System&, u32, u32 val) {
|
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||||
g_gpio_out.m_hex =
|
g_gpio_out.m_hex =
|
||||||
(g_gpio_out.m_hex & gpio_owner.m_hex) | (val & ~gpio_owner.m_hex);
|
(g_gpio_out.m_hex & gpio_owner.m_hex) | (val & ~gpio_owner.m_hex);
|
||||||
if (g_gpio_out[GPIO::DO_EJECT])
|
if (g_gpio_out[GPIO::DO_EJECT])
|
||||||
{
|
{
|
||||||
INFO_LOG_FMT(WII_IPC, "Ejecting disc due to GPIO write");
|
INFO_LOG_FMT(WII_IPC, "Ejecting disc due to GPIO write");
|
||||||
DVDInterface::EjectDisc(DVDInterface::EjectCause::Software);
|
system.GetDVDInterface().EjectDisc(DVD::EjectCause::Software);
|
||||||
}
|
}
|
||||||
// SENSOR_BAR is checked by WiimoteEmu::CameraLogic
|
// SENSOR_BAR is checked by WiimoteEmu::CameraLogic
|
||||||
// TODO: AVE, SLOT_LED
|
// TODO: AVE, SLOT_LED
|
||||||
|
@ -256,15 +256,15 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
MMIO::ComplexWrite<u32>([](Core::System&, u32, u32 val) {
|
MMIO::ComplexWrite<u32>([](Core::System&, u32, u32 val) {
|
||||||
gpio_dir.m_hex = (gpio_dir.m_hex & gpio_owner.m_hex) | (val & ~gpio_owner.m_hex);
|
gpio_dir.m_hex = (gpio_dir.m_hex & gpio_owner.m_hex) | (val & ~gpio_owner.m_hex);
|
||||||
}));
|
}));
|
||||||
mmio->Register(base | GPIO_IN, MMIO::ComplexRead<u32>([](Core::System&, u32) {
|
mmio->Register(base | GPIO_IN, MMIO::ComplexRead<u32>([](Core::System& system, u32) {
|
||||||
Common::Flags<GPIO> gpio_in;
|
Common::Flags<GPIO> gpio_in;
|
||||||
gpio_in[GPIO::SLOT_IN] = DVDInterface::IsDiscInside();
|
gpio_in[GPIO::SLOT_IN] = system.GetDVDInterface().IsDiscInside();
|
||||||
return gpio_in.m_hex;
|
return gpio_in.m_hex;
|
||||||
}),
|
}),
|
||||||
MMIO::Nop<u32>());
|
MMIO::Nop<u32>());
|
||||||
|
|
||||||
mmio->Register(base | HW_RESETS, MMIO::DirectRead<u32>(&resets),
|
mmio->Register(base | HW_RESETS, MMIO::DirectRead<u32>(&resets),
|
||||||
MMIO::ComplexWrite<u32>([](Core::System&, u32, u32 val) {
|
MMIO::ComplexWrite<u32>([](Core::System& system, u32, u32 val) {
|
||||||
// A reset occurs when the corresponding bit is cleared
|
// A reset occurs when the corresponding bit is cleared
|
||||||
const bool di_reset_triggered = (resets & 0x400) && !(val & 0x400);
|
const bool di_reset_triggered = (resets & 0x400) && !(val & 0x400);
|
||||||
resets = val;
|
resets = val;
|
||||||
|
@ -273,7 +273,7 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
||||||
// The GPIO *disables* spinning up the drive
|
// The GPIO *disables* spinning up the drive
|
||||||
const bool spinup = !g_gpio_out[GPIO::DI_SPIN];
|
const bool spinup = !g_gpio_out[GPIO::DI_SPIN];
|
||||||
INFO_LOG_FMT(WII_IPC, "Resetting DI {} spinup", spinup ? "with" : "without");
|
INFO_LOG_FMT(WII_IPC, "Resetting DI {} spinup", spinup ? "with" : "without");
|
||||||
DVDInterface::ResetDrive(spinup);
|
system.GetDVDInterface().ResetDrive(spinup);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
@ -214,8 +214,8 @@ std::optional<DIDevice::DIResult> DIDevice::StartIOCtl(const IOCtlRequest& reque
|
||||||
return DIResult::SecurityError;
|
return DIResult::SecurityError;
|
||||||
}
|
}
|
||||||
m_last_length = position; // An actual mistake in IOS
|
m_last_length = position; // An actual mistake in IOS
|
||||||
DVDInterface::PerformDecryptingRead(position, length, request.buffer_out, m_current_partition,
|
system.GetDVDInterface().PerformDecryptingRead(position, length, request.buffer_out,
|
||||||
DVDInterface::ReplyType::IOS);
|
m_current_partition, DVD::ReplyType::IOS);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
case DIIoctl::DVDLowWaitForCoverClose:
|
case DIIoctl::DVDLowWaitForCoverClose:
|
||||||
|
@ -274,12 +274,12 @@ std::optional<DIDevice::DIResult> DIDevice::StartIOCtl(const IOCtlRequest& reque
|
||||||
}
|
}
|
||||||
case DIIoctl::DVDLowMaskCoverInterrupt:
|
case DIIoctl::DVDLowMaskCoverInterrupt:
|
||||||
INFO_LOG_FMT(IOS_DI, "DVDLowMaskCoverInterrupt");
|
INFO_LOG_FMT(IOS_DI, "DVDLowMaskCoverInterrupt");
|
||||||
DVDInterface::SetInterruptEnabled(DVDInterface::DIInterruptType::CVRINT, false);
|
system.GetDVDInterface().SetInterruptEnabled(DVD::DIInterruptType::CVRINT, false);
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DI_INTERRUPT_MASK_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DI_INTERRUPT_MASK_COMMAND);
|
||||||
return DIResult::Success;
|
return DIResult::Success;
|
||||||
case DIIoctl::DVDLowClearCoverInterrupt:
|
case DIIoctl::DVDLowClearCoverInterrupt:
|
||||||
DEBUG_LOG_FMT(IOS_DI, "DVDLowClearCoverInterrupt");
|
DEBUG_LOG_FMT(IOS_DI, "DVDLowClearCoverInterrupt");
|
||||||
DVDInterface::ClearInterrupt(DVDInterface::DIInterruptType::CVRINT);
|
system.GetDVDInterface().ClearInterrupt(DVD::DIInterruptType::CVRINT);
|
||||||
return DIResult::Success;
|
return DIResult::Success;
|
||||||
case DIIoctl::DVDLowUnmaskStatusInterrupts:
|
case DIIoctl::DVDLowUnmaskStatusInterrupts:
|
||||||
INFO_LOG_FMT(IOS_DI, "DVDLowUnmaskStatusInterrupts");
|
INFO_LOG_FMT(IOS_DI, "DVDLowUnmaskStatusInterrupts");
|
||||||
|
@ -287,13 +287,15 @@ std::optional<DIDevice::DIResult> DIDevice::StartIOCtl(const IOCtlRequest& reque
|
||||||
// Dummied out
|
// Dummied out
|
||||||
return DIResult::Success;
|
return DIResult::Success;
|
||||||
case DIIoctl::DVDLowGetCoverStatus:
|
case DIIoctl::DVDLowGetCoverStatus:
|
||||||
|
{
|
||||||
// TODO: handle resetting case
|
// TODO: handle resetting case
|
||||||
INFO_LOG_FMT(IOS_DI, "DVDLowGetCoverStatus: Disc {}Inserted",
|
const bool is_disc_inside = system.GetDVDInterface().IsDiscInside();
|
||||||
DVDInterface::IsDiscInside() ? "" : "Not ");
|
INFO_LOG_FMT(IOS_DI, "DVDLowGetCoverStatus: Disc {}Inserted", is_disc_inside ? "" : "Not ");
|
||||||
return WriteIfFits(request, DVDInterface::IsDiscInside() ? 2 : 1);
|
return WriteIfFits(request, is_disc_inside ? 2 : 1);
|
||||||
|
}
|
||||||
case DIIoctl::DVDLowUnmaskCoverInterrupt:
|
case DIIoctl::DVDLowUnmaskCoverInterrupt:
|
||||||
INFO_LOG_FMT(IOS_DI, "DVDLowUnmaskCoverInterrupt");
|
INFO_LOG_FMT(IOS_DI, "DVDLowUnmaskCoverInterrupt");
|
||||||
DVDInterface::SetInterruptEnabled(DVDInterface::DIInterruptType::CVRINT, true);
|
system.GetDVDInterface().SetInterruptEnabled(DVD::DIInterruptType::CVRINT, true);
|
||||||
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DI_INTERRUPT_MASK_COMMAND);
|
DolphinAnalytics::Instance().ReportGameQuirk(GameQuirk::USES_DI_INTERRUPT_MASK_COMMAND);
|
||||||
return DIResult::Success;
|
return DIResult::Success;
|
||||||
case DIIoctl::DVDLowReset:
|
case DIIoctl::DVDLowReset:
|
||||||
|
@ -362,7 +364,7 @@ std::optional<DIDevice::DIResult> DIDevice::StartIOCtl(const IOCtlRequest& reque
|
||||||
DIMAR = request.buffer_out;
|
DIMAR = request.buffer_out;
|
||||||
m_last_length = length;
|
m_last_length = length;
|
||||||
DILENGTH = length;
|
DILENGTH = length;
|
||||||
DVDInterface::ForceOutOfBoundsRead(DVDInterface::ReplyType::IOS);
|
system.GetDVDInterface().ForceOutOfBoundsRead(DVD::ReplyType::IOS);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -579,7 +581,7 @@ std::optional<DIDevice::DIResult> DIDevice::StartDMATransfer(u32 command_length,
|
||||||
m_last_length = command_length;
|
m_last_length = command_length;
|
||||||
DILENGTH = command_length;
|
DILENGTH = command_length;
|
||||||
|
|
||||||
DVDInterface::ExecuteCommand(DVDInterface::ReplyType::IOS);
|
Core::System::GetInstance().GetDVDInterface().ExecuteCommand(DVD::ReplyType::IOS);
|
||||||
// Reply will be posted when done by FinishIOCtl.
|
// Reply will be posted when done by FinishIOCtl.
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -597,7 +599,7 @@ std::optional<DIDevice::DIResult> DIDevice::StartImmediateTransfer(const IOCtlRe
|
||||||
|
|
||||||
m_executing_command->m_copy_diimmbuf = write_to_buf;
|
m_executing_command->m_copy_diimmbuf = write_to_buf;
|
||||||
|
|
||||||
DVDInterface::ExecuteCommand(DVDInterface::ReplyType::IOS);
|
Core::System::GetInstance().GetDVDInterface().ExecuteCommand(DVD::ReplyType::IOS);
|
||||||
// Reply will be posted when done by FinishIOCtl.
|
// Reply will be posted when done by FinishIOCtl.
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -612,15 +614,15 @@ static std::shared_ptr<DIDevice> GetDevice()
|
||||||
return std::static_pointer_cast<DIDevice>(di);
|
return std::static_pointer_cast<DIDevice>(di);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DIDevice::InterruptFromDVDInterface(DVDInterface::DIInterruptType interrupt_type)
|
void DIDevice::InterruptFromDVDInterface(DVD::DIInterruptType interrupt_type)
|
||||||
{
|
{
|
||||||
DIResult result;
|
DIResult result;
|
||||||
switch (interrupt_type)
|
switch (interrupt_type)
|
||||||
{
|
{
|
||||||
case DVDInterface::DIInterruptType::TCINT:
|
case DVD::DIInterruptType::TCINT:
|
||||||
result = DIResult::Success;
|
result = DIResult::Success;
|
||||||
break;
|
break;
|
||||||
case DVDInterface::DIInterruptType::DEINT:
|
case DVD::DIInterruptType::DEINT:
|
||||||
result = DIResult::DriveError;
|
result = DIResult::DriveError;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -739,11 +741,12 @@ std::optional<IPCReply> DIDevice::IOCtlV(const IOCtlVRequest& request)
|
||||||
INFO_LOG_FMT(IOS_DI, "DVDLowOpenPartition: partition_offset {:#011x}", partition_offset);
|
INFO_LOG_FMT(IOS_DI, "DVDLowOpenPartition: partition_offset {:#011x}", partition_offset);
|
||||||
|
|
||||||
// Read TMD to the buffer
|
// Read TMD to the buffer
|
||||||
const ES::TMDReader tmd = DVDThread::GetTMD(m_current_partition);
|
auto& dvd_thread = system.GetDVDThread();
|
||||||
|
const ES::TMDReader tmd = dvd_thread.GetTMD(m_current_partition);
|
||||||
const std::vector<u8>& raw_tmd = tmd.GetBytes();
|
const std::vector<u8>& raw_tmd = tmd.GetBytes();
|
||||||
memory.CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size());
|
memory.CopyToEmu(request.io_vectors[0].address, raw_tmd.data(), raw_tmd.size());
|
||||||
|
|
||||||
ReturnCode es_result = m_ios.GetES()->DIVerify(tmd, DVDThread::GetTicket(m_current_partition));
|
ReturnCode es_result = m_ios.GetES()->DIVerify(tmd, dvd_thread.GetTicket(m_current_partition));
|
||||||
memory.Write_U32(es_result, request.io_vectors[1].address);
|
memory.Write_U32(es_result, request.io_vectors[1].address);
|
||||||
|
|
||||||
return_value = DIResult::Success;
|
return_value = DIResult::Success;
|
||||||
|
@ -814,12 +817,13 @@ void DIDevice::ResetDIRegisters()
|
||||||
{
|
{
|
||||||
// Clear transfer complete and error interrupts (normally r/z, but here we just directly write
|
// Clear transfer complete and error interrupts (normally r/z, but here we just directly write
|
||||||
// zero)
|
// zero)
|
||||||
DVDInterface::ClearInterrupt(DVDInterface::DIInterruptType::TCINT);
|
auto& di = Core::System::GetInstance().GetDVDInterface();
|
||||||
DVDInterface::ClearInterrupt(DVDInterface::DIInterruptType::DEINT);
|
di.ClearInterrupt(DVD::DIInterruptType::TCINT);
|
||||||
|
di.ClearInterrupt(DVD::DIInterruptType::DEINT);
|
||||||
// Enable transfer complete and error interrupts, and disable cover interrupt
|
// Enable transfer complete and error interrupts, and disable cover interrupt
|
||||||
DVDInterface::SetInterruptEnabled(DVDInterface::DIInterruptType::TCINT, true);
|
di.SetInterruptEnabled(DVD::DIInterruptType::TCINT, true);
|
||||||
DVDInterface::SetInterruptEnabled(DVDInterface::DIInterruptType::DEINT, true);
|
di.SetInterruptEnabled(DVD::DIInterruptType::DEINT, true);
|
||||||
DVDInterface::SetInterruptEnabled(DVDInterface::DIInterruptType::CVRINT, false);
|
di.SetInterruptEnabled(DVD::DIInterruptType::CVRINT, false);
|
||||||
// Close the current partition, if there is one
|
// Close the current partition, if there is one
|
||||||
ChangePartition(DiscIO::PARTITION_NONE);
|
ChangePartition(DiscIO::PARTITION_NONE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
class CBoot;
|
class CBoot;
|
||||||
class PointerWrap;
|
class PointerWrap;
|
||||||
|
|
||||||
namespace DVDInterface
|
namespace DVD
|
||||||
{
|
{
|
||||||
enum class DIInterruptType : int;
|
enum class DIInterruptType : int;
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ class DIDevice : public Device
|
||||||
public:
|
public:
|
||||||
DIDevice(Kernel& ios, const std::string& device_name);
|
DIDevice(Kernel& ios, const std::string& device_name);
|
||||||
|
|
||||||
static void InterruptFromDVDInterface(DVDInterface::DIInterruptType interrupt_type);
|
static void InterruptFromDVDInterface(DVD::DIInterruptType interrupt_type);
|
||||||
static DiscIO::Partition GetCurrentPartition();
|
static DiscIO::Partition GetCurrentPartition();
|
||||||
|
|
||||||
void DoState(PointerWrap& p) override;
|
void DoState(PointerWrap& p) override;
|
||||||
|
|
|
@ -42,7 +42,7 @@ static void ReinitHardware()
|
||||||
// HACK However, resetting DI will reset the DTK config, which is set by the system menu
|
// HACK However, resetting DI will reset the DTK config, which is set by the system menu
|
||||||
// (and not by MIOS), causing games that use DTK to break. Perhaps MIOS doesn't actually
|
// (and not by MIOS), causing games that use DTK to break. Perhaps MIOS doesn't actually
|
||||||
// reset DI fully, in such a way that the DTK config isn't cleared?
|
// reset DI fully, in such a way that the DTK config isn't cleared?
|
||||||
// DVDInterface::ResetDrive(true);
|
// system.GetDVDInterface().ResetDrive(true);
|
||||||
PowerPC::Reset();
|
PowerPC::Reset();
|
||||||
Wiimote::ResetAllWiimotes();
|
Wiimote::ResetAllWiimotes();
|
||||||
// Note: this is specific to Dolphin and is required because we initialised it in Wii mode.
|
// Note: this is specific to Dolphin and is required because we initialised it in Wii mode.
|
||||||
|
@ -99,7 +99,7 @@ bool Load()
|
||||||
memory.Write_U32(0x00000000, ADDRESS_INIT_SEMAPHORE);
|
memory.Write_U32(0x00000000, ADDRESS_INIT_SEMAPHORE);
|
||||||
NOTICE_LOG_FMT(IOS, "IPL ready.");
|
NOTICE_LOG_FMT(IOS, "IPL ready.");
|
||||||
SConfig::GetInstance().m_is_mios = true;
|
SConfig::GetInstance().m_is_mios = true;
|
||||||
DVDInterface::UpdateRunningGameMetadata();
|
system.GetDVDInterface().UpdateRunningGameMetadata();
|
||||||
SConfig::OnNewTitleLoad(guard);
|
SConfig::OnNewTitleLoad(guard);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1274,9 +1274,9 @@ void PlayController(GCPadStatus* PadStatus, int controllerID)
|
||||||
if (s_padState.disc)
|
if (s_padState.disc)
|
||||||
{
|
{
|
||||||
Core::RunAsCPUThread([] {
|
Core::RunAsCPUThread([] {
|
||||||
if (!DVDInterface::AutoChangeDisc())
|
auto& system = Core::System::GetInstance();
|
||||||
|
if (!system.GetDVDInterface().AutoChangeDisc())
|
||||||
{
|
{
|
||||||
auto& system = Core::System::GetInstance();
|
|
||||||
system.GetCPU().Break();
|
system.GetCPU().Break();
|
||||||
PanicAlertFmtT("Change the disc to {0}", s_discChange);
|
PanicAlertFmtT("Change the disc to {0}", s_discChange);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,8 @@ namespace Core
|
||||||
struct System::Impl
|
struct System::Impl
|
||||||
{
|
{
|
||||||
explicit Impl(System& system)
|
explicit Impl(System& system)
|
||||||
: m_audio_interface(system), m_core_timing(system), m_dsp(system), m_gp_fifo(system),
|
: m_audio_interface(system), m_core_timing(system), m_dsp(system), m_dvd_interface(system),
|
||||||
m_ppc_state(PowerPC::ppcState)
|
m_dvd_thread(system), m_gp_fifo(system), m_ppc_state(PowerPC::ppcState)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,8 +50,8 @@ struct System::Impl
|
||||||
CommandProcessor::CommandProcessorManager m_command_processor;
|
CommandProcessor::CommandProcessorManager m_command_processor;
|
||||||
CPU::CPUManager m_cpu;
|
CPU::CPUManager m_cpu;
|
||||||
DSP::DSPManager m_dsp;
|
DSP::DSPManager m_dsp;
|
||||||
DVDInterface::DVDInterfaceState m_dvd_interface_state;
|
DVD::DVDInterface m_dvd_interface;
|
||||||
DVDThread::DVDThreadState m_dvd_thread_state;
|
DVD::DVDThread m_dvd_thread;
|
||||||
ExpansionInterface::ExpansionInterfaceState m_expansion_interface_state;
|
ExpansionInterface::ExpansionInterfaceState m_expansion_interface_state;
|
||||||
Fifo::FifoManager m_fifo;
|
Fifo::FifoManager m_fifo;
|
||||||
GeometryShaderManager m_geometry_shader_manager;
|
GeometryShaderManager m_geometry_shader_manager;
|
||||||
|
@ -138,14 +138,14 @@ DSP::DSPManager& System::GetDSP() const
|
||||||
return m_impl->m_dsp;
|
return m_impl->m_dsp;
|
||||||
}
|
}
|
||||||
|
|
||||||
DVDInterface::DVDInterfaceState& System::GetDVDInterfaceState() const
|
DVD::DVDInterface& System::GetDVDInterface() const
|
||||||
{
|
{
|
||||||
return m_impl->m_dvd_interface_state;
|
return m_impl->m_dvd_interface;
|
||||||
}
|
}
|
||||||
|
|
||||||
DVDThread::DVDThreadState& System::GetDVDThreadState() const
|
DVD::DVDThread& System::GetDVDThread() const
|
||||||
{
|
{
|
||||||
return m_impl->m_dvd_thread_state;
|
return m_impl->m_dvd_thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExpansionInterface::ExpansionInterfaceState& System::GetExpansionInterfaceState() const
|
ExpansionInterface::ExpansionInterfaceState& System::GetExpansionInterfaceState() const
|
||||||
|
|
|
@ -31,14 +31,11 @@ namespace DSP
|
||||||
{
|
{
|
||||||
class DSPManager;
|
class DSPManager;
|
||||||
}
|
}
|
||||||
namespace DVDInterface
|
namespace DVD
|
||||||
{
|
{
|
||||||
class DVDInterfaceState;
|
class DVDInterface;
|
||||||
}
|
class DVDThread;
|
||||||
namespace DVDThread
|
} // namespace DVD
|
||||||
{
|
|
||||||
class DVDThreadState;
|
|
||||||
}
|
|
||||||
namespace ExpansionInterface
|
namespace ExpansionInterface
|
||||||
{
|
{
|
||||||
class ExpansionInterfaceState;
|
class ExpansionInterfaceState;
|
||||||
|
@ -127,8 +124,8 @@ public:
|
||||||
CoreTiming::CoreTimingManager& GetCoreTiming() const;
|
CoreTiming::CoreTimingManager& GetCoreTiming() const;
|
||||||
CommandProcessor::CommandProcessorManager& GetCommandProcessor() const;
|
CommandProcessor::CommandProcessorManager& GetCommandProcessor() const;
|
||||||
DSP::DSPManager& GetDSP() const;
|
DSP::DSPManager& GetDSP() const;
|
||||||
DVDInterface::DVDInterfaceState& GetDVDInterfaceState() const;
|
DVD::DVDInterface& GetDVDInterface() const;
|
||||||
DVDThread::DVDThreadState& GetDVDThreadState() const;
|
DVD::DVDThread& GetDVDThread() const;
|
||||||
ExpansionInterface::ExpansionInterfaceState& GetExpansionInterfaceState() const;
|
ExpansionInterface::ExpansionInterfaceState& GetExpansionInterfaceState() const;
|
||||||
Fifo::FifoManager& GetFifo() const;
|
Fifo::FifoManager& GetFifo() const;
|
||||||
GeometryShaderManager& GetGeometryShaderManager() const;
|
GeometryShaderManager& GetGeometryShaderManager() const;
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
#include "Core/HW/EXI/EXI.h"
|
#include "Core/HW/EXI/EXI.h"
|
||||||
#include "Core/HW/EXI/EXI_Device.h"
|
#include "Core/HW/EXI/EXI_Device.h"
|
||||||
#include "Core/HW/WiiSave.h"
|
#include "Core/HW/WiiSave.h"
|
||||||
|
#include "Core/System.h"
|
||||||
#include "Core/WiiUtils.h"
|
#include "Core/WiiUtils.h"
|
||||||
|
|
||||||
#include "DiscIO/Blob.h"
|
#include "DiscIO/Blob.h"
|
||||||
|
@ -852,7 +853,9 @@ void GameList::ChangeDisc()
|
||||||
if (!game)
|
if (!game)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Core::RunAsCPUThread([file_path = game->GetFilePath()] { DVDInterface::ChangeDisc(file_path); });
|
Core::RunAsCPUThread([file_path = game->GetFilePath()] {
|
||||||
|
Core::System::GetInstance().GetDVDInterface().ChangeDisc(file_path);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
QAbstractItemView* GameList::GetActiveView() const
|
QAbstractItemView* GameList::GetActiveView() const
|
||||||
|
|
|
@ -746,12 +746,14 @@ void MainWindow::ChangeDisc()
|
||||||
std::vector<std::string> paths = StringListToStdVector(PromptFileNames());
|
std::vector<std::string> paths = StringListToStdVector(PromptFileNames());
|
||||||
|
|
||||||
if (!paths.empty())
|
if (!paths.empty())
|
||||||
Core::RunAsCPUThread([&paths] { DVDInterface::ChangeDisc(paths); });
|
Core::RunAsCPUThread(
|
||||||
|
[&paths] { Core::System::GetInstance().GetDVDInterface().ChangeDisc(paths); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::EjectDisc()
|
void MainWindow::EjectDisc()
|
||||||
{
|
{
|
||||||
Core::RunAsCPUThread([] { DVDInterface::EjectDisc(DVDInterface::EjectCause::User); });
|
Core::RunAsCPUThread(
|
||||||
|
[] { Core::System::GetInstance().GetDVDInterface().EjectDisc(DVD::EjectCause::User); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::OpenUserFolder()
|
void MainWindow::OpenUserFolder()
|
||||||
|
|
Loading…
Reference in New Issue