Merge pull request #5573 from JosJuice/directory-blob

Turn VolumeDirectory into DirectoryBlob
This commit is contained in:
Leo Lam 2017-08-01 19:19:14 +08:00 committed by GitHub
commit bf2241ccd2
27 changed files with 1114 additions and 915 deletions

View File

@ -63,35 +63,34 @@ std::unique_ptr<BootParameters> BootParameters::GenerateFromFile(const std::stri
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
static const std::unordered_set<std::string> disc_image_extensions = {
{".gcm", ".iso", ".tgc", ".wbfs", ".ciso", ".gcz"}};
{".gcm", ".iso", ".tgc", ".wbfs", ".ciso", ".gcz", ".dol", ".elf"}};
if (disc_image_extensions.find(extension) != disc_image_extensions.end() || is_drive)
{
auto volume = DiscIO::CreateVolumeFromFilename(path);
if (!volume)
std::unique_ptr<DiscIO::Volume> volume = DiscIO::CreateVolumeFromFilename(path);
if (volume)
return std::make_unique<BootParameters>(Disc{path, std::move(volume)});
if (extension == ".elf")
return std::make_unique<BootParameters>(Executable{path, std::make_unique<ElfReader>(path)});
if (extension == ".dol")
return std::make_unique<BootParameters>(Executable{path, std::make_unique<DolReader>(path)});
if (is_drive)
{
if (is_drive)
{
PanicAlertT("Could not read \"%s\". "
"There is no disc in the drive or it is not a GameCube/Wii backup. "
"Please note that Dolphin cannot play games directly from the original "
"GameCube and Wii discs.",
path.c_str());
}
else
{
PanicAlertT("\"%s\" is an invalid GCM/ISO file, or is not a GC/Wii ISO.", path.c_str());
}
return {};
PanicAlertT("Could not read \"%s\". "
"There is no disc in the drive or it is not a GameCube/Wii backup. "
"Please note that Dolphin cannot play games directly from the original "
"GameCube and Wii discs.",
path.c_str());
}
return std::make_unique<BootParameters>(Disc{path, std::move(volume)});
else
{
PanicAlertT("\"%s\" is an invalid GCM/ISO file, or is not a GC/Wii ISO.", path.c_str());
}
return {};
}
if (extension == ".elf")
return std::make_unique<BootParameters>(Executable{path, std::make_unique<ElfReader>(path)});
if (extension == ".dol")
return std::make_unique<BootParameters>(Executable{path, std::make_unique<DolReader>(path)});
if (extension == ".dff")
return std::make_unique<BootParameters>(DFF{path});
@ -133,44 +132,6 @@ bool CBoot::DVDRead(const DiscIO::Volume& volume, u64 dvd_offset, u32 output_add
return true;
}
void CBoot::Load_FST(bool is_wii, const DiscIO::Volume* volume)
{
if (!volume)
return;
const DiscIO::Partition partition = volume->GetGamePartition();
// copy first 32 bytes of disc to start of Mem 1
DVDRead(*volume, /*offset*/ 0, /*address*/ 0, /*length*/ 0x20, DiscIO::PARTITION_NONE);
// copy of game id
Memory::Write_U32(Memory::Read_U32(0x0000), 0x3180);
u32 shift = 0;
if (is_wii)
shift = 2;
const std::optional<u32> fst_offset = volume->ReadSwapped<u32>(0x0424, partition);
const std::optional<u32> fst_size = volume->ReadSwapped<u32>(0x0428, partition);
const std::optional<u32> max_fst_size = volume->ReadSwapped<u32>(0x042c, partition);
if (!fst_offset || !fst_size || !max_fst_size)
return;
u32 arena_high = Common::AlignDown(0x817FFFFF - (*max_fst_size << shift), 0x20);
Memory::Write_U32(arena_high, 0x00000034);
// load FST
DVDRead(*volume, *fst_offset << shift, arena_high, *fst_size << shift, partition);
Memory::Write_U32(arena_high, 0x00000038);
Memory::Write_U32(*max_fst_size << shift, 0x0000003c);
if (is_wii)
{
// the apploader changes IOS MEM1_ARENA_END too
Memory::Write_U32(arena_high, 0x00003110);
}
}
void CBoot::UpdateDebugger_MapLoaded()
{
Host_NotifyMapLoaded();
@ -309,15 +270,11 @@ bool CBoot::Load_BS2(const std::string& boot_rom_filename)
return true;
}
static const DiscIO::Volume* SetDefaultDisc()
static void SetDefaultDisc()
{
const SConfig& config = SConfig::GetInstance();
// load default image or create virtual drive from directory
if (!config.m_strDVDRoot.empty())
return SetDisc(DiscIO::CreateVolumeFromDirectory(config.m_strDVDRoot, config.bWii));
if (!config.m_strDefaultISO.empty())
return SetDisc(DiscIO::CreateVolumeFromFilename(config.m_strDefaultISO));
return nullptr;
SetDisc(DiscIO::CreateVolumeFromFilename(config.m_strDefaultISO));
}
// Third boot step after BootManager and Core. See Call schedule in BootManager.cpp
@ -359,34 +316,14 @@ bool CBoot::BootUp(std::unique_ptr<BootParameters> boot)
if (!executable.reader->IsValid())
return false;
const DiscIO::Volume* volume = nullptr;
// VolumeDirectory only works with DOLs.
if (StringEndsWith(executable.path, ".dol"))
{
if (!config.m_strDVDRoot.empty())
{
NOTICE_LOG(BOOT, "Setting DVDRoot %s", config.m_strDVDRoot.c_str());
volume = SetDisc(DiscIO::CreateVolumeFromDirectory(
config.m_strDVDRoot, config.bWii, config.m_strApploader, executable.path));
}
else if (!config.m_strDefaultISO.empty())
{
NOTICE_LOG(BOOT, "Loading default ISO %s", config.m_strDefaultISO.c_str());
volume = SetDisc(DiscIO::CreateVolumeFromFilename(config.m_strDefaultISO));
}
}
else
{
volume = SetDefaultDisc();
}
if (!executable.reader->LoadIntoMemory())
{
PanicAlertT("Failed to load the executable to memory.");
return false;
}
// Poor man's bootup
SetDefaultDisc();
if (config.bWii)
{
HID4.SBE = 1;
@ -394,14 +331,13 @@ bool CBoot::BootUp(std::unique_ptr<BootParameters> boot)
SetupBAT(config.bWii);
// Because there is no TMD to get the requested system (IOS) version from,
// we default to IOS58, which is the version used by the Homebrew Channel.
SetupWiiMemory(volume, 0x000000010000003a);
SetupWiiMemory(nullptr, 0x000000010000003a);
}
else
{
EmulatedBS2_GC(volume, true);
EmulatedBS2_GC(nullptr, true);
}
Load_FST(config.bWii, volume);
PC = executable.reader->GetEntryPoint();
if (executable.reader->LoadSymbols() || LoadMapFromFilename())
@ -465,8 +401,13 @@ bool CBoot::BootUp(std::unique_ptr<BootParameters> boot)
}
BootExecutableReader::BootExecutableReader(const std::string& file_name)
: BootExecutableReader(File::IOFile{file_name, "rb"})
{
File::IOFile file{file_name, "rb"};
}
BootExecutableReader::BootExecutableReader(File::IOFile file)
{
file.Seek(0, SEEK_SET);
m_bytes.resize(file.GetSize());
file.ReadBytes(m_bytes.data(), m_bytes.size());
}

View File

@ -15,6 +15,11 @@
#include "DiscIO/Enums.h"
#include "DiscIO/Volume.h"
namespace File
{
class IOFile;
}
struct RegionSetting
{
const std::string area;
@ -101,7 +106,6 @@ private:
static bool EmulatedBS2_Wii(const DiscIO::Volume* volume);
static bool EmulatedBS2(bool is_wii, const DiscIO::Volume* volume);
static bool Load_BS2(const std::string& boot_rom_filename);
static void Load_FST(bool is_wii, const DiscIO::Volume* volume);
static bool SetupWiiMemory(const DiscIO::Volume* volume, u64 ios_title_id);
};
@ -110,6 +114,7 @@ class BootExecutableReader
{
public:
explicit BootExecutableReader(const std::string& file_name);
explicit BootExecutableReader(File::IOFile file);
explicit BootExecutableReader(const std::vector<u8>& buffer);
virtual ~BootExecutableReader();

View File

@ -6,6 +6,7 @@
#include <cstring>
#include <string>
#include <utility>
#include <vector>
#include "Common/File.h"
@ -18,6 +19,11 @@ DolReader::DolReader(const std::vector<u8>& buffer) : BootExecutableReader(buffe
m_is_valid = Initialize(buffer);
}
DolReader::DolReader(File::IOFile file) : BootExecutableReader(std::move(file))
{
m_is_valid = Initialize(m_bytes);
}
DolReader::DolReader(const std::string& filename) : BootExecutableReader(filename)
{
m_is_valid = Initialize(m_bytes);

View File

@ -10,10 +10,16 @@
#include "Common/CommonTypes.h"
#include "Core/Boot/Boot.h"
namespace File
{
class IOFile;
}
class DolReader final : public BootExecutableReader
{
public:
explicit DolReader(const std::string& filename);
explicit DolReader(File::IOFile file);
explicit DolReader(const std::vector<u8>& buffer);
~DolReader();

View File

@ -5,8 +5,10 @@
#include "Core/Boot/ElfReader.h"
#include <string>
#include <utility>
#include "Common/CommonTypes.h"
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/Swap.h"
@ -71,6 +73,11 @@ ElfReader::ElfReader(const std::vector<u8>& buffer) : BootExecutableReader(buffe
Initialize(m_bytes.data());
}
ElfReader::ElfReader(File::IOFile file) : BootExecutableReader(std::move(file))
{
Initialize(m_bytes.data());
}
ElfReader::ElfReader(const std::string& filename) : BootExecutableReader(filename)
{
Initialize(m_bytes.data());

View File

@ -8,6 +8,11 @@
#include "Core/Boot/Boot.h"
#include "Core/Boot/ElfTypes.h"
namespace File
{
class IOFile;
}
enum KnownElfTypes
{
KNOWNELF_PSP = 0,
@ -22,6 +27,7 @@ class ElfReader final : public BootExecutableReader
{
public:
explicit ElfReader(const std::string& filename);
explicit ElfReader(File::IOFile file);
explicit ElfReader(const std::vector<u8>& buffer);
~ElfReader();
u32 Read32(int off) const { return base32[off >> 2]; }

View File

@ -252,8 +252,6 @@ void SConfig::SaveCoreSettings(IniFile& ini)
core->Set("FPRF", bFPRF);
core->Set("AccurateNaNs", bAccurateNaNs);
core->Set("DefaultISO", m_strDefaultISO);
core->Set("DVDRoot", m_strDVDRoot);
core->Set("Apploader", m_strApploader);
core->Set("EnableCheats", bEnableCheats);
core->Set("SelectedLanguage", SelectedLanguage);
core->Set("OverrideGCLang", bOverrideGCLanguage);
@ -568,8 +566,6 @@ void SConfig::LoadCoreSettings(IniFile& ini)
core->Get("CPUThread", &bCPUThread, true);
core->Get("SyncOnSkipIdle", &bSyncGPUOnSkipIdleHack, true);
core->Get("DefaultISO", &m_strDefaultISO);
core->Get("DVDRoot", &m_strDVDRoot);
core->Get("Apploader", &m_strApploader);
core->Get("EnableCheats", &bEnableCheats, false);
core->Get("SelectedLanguage", &SelectedLanguage, 0);
core->Get("OverrideGCLang", &bOverrideGCLanguage, false);

View File

@ -197,8 +197,6 @@ struct SConfig : NonCopyable
std::string m_strBootROM;
std::string m_strSRAM;
std::string m_strDefaultISO;
std::string m_strDVDRoot;
std::string m_strApploader;
std::string m_strWiiSDCardPath;
std::string m_perfDir;

View File

@ -16,6 +16,7 @@
#include "DiscIO/Blob.h"
#include "DiscIO/CISOBlob.h"
#include "DiscIO/CompressedBlob.h"
#include "DiscIO/DirectoryBlob.h"
#include "DiscIO/DriveBlob.h"
#include "DiscIO/FileBlob.h"
#include "DiscIO/TGCBlob.h"
@ -183,12 +184,13 @@ std::unique_ptr<BlobReader> CreateBlobReader(const std::string& filename)
if (!file.ReadArray(&magic, 1))
return nullptr;
// Conveniently, every supported file format (except for plain disc images) starts
// with a 4-byte magic number that identifies the format, so we just need a simple
// switch statement to create the right blob type. If the magic number doesn't
// match any known magic number, we assume it's a plain disc image. If that
// assumption is wrong, the volume code that runs later will notice the error
// because the blob won't provide valid data when reading the GC/Wii disc header.
// Conveniently, every supported file format (except for plain disc images and
// extracted discs) starts with a 4-byte magic number that identifies the format,
// so we just need a simple switch statement to create the right blob type. If the
// magic number doesn't match any known magic number and the directory structure
// doesn't match the directory blob format, we assume it's a plain disc image. If
// that assumption is wrong, the volume code that runs later will notice the error
// because the blob won't provide the right data when reading the GC/Wii disc header.
switch (magic)
{
@ -201,6 +203,9 @@ std::unique_ptr<BlobReader> CreateBlobReader(const std::string& filename)
case WBFS_MAGIC:
return WbfsFileReader::Create(std::move(file), filename);
default:
if (auto directory_blob = DirectoryBlobReader::Create(filename))
return std::move(directory_blob);
return PlainFileReader::Create(std::move(file));
}
}

View File

@ -56,6 +56,12 @@ public:
return Common::FromBigEndian(temp);
}
virtual bool SupportsReadWiiDecrypted() const { return false; }
virtual bool ReadWiiDecrypted(u64 offset, u64 size, u8* out_ptr, u64 partition_offset)
{
return false;
}
protected:
BlobReader() {}
};

View File

@ -3,6 +3,7 @@ set(SRCS
CISOBlob.cpp
WbfsBlob.cpp
CompressedBlob.cpp
DirectoryBlob.cpp
DiscExtractor.cpp
DiscScrubber.cpp
DriveBlob.cpp
@ -14,7 +15,6 @@ set(SRCS
NANDImporter.cpp
TGCBlob.cpp
Volume.cpp
VolumeDirectory.cpp
VolumeGC.cpp
VolumeWad.cpp
VolumeWii.cpp

View File

@ -0,0 +1,824 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "DiscIO/DirectoryBlob.h"
#include <algorithm>
#include <array>
#include <cinttypes>
#include <cstddef>
#include <cstring>
#include <locale>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <variant>
#include <vector>
#include "Common/Align.h"
#include "Common/Assert.h"
#include "Common/CommonPaths.h"
#include "Common/CommonTypes.h"
#include "Common/File.h"
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Common/Swap.h"
#include "Core/Boot/DolReader.h"
#include "DiscIO/Blob.h"
#include "DiscIO/VolumeWii.h"
namespace DiscIO
{
static const DiscContent& AddFileToContents(std::set<DiscContent>* contents,
const std::string& path, u64 offset,
u64 max_size = UINT64_MAX);
// Reads as many bytes as the vector fits (or less, if the file is smaller).
// Returns the number of bytes read.
static size_t ReadFileToVector(const std::string& path, std::vector<u8>* vector);
static void PadToAddress(u64 start_address, u64* address, u64* length, u8** buffer);
static void Write32(u32 data, u32 offset, std::vector<u8>* buffer);
static u32 ComputeNameSize(const File::FSTEntry& parent_entry);
static std::string ASCIIToUppercase(std::string str);
static void ConvertUTF8NamesToSHIFTJIS(File::FSTEntry* parent_entry);
enum class PartitionType : u32
{
Game = 0,
Update = 1,
Channel = 2,
// There are more types used by Super Smash Bros. Brawl, but they don't have special names
};
// 0xFF is an arbitrarily picked value. Note that we can't use 0x00, because that means NTSC-J
constexpr u32 INVALID_REGION = 0xFF;
constexpr u8 ENTRY_SIZE = 0x0c;
constexpr u8 FILE_ENTRY = 0;
constexpr u8 DIRECTORY_ENTRY = 1;
DiscContent::DiscContent(u64 offset, u64 size, const std::string& path)
: m_offset(offset), m_size(size), m_content_source(path)
{
}
DiscContent::DiscContent(u64 offset, u64 size, const u8* data)
: m_offset(offset), m_size(size), m_content_source(data)
{
}
DiscContent::DiscContent(u64 offset) : m_offset(offset)
{
}
u64 DiscContent::GetOffset() const
{
return m_offset;
}
u64 DiscContent::GetSize() const
{
return m_size;
}
bool DiscContent::Read(u64* offset, u64* length, u8** buffer) const
{
if (m_size == 0)
return true;
_dbg_assert_(DISCIO, *offset >= m_offset);
const u64 offset_in_content = *offset - m_offset;
if (offset_in_content < m_size)
{
const u64 bytes_to_read = std::min(m_size - offset_in_content, *length);
if (std::holds_alternative<std::string>(m_content_source))
{
File::IOFile file(std::get<std::string>(m_content_source), "rb");
file.Seek(offset_in_content, SEEK_SET);
if (!file.ReadBytes(*buffer, bytes_to_read))
return false;
}
else
{
const u8* const content_pointer = std::get<const u8*>(m_content_source) + offset_in_content;
std::copy(content_pointer, content_pointer + bytes_to_read, *buffer);
}
*length -= bytes_to_read;
*buffer += bytes_to_read;
*offset += bytes_to_read;
}
return true;
}
static std::optional<PartitionType> ParsePartitionDirectoryName(const std::string& name)
{
if (name.size() < 2)
return {};
if (!strcasecmp(name.c_str(), "DATA"))
return PartitionType::Game;
if (!strcasecmp(name.c_str(), "UPDATE"))
return PartitionType::Update;
if (!strcasecmp(name.c_str(), "CHANNEL"))
return PartitionType::Channel;
if (name[0] == 'P' || name[0] == 'p')
{
// e.g. "P-HA8E" (normally only used for Super Smash Bros. Brawl's VC partitions)
if (name[1] == '-' && name.size() == 6)
{
const u32 result = Common::swap32(reinterpret_cast<const u8*>(name.data() + 2));
return static_cast<PartitionType>(result);
}
// e.g. "P0"
if (std::all_of(name.cbegin() + 1, name.cend(), [](char c) { return c >= '0' && c <= '9'; }))
{
u32 result;
if (TryParse(name.substr(1), &result))
return static_cast<PartitionType>(result);
}
}
return {};
}
static bool IsDirectorySeparator(char c)
{
return c == '/'
#ifdef _WIN32
|| c == '\\'
#endif
;
}
static bool PathCharactersEqual(char a, char b)
{
return a == b || (IsDirectorySeparator(a) && IsDirectorySeparator(b));
}
static bool PathEndsWith(const std::string& path, const std::string& suffix)
{
if (suffix.size() > path.size())
return false;
std::string::const_iterator path_iterator = path.cend() - suffix.size();
std::string::const_iterator suffix_iterator = suffix.cbegin();
while (path_iterator != path.cend())
{
if (!PathCharactersEqual(*path_iterator, *suffix_iterator))
return false;
path_iterator++;
suffix_iterator++;
}
return true;
}
static bool IsValidDirectoryBlob(const std::string& dol_path, std::string* partition_root,
std::string* true_root = nullptr)
{
if (!PathEndsWith(dol_path, "/sys/main.dol"))
return false;
const size_t chars_to_remove = std::string("sys/main.dol").size();
*partition_root = dol_path.substr(0, dol_path.size() - chars_to_remove);
if (File::GetSize(*partition_root + "sys/boot.bin") < 0x20)
return false;
#ifdef _WIN32
constexpr const char* dir_separator = "/\\";
#else
constexpr char dir_separator = '/';
#endif
if (true_root)
{
*true_root =
dol_path.substr(0, dol_path.find_last_of(dir_separator, partition_root->size() - 2) + 1);
}
return true;
}
static bool ExistsAndIsValidDirectoryBlob(const std::string& dol_path)
{
std::string partition_root;
return File::Exists(dol_path) && IsValidDirectoryBlob(dol_path, &partition_root);
}
static bool IsInFilesDirectory(const std::string& path)
{
size_t files_pos = std::string::npos;
while (true)
{
files_pos = path.rfind("files", files_pos);
if (files_pos == std::string::npos)
return false;
const size_t slash_before_pos = files_pos - 1;
const size_t slash_after_pos = files_pos + 5;
if ((files_pos == 0 || IsDirectorySeparator(path[slash_before_pos])) &&
(slash_after_pos == path.size() || (IsDirectorySeparator(path[slash_after_pos]))) &&
ExistsAndIsValidDirectoryBlob(path.substr(0, files_pos) + "sys/main.dol"))
{
return true;
}
--files_pos;
}
}
static bool IsMainDolForNonGamePartition(const std::string& path)
{
std::string partition_root, true_root;
if (!IsValidDirectoryBlob(path, &partition_root, &true_root))
return false; // This is not a /sys/main.dol
std::string partition_directory_name = partition_root.substr(true_root.size());
partition_directory_name.pop_back(); // Remove trailing slash
const std::optional<PartitionType> partition_type =
ParsePartitionDirectoryName(partition_directory_name);
if (!partition_type || *partition_type == PartitionType::Game)
return false; // volume_path is the game partition's /sys/main.dol
const File::FSTEntry true_root_entry = File::ScanDirectoryTree(true_root, false);
for (const File::FSTEntry& entry : true_root_entry.children)
{
if (entry.isDirectory &&
ParsePartitionDirectoryName(entry.virtualName) == PartitionType::Game &&
ExistsAndIsValidDirectoryBlob(entry.physicalName + "/sys/main.dol"))
{
return true; // volume_path is the /sys/main.dol for a non-game partition
}
}
return false; // volume_path is the game partition's /sys/main.dol
}
bool ShouldHideFromGameList(const std::string& volume_path)
{
return IsInFilesDirectory(volume_path) || IsMainDolForNonGamePartition(volume_path);
}
std::unique_ptr<DirectoryBlobReader> DirectoryBlobReader::Create(const std::string& dol_path)
{
std::string partition_root, true_root;
if (!IsValidDirectoryBlob(dol_path, &partition_root, &true_root))
return nullptr;
return std::unique_ptr<DirectoryBlobReader>(new DirectoryBlobReader(partition_root, true_root));
}
DirectoryBlobReader::DirectoryBlobReader(const std::string& game_partition_root,
const std::string& true_root)
{
DirectoryBlobPartition game_partition(game_partition_root, {});
m_is_wii = game_partition.IsWii();
if (!m_is_wii)
{
m_gamecube_pseudopartition = std::move(game_partition);
m_data_size = m_gamecube_pseudopartition.GetDataSize();
}
else
{
SetNonpartitionDiscHeader(game_partition.GetHeader(), game_partition_root);
SetWiiRegionData(game_partition_root);
std::vector<PartitionWithType> partitions;
partitions.emplace_back(std::move(game_partition), PartitionType::Game);
std::string game_partition_directory_name = game_partition_root.substr(true_root.size());
game_partition_directory_name.pop_back(); // Remove trailing slash
if (ParsePartitionDirectoryName(game_partition_directory_name) == PartitionType::Game)
{
const File::FSTEntry true_root_entry = File::ScanDirectoryTree(true_root, false);
for (const File::FSTEntry& entry : true_root_entry.children)
{
if (entry.isDirectory)
{
const std::optional<PartitionType> type = ParsePartitionDirectoryName(entry.virtualName);
if (type && *type != PartitionType::Game)
{
partitions.emplace_back(DirectoryBlobPartition(entry.physicalName + "/", m_is_wii),
*type);
}
}
}
}
SetPartitions(std::move(partitions));
}
}
bool DirectoryBlobReader::ReadInternal(u64 offset, u64 length, u8* buffer,
const std::set<DiscContent>& contents)
{
if (contents.empty())
return true;
// Determine which DiscContent the offset refers to
std::set<DiscContent>::const_iterator it = contents.lower_bound(DiscContent(offset));
if (it->GetOffset() > offset && it != contents.begin())
--it;
// zero fill to start of file data
PadToAddress(it->GetOffset(), &offset, &length, &buffer);
while (it != contents.end() && length > 0)
{
_dbg_assert_(DISCIO, it->GetOffset() <= offset);
if (!it->Read(&offset, &length, &buffer))
return false;
++it;
if (it != contents.end())
{
_dbg_assert_(DISCIO, it->GetOffset() >= offset);
PadToAddress(it->GetOffset(), &offset, &length, &buffer);
}
}
return true;
}
bool DirectoryBlobReader::Read(u64 offset, u64 length, u8* buffer)
{
// TODO: We don't handle raw access to the encrypted area of Wii discs correctly.
const std::set<DiscContent>& contents =
m_is_wii ? m_nonpartition_contents : m_gamecube_pseudopartition.GetContents();
return ReadInternal(offset, length, buffer, contents);
}
bool DirectoryBlobReader::SupportsReadWiiDecrypted() const
{
return m_is_wii;
}
bool DirectoryBlobReader::ReadWiiDecrypted(u64 offset, u64 size, u8* buffer, u64 partition_offset)
{
if (!m_is_wii)
return false;
auto it = m_partitions.find(partition_offset);
if (it == m_partitions.end())
return false;
return ReadInternal(offset, size, buffer, it->second.GetContents());
}
BlobType DirectoryBlobReader::GetBlobType() const
{
return BlobType::DIRECTORY;
}
u64 DirectoryBlobReader::GetRawSize() const
{
// Not implemented
return 0;
}
u64 DirectoryBlobReader::GetDataSize() const
{
return m_data_size;
}
void DirectoryBlobReader::SetNonpartitionDiscHeader(const std::vector<u8>& partition_header,
const std::string& game_partition_root)
{
constexpr u64 NONPARTITION_DISCHEADER_ADDRESS = 0;
constexpr u64 NONPARTITION_DISCHEADER_SIZE = 0x100;
m_disc_header_nonpartition.resize(NONPARTITION_DISCHEADER_SIZE);
const size_t header_bin_bytes_read =
ReadFileToVector(game_partition_root + "disc/header.bin", &m_disc_header_nonpartition);
// If header.bin is missing or smaller than expected, use the content of sys/boot.bin instead
std::copy(partition_header.data() + header_bin_bytes_read,
partition_header.data() + m_disc_header_nonpartition.size(),
m_disc_header_nonpartition.data() + header_bin_bytes_read);
// 0x60 and 0x61 are the only differences between the partition and non-partition headers
if (header_bin_bytes_read < 0x60)
m_disc_header_nonpartition[0x60] = 0;
if (header_bin_bytes_read < 0x61)
m_disc_header_nonpartition[0x61] = 0;
m_nonpartition_contents.emplace(NONPARTITION_DISCHEADER_ADDRESS, NONPARTITION_DISCHEADER_SIZE,
m_disc_header_nonpartition.data());
}
void DirectoryBlobReader::SetWiiRegionData(const std::string& game_partition_root)
{
m_wii_region_data.resize(0x10, 0x00);
m_wii_region_data.resize(0x20, 0x80);
Write32(INVALID_REGION, 0, &m_wii_region_data);
const std::string region_bin_path = game_partition_root + "disc/region.bin";
const size_t bytes_read = ReadFileToVector(region_bin_path, &m_wii_region_data);
if (bytes_read < 0x4)
ERROR_LOG(DISCIO, "Couldn't read region from %s", region_bin_path.c_str());
else if (bytes_read < 0x20)
ERROR_LOG(DISCIO, "Couldn't read age ratings from %s", region_bin_path.c_str());
constexpr u64 WII_REGION_DATA_ADDRESS = 0x4E000;
constexpr u64 WII_REGION_DATA_SIZE = 0x20;
m_nonpartition_contents.emplace(WII_REGION_DATA_ADDRESS, WII_REGION_DATA_SIZE,
m_wii_region_data.data());
}
void DirectoryBlobReader::SetPartitions(std::vector<PartitionWithType>&& partitions)
{
std::sort(partitions.begin(), partitions.end(),
[](const PartitionWithType& lhs, const PartitionWithType& rhs) {
if (lhs.type == rhs.type)
return lhs.partition.GetRootDirectory() < rhs.partition.GetRootDirectory();
// Ascending sort by partition type, except Update (1) comes before before Game (0)
return (lhs.type > PartitionType::Update || rhs.type > PartitionType::Update) ?
lhs.type < rhs.type :
lhs.type > rhs.type;
});
u32 subtable_1_size = 0;
while (subtable_1_size < partitions.size() && subtable_1_size < 3 &&
partitions[subtable_1_size].type <= PartitionType::Channel)
{
++subtable_1_size;
}
const u32 subtable_2_size = static_cast<u32>(partitions.size() - subtable_1_size);
constexpr u32 PARTITION_TABLE_ADDRESS = 0x40000;
constexpr u32 PARTITION_SUBTABLE1_OFFSET = 0x20;
constexpr u32 PARTITION_SUBTABLE2_OFFSET = 0x40;
m_partition_table.resize(PARTITION_SUBTABLE2_OFFSET + subtable_2_size * 8);
Write32(subtable_1_size, 0x0, &m_partition_table);
Write32((PARTITION_TABLE_ADDRESS + PARTITION_SUBTABLE1_OFFSET) >> 2, 0x4, &m_partition_table);
if (subtable_2_size != 0)
{
Write32(subtable_2_size, 0x8, &m_partition_table);
Write32((PARTITION_TABLE_ADDRESS + PARTITION_SUBTABLE2_OFFSET) >> 2, 0xC, &m_partition_table);
}
constexpr u64 STANDARD_UPDATE_PARTITION_ADDRESS = 0x50000;
constexpr u64 STANDARD_GAME_PARTITION_ADDRESS = 0xF800000;
u64 partition_address = STANDARD_UPDATE_PARTITION_ADDRESS;
u64 offset_in_table = PARTITION_SUBTABLE1_OFFSET;
for (size_t i = 0; i < partitions.size(); ++i)
{
if (i == subtable_1_size)
offset_in_table = PARTITION_SUBTABLE2_OFFSET;
if (partitions[i].type == PartitionType::Game)
partition_address = std::max(partition_address, STANDARD_GAME_PARTITION_ADDRESS);
Write32(static_cast<u32>(partition_address >> 2), offset_in_table, &m_partition_table);
offset_in_table += 4;
Write32(static_cast<u32>(partitions[i].type), offset_in_table, &m_partition_table);
offset_in_table += 4;
SetPartitionHeader(partitions[i].partition, partition_address);
const u64 partition_data_size = partitions[i].partition.GetDataSize();
m_partitions.emplace(partition_address, std::move(partitions[i].partition));
const u64 unaligned_next_partition_address =
VolumeWii::PartitionOffsetToRawOffset(partition_data_size, Partition(partition_address));
partition_address = Common::AlignUp(unaligned_next_partition_address, 0x10000ull);
}
m_data_size = partition_address;
m_nonpartition_contents.emplace(PARTITION_TABLE_ADDRESS, m_partition_table.size(),
m_partition_table.data());
}
// This function sets the header that's shortly before the start of the encrypted
// area, not the header that's right at the beginning of the encrypted area
void DirectoryBlobReader::SetPartitionHeader(const DirectoryBlobPartition& partition,
u64 partition_address)
{
constexpr u32 TICKET_OFFSET = 0x0;
constexpr u32 TICKET_SIZE = 0x2a4;
constexpr u32 TMD_OFFSET = 0x2c0;
constexpr u32 MAX_TMD_SIZE = 0x49e4;
constexpr u32 H3_OFFSET = 0x4000;
constexpr u32 H3_SIZE = 0x18000;
const std::string& partition_root = partition.GetRootDirectory();
AddFileToContents(&m_nonpartition_contents, partition_root + "ticket.bin",
partition_address + TICKET_OFFSET, TICKET_SIZE);
const DiscContent& tmd = AddFileToContents(&m_nonpartition_contents, partition_root + "tmd.bin",
partition_address + TMD_OFFSET, MAX_TMD_SIZE);
const u64 cert_offset = Common::AlignUp(TMD_OFFSET + tmd.GetSize(), 0x20ull);
const u64 max_cert_size = H3_OFFSET - cert_offset;
const DiscContent& cert = AddFileToContents(&m_nonpartition_contents, partition_root + "cert.bin",
partition_address + cert_offset, max_cert_size);
AddFileToContents(&m_nonpartition_contents, partition_root + "h3.bin",
partition_address + H3_OFFSET, H3_SIZE);
constexpr u32 PARTITION_HEADER_SIZE = 0x1c;
constexpr u32 DATA_OFFSET = 0x20000;
const u64 data_size = Common::AlignUp(partition.GetDataSize(), 0x7c00) / 0x7c00 * 0x8000;
m_partition_headers.emplace_back(PARTITION_HEADER_SIZE);
std::vector<u8>& partition_header = m_partition_headers.back();
Write32(static_cast<u32>(tmd.GetSize()), 0x0, &partition_header);
Write32(TMD_OFFSET >> 2, 0x4, &partition_header);
Write32(static_cast<u32>(cert.GetSize()), 0x8, &partition_header);
Write32(static_cast<u32>(cert_offset >> 2), 0x0C, &partition_header);
Write32(H3_OFFSET >> 2, 0x10, &partition_header);
Write32(DATA_OFFSET >> 2, 0x14, &partition_header);
Write32(static_cast<u32>(data_size >> 2), 0x18, &partition_header);
m_nonpartition_contents.emplace(partition_address + TICKET_SIZE, PARTITION_HEADER_SIZE,
partition_header.data());
}
DirectoryBlobPartition::DirectoryBlobPartition(const std::string& root_directory,
std::optional<bool> is_wii)
: m_root_directory(root_directory)
{
SetDiscHeaderAndDiscType(is_wii);
SetBI2();
BuildFST(SetDOL(SetApploader()));
}
void DirectoryBlobPartition::SetDiscHeaderAndDiscType(std::optional<bool> is_wii)
{
constexpr u64 DISCHEADER_ADDRESS = 0;
constexpr u64 DISCHEADER_SIZE = 0x440;
m_disc_header.resize(DISCHEADER_SIZE);
const std::string boot_bin_path = m_root_directory + "sys/boot.bin";
if (ReadFileToVector(boot_bin_path, &m_disc_header) < 0x20)
ERROR_LOG(DISCIO, "%s doesn't exist or is too small", boot_bin_path.c_str());
m_contents.emplace(DISCHEADER_ADDRESS, DISCHEADER_SIZE, m_disc_header.data());
if (is_wii.has_value())
{
m_is_wii = *is_wii;
}
else
{
m_is_wii = Common::swap32(&m_disc_header[0x18]) == 0x5d1c9ea3;
const bool is_gc = Common::swap32(&m_disc_header[0x1c]) == 0xc2339f3d;
if (m_is_wii == is_gc)
ERROR_LOG(DISCIO, "Couldn't detect disc type based on %s", boot_bin_path.c_str());
}
m_address_shift = m_is_wii ? 2 : 0;
}
void DirectoryBlobPartition::SetBI2()
{
constexpr u64 BI2_ADDRESS = 0x440;
constexpr u64 BI2_SIZE = 0x2000;
m_bi2.resize(BI2_SIZE);
if (!m_is_wii)
Write32(INVALID_REGION, 0x18, &m_bi2);
const std::string bi2_path = m_root_directory + "sys/bi2.bin";
const size_t bytes_read = ReadFileToVector(bi2_path, &m_bi2);
if (!m_is_wii && bytes_read < 0x1C)
ERROR_LOG(DISCIO, "Couldn't read region from %s", bi2_path.c_str());
m_contents.emplace(BI2_ADDRESS, BI2_SIZE, m_bi2.data());
}
u64 DirectoryBlobPartition::SetApploader()
{
bool success = false;
const std::string path = m_root_directory + "sys/apploader.img";
File::IOFile file(path, "rb");
m_apploader.resize(file.GetSize());
if (m_apploader.size() < 0x20 || !file.ReadBytes(m_apploader.data(), m_apploader.size()))
{
ERROR_LOG(DISCIO, "%s couldn't be accessed or is too small", path.c_str());
}
else
{
const size_t apploader_size = 0x20 + Common::swap32(*(u32*)&m_apploader[0x14]) +
Common::swap32(*(u32*)&m_apploader[0x18]);
if (apploader_size != m_apploader.size())
ERROR_LOG(DISCIO, "%s is the wrong size... Is it really an apploader?", path.c_str());
else
success = true;
}
if (!success)
{
m_apploader.resize(0x20);
// Make sure BS2 HLE doesn't try to run the apploader
Write32(static_cast<u32>(-1), 0x10, &m_apploader);
}
constexpr u64 APPLOADER_ADDRESS = 0x2440;
m_contents.emplace(APPLOADER_ADDRESS, m_apploader.size(), m_apploader.data());
// Return DOL address, 32 byte aligned (plus 32 byte padding)
return Common::AlignUp(APPLOADER_ADDRESS + m_apploader.size() + 0x20, 0x20ull);
}
u64 DirectoryBlobPartition::SetDOL(u64 dol_address)
{
const DiscContent& dol =
AddFileToContents(&m_contents, m_root_directory + "sys/main.dol", dol_address);
Write32(static_cast<u32>(dol_address >> m_address_shift), 0x0420, &m_disc_header);
// Return FST address, 32 byte aligned (plus 32 byte padding)
return Common::AlignUp(dol_address + dol.GetSize() + 0x20, 0x20ull);
}
void DirectoryBlobPartition::BuildFST(u64 fst_address)
{
m_fst_data.clear();
File::FSTEntry rootEntry = File::ScanDirectoryTree(m_root_directory + "files/", true);
ConvertUTF8NamesToSHIFTJIS(&rootEntry);
u32 name_table_size = Common::AlignUp(ComputeNameSize(rootEntry), 1ull << m_address_shift);
u64 total_entries = rootEntry.size + 1; // The root entry itself isn't counted in rootEntry.size
const u64 name_table_offset = total_entries * ENTRY_SIZE;
m_fst_data.resize(name_table_offset + name_table_size);
// 32 KiB aligned start of data on disc
u64 current_data_address = Common::AlignUp(fst_address + m_fst_data.size(), 0x8000ull);
u32 fst_offset = 0; // Offset within FST data
u32 name_offset = 0; // Offset within name table
u32 root_offset = 0; // Offset of root of FST
// write root entry
WriteEntryData(&fst_offset, DIRECTORY_ENTRY, 0, 0, total_entries, m_address_shift);
WriteDirectory(rootEntry, &fst_offset, &name_offset, &current_data_address, root_offset,
name_table_offset);
// overflow check, compare the aligned name offset with the aligned name table size
_assert_(Common::AlignUp(name_offset, 1ull << m_address_shift) == name_table_size);
// write FST size and location
Write32((u32)(fst_address >> m_address_shift), 0x0424, &m_disc_header);
Write32((u32)(m_fst_data.size() >> m_address_shift), 0x0428, &m_disc_header);
Write32((u32)(m_fst_data.size() >> m_address_shift), 0x042c, &m_disc_header);
m_contents.emplace(fst_address, m_fst_data.size(), m_fst_data.data());
m_data_size = current_data_address;
}
void DirectoryBlobPartition::WriteEntryData(u32* entry_offset, u8 type, u32 name_offset,
u64 data_offset, u64 length, u32 address_shift)
{
m_fst_data[(*entry_offset)++] = type;
m_fst_data[(*entry_offset)++] = (name_offset >> 16) & 0xff;
m_fst_data[(*entry_offset)++] = (name_offset >> 8) & 0xff;
m_fst_data[(*entry_offset)++] = (name_offset)&0xff;
Write32((u32)(data_offset >> address_shift), *entry_offset, &m_fst_data);
*entry_offset += 4;
Write32((u32)length, *entry_offset, &m_fst_data);
*entry_offset += 4;
}
void DirectoryBlobPartition::WriteEntryName(u32* name_offset, const std::string& name,
u64 name_table_offset)
{
strncpy((char*)&m_fst_data[*name_offset + name_table_offset], name.c_str(), name.length() + 1);
*name_offset += (u32)(name.length() + 1);
}
void DirectoryBlobPartition::WriteDirectory(const File::FSTEntry& parent_entry, u32* fst_offset,
u32* name_offset, u64* data_offset,
u32 parent_entry_index, u64 name_table_offset)
{
std::vector<File::FSTEntry> sorted_entries = parent_entry.children;
// Sort for determinism
std::sort(sorted_entries.begin(), sorted_entries.end(), [](const File::FSTEntry& one,
const File::FSTEntry& two) {
const std::string one_upper = ASCIIToUppercase(one.virtualName);
const std::string two_upper = ASCIIToUppercase(two.virtualName);
return one_upper == two_upper ? one.virtualName < two.virtualName : one_upper < two_upper;
});
for (const File::FSTEntry& entry : sorted_entries)
{
if (entry.isDirectory)
{
u32 entry_index = *fst_offset / ENTRY_SIZE;
WriteEntryData(fst_offset, DIRECTORY_ENTRY, *name_offset, parent_entry_index,
entry_index + entry.size + 1, 0);
WriteEntryName(name_offset, entry.virtualName, name_table_offset);
WriteDirectory(entry, fst_offset, name_offset, data_offset, entry_index, name_table_offset);
}
else
{
// put entry in FST
WriteEntryData(fst_offset, FILE_ENTRY, *name_offset, *data_offset, entry.size,
m_address_shift);
WriteEntryName(name_offset, entry.virtualName, name_table_offset);
// write entry to virtual disc
auto result = m_contents.emplace(*data_offset, entry.size, entry.physicalName);
_dbg_assert_(DISCIO, result.second); // Check that this offset wasn't already occupied
// 32 KiB aligned - many games are fine with less alignment, but not all
*data_offset = Common::AlignUp(*data_offset + std::max<u64>(entry.size, 1ull), 0x8000ull);
}
}
}
static const DiscContent& AddFileToContents(std::set<DiscContent>* contents,
const std::string& path, u64 offset, u64 max_size)
{
return *(contents->emplace(offset, std::min(File::GetSize(path), max_size), path).first);
}
static size_t ReadFileToVector(const std::string& path, std::vector<u8>* vector)
{
File::IOFile file(path, "rb");
size_t bytes_read;
file.ReadArray<u8>(vector->data(), std::min<u64>(file.GetSize(), vector->size()), &bytes_read);
return bytes_read;
}
static void PadToAddress(u64 start_address, u64* address, u64* length, u8** buffer)
{
if (start_address > *address && *length > 0)
{
u64 padBytes = std::min(start_address - *address, *length);
memset(*buffer, 0, (size_t)padBytes);
*length -= padBytes;
*buffer += padBytes;
*address += padBytes;
}
}
static void Write32(u32 data, u32 offset, std::vector<u8>* buffer)
{
(*buffer)[offset++] = (data >> 24);
(*buffer)[offset++] = (data >> 16) & 0xff;
(*buffer)[offset++] = (data >> 8) & 0xff;
(*buffer)[offset] = data & 0xff;
}
static u32 ComputeNameSize(const File::FSTEntry& parent_entry)
{
u32 name_size = 0;
for (const File::FSTEntry& entry : parent_entry.children)
{
if (entry.isDirectory)
name_size += ComputeNameSize(entry);
name_size += (u32)entry.virtualName.length() + 1;
}
return name_size;
}
static void ConvertUTF8NamesToSHIFTJIS(File::FSTEntry* parent_entry)
{
for (File::FSTEntry& entry : parent_entry->children)
{
if (entry.isDirectory)
ConvertUTF8NamesToSHIFTJIS(&entry);
entry.virtualName = UTF8ToSHIFTJIS(entry.virtualName);
}
}
static std::string ASCIIToUppercase(std::string str)
{
std::transform(str.begin(), str.end(), str.begin(),
[](char c) { return std::toupper(c, std::locale::classic()); });
return str;
}
} // namespace

View File

@ -0,0 +1,169 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <variant>
#include <vector>
#include "Common/CommonTypes.h"
#include "Common/FileUtil.h"
#include "DiscIO/Blob.h"
namespace File
{
struct FSTEntry;
class IOFile;
}
namespace DiscIO
{
enum class PartitionType : u32;
// Returns true if the path is inside a DirectoryBlob and doesn't represent the DirectoryBlob itself
bool ShouldHideFromGameList(const std::string& volume_path);
class DiscContent
{
public:
using ContentSource = std::variant<std::string, const u8*>;
DiscContent(u64 offset, u64 size, const std::string& path);
DiscContent(u64 offset, u64 size, const u8* data);
// Provided because it's convenient when searching for DiscContent in an std::set
explicit DiscContent(u64 offset);
u64 GetOffset() const;
u64 GetSize() const;
bool Read(u64* offset, u64* length, u8** buffer) const;
bool operator==(const DiscContent& other) const { return m_offset == other.m_offset; }
bool operator!=(const DiscContent& other) const { return !(*this == other); }
bool operator<(const DiscContent& other) const { return m_offset < other.m_offset; }
bool operator>(const DiscContent& other) const { return other < *this; }
bool operator<=(const DiscContent& other) const { return !(*this < other); }
bool operator>=(const DiscContent& other) const { return !(*this > other); }
private:
u64 m_offset;
u64 m_size = 0;
std::string m_path;
ContentSource m_content_source;
};
class DirectoryBlobPartition
{
public:
DirectoryBlobPartition() = default;
DirectoryBlobPartition(const std::string& root_directory, std::optional<bool> is_wii);
// We do not allow copying, because it might mess up the pointers inside DiscContents
DirectoryBlobPartition(const DirectoryBlobPartition&) = delete;
DirectoryBlobPartition& operator=(const DirectoryBlobPartition&) = delete;
DirectoryBlobPartition(DirectoryBlobPartition&&) = default;
DirectoryBlobPartition& operator=(DirectoryBlobPartition&&) = default;
bool IsWii() const { return m_is_wii; }
u64 GetDataSize() const { return m_data_size; }
const std::string& GetRootDirectory() const { return m_root_directory; }
const std::vector<u8>& GetHeader() const { return m_disc_header; }
const std::set<DiscContent>& GetContents() const { return m_contents; }
private:
void SetDiscHeaderAndDiscType(std::optional<bool> is_wii);
void SetBI2();
// Returns DOL address
u64 SetApploader();
// Returns FST address
u64 SetDOL(u64 dol_address);
void BuildFST(u64 fst_address);
// FST creation
void WriteEntryData(u32* entry_offset, u8 type, u32 name_offset, u64 data_offset, u64 length,
u32 address_shift);
void WriteEntryName(u32* name_offset, const std::string& name, u64 name_table_offset);
void WriteDirectory(const File::FSTEntry& parent_entry, u32* fst_offset, u32* name_offset,
u64* data_offset, u32 parent_entry_index, u64 name_table_offset);
std::set<DiscContent> m_contents;
std::vector<u8> m_disc_header;
std::vector<u8> m_bi2;
std::vector<u8> m_apploader;
std::vector<u8> m_fst_data;
std::string m_root_directory;
bool m_is_wii = false;
// GameCube has no shift, Wii has 2 bit shift
u32 m_address_shift = 0;
u64 m_data_size;
};
class DirectoryBlobReader : public BlobReader
{
public:
static std::unique_ptr<DirectoryBlobReader> Create(const std::string& dol_path);
// We do not allow copying, because it might mess up the pointers inside DiscContents
DirectoryBlobReader(const DirectoryBlobReader&) = delete;
DirectoryBlobReader& operator=(const DirectoryBlobReader&) = delete;
DirectoryBlobReader(DirectoryBlobReader&&) = default;
DirectoryBlobReader& operator=(DirectoryBlobReader&&) = default;
bool Read(u64 offset, u64 length, u8* buffer) override;
bool SupportsReadWiiDecrypted() const override;
bool ReadWiiDecrypted(u64 offset, u64 size, u8* buffer, u64 partition_offset) override;
BlobType GetBlobType() const override;
u64 GetRawSize() const override;
u64 GetDataSize() const override;
private:
struct PartitionWithType
{
PartitionWithType(DirectoryBlobPartition&& partition_, PartitionType type_)
: partition(std::move(partition_)), type(type_)
{
}
DirectoryBlobPartition partition;
PartitionType type;
};
explicit DirectoryBlobReader(const std::string& game_partition_root,
const std::string& true_root);
bool ReadInternal(u64 offset, u64 length, u8* buffer, const std::set<DiscContent>& contents);
void SetNonpartitionDiscHeader(const std::vector<u8>& partition_header,
const std::string& game_partition_root);
void SetWiiRegionData(const std::string& game_partition_root);
void SetPartitions(std::vector<PartitionWithType>&& partitions);
void SetPartitionHeader(const DirectoryBlobPartition& partition, u64 partition_address);
// For GameCube:
DirectoryBlobPartition m_gamecube_pseudopartition;
// For Wii:
std::set<DiscContent> m_nonpartition_contents;
std::map<u64, DirectoryBlobPartition> m_partitions;
bool m_is_wii;
std::vector<u8> m_disc_header_nonpartition;
std::vector<u8> m_partition_table;
std::vector<u8> m_wii_region_data;
std::vector<std::vector<u8>> m_partition_headers;
u64 m_data_size;
};
} // namespace

View File

@ -39,6 +39,7 @@
<ClCompile Include="Blob.cpp" />
<ClCompile Include="CISOBlob.cpp" />
<ClCompile Include="CompressedBlob.cpp" />
<ClCompile Include="DirectoryBlob.cpp" />
<ClCompile Include="DiscExtractor.cpp" />
<ClCompile Include="DiscScrubber.cpp" />
<ClCompile Include="DriveBlob.cpp" />
@ -50,7 +51,6 @@
<ClCompile Include="NANDImporter.cpp" />
<ClCompile Include="TGCBlob.cpp" />
<ClCompile Include="Volume.cpp" />
<ClCompile Include="VolumeDirectory.cpp" />
<ClCompile Include="VolumeGC.cpp" />
<ClCompile Include="VolumeWad.cpp" />
<ClCompile Include="VolumeWii.cpp" />
@ -61,6 +61,7 @@
<ClInclude Include="Blob.h" />
<ClInclude Include="CISOBlob.h" />
<ClInclude Include="CompressedBlob.h" />
<ClInclude Include="DirectoryBlob.h" />
<ClInclude Include="DiscExtractor.h" />
<ClInclude Include="DiscScrubber.h" />
<ClInclude Include="DriveBlob.h" />
@ -72,7 +73,6 @@
<ClInclude Include="NANDImporter.h" />
<ClInclude Include="TGCBlob.h" />
<ClInclude Include="Volume.h" />
<ClInclude Include="VolumeDirectory.h" />
<ClInclude Include="VolumeGC.h" />
<ClInclude Include="VolumeWad.h" />
<ClInclude Include="VolumeWii.h" />

View File

@ -57,8 +57,8 @@
<ClCompile Include="WbfsBlob.cpp">
<Filter>Volume\Blob</Filter>
</ClCompile>
<ClCompile Include="VolumeDirectory.cpp">
<Filter>Volume</Filter>
<ClCompile Include="DirectoryBlob.cpp">
<Filter>Volume\Blob</Filter>
</ClCompile>
<ClCompile Include="VolumeGC.cpp">
<Filter>Volume</Filter>
@ -122,8 +122,8 @@
<ClInclude Include="Volume.h">
<Filter>Volume</Filter>
</ClInclude>
<ClInclude Include="VolumeDirectory.h">
<Filter>Volume</Filter>
<ClInclude Include="DirectoryBlob.h">
<Filter>Volume\Blob</Filter>
</ClInclude>
<ClInclude Include="VolumeGC.h">
<Filter>Volume</Filter>

View File

@ -22,7 +22,6 @@
#include "DiscIO/Blob.h"
#include "DiscIO/Enums.h"
#include "DiscIO/VolumeDirectory.h"
#include "DiscIO/VolumeGC.h"
#include "DiscIO/VolumeWad.h"
#include "DiscIO/VolumeWii.h"
@ -112,14 +111,4 @@ std::unique_ptr<Volume> CreateVolumeFromFilename(const std::string& filename)
return nullptr;
}
std::unique_ptr<Volume> CreateVolumeFromDirectory(const std::string& directory, bool is_wii,
const std::string& apploader,
const std::string& dol)
{
if (VolumeDirectory::IsValidDirectory(directory))
return std::make_unique<VolumeDirectory>(directory, is_wii, apploader, dol);
return nullptr;
}
} // namespace

View File

@ -120,8 +120,5 @@ protected:
};
std::unique_ptr<Volume> CreateVolumeFromFilename(const std::string& filename);
std::unique_ptr<Volume> CreateVolumeFromDirectory(const std::string& directory, bool is_wii,
const std::string& apploader = "",
const std::string& dol = "");
} // namespace

View File

@ -1,535 +0,0 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <algorithm>
#include <cstddef>
#include <cstring>
#include <locale>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "Common/Align.h"
#include "Common/Assert.h"
#include "Common/CommonPaths.h"
#include "Common/CommonTypes.h"
#include "Common/File.h"
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "DiscIO/Blob.h"
#include "DiscIO/Enums.h"
#include "DiscIO/Volume.h"
#include "DiscIO/VolumeDirectory.h"
namespace DiscIO
{
static u32 ComputeNameSize(const File::FSTEntry& parent_entry);
static std::string ASCIIToUppercase(std::string str);
static void ConvertUTF8NamesToSHIFTJIS(File::FSTEntry& parent_entry);
const size_t VolumeDirectory::MAX_NAME_LENGTH;
const size_t VolumeDirectory::MAX_ID_LENGTH;
VolumeDirectory::VolumeDirectory(const std::string& directory, bool is_wii,
const std::string& apploader, const std::string& dol)
: m_data_start_address(UINT64_MAX), m_disk_header(DISKHEADERINFO_ADDRESS),
m_disk_header_info(std::make_unique<SDiskHeaderInfo>()), m_fst_address(0), m_dol_address(0)
{
m_root_directory = ExtractDirectoryName(directory);
// create the default disk header
SetGameID("AGBJ01");
SetName("Default name");
if (is_wii)
SetDiskTypeWii();
else
SetDiskTypeGC();
// Don't load the DOL if we don't have an apploader
if (SetApploader(apploader))
SetDOL(dol);
BuildFST();
}
VolumeDirectory::~VolumeDirectory()
{
}
bool VolumeDirectory::IsValidDirectory(const std::string& directory)
{
return File::IsDirectory(ExtractDirectoryName(directory));
}
bool VolumeDirectory::Read(u64 offset, u64 length, u8* buffer, const Partition& partition) const
{
bool decrypt = partition != PARTITION_NONE;
if (!decrypt && (offset + length >= 0x400) && m_is_wii)
{
// Fully supporting this would require re-encrypting every file that's read.
// Only supporting the areas that IOS allows software to read could be more feasible.
// Currently, only the header (up to 0x400) is supported, though we're cheating a bit
// with it by reading the header inside the current partition instead. Supporting the
// header is enough for booting games, but not for running things like the Disc Channel.
return false;
}
if (decrypt && !m_is_wii)
return false;
// header
if (offset < DISKHEADERINFO_ADDRESS)
{
WriteToBuffer(DISKHEADER_ADDRESS, DISKHEADERINFO_ADDRESS, m_disk_header.data(), &offset,
&length, &buffer);
}
// header info
if (offset >= DISKHEADERINFO_ADDRESS && offset < APPLOADER_ADDRESS)
{
WriteToBuffer(DISKHEADERINFO_ADDRESS, sizeof(m_disk_header_info), (u8*)m_disk_header_info.get(),
&offset, &length, &buffer);
}
// apploader
if (offset >= APPLOADER_ADDRESS && offset < APPLOADER_ADDRESS + m_apploader.size())
{
WriteToBuffer(APPLOADER_ADDRESS, m_apploader.size(), m_apploader.data(), &offset, &length,
&buffer);
}
// dol
if (offset >= m_dol_address && offset < m_dol_address + m_dol.size())
{
WriteToBuffer(m_dol_address, m_dol.size(), m_dol.data(), &offset, &length, &buffer);
}
// fst
if (offset >= m_fst_address && offset < m_data_start_address)
{
WriteToBuffer(m_fst_address, m_fst_data.size(), m_fst_data.data(), &offset, &length, &buffer);
}
if (m_virtual_disk.empty())
return true;
// Determine which file the offset refers to
std::map<u64, std::string>::const_iterator fileIter = m_virtual_disk.lower_bound(offset);
if (fileIter->first > offset && fileIter != m_virtual_disk.begin())
--fileIter;
// zero fill to start of file data
PadToAddress(fileIter->first, &offset, &length, &buffer);
while (fileIter != m_virtual_disk.end() && length > 0)
{
_dbg_assert_(DVDINTERFACE, fileIter->first <= offset);
u64 fileOffset = offset - fileIter->first;
const std::string fileName = fileIter->second;
File::IOFile file(fileName, "rb");
if (!file)
return false;
u64 fileSize = file.GetSize();
if (fileOffset < fileSize)
{
u64 fileBytes = std::min(fileSize - fileOffset, length);
if (!file.Seek(fileOffset, SEEK_SET))
return false;
if (!file.ReadBytes(buffer, fileBytes))
return false;
length -= fileBytes;
buffer += fileBytes;
offset += fileBytes;
}
++fileIter;
if (fileIter != m_virtual_disk.end())
{
_dbg_assert_(DVDINTERFACE, fileIter->first >= offset);
PadToAddress(fileIter->first, &offset, &length, &buffer);
}
}
return true;
}
std::vector<Partition> VolumeDirectory::GetPartitions() const
{
return m_is_wii ? std::vector<Partition>{GetGamePartition()} : std::vector<Partition>();
}
Partition VolumeDirectory::GetGamePartition() const
{
return m_is_wii ? Partition(0x50000) : PARTITION_NONE;
}
std::string VolumeDirectory::GetGameID(const Partition& partition) const
{
return std::string(m_disk_header.begin(), m_disk_header.begin() + MAX_ID_LENGTH);
}
void VolumeDirectory::SetGameID(const std::string& id)
{
memcpy(m_disk_header.data(), id.c_str(), std::min(id.length(), MAX_ID_LENGTH));
}
Region VolumeDirectory::GetRegion() const
{
if (m_is_wii)
return RegionSwitchWii(m_disk_header[3]);
return RegionSwitchGC(m_disk_header[3]);
}
Country VolumeDirectory::GetCountry(const Partition& partition) const
{
return CountrySwitch(m_disk_header[3]);
}
std::string VolumeDirectory::GetMakerID(const Partition& partition) const
{
// Not implemented
return "00";
}
std::string VolumeDirectory::GetInternalName(const Partition& partition) const
{
char name[0x60];
if (Read(0x20, 0x60, (u8*)name, partition))
return DecodeString(name);
else
return "";
}
std::map<Language, std::string> VolumeDirectory::GetLongNames() const
{
std::string name = GetInternalName();
if (name.empty())
return {};
return {{Language::LANGUAGE_UNKNOWN, name}};
}
std::vector<u32> VolumeDirectory::GetBanner(int* width, int* height) const
{
// Not implemented
*width = 0;
*height = 0;
return std::vector<u32>();
}
void VolumeDirectory::SetName(const std::string& name)
{
size_t length = std::min(name.length(), MAX_NAME_LENGTH);
memcpy(&m_disk_header[0x20], name.c_str(), length);
m_disk_header[length + 0x20] = 0;
}
std::string VolumeDirectory::GetApploaderDate(const Partition& partition) const
{
// Not implemented
return "VOID";
}
Platform VolumeDirectory::GetVolumeType() const
{
return m_is_wii ? Platform::WII_DISC : Platform::GAMECUBE_DISC;
}
BlobType VolumeDirectory::GetBlobType() const
{
// VolumeDirectory isn't actually a blob, but it sort of acts
// like one, so it makes sense that it has its own blob type.
// It should be made into a proper blob in the future.
return BlobType::DIRECTORY;
}
u64 VolumeDirectory::GetSize() const
{
// Not implemented
return 0;
}
u64 VolumeDirectory::GetRawSize() const
{
// Not implemented
return 0;
}
std::string VolumeDirectory::ExtractDirectoryName(const std::string& directory)
{
std::string result = directory;
size_t last_separator = result.find_last_of(DIR_SEP_CHR);
if (last_separator != result.size() - 1)
{
// TODO: This assumes that file names will always have a dot in them
// and directory names never will; both assumptions are often
// right but in general wrong.
size_t extension_start = result.find_last_of('.');
if (extension_start != std::string::npos && extension_start > last_separator)
{
result.resize(last_separator);
}
}
else
{
result.resize(last_separator);
}
return result;
}
void VolumeDirectory::SetDiskTypeWii()
{
Write32(0x5d1c9ea3, 0x18, &m_disk_header);
memset(&m_disk_header[0x1c], 0, 4);
m_is_wii = true;
m_address_shift = 2;
}
void VolumeDirectory::SetDiskTypeGC()
{
memset(&m_disk_header[0x18], 0, 4);
Write32(0xc2339f3d, 0x1c, &m_disk_header);
m_is_wii = false;
m_address_shift = 0;
}
bool VolumeDirectory::SetApploader(const std::string& apploader)
{
if (!apploader.empty())
{
std::string data;
if (!File::ReadFileToString(apploader, data))
{
PanicAlertT("Apploader unable to load from file");
return false;
}
size_t apploader_size = 0x20 + Common::swap32(*(u32*)&data.data()[0x14]) +
Common::swap32(*(u32*)&data.data()[0x18]);
if (apploader_size != data.size())
{
PanicAlertT("Apploader is the wrong size...is it really an apploader?");
return false;
}
m_apploader.resize(apploader_size);
std::copy(data.begin(), data.end(), m_apploader.begin());
// 32byte aligned (plus 0x20 padding)
m_dol_address = Common::AlignUp(APPLOADER_ADDRESS + m_apploader.size() + 0x20, 0x20ull);
return true;
}
else
{
m_apploader.resize(0x20);
// Make sure BS2 HLE doesn't try to run the apploader
Write32(static_cast<u32>(-1), 0x10, &m_apploader);
return false;
}
}
void VolumeDirectory::SetDOL(const std::string& dol)
{
if (!dol.empty())
{
std::string data;
File::ReadFileToString(dol, data);
m_dol.resize(data.size());
std::copy(data.begin(), data.end(), m_dol.begin());
Write32((u32)(m_dol_address >> m_address_shift), 0x0420, &m_disk_header);
// 32byte aligned (plus 0x20 padding)
m_fst_address = Common::AlignUp(m_dol_address + m_dol.size() + 0x20, 0x20ull);
}
}
void VolumeDirectory::BuildFST()
{
m_fst_data.clear();
File::FSTEntry rootEntry = File::ScanDirectoryTree(m_root_directory, true);
ConvertUTF8NamesToSHIFTJIS(rootEntry);
u32 name_table_size = Common::AlignUp(ComputeNameSize(rootEntry), 1ull << m_address_shift);
u64 total_entries = rootEntry.size + 1; // The root entry itself isn't counted in rootEntry.size
m_fst_name_offset = total_entries * ENTRY_SIZE; // offset of name table in FST
m_fst_data.resize(m_fst_name_offset + name_table_size);
// if FST hasn't been assigned (ie no apploader/dol setup), set to default
if (m_fst_address == 0)
m_fst_address = APPLOADER_ADDRESS + 0x2000;
// 32 KiB aligned start of data on disk
m_data_start_address = Common::AlignUp(m_fst_address + m_fst_data.size(), 0x8000ull);
u64 current_data_address = m_data_start_address;
u32 fst_offset = 0; // Offset within FST data
u32 name_offset = 0; // Offset within name table
u32 root_offset = 0; // Offset of root of FST
// write root entry
WriteEntryData(&fst_offset, DIRECTORY_ENTRY, 0, 0, total_entries, m_address_shift);
WriteDirectory(rootEntry, &fst_offset, &name_offset, &current_data_address, root_offset);
// overflow check, compare the aligned name offset with the aligned name table size
_assert_(Common::AlignUp(name_offset, 1ull << m_address_shift) == name_table_size);
// write FST size and location
Write32((u32)(m_fst_address >> m_address_shift), 0x0424, &m_disk_header);
Write32((u32)(m_fst_data.size() >> m_address_shift), 0x0428, &m_disk_header);
Write32((u32)(m_fst_data.size() >> m_address_shift), 0x042c, &m_disk_header);
}
void VolumeDirectory::WriteToBuffer(u64 source_start_address, u64 source_length, const u8* source,
u64* address, u64* length, u8** buffer) const
{
if (*length == 0)
return;
_dbg_assert_(DVDINTERFACE, *address >= source_start_address);
u64 source_offset = *address - source_start_address;
if (source_offset < source_length)
{
size_t bytes_to_read = std::min(source_length - source_offset, *length);
memcpy(*buffer, source + source_offset, bytes_to_read);
*length -= bytes_to_read;
*buffer += bytes_to_read;
*address += bytes_to_read;
}
}
void VolumeDirectory::PadToAddress(u64 start_address, u64* address, u64* length, u8** buffer) const
{
if (start_address > *address && *length > 0)
{
u64 padBytes = std::min(start_address - *address, *length);
memset(*buffer, 0, (size_t)padBytes);
*length -= padBytes;
*buffer += padBytes;
*address += padBytes;
}
}
void VolumeDirectory::Write32(u32 data, u32 offset, std::vector<u8>* const buffer)
{
(*buffer)[offset++] = (data >> 24);
(*buffer)[offset++] = (data >> 16) & 0xff;
(*buffer)[offset++] = (data >> 8) & 0xff;
(*buffer)[offset] = (data)&0xff;
}
void VolumeDirectory::WriteEntryData(u32* entry_offset, u8 type, u32 name_offset, u64 data_offset,
u64 length, u32 address_shift)
{
m_fst_data[(*entry_offset)++] = type;
m_fst_data[(*entry_offset)++] = (name_offset >> 16) & 0xff;
m_fst_data[(*entry_offset)++] = (name_offset >> 8) & 0xff;
m_fst_data[(*entry_offset)++] = (name_offset)&0xff;
Write32((u32)(data_offset >> address_shift), *entry_offset, &m_fst_data);
*entry_offset += 4;
Write32((u32)length, *entry_offset, &m_fst_data);
*entry_offset += 4;
}
void VolumeDirectory::WriteEntryName(u32* name_offset, const std::string& name)
{
strncpy((char*)&m_fst_data[*name_offset + m_fst_name_offset], name.c_str(), name.length() + 1);
*name_offset += (u32)(name.length() + 1);
}
void VolumeDirectory::WriteDirectory(const File::FSTEntry& parent_entry, u32* fst_offset,
u32* name_offset, u64* data_offset, u32 parent_entry_index)
{
std::vector<File::FSTEntry> sorted_entries = parent_entry.children;
// Sort for determinism
std::sort(sorted_entries.begin(), sorted_entries.end(), [](const File::FSTEntry& one,
const File::FSTEntry& two) {
const std::string one_upper = ASCIIToUppercase(one.virtualName);
const std::string two_upper = ASCIIToUppercase(two.virtualName);
return one_upper == two_upper ? one.virtualName < two.virtualName : one_upper < two_upper;
});
for (const File::FSTEntry& entry : sorted_entries)
{
if (entry.isDirectory)
{
u32 entry_index = *fst_offset / ENTRY_SIZE;
WriteEntryData(fst_offset, DIRECTORY_ENTRY, *name_offset, parent_entry_index,
entry_index + entry.size + 1, 0);
WriteEntryName(name_offset, entry.virtualName);
WriteDirectory(entry, fst_offset, name_offset, data_offset, entry_index);
}
else
{
// put entry in FST
WriteEntryData(fst_offset, FILE_ENTRY, *name_offset, *data_offset, entry.size,
m_address_shift);
WriteEntryName(name_offset, entry.virtualName);
// write entry to virtual disk
_dbg_assert_(DVDINTERFACE, m_virtual_disk.find(*data_offset) == m_virtual_disk.end());
m_virtual_disk.emplace(*data_offset, entry.physicalName);
// 32 KiB aligned - many games are fine with less alignment, but not all
*data_offset = Common::AlignUp(*data_offset + std::max<u64>(entry.size, 1ull), 0x8000ull);
}
}
}
static u32 ComputeNameSize(const File::FSTEntry& parent_entry)
{
u32 name_size = 0;
for (const File::FSTEntry& entry : parent_entry.children)
{
if (entry.isDirectory)
name_size += ComputeNameSize(entry);
name_size += (u32)entry.virtualName.length() + 1;
}
return name_size;
}
static void ConvertUTF8NamesToSHIFTJIS(File::FSTEntry& parent_entry)
{
for (File::FSTEntry& entry : parent_entry.children)
{
if (entry.isDirectory)
ConvertUTF8NamesToSHIFTJIS(entry);
entry.virtualName = UTF8ToSHIFTJIS(entry.virtualName);
}
}
static std::string ASCIIToUppercase(std::string str)
{
std::transform(str.begin(), str.end(), str.begin(),
[](char c) { return std::toupper(c, std::locale::classic()); });
return str;
}
} // namespace

View File

@ -1,161 +0,0 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "Common/CommonTypes.h"
#include "DiscIO/Volume.h"
namespace File
{
struct FSTEntry;
}
//
// --- this volume type is used for reading files directly from the hard drive ---
//
namespace DiscIO
{
enum class BlobType;
enum class Country;
enum class Language;
enum class Region;
enum class Platform;
class VolumeDirectory : public Volume
{
public:
VolumeDirectory(const std::string& directory, bool is_wii, const std::string& apploader = "",
const std::string& dol = "");
~VolumeDirectory();
static bool IsValidDirectory(const std::string& directory);
bool Read(u64 offset, u64 length, u8* buffer, const Partition& partition) const override;
std::vector<Partition> GetPartitions() const override;
Partition GetGamePartition() const override;
std::string GetGameID(const Partition& partition = PARTITION_NONE) const override;
void SetGameID(const std::string& id);
std::string GetMakerID(const Partition& partition = PARTITION_NONE) const override;
std::optional<u16> GetRevision(const Partition& partition = PARTITION_NONE) const override
{
return {};
}
std::string GetInternalName(const Partition& partition = PARTITION_NONE) const override;
std::map<Language, std::string> GetLongNames() const override;
std::vector<u32> GetBanner(int* width, int* height) const override;
void SetName(const std::string&);
std::string GetApploaderDate(const Partition& partition = PARTITION_NONE) const override;
Platform GetVolumeType() const override;
Region GetRegion() const override;
Country GetCountry(const Partition& partition = PARTITION_NONE) const override;
BlobType GetBlobType() const override;
u64 GetSize() const override;
u64 GetRawSize() const override;
void BuildFST();
private:
static std::string ExtractDirectoryName(const std::string& directory);
void SetDiskTypeWii();
void SetDiskTypeGC();
bool SetApploader(const std::string& apploader);
void SetDOL(const std::string& dol);
// writing to read buffer
void WriteToBuffer(u64 source_start_address, u64 source_length, const u8* source, u64* address,
u64* length, u8** buffer) const;
void PadToAddress(u64 start_address, u64* address, u64* length, u8** buffer) const;
void Write32(u32 data, u32 offset, std::vector<u8>* const buffer);
// FST creation
void WriteEntryData(u32* entry_offset, u8 type, u32 name_offset, u64 data_offset, u64 length,
u32 address_shift);
void WriteEntryName(u32* name_offset, const std::string& name);
void WriteDirectory(const File::FSTEntry& parent_entry, u32* fst_offset, u32* name_offset,
u64* data_offset, u32 parent_entry_index);
std::string m_root_directory;
std::map<u64, std::string> m_virtual_disk;
bool m_is_wii;
// GameCube has no shift, Wii has 2 bit shift
u32 m_address_shift;
// first address on disk containing file data
u64 m_data_start_address;
u64 m_fst_name_offset;
std::vector<u8> m_fst_data;
std::vector<u8> m_disk_header;
#pragma pack(push, 1)
struct SDiskHeaderInfo
{
u32 debug_monitor_size;
u32 simulated_mem_size;
u32 arg_offset;
u32 debug_flag;
u32 track_location;
u32 track_size;
u32 country_code;
u32 unknown;
u32 unknown2;
// All the data is byteswapped
SDiskHeaderInfo()
{
debug_monitor_size = 0;
simulated_mem_size = 0;
arg_offset = 0;
debug_flag = 0;
track_location = 0;
track_size = 0;
country_code = 0;
unknown = 0;
unknown2 = 0;
}
};
#pragma pack(pop)
std::unique_ptr<SDiskHeaderInfo> m_disk_header_info;
std::vector<u8> m_apploader;
std::vector<u8> m_dol;
u64 m_fst_address;
u64 m_dol_address;
static constexpr u8 ENTRY_SIZE = 0x0c;
static constexpr u8 FILE_ENTRY = 0;
static constexpr u8 DIRECTORY_ENTRY = 1;
static constexpr u64 DISKHEADER_ADDRESS = 0;
static constexpr u64 DISKHEADERINFO_ADDRESS = 0x440;
static constexpr u64 APPLOADER_ADDRESS = 0x2440;
static const size_t MAX_NAME_LENGTH = 0x3df;
static const size_t MAX_ID_LENGTH = 6;
};
} // namespace

View File

@ -128,6 +128,9 @@ bool VolumeWii::Read(u64 _ReadOffset, u64 _Length, u8* _pBuffer, const Partition
if (partition == PARTITION_NONE)
return m_pReader->Read(_ReadOffset, _Length, _pBuffer);
if (m_pReader->SupportsReadWiiDecrypted())
return m_pReader->ReadWiiDecrypted(_ReadOffset, _Length, _pBuffer, partition.offset);
// Get the decryption key for the partition
auto it = m_partitions.find(partition);
if (it == m_partitions.end())

View File

@ -6,6 +6,7 @@
#include <QDirIterator>
#include <QFile>
#include "DiscIO/DirectoryBlob.h"
#include "DolphinQt2/GameList/GameTracker.h"
#include "DolphinQt2/Settings.h"
@ -137,3 +138,13 @@ void GameTracker::UpdateFile(const QString& file)
emit GameRemoved(file);
}
}
void GameLoader::LoadGame(const QString& path)
{
if (!DiscIO::ShouldHideFromGameList(path.toStdString()))
{
auto game = QSharedPointer<GameFile>::create(path);
if (game->IsValid())
emit GameLoaded(game);
}
}

View File

@ -54,12 +54,7 @@ class GameLoader final : public QObject
Q_OBJECT
public:
void LoadGame(const QString& path)
{
GameFile* game = new GameFile(path);
if (game->IsValid())
emit GameLoaded(QSharedPointer<GameFile>(game));
}
void LoadGame(const QString& path);
signals:
void GameLoaded(QSharedPointer<GameFile> game);

View File

@ -48,27 +48,6 @@ void PathPane::BrowseDefaultGame()
}
}
void PathPane::BrowseDVDRoot()
{
QString dir = QFileDialog::getExistingDirectory(this, tr("Select DVD Root"), QDir::currentPath());
if (!dir.isEmpty())
{
m_dvd_edit->setText(dir);
SConfig::GetInstance().m_strDVDRoot = dir.toStdString();
}
}
void PathPane::BrowseApploader()
{
QString file = QFileDialog::getOpenFileName(this, tr("Select an Apploader"), QDir::currentPath(),
tr("Apploaders (*.img)"));
if (!file.isEmpty())
{
m_app_edit->setText(file);
SConfig::GetInstance().m_strApploader = file.toStdString();
}
}
void PathPane::BrowseWiiNAND()
{
QString dir =
@ -127,32 +106,14 @@ QGridLayout* PathPane::MakePathsLayout()
layout->addWidget(m_game_edit, 0, 1);
layout->addWidget(game_open, 0, 2);
m_dvd_edit = new QLineEdit(QString::fromStdString(SConfig::GetInstance().m_strDVDRoot));
connect(m_dvd_edit, &QLineEdit::editingFinished,
[=] { SConfig::GetInstance().m_strDVDRoot = m_dvd_edit->text().toStdString(); });
QPushButton* dvd_open = new QPushButton;
connect(dvd_open, &QPushButton::clicked, this, &PathPane::BrowseDVDRoot);
layout->addWidget(new QLabel(tr("DVD Root:")), 1, 0);
layout->addWidget(m_dvd_edit, 1, 1);
layout->addWidget(dvd_open, 1, 2);
m_app_edit = new QLineEdit(QString::fromStdString(SConfig::GetInstance().m_strApploader));
connect(m_app_edit, &QLineEdit::editingFinished,
[=] { SConfig::GetInstance().m_strApploader = m_app_edit->text().toStdString(); });
QPushButton* app_open = new QPushButton;
connect(app_open, &QPushButton::clicked, this, &PathPane::BrowseApploader);
layout->addWidget(new QLabel(tr("Apploader:")), 2, 0);
layout->addWidget(m_app_edit, 2, 1);
layout->addWidget(app_open, 2, 2);
m_nand_edit = new QLineEdit(QString::fromStdString(SConfig::GetInstance().m_NANDPath));
connect(m_nand_edit, &QLineEdit::editingFinished,
[=] { SConfig::GetInstance().m_NANDPath = m_nand_edit->text().toStdString(); });
QPushButton* nand_open = new QPushButton;
connect(nand_open, &QPushButton::clicked, this, &PathPane::BrowseWiiNAND);
layout->addWidget(new QLabel(tr("Wii NAND Root:")), 3, 0);
layout->addWidget(m_nand_edit, 3, 1);
layout->addWidget(nand_open, 3, 2);
layout->addWidget(new QLabel(tr("Wii NAND Root:")), 1, 0);
layout->addWidget(m_nand_edit, 1, 1);
layout->addWidget(nand_open, 1, 2);
return layout;
}

View File

@ -19,8 +19,6 @@ public:
private:
void Browse();
void BrowseDefaultGame();
void BrowseDVDRoot();
void BrowseApploader();
void BrowseWiiNAND();
QGroupBox* MakeGameFolderBox();
QGridLayout* MakePathsLayout();
@ -28,7 +26,5 @@ private:
QListWidget* m_path_list;
QLineEdit* m_game_edit;
QLineEdit* m_dvd_edit;
QLineEdit* m_app_edit;
QLineEdit* m_nand_edit;
};

View File

@ -45,14 +45,6 @@ void PathConfigPane::InitializeGUI()
wxString::Format("|*.elf;*.dol;*.gcm;*.iso;*.tgc;*.wbfs;*.ciso;*.gcz;*.wad|%s",
wxGetTranslation(wxALL_FILES)),
wxDefaultPosition, wxDefaultSize, wxFLP_USE_TEXTCTRL | wxFLP_OPEN | wxFLP_SMALL);
m_dvd_root_dirpicker =
new wxDirPickerCtrl(this, wxID_ANY, wxEmptyString, _("Choose a DVD root directory:"),
wxDefaultPosition, wxDefaultSize, wxDIRP_USE_TEXTCTRL | wxDIRP_SMALL);
m_apploader_path_filepicker = new wxFilePickerCtrl(
this, wxID_ANY, wxEmptyString,
_("Choose file to use as apploader: (applies to discs constructed from directories only)"),
_("apploader (.img)") + wxString::Format("|*.img|%s", wxGetTranslation(wxALL_FILES)),
wxDefaultPosition, wxDefaultSize, wxFLP_USE_TEXTCTRL | wxFLP_OPEN | wxFLP_SMALL);
m_nand_root_dirpicker =
new wxDirPickerCtrl(this, wxID_ANY, wxEmptyString, _("Choose a NAND root directory:"),
wxDefaultPosition, wxDefaultSize, wxDIRP_USE_TEXTCTRL | wxDIRP_SMALL);
@ -82,21 +74,15 @@ void PathConfigPane::InitializeGUI()
picker_sizer->Add(new wxStaticText(this, wxID_ANY, _("Default ISO:")), wxGBPosition(0, 0),
wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
picker_sizer->Add(m_default_iso_filepicker, wxGBPosition(0, 1), wxDefaultSpan, wxEXPAND);
picker_sizer->Add(new wxStaticText(this, wxID_ANY, _("DVD Root:")), wxGBPosition(1, 0),
picker_sizer->Add(new wxStaticText(this, wxID_ANY, _("Wii NAND Root:")), wxGBPosition(1, 0),
wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
picker_sizer->Add(m_dvd_root_dirpicker, wxGBPosition(1, 1), wxDefaultSpan, wxEXPAND);
picker_sizer->Add(new wxStaticText(this, wxID_ANY, _("Apploader:")), wxGBPosition(2, 0),
picker_sizer->Add(m_nand_root_dirpicker, wxGBPosition(1, 1), wxDefaultSpan, wxEXPAND);
picker_sizer->Add(new wxStaticText(this, wxID_ANY, _("Dump Path:")), wxGBPosition(2, 0),
wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
picker_sizer->Add(m_apploader_path_filepicker, wxGBPosition(2, 1), wxDefaultSpan, wxEXPAND);
picker_sizer->Add(new wxStaticText(this, wxID_ANY, _("Wii NAND Root:")), wxGBPosition(3, 0),
picker_sizer->Add(m_dump_path_dirpicker, wxGBPosition(2, 1), wxDefaultSpan, wxEXPAND);
picker_sizer->Add(new wxStaticText(this, wxID_ANY, _("SD Card Path:")), wxGBPosition(3, 0),
wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
picker_sizer->Add(m_nand_root_dirpicker, wxGBPosition(3, 1), wxDefaultSpan, wxEXPAND);
picker_sizer->Add(new wxStaticText(this, wxID_ANY, _("Dump Path:")), wxGBPosition(4, 0),
wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
picker_sizer->Add(m_dump_path_dirpicker, wxGBPosition(4, 1), wxDefaultSpan, wxEXPAND);
picker_sizer->Add(new wxStaticText(this, wxID_ANY, _("SD Card Path:")), wxGBPosition(5, 0),
wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
picker_sizer->Add(m_wii_sdcard_filepicker, wxGBPosition(5, 1), wxDefaultSpan, wxEXPAND);
picker_sizer->Add(m_wii_sdcard_filepicker, wxGBPosition(3, 1), wxDefaultSpan, wxEXPAND);
picker_sizer->AddGrowableCol(1);
// Populate the Paths page
@ -116,8 +102,6 @@ void PathConfigPane::LoadGUIValues()
m_recursive_iso_paths_checkbox->SetValue(SConfig::GetInstance().m_RecursiveISOFolder);
m_default_iso_filepicker->SetPath(StrToWxStr(startup_params.m_strDefaultISO));
m_dvd_root_dirpicker->SetPath(StrToWxStr(startup_params.m_strDVDRoot));
m_apploader_path_filepicker->SetPath(StrToWxStr(startup_params.m_strApploader));
m_nand_root_dirpicker->SetPath(StrToWxStr(SConfig::GetInstance().m_NANDPath));
m_dump_path_dirpicker->SetPath(StrToWxStr(SConfig::GetInstance().m_DumpPath));
m_wii_sdcard_filepicker->SetPath(StrToWxStr(SConfig::GetInstance().m_strWiiSDCardPath));
@ -136,9 +120,6 @@ void PathConfigPane::BindEvents()
m_remove_iso_path_button->Bind(wxEVT_BUTTON, &PathConfigPane::OnRemoveISOPath, this);
m_default_iso_filepicker->Bind(wxEVT_FILEPICKER_CHANGED, &PathConfigPane::OnDefaultISOChanged,
this);
m_dvd_root_dirpicker->Bind(wxEVT_DIRPICKER_CHANGED, &PathConfigPane::OnDVDRootChanged, this);
m_apploader_path_filepicker->Bind(wxEVT_FILEPICKER_CHANGED,
&PathConfigPane::OnApploaderPathChanged, this);
m_nand_root_dirpicker->Bind(wxEVT_DIRPICKER_CHANGED, &PathConfigPane::OnNANDRootChanged, this);
m_dump_path_dirpicker->Bind(wxEVT_DIRPICKER_CHANGED, &PathConfigPane::OnDumpPathChanged, this);
m_wii_sdcard_filepicker->Bind(wxEVT_FILEPICKER_CHANGED, &PathConfigPane::OnSdCardPathChanged,
@ -206,16 +187,6 @@ void PathConfigPane::OnDefaultISOChanged(wxCommandEvent& event)
SConfig::GetInstance().m_strDefaultISO = WxStrToStr(m_default_iso_filepicker->GetPath());
}
void PathConfigPane::OnDVDRootChanged(wxCommandEvent& event)
{
SConfig::GetInstance().m_strDVDRoot = WxStrToStr(m_dvd_root_dirpicker->GetPath());
}
void PathConfigPane::OnApploaderPathChanged(wxCommandEvent& event)
{
SConfig::GetInstance().m_strApploader = WxStrToStr(m_apploader_path_filepicker->GetPath());
}
void PathConfigPane::OnSdCardPathChanged(wxCommandEvent& event)
{
std::string sd_card_path = WxStrToStr(m_wii_sdcard_filepicker->GetPath());

View File

@ -29,8 +29,6 @@ private:
void OnAddISOPath(wxCommandEvent&);
void OnRemoveISOPath(wxCommandEvent&);
void OnDefaultISOChanged(wxCommandEvent&);
void OnDVDRootChanged(wxCommandEvent&);
void OnApploaderPathChanged(wxCommandEvent&);
void OnNANDRootChanged(wxCommandEvent&);
void OnDumpPathChanged(wxCommandEvent&);
void OnSdCardPathChanged(wxCommandEvent&);
@ -42,10 +40,8 @@ private:
wxButton* m_add_iso_path_button;
wxButton* m_remove_iso_path_button;
wxDirPickerCtrl* m_dvd_root_dirpicker;
wxDirPickerCtrl* m_nand_root_dirpicker;
wxFilePickerCtrl* m_default_iso_filepicker;
wxFilePickerCtrl* m_apploader_path_filepicker;
wxDirPickerCtrl* m_dump_path_dirpicker;
wxFilePickerCtrl* m_wii_sdcard_filepicker;
};

View File

@ -53,6 +53,7 @@
#include "Core/Movie.h"
#include "Core/TitleDatabase.h"
#include "DiscIO/Blob.h"
#include "DiscIO/DirectoryBlob.h"
#include "DiscIO/Enums.h"
#include "DiscIO/Volume.h"
#include "DolphinWX/Frame.h"
@ -80,7 +81,7 @@ public:
wxProgressDialog* dialog;
};
static constexpr u32 CACHE_REVISION = 2; // Last changed in PR 5687
static constexpr u32 CACHE_REVISION = 3; // Last changed in PR 5573
static bool sorted = false;
@ -761,6 +762,12 @@ void GameListCtrl::RescanList()
auto search_results = Common::DoFileSearch(SConfig::GetInstance().m_ISOFolder, search_extensions,
SConfig::GetInstance().m_RecursiveISOFolder);
// TODO Prevent DoFileSearch from looking inside /files/ directories of DirectoryBlobs at all?
// TODO Make DoFileSearch support filter predicates so we don't have remove things afterwards?
search_results.erase(
std::remove_if(search_results.begin(), search_results.end(), DiscIO::ShouldHideFromGameList),
search_results.end());
std::vector<std::string> cached_paths;
for (const auto& file : m_cached_files)
cached_paths.emplace_back(file->GetFileName());