From 71e8fb278f72443a098bf4bd6d900cefd54e84ec Mon Sep 17 00:00:00 2001 From: Pokechu22 Date: Wed, 4 Sep 2019 15:42:22 -0700 Subject: [PATCH] Return more errors from DTK --- Source/Core/Core/Boot/Boot_BS2Emu.cpp | 15 +++ Source/Core/Core/HW/DVD/DVDInterface.cpp | 145 ++++++++++++++++++----- Source/Core/Core/HW/DVD/DVDInterface.h | 2 + Source/Core/Core/IOS/DI/DI.cpp | 9 +- 4 files changed, 139 insertions(+), 32 deletions(-) diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index 25efebb12b..77fb61170e 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -212,6 +212,21 @@ bool CBoot::EmulatedBS2_GC(const DiscIO::VolumeDisc& volume) DVDReadDiscID(volume, 0x00000000); + bool streaming = Memory::Read_U8(0x80000008); + if (streaming) + { + u8 streaming_size = Memory::Read_U8(0x80000009); + // If the streaming buffer size is 0, then BS2 uses a default size of 10 instead. + // No known game uses a size other than the default. + if (streaming_size == 0) + streaming_size = 10; + DVDInterface::AudioBufferConfig(true, streaming_size); + } + else + { + DVDInterface::AudioBufferConfig(false, 0); + } + const bool ntsc = DiscIO::IsNTSC(SConfig::GetInstance().m_region); // Setup pointers like real BS2 does diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index 9f54563fb7..ad45814fee 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -155,6 +155,9 @@ static u32 s_current_length; static u64 s_next_start; static u32 s_next_length; static u32 s_pending_samples; +static bool s_can_configure_dtk = true; +static bool s_enable_dtk = false; +static u8 s_dtk_buffer_length = 0; // TODO: figure out how this affects the regular buffer // Disc drive state static u32 s_error_code = 0; @@ -215,6 +218,9 @@ void DoState(PointerWrap& p) p.Do(s_next_start); p.Do(s_next_length); p.Do(s_pending_samples); + p.Do(s_can_configure_dtk); + p.Do(s_enable_dtk); + p.Do(s_dtk_buffer_length); p.Do(s_error_code); p.Do(s_current_partition); @@ -285,21 +291,31 @@ static u32 AdvanceDTK(u32 maximum_samples, u32* samples_to_process) return bytes_to_process; } -static void DTKStreamingCallback(const std::vector& audio_data, s64 cycles_late) +static void DTKStreamingCallback(DIInterruptType interrupt_type, const std::vector& audio_data, + s64 cycles_late) { - // Send audio to the mixer. - std::vector temp_pcm(s_pending_samples * 2, 0); - ProcessDTKSamples(&temp_pcm, audio_data); - g_sound_stream->GetMixer()->PushStreamingSamples(temp_pcm.data(), s_pending_samples); - // Determine which audio data to read next. static const int MAXIMUM_SAMPLES = 48000 / 2000 * 7; // 3.5ms of 48kHz samples u64 read_offset = 0; u32 read_length = 0; - if (s_stream && AudioInterface::IsPlaying()) + + if (interrupt_type == DIInterruptType::TCINT) { - read_offset = s_audio_position; - read_length = AdvanceDTK(MAXIMUM_SAMPLES, &s_pending_samples); + // Send audio to the mixer. + std::vector temp_pcm(s_pending_samples * 2, 0); + ProcessDTKSamples(&temp_pcm, audio_data); + g_sound_stream->GetMixer()->PushStreamingSamples(temp_pcm.data(), s_pending_samples); + + if (s_stream && AudioInterface::IsPlaying()) + { + read_offset = s_audio_position; + read_length = AdvanceDTK(MAXIMUM_SAMPLES, &s_pending_samples); + } + else + { + read_length = 0; + s_pending_samples = MAXIMUM_SAMPLES; + } } else { @@ -367,6 +383,9 @@ void Reset(bool spinup) s_current_start = 0; s_current_length = 0; s_pending_samples = 0; + s_can_configure_dtk = true; + s_enable_dtk = false; + s_dtk_buffer_length = 0; if (!IsDiscInside()) { @@ -792,6 +811,7 @@ void ExecuteCommand(ReplyType reply_type) ", DMABuffer = %08x, SrcLength = %08x, DMALength = %08x", iDVDOffset, s_DIMAR, s_DICMDBUF[2], s_DILENGTH); + s_can_configure_dtk = false; command_handled_by_thread = ExecuteReadCommand(iDVDOffset, s_DIMAR, s_DICMDBUF[2], s_DILENGTH, DiscIO::PARTITION_NONE, reply_type, &interrupt_type); @@ -808,6 +828,14 @@ void ExecuteCommand(ReplyType reply_type) { SetLowError(ERROR_READY); } + else + { + // The first disc ID reading is required before DTK can be configured. + // If the disc ID is read again (or any other read occurs), it no longer can + // be configured. + s_can_configure_dtk = false; + } + command_handled_by_thread = ExecuteReadCommand( 0, s_DIMAR, 0x20, s_DILENGTH, DiscIO::PARTITION_NONE, reply_type, &interrupt_type); break; @@ -894,22 +922,42 @@ void ExecuteCommand(ReplyType reply_type) // command_2 = Length of the stream case DICommand::AudioStream: { - u8 cancel_stream = (s_DICMDBUF[0] >> 16) & 0xFF; - if (cancel_stream) + if (!CheckReadPreconditions()) { - s_stop_at_track_end = false; - s_stream = false; + ERROR_LOG(DVDINTERFACE, "Cannot play audio (command %08x)", s_DICMDBUF[0]); + SetHighError(ERROR_AUDIO_BUF); + interrupt_type = DIInterruptType::DEINT; + break; } - else + if (!s_enable_dtk) { - if ((s_DICMDBUF[1] == 0) && (s_DICMDBUF[2] == 0)) + ERROR_LOG(DVDINTERFACE, + "Attempted to change playing audio while audio is disabled! (%08x %08x %08x)", + s_DICMDBUF[0], s_DICMDBUF[1], s_DICMDBUF[2]); + SetHighError(ERROR_AUDIO_BUF); + interrupt_type = DIInterruptType::DEINT; + break; + } + + s_can_configure_dtk = false; + + switch ((s_DICMDBUF[0] >> 16) & 0xFF) + { + case 0x00: + { + u64 offset = static_cast(s_DICMDBUF[1]) << 2; + u32 length = s_DICMDBUF[2]; + INFO_LOG(DVDINTERFACE, "(Audio) Start stream: offset: %08" PRIx64 " length: %08x", offset, + length); + + if ((offset == 0) && (length == 0)) { s_stop_at_track_end = true; } else if (!s_stop_at_track_end) { - s_next_start = static_cast(s_DICMDBUF[1]) << 2; - s_next_length = s_DICMDBUF[2]; + s_next_start = offset; + s_next_length = length; if (!s_stream) { s_current_start = s_next_start; @@ -919,16 +967,34 @@ void ExecuteCommand(ReplyType reply_type) s_stream = true; } } + break; + } + case 0x01: + INFO_LOG(DVDINTERFACE, "(Audio) Stop stream"); + s_stop_at_track_end = false; + s_stream = false; + break; + default: + ERROR_LOG(DVDINTERFACE, "Invalid audio command! (%08x %08x %08x)", s_DICMDBUF[0], + s_DICMDBUF[1], s_DICMDBUF[2]); + SetHighError(ERROR_INV_AUDIO); + interrupt_type = DIInterruptType::DEINT; + break; } - - INFO_LOG(DVDINTERFACE, "(Audio) Stream cmd: %08x offset: %08" PRIx64 " length: %08x", - s_DICMDBUF[0], (u64)s_DICMDBUF[1] << 2, s_DICMDBUF[2]); } break; // Request Audio Status (Immediate). Only used by some GC games, but does exist on the Wii case DICommand::RequestAudioStatus: { + if (!s_enable_dtk) + { + ERROR_LOG(DVDINTERFACE, "Attempted to request audio status while audio is disabled!"); + SetHighError(ERROR_AUDIO_BUF); + interrupt_type = DIInterruptType::DEINT; + break; + } + switch (s_DICMDBUF[0] >> 16 & 0xFF) { case 0x00: // Returns streaming status @@ -956,8 +1022,10 @@ void ExecuteCommand(ReplyType reply_type) s_DIIMMBUF = s_current_length; break; default: - INFO_LOG(DVDINTERFACE, "(Audio): Subcommand: %02x Request Audio status %s", - s_DICMDBUF[0] >> 16 & 0xFF, s_stream ? "on" : "off"); + ERROR_LOG(DVDINTERFACE, "Invalid audio status command! (%08x %08x %08x)", s_DICMDBUF[0], + s_DICMDBUF[1], s_DICMDBUF[2]); + SetHighError(ERROR_INV_AUDIO); + interrupt_type = DIInterruptType::DEINT; break; } } @@ -987,20 +1055,24 @@ void ExecuteCommand(ReplyType reply_type) break; } - // DVD Audio Enable/Disable (Immediate). GC uses this, and apparently Wii also does...? + // DVD Audio Enable/Disable (Immediate). GC uses this, and the Wii can use it to configure GC + // games. case DICommand::AudioBufferConfig: // The IPL uses this command to enable or disable DTK audio depending on the value of byte 0x8 // in the disc header. See http://www.crazynation.org/GC/GC_DD_TECH/GCTech.htm for more info. // The link is dead, but you can access the page using the Wayback Machine at archive.org. - // TODO: Dolphin doesn't prevent the game from using DTK when the IPL doesn't enable it. - // Should we be failing with an error code when the game tries to use the 0xE1 command? - // (Not that this should matter normally, since games that use DTK set the header byte to 1) + // This command can only be used immediately after reading the disc ID, before any other + // reads. Too early, and you get ERROR_NO_DISKID. Too late, and you get ERROR_INV_PERIOD. + if (!s_can_configure_dtk) + { + ERROR_LOG(DVDINTERFACE, "Attempted to change DTK configuration after a read has been made!"); + SetHighError(ERROR_INV_PERIOD); + interrupt_type = DIInterruptType::DEINT; + break; + } - if ((s_DICMDBUF[0] >> 16) & 0xFF) - INFO_LOG(DVDINTERFACE, "DTK enabled"); - else - INFO_LOG(DVDINTERFACE, "DTK disabled"); + AudioBufferConfig((s_DICMDBUF[0] >> 16) & 1, s_DICMDBUF[0] & 0xf); break; // GC-only patched drive firmware command, used by libogc @@ -1056,6 +1128,7 @@ void ExecuteCommand(ReplyType reply_type) void PerformDecryptingRead(u32 position, u32 length, u32 output_address, ReplyType reply_type) { DIInterruptType interrupt_type = DIInterruptType::TCINT; + s_can_configure_dtk = false; const bool command_handled_by_thread = ExecuteReadCommand(static_cast(position) << 2, output_address, length, length, @@ -1070,6 +1143,16 @@ void PerformDecryptingRead(u32 position, u32 length, u32 output_address, ReplyTy } } +void AudioBufferConfig(bool enable_dtk, u8 dtk_buffer_length) +{ + s_enable_dtk = enable_dtk; + s_dtk_buffer_length = dtk_buffer_length; + if (s_enable_dtk) + INFO_LOG(DVDINTERFACE, "DTK enabled: buffer size %d", s_dtk_buffer_length); + else + INFO_LOG(DVDINTERFACE, "DTK disabled"); +} + u64 PackFinishExecutingCommandUserdata(ReplyType reply_type, DIInterruptType interrupt_type) { return (static_cast(reply_type) << 32) + static_cast(interrupt_type); @@ -1125,7 +1208,7 @@ void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type case ReplyType::DTK: { - DTKStreamingCallback(data, cycles_late); + DTKStreamingCallback(interrupt_type, data, cycles_late); break; } } diff --git a/Source/Core/Core/HW/DVD/DVDInterface.h b/Source/Core/Core/HW/DVD/DVDInterface.h index 37a7bbf8d8..0242c29b29 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.h +++ b/Source/Core/Core/HW/DVD/DVDInterface.h @@ -127,6 +127,8 @@ bool UpdateRunningGameMetadata(std::optional title_id = {}); void ChangePartition(const DiscIO::Partition& partition); void ExecuteCommand(ReplyType reply_type); void PerformDecryptingRead(u32 position, u32 length, u32 output_address, ReplyType reply_type); +// Exposed for use by emulated BS2; does not perform any checks on drive state +void AudioBufferConfig(bool enable_dtk, u8 dtk_buffer_length); void SetLowError(u32 low_error); void SetHighError(u32 high_error); diff --git a/Source/Core/Core/IOS/DI/DI.cpp b/Source/Core/Core/IOS/DI/DI.cpp index e4eeff686a..70a6038a47 100644 --- a/Source/Core/Core/IOS/DI/DI.cpp +++ b/Source/Core/Core/IOS/DI/DI.cpp @@ -152,7 +152,14 @@ std::optional DI::StartIOCtl(const IOCtlRequest& request) DICMDBUF1 = 0; DICMDBUF2 = 0x20; return StartDMATransfer(0x20, request); - // TODO: also include the post-read second read + // TODO: Include an additional read that happens on Wii discs, or at least + // emulate its side effect of disabling DTK configuration + // if (Memory::Read_U32(request.buffer_out + 24) == 0x5d1c9ea3) { // Wii Magic + // if (!m_has_read_encryption_info) { + // // Read 0x44 (=> 0x60) bytes starting from offset 8 or byte 0x20; + // // byte 0x60 is disable hashing and byte 0x61 is disable encryption + // } + // } case DIIoctl::DVDLowRead: { // TODO. Needs to include decryption.