Merge pull request #5573 from JosJuice/directory-blob
Turn VolumeDirectory into DirectoryBlob
This commit is contained in:
commit
bf2241ccd2
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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]; }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, ¤t_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
|
|
@ -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
|
|
@ -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" />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, ¤t_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
|
|
@ -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
|
|
@ -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())
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in New Issue