IOS/DI: Fake the error 001 read when running DirectoryBlobs or Riivolution-patched games.
This commit is contained in:
parent
ffe0bcbb84
commit
8d0f52032b
|
@ -15,4 +15,6 @@ const Info<bool> SESSION_GCI_FOLDER_CURRENT_GAME_ONLY{
|
||||||
{System::Session, "Core", "GCIFolderCurrentGameOnly"}, false};
|
{System::Session, "Core", "GCIFolderCurrentGameOnly"}, false};
|
||||||
const Info<bool> SESSION_CODE_SYNC_OVERRIDE{{System::Session, "Core", "CheatSyncOverride"}, 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_SAVE_DATA_WRITABLE{{System::Session, "Core", "SaveDataWritable"}, true};
|
||||||
|
const Info<bool> SESSION_SHOULD_FAKE_ERROR_001{{System::Session, "Core", "ShouldFakeError001"},
|
||||||
|
false};
|
||||||
} // namespace Config
|
} // namespace Config
|
||||||
|
|
|
@ -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_GCI_FOLDER_CURRENT_GAME_ONLY;
|
||||||
extern const Info<bool> SESSION_CODE_SYNC_OVERRIDE;
|
extern const Info<bool> SESSION_CODE_SYNC_OVERRIDE;
|
||||||
extern const Info<bool> SESSION_SAVE_DATA_WRITABLE;
|
extern const Info<bool> SESSION_SAVE_DATA_WRITABLE;
|
||||||
|
extern const Info<bool> SESSION_SHOULD_FAKE_ERROR_001;
|
||||||
} // namespace Config
|
} // namespace Config
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
|
|
||||||
#include "Core/Config/MainSettings.h"
|
#include "Core/Config/MainSettings.h"
|
||||||
|
#include "Core/Config/SessionSettings.h"
|
||||||
#include "Core/CoreTiming.h"
|
#include "Core/CoreTiming.h"
|
||||||
#include "Core/DolphinAnalytics.h"
|
#include "Core/DolphinAnalytics.h"
|
||||||
#include "Core/HW/AudioInterface.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);
|
WARN_LOG_FMT(DVDINTERFACE, "Unknown disc size, guessing {0} bytes", s_disc_end_offset);
|
||||||
|
|
||||||
const DiscIO::BlobReader& blob = disc->GetBlobReader();
|
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)
|
if (!blob.HasFastRandomAccessInBlock() && blob.GetBlockSize() > 0x200000)
|
||||||
{
|
{
|
||||||
OSD::AddMessage("You are running a disc image with a very large block size.", 60000);
|
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)
|
void AudioBufferConfig(bool enable_dtk, u8 dtk_buffer_length)
|
||||||
{
|
{
|
||||||
s_enable_dtk = enable_dtk;
|
s_enable_dtk = enable_dtk;
|
||||||
|
|
|
@ -134,6 +134,10 @@ bool UpdateRunningGameMetadata(std::optional<u64> title_id = {});
|
||||||
void ExecuteCommand(ReplyType reply_type);
|
void ExecuteCommand(ReplyType reply_type);
|
||||||
void PerformDecryptingRead(u32 position, u32 length, u32 output_address,
|
void PerformDecryptingRead(u32 position, u32 length, u32 output_address,
|
||||||
const DiscIO::Partition& partition, ReplyType reply_type);
|
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
|
// Exposed for use by emulated BS2; does not perform any checks on drive state
|
||||||
void AudioBufferConfig(bool enable_dtk, u8 dtk_buffer_length);
|
void AudioBufferConfig(bool enable_dtk, u8 dtk_buffer_length);
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
|
#include "Core/Config/SessionSettings.h"
|
||||||
#include "Core/CoreTiming.h"
|
#include "Core/CoreTiming.h"
|
||||||
#include "Core/DolphinAnalytics.h"
|
#include "Core/DolphinAnalytics.h"
|
||||||
#include "Core/HW/DVD/DVDInterface.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)
|
std::optional<DIDevice::DIResult> DIDevice::StartIOCtl(const IOCtlRequest& request)
|
||||||
{
|
{
|
||||||
if (request.buffer_in_size != 0x20)
|
if (request.buffer_in_size != 0x20)
|
||||||
|
@ -315,20 +326,31 @@ std::optional<DIDevice::DIResult> DIDevice::StartIOCtl(const IOCtlRequest& reque
|
||||||
// (start, end) as 32-bit offsets
|
// (start, end) as 32-bit offsets
|
||||||
// Older IOS versions only accept the first range. Later versions added the extra ranges to
|
// 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).
|
// check how the drive responds to out of bounds reads (an error 001 check).
|
||||||
constexpr std::array<std::pair<u32, u32>, 3> valid_ranges = {
|
constexpr std::array<DiscRange, 3> valid_ranges = {
|
||||||
std::make_pair(0, 0x14000), // "System area"
|
DiscRange{0, 0x14000, false}, // "System area"
|
||||||
std::make_pair(0x460A0000, 0x460A0008),
|
DiscRange{0x460A0000, 0x460A0008, true},
|
||||||
std::make_pair(0x7ED40000, 0x7ED40008),
|
DiscRange{0x7ED40000, 0x7ED40008, true},
|
||||||
};
|
};
|
||||||
for (auto range : valid_ranges)
|
for (auto range : valid_ranges)
|
||||||
{
|
{
|
||||||
if (range.first <= position && position <= range.second && range.first <= end &&
|
if (range.start <= position && position <= range.end && range.start <= end &&
|
||||||
end <= range.second)
|
end <= range.end)
|
||||||
{
|
{
|
||||||
DICMDBUF0 = 0xA8000000;
|
DICMDBUF0 = 0xA8000000;
|
||||||
DICMDBUF1 = position;
|
DICMDBUF1 = position;
|
||||||
DICMDBUF2 = length;
|
DICMDBUF2 = length;
|
||||||
return StartDMATransfer(length, request);
|
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!");
|
WARN_LOG_FMT(IOS_DI, "DVDLowUnencryptedRead: trying to read from an illegal region!");
|
||||||
|
|
Loading…
Reference in New Issue