From c96bcace2f64796a5098b58ae620a158e9d3e9dc Mon Sep 17 00:00:00 2001 From: JosJuice Date: Fri, 10 Feb 2017 16:56:40 +0100 Subject: [PATCH 1/2] VolumeWiiCrypted: Use constant naming style for constants --- Source/Core/DiscIO/VolumeWiiCrypted.cpp | 16 ++++++++-------- Source/Core/DiscIO/VolumeWiiCrypted.h | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Source/Core/DiscIO/VolumeWiiCrypted.cpp b/Source/Core/DiscIO/VolumeWiiCrypted.cpp index 7976e1d758..cb5fe4464f 100644 --- a/Source/Core/DiscIO/VolumeWiiCrypted.cpp +++ b/Source/Core/DiscIO/VolumeWiiCrypted.cpp @@ -60,26 +60,26 @@ bool CVolumeWiiCrypted::Read(u64 _ReadOffset, u64 _Length, u8* _pBuffer, bool de FileMon::FindFilename(_ReadOffset); - std::vector read_buffer(s_block_total_size); + std::vector read_buffer(BLOCK_TOTAL_SIZE); while (_Length > 0) { // Calculate block offset - u64 Block = _ReadOffset / s_block_data_size; - u64 Offset = _ReadOffset % s_block_data_size; + u64 Block = _ReadOffset / BLOCK_DATA_SIZE; + u64 Offset = _ReadOffset % BLOCK_DATA_SIZE; if (m_LastDecryptedBlockOffset != Block) { // Read the current block - if (!m_pReader->Read(m_VolumeOffset + m_dataOffset + Block * s_block_total_size, - s_block_total_size, read_buffer.data())) + if (!m_pReader->Read(m_VolumeOffset + m_dataOffset + Block * BLOCK_TOTAL_SIZE, + BLOCK_TOTAL_SIZE, read_buffer.data())) return false; // Decrypt the block's data. // 0x3D0 - 0x3DF in m_pBuffer will be overwritten, // but that won't affect anything, because we won't // use the content of m_pBuffer anymore after this - mbedtls_aes_crypt_cbc(m_AES_ctx.get(), MBEDTLS_AES_DECRYPT, s_block_data_size, - &read_buffer[0x3D0], &read_buffer[s_block_header_size], + mbedtls_aes_crypt_cbc(m_AES_ctx.get(), MBEDTLS_AES_DECRYPT, BLOCK_DATA_SIZE, + &read_buffer[0x3D0], &read_buffer[BLOCK_HEADER_SIZE], m_LastDecryptedBlock); m_LastDecryptedBlockOffset = Block; @@ -90,7 +90,7 @@ bool CVolumeWiiCrypted::Read(u64 _ReadOffset, u64 _Length, u8* _pBuffer, bool de } // Copy the decrypted data - u64 MaxSizeToCopy = s_block_data_size - Offset; + u64 MaxSizeToCopy = BLOCK_DATA_SIZE - Offset; u64 CopySize = (_Length > MaxSizeToCopy) ? MaxSizeToCopy : _Length; memcpy(_pBuffer, &m_LastDecryptedBlock[Offset], (size_t)CopySize); diff --git a/Source/Core/DiscIO/VolumeWiiCrypted.h b/Source/Core/DiscIO/VolumeWiiCrypted.h index d578dd5ba0..623ce5366f 100644 --- a/Source/Core/DiscIO/VolumeWiiCrypted.h +++ b/Source/Core/DiscIO/VolumeWiiCrypted.h @@ -54,9 +54,9 @@ public: u64 GetRawSize() const override; private: - static const unsigned int s_block_header_size = 0x0400; - static const unsigned int s_block_data_size = 0x7C00; - static const unsigned int s_block_total_size = s_block_header_size + s_block_data_size; + static constexpr unsigned int BLOCK_HEADER_SIZE = 0x0400; + static constexpr unsigned int BLOCK_DATA_SIZE = 0x7C00; + static constexpr unsigned int BLOCK_TOTAL_SIZE = BLOCK_HEADER_SIZE + BLOCK_DATA_SIZE; std::unique_ptr m_pReader; std::unique_ptr m_AES_ctx; @@ -65,7 +65,7 @@ private: u64 m_dataOffset; mutable u64 m_LastDecryptedBlockOffset; - mutable unsigned char m_LastDecryptedBlock[s_block_data_size]; + mutable unsigned char m_LastDecryptedBlock[BLOCK_DATA_SIZE]; }; } // namespace From 49ec22bc42c7a1cf12f2b1ad181dc5efe46f97b5 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Fri, 10 Feb 2017 18:57:58 +0100 Subject: [PATCH 2/2] DVDInterface: Translate Wii partition offsets for timing purposes Until now, Dolphin has been using the wrong values for calculating DVD timing for decrypted Wii reads (which Wii games essentially always use). --- Source/Core/Core/HW/DVDInterface.cpp | 62 ++++++++++++++----------- Source/Core/DiscIO/Volume.h | 1 + Source/Core/DiscIO/VolumeWiiCrypted.cpp | 6 +++ Source/Core/DiscIO/VolumeWiiCrypted.h | 3 +- 4 files changed, 45 insertions(+), 27 deletions(-) diff --git a/Source/Core/Core/HW/DVDInterface.cpp b/Source/Core/Core/HW/DVDInterface.cpp index e338e76698..90b208398d 100644 --- a/Source/Core/Core/HW/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVDInterface.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include #include #include #include @@ -31,6 +32,7 @@ #include "DiscIO/Enums.h" #include "DiscIO/Volume.h" #include "DiscIO/VolumeCreator.h" +#include "DiscIO/VolumeWiiCrypted.h" // The minimum time it takes for the DVD drive to process a command (in // microseconds) @@ -273,8 +275,7 @@ bool ExecuteReadCommand(u64 DVD_offset, u32 output_address, u32 DVD_length, u32 u64 PackFinishExecutingCommandUserdata(ReplyType reply_type, DIInterruptType interrupt_type); -void ScheduleReads(u64 dvd_offset, u32 length, bool decrypt, u32 output_address, - ReplyType reply_type); +void ScheduleReads(u64 offset, u32 length, bool decrypt, u32 output_address, ReplyType reply_type); double CalculatePhysicalDiscPosition(u64 offset); u64 CalculateSeekTime(u64 offset_from, u64 offset_to); u64 CalculateRawDiscReadTime(u64 offset, u64 length); @@ -1168,8 +1169,7 @@ void FinishExecutingCommand(ReplyType reply_type, DIInterruptType interrupt_type // Determines from a given read request how much of the request is buffered, // and how much is required to be read from disc. -void ScheduleReads(u64 dvd_offset, u32 dvd_length, bool decrypt, u32 output_address, - ReplyType reply_type) +void ScheduleReads(u64 offset, u32 length, bool decrypt, u32 output_address, ReplyType reply_type) { // The drive continues to read 1 MiB beyond the last read position when idle. // If a future read falls within this window, part of the read may be returned @@ -1190,6 +1190,15 @@ void ScheduleReads(u64 dvd_offset, u32 dvd_length, bool decrypt, u32 output_addr // If we fall within its bounds, we get DMA-speed reads. u64 buffer_start, buffer_end; + // The variable offset uses the same addressing as games do. + // The variable dvd_offset tracks the actual offset on the DVD + // that the disc drive starts reading at, which differs in two ways: + // It's rounded to a whole ECC block and never uses Wii partition addressing. + u64 dvd_offset = offset; + if (decrypt) + dvd_offset = s_inserted_volume->PartitionOffsetToRawOffset(offset); + dvd_offset = Common::AlignDown(dvd_offset, DVD_ECC_BLOCK_SIZE); + if (SConfig::GetInstance().bFastDiscSpeed) { // The SUDTR setting makes us act as if all reads are buffered @@ -1244,8 +1253,8 @@ void ScheduleReads(u64 dvd_offset, u32 dvd_length, bool decrypt, u32 output_addr buffer_start, buffer_end, buffer_end - buffer_start); DEBUG_LOG(DVDINTERFACE, - "Schedule reads: offset=0x%" PRIx64 " length=0x%" PRIx32 " address=0x%" PRIx32, - dvd_offset, dvd_length, output_address); + "Schedule reads: offset=0x%" PRIx64 " length=0x%" PRIx32 " address=0x%" PRIx32, offset, + length, output_address); // The DVD drive's minimum turnaround time on a command, based on a hardware test. s64 ticks_until_completion = COMMAND_LATENCY_US * (SystemTimers::GetTicksPerSecond() / 1000000); @@ -1253,23 +1262,23 @@ void ScheduleReads(u64 dvd_offset, u32 dvd_length, bool decrypt, u32 output_addr u32 buffered_blocks = 0; u32 unbuffered_blocks = 0; - while (dvd_length > 0) - { - // Where the read actually takes place on disc - u64 rounded_offset = Common::AlignDown(dvd_offset, DVD_ECC_BLOCK_SIZE); + const u32 bytes_per_chunk = + decrypt ? DiscIO::CVolumeWiiCrypted::BLOCK_DATA_SIZE : DVD_ECC_BLOCK_SIZE; + while (length > 0) + { // The length of this read - "+1" so that if this read is already - // aligned to an ECC block we'll read the entire block. - u32 chunk_length = - static_cast(Common::AlignUp(dvd_offset + 1, DVD_ECC_BLOCK_SIZE) - dvd_offset); + // aligned to a block we'll read the entire block. + u32 chunk_length = static_cast(Common::AlignUp(offset + 1, bytes_per_chunk) - offset); // The last chunk may be short - if (chunk_length > dvd_length) - chunk_length = dvd_length; + chunk_length = std::min(chunk_length, length); - if (rounded_offset >= buffer_start && rounded_offset < buffer_end) + if (dvd_offset >= buffer_start && dvd_offset < buffer_end) { // Number of ticks it takes to transfer the data from the buffer to memory. + // TODO: This calculation is slightly wrong when decrypt is true - it uses the size of + // the copy from IOS to PPC but is supposed to model the copy from the disc drive to IOS. ticks_until_completion += static_cast(chunk_length) * SystemTimers::GetTicksPerSecond() / BUFFER_TRANSFER_RATE; buffered_blocks++; @@ -1278,39 +1287,40 @@ void ScheduleReads(u64 dvd_offset, u32 dvd_length, bool decrypt, u32 output_addr { // In practice we'll only ever seek if this is the first time // through this loop. - if (rounded_offset != head_position) + if (dvd_offset != head_position) { // Unbuffered seek+read - ticks_until_completion += CalculateSeekTime(head_position, rounded_offset); + ticks_until_completion += CalculateSeekTime(head_position, dvd_offset); DEBUG_LOG(DVDINTERFACE, "Seek+read 0x%" PRIx32 " bytes @ 0x%" PRIx64 " ticks=%" PRId64, - chunk_length, rounded_offset, ticks_until_completion); + chunk_length, offset, ticks_until_completion); } else { // Unbuffered read - ticks_until_completion += CalculateRawDiscReadTime(rounded_offset, DVD_ECC_BLOCK_SIZE); + ticks_until_completion += CalculateRawDiscReadTime(dvd_offset, DVD_ECC_BLOCK_SIZE); } unbuffered_blocks++; - head_position = rounded_offset + DVD_ECC_BLOCK_SIZE; + head_position = dvd_offset + DVD_ECC_BLOCK_SIZE; } // Schedule this read to complete at the appropriate time - const ReplyType chunk_reply_type = chunk_length == dvd_length ? reply_type : ReplyType::NoReply; - DVDThread::StartReadToEmulatedRAM(output_address, dvd_offset, chunk_length, decrypt, + const ReplyType chunk_reply_type = chunk_length == length ? reply_type : ReplyType::NoReply; + DVDThread::StartReadToEmulatedRAM(output_address, offset, chunk_length, decrypt, chunk_reply_type, ticks_until_completion); // Advance the read window output_address += chunk_length; - dvd_offset += chunk_length; - dvd_length -= chunk_length; + offset += chunk_length; + length -= chunk_length; + dvd_offset += DVD_ECC_BLOCK_SIZE; } // Update the buffer based on this read. Based on experimental testing, // we will only reuse the old buffer while reading forward. Note that the // buffer start we calculate here is not the actual start of the buffer - // it is just the start of the portion we need to read. - u64 last_block = Common::AlignUp(dvd_offset, DVD_ECC_BLOCK_SIZE); + const u64 last_block = dvd_offset; if (last_block == buffer_start + DVD_ECC_BLOCK_SIZE && buffer_start != buffer_end) { // Special case: reading less than one block at the start of the diff --git a/Source/Core/DiscIO/Volume.h b/Source/Core/DiscIO/Volume.h index 6f4a6defe4..966bbe2ae1 100644 --- a/Source/Core/DiscIO/Volume.h +++ b/Source/Core/DiscIO/Volume.h @@ -37,6 +37,7 @@ public: virtual bool GetTitleID(u64*) const { return false; } virtual std::vector GetTMD() const { return {}; } + virtual u64 PartitionOffsetToRawOffset(u64 offset) const { return offset; } virtual std::string GetGameID() const = 0; virtual std::string GetMakerID() const = 0; virtual u16 GetRevision() const = 0; diff --git a/Source/Core/DiscIO/VolumeWiiCrypted.cpp b/Source/Core/DiscIO/VolumeWiiCrypted.cpp index cb5fe4464f..ae174604cc 100644 --- a/Source/Core/DiscIO/VolumeWiiCrypted.cpp +++ b/Source/Core/DiscIO/VolumeWiiCrypted.cpp @@ -140,6 +140,12 @@ std::vector CVolumeWiiCrypted::GetTMD() const return buffer; } +u64 CVolumeWiiCrypted::PartitionOffsetToRawOffset(u64 offset) const +{ + return m_VolumeOffset + m_dataOffset + (offset / BLOCK_DATA_SIZE * BLOCK_TOTAL_SIZE) + + (offset % BLOCK_DATA_SIZE); +} + std::string CVolumeWiiCrypted::GetGameID() const { if (m_pReader == nullptr) diff --git a/Source/Core/DiscIO/VolumeWiiCrypted.h b/Source/Core/DiscIO/VolumeWiiCrypted.h index 623ce5366f..93c289cc43 100644 --- a/Source/Core/DiscIO/VolumeWiiCrypted.h +++ b/Source/Core/DiscIO/VolumeWiiCrypted.h @@ -32,6 +32,7 @@ public: bool Read(u64 _Offset, u64 _Length, u8* _pBuffer, bool decrypt) const override; bool GetTitleID(u64* buffer) const override; std::vector GetTMD() const override; + u64 PartitionOffsetToRawOffset(u64 offset) const override; std::string GetGameID() const override; std::string GetMakerID() const override; u16 GetRevision() const override; @@ -53,11 +54,11 @@ public: u64 GetSize() const override; u64 GetRawSize() const override; -private: static constexpr unsigned int BLOCK_HEADER_SIZE = 0x0400; static constexpr unsigned int BLOCK_DATA_SIZE = 0x7C00; static constexpr unsigned int BLOCK_TOTAL_SIZE = BLOCK_HEADER_SIZE + BLOCK_DATA_SIZE; +private: std::unique_ptr m_pReader; std::unique_ptr m_AES_ctx;