IOS/DI: Fake the error 001 read when running DirectoryBlobs or Riivolution-patched games.

This commit is contained in:
Admiral H. Curtiss 2022-05-15 23:22:08 +02:00
parent ffe0bcbb84
commit 8d0f52032b
No known key found for this signature in database
GPG Key ID: F051B4C4044F33FB
5 changed files with 61 additions and 7 deletions

View File

@ -15,4 +15,6 @@ const Info<bool> SESSION_GCI_FOLDER_CURRENT_GAME_ONLY{
{System::Session, "Core", "GCIFolderCurrentGameOnly"}, false};
const Info<bool> SESSION_CODE_SYNC_OVERRIDE{{System::Session, "Core", "CheatSyncOverride"}, false};
const Info<bool> SESSION_SAVE_DATA_WRITABLE{{System::Session, "Core", "SaveDataWritable"}, true};
const Info<bool> SESSION_SHOULD_FAKE_ERROR_001{{System::Session, "Core", "ShouldFakeError001"},
false};
} // namespace Config

View File

@ -13,4 +13,5 @@ extern const Info<bool> SESSION_LOAD_IPL_DUMP;
extern const Info<bool> SESSION_GCI_FOLDER_CURRENT_GAME_ONLY;
extern const Info<bool> SESSION_CODE_SYNC_OVERRIDE;
extern const Info<bool> SESSION_SAVE_DATA_WRITABLE;
extern const Info<bool> SESSION_SHOULD_FAKE_ERROR_001;
} // namespace Config

View File

@ -19,6 +19,7 @@
#include "Common/Logging/Log.h"
#include "Core/Config/MainSettings.h"
#include "Core/Config/SessionSettings.h"
#include "Core/CoreTiming.h"
#include "Core/DolphinAnalytics.h"
#include "Core/HW/AudioInterface.h"
@ -462,6 +463,14 @@ void SetDisc(std::unique_ptr<DiscIO::VolumeDisc> disc,
WARN_LOG_FMT(DVDINTERFACE, "Unknown disc size, guessing {0} bytes", s_disc_end_offset);
const DiscIO::BlobReader& blob = disc->GetBlobReader();
// DirectoryBlobs (including Riivolution-patched discs) may end up larger than a real physical
// Wii disc, which triggers Error #001. In those cases we manually make the check succeed to
// avoid problems.
const bool should_fake_error_001 =
SConfig::GetInstance().bWii && blob.GetBlobType() == DiscIO::BlobType::DIRECTORY;
Config::SetCurrent(Config::SESSION_SHOULD_FAKE_ERROR_001, should_fake_error_001);
if (!blob.HasFastRandomAccessInBlock() && blob.GetBlockSize() > 0x200000)
{
OSD::AddMessage("You are running a disc image with a very large block size.", 60000);
@ -1263,6 +1272,22 @@ void PerformDecryptingRead(u32 position, u32 length, u32 output_address,
}
}
void ForceOutOfBoundsRead(ReplyType reply_type)
{
INFO_LOG_FMT(DVDINTERFACE, "Forcing an out-of-bounds disc read.");
if (s_drive_state == DriveState::ReadyNoReadsMade)
SetDriveState(DriveState::Ready);
SetDriveError(DriveError::BlockOOB);
// TODO: Needs testing to determine if MINIMUM_COMMAND_LATENCY_US is accurate for this
const DIInterruptType interrupt_type = DIInterruptType::DEINT;
CoreTiming::ScheduleEvent(
MINIMUM_COMMAND_LATENCY_US * (SystemTimers::GetTicksPerSecond() / 1000000),
s_finish_executing_command, PackFinishExecutingCommandUserdata(reply_type, interrupt_type));
}
void AudioBufferConfig(bool enable_dtk, u8 dtk_buffer_length)
{
s_enable_dtk = enable_dtk;

View File

@ -134,6 +134,10 @@ bool UpdateRunningGameMetadata(std::optional<u64> title_id = {});
void ExecuteCommand(ReplyType reply_type);
void PerformDecryptingRead(u32 position, u32 length, u32 output_address,
const DiscIO::Partition& partition, ReplyType reply_type);
// For circumventing Error #001 in DirectoryBlobs, which may have data in the offsets being checked.
void ForceOutOfBoundsRead(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);

View File

@ -11,6 +11,7 @@
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Core/Config/SessionSettings.h"
#include "Core/CoreTiming.h"
#include "Core/DolphinAnalytics.h"
#include "Core/HW/DVD/DVDInterface.h"
@ -132,6 +133,16 @@ std::optional<DIDevice::DIResult> DIDevice::WriteIfFits(const IOCtlRequest& requ
}
}
namespace
{
struct DiscRange
{
u32 start;
u32 end;
bool is_error_001_range;
};
} // namespace
std::optional<DIDevice::DIResult> DIDevice::StartIOCtl(const IOCtlRequest& request)
{
if (request.buffer_in_size != 0x20)
@ -315,22 +326,33 @@ std::optional<DIDevice::DIResult> DIDevice::StartIOCtl(const IOCtlRequest& reque
// (start, end) as 32-bit offsets
// Older IOS versions only accept the first range. Later versions added the extra ranges to
// check how the drive responds to out of bounds reads (an error 001 check).
constexpr std::array<std::pair<u32, u32>, 3> valid_ranges = {
std::make_pair(0, 0x14000), // "System area"
std::make_pair(0x460A0000, 0x460A0008),
std::make_pair(0x7ED40000, 0x7ED40008),
constexpr std::array<DiscRange, 3> valid_ranges = {
DiscRange{0, 0x14000, false}, // "System area"
DiscRange{0x460A0000, 0x460A0008, true},
DiscRange{0x7ED40000, 0x7ED40008, true},
};
for (auto range : valid_ranges)
{
if (range.first <= position && position <= range.second && range.first <= end &&
end <= range.second)
if (range.start <= position && position <= range.end && range.start <= end &&
end <= range.end)
{
DICMDBUF0 = 0xA8000000;
DICMDBUF1 = position;
DICMDBUF2 = length;
if (range.is_error_001_range && Config::Get(Config::SESSION_SHOULD_FAKE_ERROR_001))
{
DIMAR = request.buffer_out;
m_last_length = length;
DILENGTH = length;
DVDInterface::ForceOutOfBoundsRead(DVDInterface::ReplyType::IOS);
return {};
}
else
{
return StartDMATransfer(length, request);
}
}
}
WARN_LOG_FMT(IOS_DI, "DVDLowUnencryptedRead: trying to read from an illegal region!");
return DIResult::SecurityError;
}