Skip to content
Permalink
Browse files
Merge pull request #9577 from JosJuice/di-unknown-size-hack
DVDInterface: Make the WBFS/CISO hack only affect WBFS/CISO
  • Loading branch information
leoetlino committed Mar 16, 2021
2 parents 3fcc018 + e47c976 commit c8d8f9e
Show file tree
Hide file tree
Showing 18 changed files with 309 additions and 219 deletions.
@@ -36,6 +36,7 @@
#include "Core/Movie.h"

#include "DiscIO/Blob.h"
#include "DiscIO/DiscUtils.h"
#include "DiscIO/Enums.h"
#include "DiscIO/VolumeDisc.h"
#include "DiscIO/VolumeWii.h"
@@ -164,6 +165,7 @@ static u8 s_dtk_buffer_length = 0; // TODO: figure out how this affects the reg
// Disc drive state
static DriveState s_drive_state;
static DriveError s_error_code;
static u64 s_disc_end_offset;

// Disc drive timing
static u64 s_read_buffer_start_time;
@@ -425,6 +427,33 @@ void Shutdown()
DVDThread::Stop();
}

static u64 GetDiscEndOffset(const DiscIO::VolumeDisc& disc)
{
u64 size = disc.GetSize();

if (disc.IsSizeAccurate())
{
if (size == DiscIO::MINI_DVD_SIZE)
return DiscIO::MINI_DVD_SIZE;
}
else
{
size = DiscIO::GetBiggestReferencedOffset(disc);
}

const bool should_be_mini_dvd =
disc.GetVolumeType() == DiscIO::Platform::GameCubeDisc || disc.IsDatelDisc();

// We always return standard DVD sizes here, not DVD-R sizes.
// RVT-R (devkit) consoles can't read the extra megabytes there are on RVT-R (DVD-R) discs.
if (should_be_mini_dvd && size <= DiscIO::MINI_DVD_SIZE)
return DiscIO::MINI_DVD_SIZE;
else if (size <= DiscIO::SL_DVD_R_SIZE)
return DiscIO::SL_DVD_SIZE;
else
return DiscIO::DL_DVD_SIZE;
}

void SetDisc(std::unique_ptr<DiscIO::VolumeDisc> disc,
std::optional<std::vector<std::string>> auto_disc_change_paths = {})
{
@@ -433,6 +462,10 @@ void SetDisc(std::unique_ptr<DiscIO::VolumeDisc> disc,

if (has_disc)
{
s_disc_end_offset = GetDiscEndOffset(*disc);
if (!disc->IsSizeAccurate())
WARN_LOG_FMT(DVDINTERFACE, "Unknown disc size, guessing {0} bytes", s_disc_end_offset);

const DiscIO::BlobReader& blob = disc->GetBlobReader();
if (!blob.HasFastRandomAccessInBlock() && blob.GetBlockSize() > 0x200000)
{
@@ -763,20 +796,11 @@ static bool ExecuteReadCommand(u64 dvd_offset, u32 output_address, u32 dvd_lengt
dvd_length = output_length;
}

// Many Wii games intentionally try to read from an offset which is just past the end of a regular
// DVD but just before the end of a DVD-R, displaying "Error #001" and failing to boot if the read
// succeeds (see https://wiibrew.org/wiki//dev/di#0x8D_DVDLowUnencryptedRead for more details).
// It would be nice if we simply could rely on DiscIO for letting us know whether a read is out
// of bounds, but this unfortunately doesn't work when using a disc image format that doesn't
// store the original size of the disc, most notably WBFS. Instead, we have a little hack here:
// reject all non-partition reads that come from IOS that go past the offset 0x50000. IOS only
// allows non-partition reads if they are before 0x50000 or if they are in one of the two small
// areas 0x118240000-0x118240020 and 0x1FB4E0000-0x1FB4E0020 (both of which only are used for
// Error #001 checks), so the only thing we disallow with this hack that actually should be
// allowed is non-partition reads in the 0x118240000-0x118240020 area on dual-layer discs.
// In practice, dual-layer games don't attempt to do non-partition reads in that area.
if (reply_type == ReplyType::IOS && partition == DiscIO::PARTITION_NONE &&
dvd_offset + dvd_length > 0x50000)
// Many Wii games intentionally try to read from an offset which is just past the end of a
// regular DVD but just before the end of a DVD-R, displaying "Error #001" and failing to boot
// if the read succeeds, so it's critical that we set the correct error code for such reads.
// See https://wiibrew.org/wiki//dev/di#0x8D_DVDLowUnencryptedRead for details on Error #001.
if (dvd_offset + dvd_length > s_disc_end_offset)
{
SetDriveError(DriveError::BlockOOB);
*interrupt_type = DIInterruptType::DEINT;
@@ -347,7 +347,7 @@ static void FinishRead(u64 id, s64 cycles_late)
PanicAlertFmtT("The disc could not be read (at {0:#x} - {1:#x}).", request.dvd_offset,
request.dvd_offset + request.length);

DVDInterface::SetDriveError(DVDInterface::DriveError::BlockOOB);
DVDInterface::SetDriveError(DVDInterface::DriveError::ReadError);
interrupt = DVDInterface::DIInterruptType::DEINT;
}
else
@@ -11,6 +11,8 @@ add_library(discio
DiscExtractor.h
DiscScrubber.cpp
DiscScrubber.h
DiscUtils.cpp
DiscUtils.h
DriveBlob.cpp
DriveBlob.h
Enums.cpp
@@ -28,6 +28,7 @@
#include "Core/Boot/DolReader.h"
#include "Core/IOS/ES/Formats.h"
#include "DiscIO/Blob.h"
#include "DiscIO/DiscUtils.h"
#include "DiscIO/VolumeWii.h"
#include "DiscIO/WiiEncryptionCache.h"

@@ -653,8 +654,8 @@ void DirectoryBlobPartition::SetDiscHeaderAndDiscType(std::optional<bool> is_wii
}
else
{
m_is_wii = Common::swap32(&m_disc_header[0x18]) == 0x5d1c9ea3;
const bool is_gc = Common::swap32(&m_disc_header[0x1c]) == 0xc2339f3d;
m_is_wii = Common::swap32(&m_disc_header[0x18]) == WII_DISC_MAGIC;
const bool is_gc = Common::swap32(&m_disc_header[0x1c]) == GAMECUBE_DISC_MAGIC;
if (m_is_wii == is_gc)
ERROR_LOG_FMT(DISCIO, "Couldn't detect disc type based on {}", boot_bin_path);
}
@@ -5,50 +5,21 @@
#include "DiscIO/DiscExtractor.h"

#include <algorithm>
#include <locale>
#include <functional>
#include <optional>

#include <fmt/format.h>
#include <string>
#include <string_view>

#include "Common/CommonTypes.h"
#include "Common/FileUtil.h"
#include "Common/IOFile.h"
#include "DiscIO/DiscUtils.h"
#include "DiscIO/Enums.h"
#include "DiscIO/Filesystem.h"
#include "DiscIO/Volume.h"

namespace DiscIO
{
std::string NameForPartitionType(u32 partition_type, bool include_prefix)
{
switch (partition_type)
{
case PARTITION_DATA:
return "DATA";
case PARTITION_UPDATE:
return "UPDATE";
case PARTITION_CHANNEL:
return "CHANNEL";
case PARTITION_INSTALL:
// wit doesn't recognize the name "INSTALL", so we can't use it when naming partition folders
if (!include_prefix)
return "INSTALL";
[[fallthrough]];
default:
const std::string type_as_game_id{static_cast<char>((partition_type >> 24) & 0xFF),
static_cast<char>((partition_type >> 16) & 0xFF),
static_cast<char>((partition_type >> 8) & 0xFF),
static_cast<char>(partition_type & 0xFF)};
if (std::all_of(type_as_game_id.cbegin(), type_as_game_id.cend(),
[](char c) { return std::isalnum(c, std::locale::classic()); }))
{
return include_prefix ? "P-" + type_as_game_id : type_as_game_id;
}

return fmt::format(include_prefix ? "P{}" : "{}", partition_type);
}
}

u64 ReadFile(const Volume& volume, const Partition& partition, const FileInfo* file_info,
u8* buffer, u64 max_buffer_size, u64 offset_in_file)
{
@@ -248,17 +219,6 @@ bool ExportBI2Data(const Volume& volume, const Partition& partition,
return ExportData(volume, partition, 0x440, 0x2000, export_filename);
}

std::optional<u64> GetApploaderSize(const Volume& volume, const Partition& partition)
{
constexpr u64 header_size = 0x20;
const std::optional<u32> apploader_size = volume.ReadSwapped<u32>(0x2440 + 0x14, partition);
const std::optional<u32> trailer_size = volume.ReadSwapped<u32>(0x2440 + 0x18, partition);
if (!apploader_size || !trailer_size)
return std::nullopt;

return header_size + *apploader_size + *trailer_size;
}

bool ExportApploader(const Volume& volume, const Partition& partition,
const std::string& export_filename)
{
@@ -272,51 +232,6 @@ bool ExportApploader(const Volume& volume, const Partition& partition,
return ExportData(volume, partition, 0x2440, *apploader_size, export_filename);
}

std::optional<u64> GetBootDOLOffset(const Volume& volume, const Partition& partition)
{
const Platform volume_type = volume.GetVolumeType();
if (!IsDisc(volume_type))
return std::nullopt;

std::optional<u64> dol_offset = volume.ReadSwappedAndShifted(0x420, partition);

// Datel AR disc has 0x00000000 as the offset (invalid) and doesn't use it in the AppLoader.
if (dol_offset && *dol_offset == 0)
dol_offset.reset();

return dol_offset;
}

std::optional<u32> GetBootDOLSize(const Volume& volume, const Partition& partition, u64 dol_offset)
{
if (!IsDisc(volume.GetVolumeType()))
return std::nullopt;

u32 dol_size = 0;

// Iterate through the 7 code segments
for (u8 i = 0; i < 7; i++)
{
const std::optional<u32> offset = volume.ReadSwapped<u32>(dol_offset + 0x00 + i * 4, partition);
const std::optional<u32> size = volume.ReadSwapped<u32>(dol_offset + 0x90 + i * 4, partition);
if (!offset || !size)
return {};
dol_size = std::max(*offset + *size, dol_size);
}

// Iterate through the 11 data segments
for (u8 i = 0; i < 11; i++)
{
const std::optional<u32> offset = volume.ReadSwapped<u32>(dol_offset + 0x1c + i * 4, partition);
const std::optional<u32> size = volume.ReadSwapped<u32>(dol_offset + 0xac + i * 4, partition);
if (!offset || !size)
return {};
dol_size = std::max(*offset + *size, dol_size);
}

return dol_size;
}

bool ExportDOL(const Volume& volume, const Partition& partition, const std::string& export_filename)
{
if (!IsDisc(volume.GetVolumeType()))
@@ -332,24 +247,6 @@ bool ExportDOL(const Volume& volume, const Partition& partition, const std::stri
return ExportData(volume, partition, *dol_offset, *dol_size, export_filename);
}

std::optional<u64> GetFSTOffset(const Volume& volume, const Partition& partition)
{
const Platform volume_type = volume.GetVolumeType();
if (!IsDisc(volume_type))
return std::nullopt;

return volume.ReadSwappedAndShifted(0x424, partition);
}

std::optional<u64> GetFSTSize(const Volume& volume, const Partition& partition)
{
const Platform volume_type = volume.GetVolumeType();
if (!IsDisc(volume_type))
return std::nullopt;

return volume.ReadSwappedAndShifted(0x428, partition);
}

bool ExportFST(const Volume& volume, const Partition& partition, const std::string& export_filename)
{
if (!IsDisc(volume.GetVolumeType()))
@@ -17,13 +17,6 @@ class FileInfo;
struct Partition;
class Volume;

constexpr u32 PARTITION_DATA = 0;
constexpr u32 PARTITION_UPDATE = 1;
constexpr u32 PARTITION_CHANNEL = 2; // Mario Kart Wii, Wii Fit, Wii Fit Plus, Rabbids Go Home
constexpr u32 PARTITION_INSTALL = 3; // Dragon Quest X only

std::string NameForPartitionType(u32 partition_type, bool include_prefix);

u64 ReadFile(const Volume& volume, const Partition& partition, const FileInfo* file_info,
u8* buffer, u64 max_buffer_size, u64 offset_in_file = 0);
u64 ReadFile(const Volume& volume, const Partition& partition, std::string_view path, u8* buffer,
@@ -61,15 +54,10 @@ bool ExportHeader(const Volume& volume, const Partition& partition,
const std::string& export_filename);
bool ExportBI2Data(const Volume& volume, const Partition& partition,
const std::string& export_filename);
std::optional<u64> GetApploaderSize(const Volume& volume, const Partition& partition);
bool ExportApploader(const Volume& volume, const Partition& partition,
const std::string& export_filename);
std::optional<u64> GetBootDOLOffset(const Volume& volume, const Partition& partition);
std::optional<u32> GetBootDOLSize(const Volume& volume, const Partition& partition, u64 dol_offset);
bool ExportDOL(const Volume& volume, const Partition& partition,
const std::string& export_filename);
std::optional<u64> GetFSTOffset(const Volume& volume, const Partition& partition);
std::optional<u64> GetFSTSize(const Volume& volume, const Partition& partition);
bool ExportFST(const Volume& volume, const Partition& partition,
const std::string& export_filename);

@@ -16,7 +16,7 @@
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"

#include "DiscIO/DiscExtractor.h"
#include "DiscIO/DiscUtils.h"
#include "DiscIO/Filesystem.h"
#include "DiscIO/Volume.h"

0 comments on commit c8d8f9e

Please sign in to comment.