Merge pull request #10127 from AdmiralCurtiss/riivolution

HLE Riivolution patch support
This commit is contained in:
Léo Lam
2021-10-24 00:57:54 +02:00
committed by GitHub
37 changed files with 2917 additions and 229 deletions
+1
View File
@@ -47,6 +47,7 @@
#define SCREENSHOTS_DIR "ScreenShots"
#define LOAD_DIR "Load"
#define HIRES_TEXTURES_DIR "Textures"
#define RIIVOLUTION_DIR "Riivolution"
#define DUMP_DIR "Dump"
#define DUMP_TEXTURES_DIR "Textures"
#define DUMP_FRAMES_DIR "Frames"
+2
View File
@@ -943,6 +943,7 @@ static void RebuildUserDirectories(unsigned int dir_index)
s_user_paths[D_SCREENSHOTS_IDX] = s_user_paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP;
s_user_paths[D_LOAD_IDX] = s_user_paths[D_USER_IDX] + LOAD_DIR DIR_SEP;
s_user_paths[D_HIRESTEXTURES_IDX] = s_user_paths[D_LOAD_IDX] + HIRES_TEXTURES_DIR DIR_SEP;
s_user_paths[D_RIIVOLUTION_IDX] = s_user_paths[D_LOAD_IDX] + RIIVOLUTION_DIR DIR_SEP;
s_user_paths[D_DUMP_IDX] = s_user_paths[D_USER_IDX] + DUMP_DIR DIR_SEP;
s_user_paths[D_DUMPFRAMES_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_FRAMES_DIR DIR_SEP;
s_user_paths[D_DUMPOBJECTS_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_OBJECTS_DIR DIR_SEP;
@@ -1035,6 +1036,7 @@ static void RebuildUserDirectories(unsigned int dir_index)
case D_LOAD_IDX:
s_user_paths[D_HIRESTEXTURES_IDX] = s_user_paths[D_LOAD_IDX] + HIRES_TEXTURES_DIR DIR_SEP;
s_user_paths[D_RIIVOLUTION_IDX] = s_user_paths[D_LOAD_IDX] + RIIVOLUTION_DIR DIR_SEP;
s_user_paths[D_DYNAMICINPUT_IDX] = s_user_paths[D_LOAD_IDX] + DYNAMICINPUT_DIR DIR_SEP;
break;
}
+1
View File
@@ -41,6 +41,7 @@ enum
D_STATESAVES_IDX,
D_SCREENSHOTS_IDX,
D_HIRESTEXTURES_IDX,
D_RIIVOLUTION_IDX,
D_DUMP_IDX,
D_DUMPFRAMES_IDX,
D_DUMPOBJECTS_IDX,
+54 -3
View File
@@ -57,6 +57,9 @@ namespace fs = std::filesystem;
#include "Core/PowerPC/PowerPC.h"
#include "DiscIO/Enums.h"
#include "DiscIO/GameModDescriptor.h"
#include "DiscIO/RiivolutionParser.h"
#include "DiscIO/RiivolutionPatcher.h"
#include "DiscIO/VolumeDisc.h"
#include "DiscIO/VolumeWad.h"
@@ -216,6 +219,31 @@ BootParameters::GenerateFromFile(std::vector<std::string> paths,
return std::make_unique<BootParameters>(std::move(*wad), savestate_path);
}
if (extension == ".json")
{
auto descriptor = DiscIO::ParseGameModDescriptorFile(path);
if (descriptor)
{
auto boot_params = GenerateFromFile(descriptor->base_file, savestate_path);
if (!boot_params)
{
PanicAlertFmtT("Could not recognize file {0}", descriptor->base_file);
return nullptr;
}
if (descriptor->riivolution && std::holds_alternative<Disc>(boot_params->parameters))
{
const auto& volume = *std::get<Disc>(boot_params->parameters).volume;
AddRiivolutionPatches(boot_params.get(),
DiscIO::Riivolution::GenerateRiivolutionPatchesFromGameModDescriptor(
*descriptor->riivolution, volume.GetGameID(),
volume.GetRevision(), volume.GetDiscNumber()));
}
return boot_params;
}
}
PanicAlertFmtT("Could not recognize file {0}", path);
return {};
}
@@ -422,7 +450,10 @@ bool CBoot::BootUp(std::unique_ptr<BootParameters> boot)
struct BootTitle
{
BootTitle() : config(SConfig::GetInstance()) {}
BootTitle(const std::vector<DiscIO::Riivolution::Patch>& patches)
: config(SConfig::GetInstance()), riivolution_patches(patches)
{
}
bool operator()(BootParameters::Disc& disc) const
{
NOTICE_LOG_FMT(BOOT, "Booting from disc: {}", disc.path);
@@ -432,7 +463,7 @@ bool CBoot::BootUp(std::unique_ptr<BootParameters> boot)
if (!volume)
return false;
if (!EmulatedBS2(config.bWii, *volume))
if (!EmulatedBS2(config.bWii, *volume, riivolution_patches))
return false;
SConfig::OnNewTitleLoad();
@@ -542,11 +573,14 @@ bool CBoot::BootUp(std::unique_ptr<BootParameters> boot)
private:
const SConfig& config;
const std::vector<DiscIO::Riivolution::Patch>& riivolution_patches;
};
if (!std::visit(BootTitle(), boot->parameters))
if (!std::visit(BootTitle(boot->riivolution_patches), boot->parameters))
return false;
DiscIO::Riivolution::ApplyGeneralMemoryPatches(boot->riivolution_patches);
return true;
}
@@ -604,3 +638,20 @@ void CreateSystemMenuTitleDirs()
const auto es = IOS::HLE::GetIOS()->GetES();
es->CreateTitleDirectories(Titles::SYSTEM_MENU, IOS::SYSMENU_GID);
}
void AddRiivolutionPatches(BootParameters* boot_params,
std::vector<DiscIO::Riivolution::Patch> riivolution_patches)
{
if (riivolution_patches.empty())
return;
if (!std::holds_alternative<BootParameters::Disc>(boot_params->parameters))
return;
auto& disc = std::get<BootParameters::Disc>(boot_params->parameters);
disc.volume = DiscIO::CreateDisc(DiscIO::DirectoryBlobReader::Create(
std::move(disc.volume),
[&](std::vector<DiscIO::FSTBuilderNode>* fst, DiscIO::FSTBuilderNode* dol_node) {
DiscIO::Riivolution::ApplyPatchesToFiles(riivolution_patches, fst, dol_node);
}));
boot_params->riivolution_patches = std::move(riivolution_patches);
}
+13 -4
View File
@@ -15,6 +15,7 @@
#include "Core/IOS/IOSC.h"
#include "DiscIO/Blob.h"
#include "DiscIO/Enums.h"
#include "DiscIO/RiivolutionParser.h"
#include "DiscIO/VolumeDisc.h"
#include "DiscIO/VolumeWad.h"
@@ -78,6 +79,7 @@ struct BootParameters
BootParameters(Parameters&& parameters_, const std::optional<std::string>& savestate_path_ = {});
Parameters parameters;
std::vector<DiscIO::Riivolution::Patch> riivolution_patches;
std::optional<std::string> savestate_path;
bool delete_savestate = false;
};
@@ -113,10 +115,14 @@ private:
static void SetupMSR();
static void SetupBAT(bool is_wii);
static bool RunApploader(bool is_wii, const DiscIO::VolumeDisc& volume);
static bool EmulatedBS2_GC(const DiscIO::VolumeDisc& volume);
static bool EmulatedBS2_Wii(const DiscIO::VolumeDisc& volume);
static bool EmulatedBS2(bool is_wii, const DiscIO::VolumeDisc& volume);
static bool RunApploader(bool is_wii, const DiscIO::VolumeDisc& volume,
const std::vector<DiscIO::Riivolution::Patch>& riivolution_patches);
static bool EmulatedBS2_GC(const DiscIO::VolumeDisc& volume,
const std::vector<DiscIO::Riivolution::Patch>& riivolution_patches);
static bool EmulatedBS2_Wii(const DiscIO::VolumeDisc& volume,
const std::vector<DiscIO::Riivolution::Patch>& riivolution_patches);
static bool EmulatedBS2(bool is_wii, const DiscIO::VolumeDisc& volume,
const std::vector<DiscIO::Riivolution::Patch>& riivolution_patches);
static bool Load_BS2(const std::string& boot_rom_filename);
static void SetupGCMemory();
@@ -161,3 +167,6 @@ void UpdateStateFlags(std::function<void(StateFlags*)> update_function);
/// Normally, this is automatically done by ES when the System Menu is installed,
/// but we cannot rely on this because we don't require any system titles to be installed.
void CreateSystemMenuTitleDirs();
void AddRiivolutionPatches(BootParameters* boot_params,
std::vector<DiscIO::Riivolution::Patch> riivolution_patches);
+15 -7
View File
@@ -33,6 +33,7 @@
#include "Core/PowerPC/PowerPC.h"
#include "DiscIO/Enums.h"
#include "DiscIO/RiivolutionPatcher.h"
#include "DiscIO/VolumeDisc.h"
namespace
@@ -87,7 +88,8 @@ void CBoot::SetupBAT(bool is_wii)
PowerPC::IBATUpdated();
}
bool CBoot::RunApploader(bool is_wii, const DiscIO::VolumeDisc& volume)
bool CBoot::RunApploader(bool is_wii, const DiscIO::VolumeDisc& volume,
const std::vector<DiscIO::Riivolution::Patch>& riivolution_patches)
{
const DiscIO::Partition partition = volume.GetGamePartition();
@@ -148,6 +150,8 @@ bool CBoot::RunApploader(bool is_wii, const DiscIO::VolumeDisc& volume)
ram_address, length);
DVDRead(volume, dvd_offset, ram_address, length, partition);
DiscIO::Riivolution::ApplyApploaderMemoryPatches(riivolution_patches, ram_address, length);
PowerPC::ppcState.gpr[3] = 0x81300004;
PowerPC::ppcState.gpr[4] = 0x81300008;
PowerPC::ppcState.gpr[5] = 0x8130000c;
@@ -203,7 +207,8 @@ void CBoot::SetupGCMemory()
// GameCube Bootstrap 2 HLE:
// copy the apploader to 0x81200000
// execute the apploader, function by function, using the above utility.
bool CBoot::EmulatedBS2_GC(const DiscIO::VolumeDisc& volume)
bool CBoot::EmulatedBS2_GC(const DiscIO::VolumeDisc& volume,
const std::vector<DiscIO::Riivolution::Patch>& riivolution_patches)
{
INFO_LOG_FMT(BOOT, "Faking GC BS2...");
@@ -240,7 +245,7 @@ bool CBoot::EmulatedBS2_GC(const DiscIO::VolumeDisc& volume)
// Global pointer to Small Data Area Base (Luigi's Mansion's apploader uses it)
PowerPC::ppcState.gpr[13] = ntsc ? 0x81465320 : 0x814b4fc0;
return RunApploader(/*is_wii*/ false, volume);
return RunApploader(/*is_wii*/ false, volume, riivolution_patches);
}
static DiscIO::Region CodeRegion(char c)
@@ -436,7 +441,8 @@ static void WriteEmptyPlayRecord()
// Wii Bootstrap 2 HLE:
// copy the apploader to 0x81200000
// execute the apploader
bool CBoot::EmulatedBS2_Wii(const DiscIO::VolumeDisc& volume)
bool CBoot::EmulatedBS2_Wii(const DiscIO::VolumeDisc& volume,
const std::vector<DiscIO::Riivolution::Patch>& riivolution_patches)
{
INFO_LOG_FMT(BOOT, "Faking Wii BS2...");
if (volume.GetVolumeType() != DiscIO::Platform::WiiDisc)
@@ -493,7 +499,7 @@ bool CBoot::EmulatedBS2_Wii(const DiscIO::VolumeDisc& volume)
PowerPC::ppcState.gpr[1] = 0x816ffff0; // StackPointer
if (!RunApploader(/*is_wii*/ true, volume))
if (!RunApploader(/*is_wii*/ true, volume, riivolution_patches))
return false;
// The Apploader probably just overwrote values needed for RAM Override. Run this again!
@@ -508,7 +514,9 @@ bool CBoot::EmulatedBS2_Wii(const DiscIO::VolumeDisc& volume)
// Returns true if apploader has run successfully. If is_wii is true, the disc
// that volume refers to must currently be inserted into the emulated disc drive.
bool CBoot::EmulatedBS2(bool is_wii, const DiscIO::VolumeDisc& volume)
bool CBoot::EmulatedBS2(bool is_wii, const DiscIO::VolumeDisc& volume,
const std::vector<DiscIO::Riivolution::Patch>& riivolution_patches)
{
return is_wii ? EmulatedBS2_Wii(volume) : EmulatedBS2_GC(volume);
return is_wii ? EmulatedBS2_Wii(volume, riivolution_patches) :
EmulatedBS2_GC(volume, riivolution_patches);
}
+4
View File
@@ -418,6 +418,10 @@ bool BootCore(std::unique_ptr<BootParameters> boot, const WindowSystemInfo& wsi)
if (StartUp.bWii && DiscIO::IsNTSC(StartUp.m_region) && Config::Get(Config::SYSCONF_PAL60))
Config::SetCurrent(Config::SYSCONF_PAL60, false);
// Disable loading time emulation for Riivolution-patched games until we have proper emulation.
if (!boot->riivolution_patches.empty())
StartUp.bFastDiscSpeed = true;
Core::UpdateWantDeterminism(/*initial*/ true);
if (StartUp.bWii)
+7 -1
View File
@@ -73,6 +73,8 @@
#include "Core/MemoryWatcher.h"
#endif
#include "DiscIO/RiivolutionPatcher.h"
#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"
#include "InputCommon/GCAdapter.h"
@@ -603,6 +605,10 @@ static void EmuThread(std::unique_ptr<BootParameters> boot, WindowSystemInfo wsi
else
cpuThreadFunc = CpuThread;
std::optional<DiscIO::Riivolution::SavegameRedirect> savegame_redirect = std::nullopt;
if (SConfig::GetInstance().bWii)
savegame_redirect = DiscIO::Riivolution::ExtractSavegameRedirect(boot->riivolution_patches);
if (!CBoot::BootUp(std::move(boot)))
return;
@@ -611,7 +617,7 @@ static void EmuThread(std::unique_ptr<BootParameters> boot, WindowSystemInfo wsi
// with the correct title context since save copying requires title directories to exist.
Common::ScopeGuard wiifs_guard{&Core::CleanUpWiiFileSystemContents};
if (SConfig::GetInstance().bWii)
Core::InitializeWiiFileSystemContents();
Core::InitializeWiiFileSystemContents(savegame_redirect);
else
wiifs_guard.Dismiss();
+13 -1
View File
@@ -72,6 +72,15 @@ enum class SeekMode : u32
using FileAttribute = u8;
struct NandRedirect
{
// A Wii FS path, eg. "/title/00010000/534d4e45/data".
std::string source_path;
// An absolute host filesystem path the above should be redirected to.
std::string target_path;
};
struct Modes
{
Mode owner, group, other;
@@ -239,6 +248,8 @@ public:
virtual Result<NandStats> GetNandStats() = 0;
/// Get usage information about a directory (used cluster and inode counts).
virtual Result<DirectoryStats> GetDirectoryStats(const std::string& path) = 0;
virtual void SetNandRedirects(std::vector<NandRedirect> nand_redirects) = 0;
};
template <typename T>
@@ -269,7 +280,8 @@ enum class Location
Session,
};
std::unique_ptr<FileSystem> MakeFileSystem(Location location = Location::Session);
std::unique_ptr<FileSystem> MakeFileSystem(Location location = Location::Session,
std::vector<NandRedirect> nand_redirects = {});
/// Convert a FS result code to an IOS error code.
IOS::HLE::ReturnCode ConvertResult(ResultCode code);
+3 -2
View File
@@ -28,11 +28,12 @@ SplitPathResult SplitPathAndBasename(std::string_view path)
std::string(path.substr(last_separator + 1))};
}
std::unique_ptr<FileSystem> MakeFileSystem(Location location)
std::unique_ptr<FileSystem> MakeFileSystem(Location location,
std::vector<NandRedirect> nand_redirects)
{
const std::string nand_root =
File::GetUserPath(location == Location::Session ? D_SESSION_WIIROOT_IDX : D_WIIROOT_IDX);
return std::make_unique<HostFileSystem>(nand_root);
return std::make_unique<HostFileSystem>(nand_root, std::move(nand_redirects));
}
IOS::HLE::ReturnCode ConvertResult(ResultCode code)
+60 -20
View File
@@ -22,13 +22,24 @@
namespace IOS::HLE::FS
{
std::string HostFileSystem::BuildFilename(const std::string& wii_path) const
HostFileSystem::HostFilename HostFileSystem::BuildFilename(const std::string& wii_path) const
{
for (const auto& redirect : m_nand_redirects)
{
if (StringBeginsWith(wii_path, redirect.source_path) &&
(wii_path.size() == redirect.source_path.size() ||
wii_path[redirect.source_path.size()] == '/'))
{
std::string relative_to_redirect = wii_path.substr(redirect.source_path.size());
return HostFilename{redirect.target_path + Common::EscapePath(relative_to_redirect), true};
}
}
if (wii_path.compare(0, 1, "/") == 0)
return m_root_path + Common::EscapePath(wii_path);
return HostFilename{m_root_path + Common::EscapePath(wii_path), false};
ASSERT(false);
return m_root_path;
return HostFilename{m_root_path, false};
}
// Get total filesize of contents of a directory (recursive)
@@ -101,7 +112,9 @@ bool HostFileSystem::FstEntry::CheckPermission(Uid caller_uid, Gid caller_gid,
return (u8(requested_mode) & u8(file_mode)) == u8(requested_mode);
}
HostFileSystem::HostFileSystem(const std::string& root_path) : m_root_path{root_path}
HostFileSystem::HostFileSystem(const std::string& root_path,
std::vector<NandRedirect> nand_redirects)
: m_root_path{root_path}, m_nand_redirects(std::move(nand_redirects))
{
File::CreateFullPath(m_root_path + "/");
ResetFst();
@@ -197,11 +210,12 @@ HostFileSystem::FstEntry* HostFileSystem::GetFstEntryForPath(const std::string&
if (!IsValidNonRootPath(path))
return nullptr;
const File::FileInfo host_file_info{BuildFilename(path)};
auto host_file = BuildFilename(path);
const File::FileInfo host_file_info{host_file.host_path};
if (!host_file_info.Exists())
return nullptr;
FstEntry* entry = &m_root_entry;
FstEntry* entry = host_file.is_redirect ? &m_redirect_fst : &m_root_entry;
std::string complete_path = "";
for (const std::string& component : SplitString(std::string(path.substr(1)), '/'))
{
@@ -217,7 +231,8 @@ HostFileSystem::FstEntry* HostFileSystem::GetFstEntryForPath(const std::string&
// Fall back to dummy data to avoid breaking existing filesystems.
// This code path is also reached when creating a new file or directory;
// proper metadata is filled in later.
INFO_LOG_FMT(IOS_FS, "Creating a default entry for {}", complete_path);
INFO_LOG_FMT(IOS_FS, "Creating a default entry for {} ({})", complete_path,
host_file.is_redirect ? "redirect" : "NAND");
entry = &entry->children.emplace_back();
entry->name = component;
entry->data.modes = {Mode::ReadWrite, Mode::ReadWrite, Mode::ReadWrite};
@@ -241,7 +256,7 @@ void HostFileSystem::DoState(PointerWrap& p)
handle.host_file.reset();
// handle /tmp
std::string Path = BuildFilename("/tmp");
std::string Path = BuildFilename("/tmp").host_path;
if (p.GetMode() == PointerWrap::MODE_READ)
{
File::DeleteDirRecursively(Path);
@@ -336,7 +351,7 @@ void HostFileSystem::DoState(PointerWrap& p)
p.Do(handle.wii_path);
p.Do(handle.file_offset);
if (handle.opened)
handle.host_file = OpenHostFile(BuildFilename(handle.wii_path));
handle.host_file = OpenHostFile(BuildFilename(handle.wii_path).host_path);
}
}
@@ -346,7 +361,7 @@ ResultCode HostFileSystem::Format(Uid uid)
return ResultCode::AccessDenied;
if (m_root_path.empty())
return ResultCode::AccessDenied;
const std::string root = BuildFilename("/");
const std::string root = BuildFilename("/").host_path;
if (!File::DeleteDirRecursively(root) || !File::CreateDir(root))
return ResultCode::UnknownError;
ResetFst();
@@ -366,7 +381,7 @@ ResultCode HostFileSystem::CreateFileOrDirectory(Uid uid, Gid gid, const std::st
return ResultCode::TooManyPathComponents;
const auto split_path = SplitPathAndBasename(path);
const std::string host_path = BuildFilename(path);
const std::string host_path = BuildFilename(path).host_path;
FstEntry* parent = GetFstEntryForPath(split_path.parent);
if (!parent)
@@ -428,7 +443,7 @@ ResultCode HostFileSystem::Delete(Uid uid, Gid gid, const std::string& path)
if (!IsValidNonRootPath(path))
return ResultCode::Invalid;
const std::string host_path = BuildFilename(path);
const std::string host_path = BuildFilename(path).host_path;
const auto split_path = SplitPathAndBasename(path);
FstEntry* parent = GetFstEntryForPath(split_path.parent);
@@ -491,8 +506,10 @@ ResultCode HostFileSystem::Rename(Uid uid, Gid gid, const std::string& old_path,
return ResultCode::InUse;
}
const std::string host_old_path = BuildFilename(old_path);
const std::string host_new_path = BuildFilename(new_path);
const auto host_old_info = BuildFilename(old_path);
const auto host_new_info = BuildFilename(new_path);
const std::string& host_old_path = host_old_info.host_path;
const std::string& host_new_path = host_new_info.host_path;
// If there is already something of the same type at the new path, delete it.
if (File::Exists(host_new_path))
@@ -509,8 +526,27 @@ ResultCode HostFileSystem::Rename(Uid uid, Gid gid, const std::string& old_path,
if (!File::Rename(host_old_path, host_new_path))
{
ERROR_LOG_FMT(IOS_FS, "Rename {} to {} - failed", host_old_path, host_new_path);
return ResultCode::NotFound;
if (host_old_info.is_redirect || host_new_info.is_redirect)
{
// If either path is a redirect, the source and target may be on a different partition or
// device, so a simple rename may not work. Fall back to Copy & Delete and see if that works.
if (!File::Copy(host_old_path, host_new_path))
{
ERROR_LOG_FMT(IOS_FS, "Copying {} to {} in Rename fallback failed", host_old_path,
host_new_path);
return ResultCode::NotFound;
}
if (!File::Delete(host_old_path))
{
ERROR_LOG_FMT(IOS_FS, "Deleting {} in Rename fallback failed", host_old_path);
return ResultCode::Invalid;
}
}
else
{
ERROR_LOG_FMT(IOS_FS, "Rename {} to {} - failed", host_old_path, host_new_path);
return ResultCode::NotFound;
}
}
// Finally, remove the child from the old parent and move it to the new parent.
@@ -544,7 +580,7 @@ Result<std::vector<std::string>> HostFileSystem::ReadDirectory(Uid uid, Gid gid,
if (entry->data.is_file)
return ResultCode::Invalid;
const std::string host_path = BuildFilename(path);
const std::string host_path = BuildFilename(path).host_path;
File::FSTEntry host_entry = File::ScanDirectoryTree(host_path, false);
for (File::FSTEntry& child : host_entry.children)
{
@@ -612,7 +648,7 @@ Result<Metadata> HostFileSystem::GetMetadata(Uid uid, Gid gid, const std::string
return ResultCode::NotFound;
Metadata metadata = entry->data;
metadata.size = File::GetSize(BuildFilename(path));
metadata.size = File::GetSize(BuildFilename(path).host_path);
return metadata;
}
@@ -631,7 +667,7 @@ ResultCode HostFileSystem::SetMetadata(Uid caller_uid, const std::string& path,
if (caller_uid != 0 && uid != entry->data.uid)
return ResultCode::AccessDenied;
const bool is_empty = File::GetSize(BuildFilename(path)) == 0;
const bool is_empty = File::GetSize(BuildFilename(path).host_path) == 0;
if (entry->data.uid != uid && entry->data.is_file && !is_empty)
return ResultCode::FileNotEmpty;
@@ -667,7 +703,7 @@ Result<DirectoryStats> HostFileSystem::GetDirectoryStats(const std::string& wii_
return ResultCode::Invalid;
DirectoryStats stats{};
std::string path(BuildFilename(wii_path));
std::string path(BuildFilename(wii_path).host_path);
if (File::IsDirectory(path))
{
File::FSTEntry parent_dir = File::ScanDirectoryTree(path, true);
@@ -685,4 +721,8 @@ Result<DirectoryStats> HostFileSystem::GetDirectoryStats(const std::string& wii_
return stats;
}
void HostFileSystem::SetNandRedirects(std::vector<NandRedirect> nand_redirects)
{
m_nand_redirects = std::move(nand_redirects);
}
} // namespace IOS::HLE::FS
+12 -2
View File
@@ -22,7 +22,7 @@ namespace IOS::HLE::FS
class HostFileSystem final : public FileSystem
{
public:
HostFileSystem(const std::string& root_path);
HostFileSystem(const std::string& root_path, std::vector<NandRedirect> nand_redirects = {});
~HostFileSystem();
void DoState(PointerWrap& p) override;
@@ -56,6 +56,8 @@ public:
Result<NandStats> GetNandStats() override;
Result<DirectoryStats> GetDirectoryStats(const std::string& path) override;
void SetNandRedirects(std::vector<NandRedirect> nand_redirects) override;
private:
struct FstEntry
{
@@ -83,7 +85,12 @@ private:
Handle* GetHandleFromFd(Fd fd);
Fd ConvertHandleToFd(const Handle* handle) const;
std::string BuildFilename(const std::string& wii_path) const;
struct HostFilename
{
std::string host_path;
bool is_redirect;
};
HostFilename BuildFilename(const std::string& wii_path) const;
std::shared_ptr<File::IOFile> OpenHostFile(const std::string& host_path);
ResultCode CreateFileOrDirectory(Uid uid, Gid gid, const std::string& path,
@@ -112,6 +119,9 @@ private:
std::string m_root_path;
std::map<std::string, std::weak_ptr<File::IOFile>> m_open_files;
std::array<Handle, 16> m_handles{};
FstEntry m_redirect_fst{};
std::vector<NandRedirect> m_nand_redirects;
};
} // namespace IOS::HLE::FS
+1 -1
View File
@@ -80,7 +80,7 @@ Result<FileHandle> HostFileSystem::OpenFile(Uid, Gid, const std::string& path, M
if (!handle)
return ResultCode::NoFreeHandle;
const std::string host_path = BuildFilename(path);
const std::string host_path = BuildFilename(path).host_path;
if (!File::IsFile(host_path))
{
*handle = Handle{};
+1 -1
View File
@@ -498,7 +498,7 @@ void Kernel::AddDevice(std::unique_ptr<Device> device)
void Kernel::AddCoreDevices()
{
m_fs = FS::MakeFileSystem();
m_fs = FS::MakeFileSystem(IOS::HLE::FS::Location::Session, Core::GetActiveNandRedirects());
ASSERT(m_fs);
std::lock_guard lock(m_device_map_mutex);
+35 -8
View File
@@ -4,6 +4,7 @@
#include "Core/WiiRoot.h"
#include <cinttypes>
#include <optional>
#include <string>
#include <vector>
@@ -34,6 +35,12 @@ namespace FS = IOS::HLE::FS;
static std::string s_temp_wii_root;
static bool s_wii_root_initialized = false;
static std::vector<IOS::HLE::FS::NandRedirect> s_nand_redirects;
const std::vector<IOS::HLE::FS::NandRedirect>& GetActiveNandRedirects()
{
return s_nand_redirects;
}
static bool CopyBackupFile(const std::string& path_from, const std::string& path_to)
{
@@ -202,6 +209,7 @@ void InitializeWiiRoot(bool use_temporary)
File::SetUserPath(D_SESSION_WIIROOT_IDX, File::GetUserPath(D_WIIROOT_IDX));
}
s_nand_redirects.clear();
s_wii_root_initialized = true;
}
@@ -213,6 +221,7 @@ void ShutdownWiiRoot()
s_temp_wii_root.clear();
}
s_nand_redirects.clear();
s_wii_root_initialized = false;
}
@@ -288,7 +297,8 @@ static bool CopySysmenuFilesToFS(FS::FileSystem* fs, const std::string& host_sou
return true;
}
void InitializeWiiFileSystemContents()
void InitializeWiiFileSystemContents(
std::optional<DiscIO::Riivolution::SavegameRedirect> save_redirect)
{
const auto fs = IOS::HLE::GetIOS()->GetFS();
@@ -299,14 +309,31 @@ void InitializeWiiFileSystemContents()
if (!CopySysmenuFilesToFS(fs.get(), File::GetSysDirectory() + WII_USER_DIR, ""))
WARN_LOG_FMT(CORE, "Failed to copy initial System Menu files to the NAND");
if (!WiiRootIsTemporary())
return;
if (WiiRootIsTemporary())
{
// Generate a SYSCONF with default settings for the temporary Wii NAND.
SysConf sysconf{fs};
sysconf.Save();
// Generate a SYSCONF with default settings for the temporary Wii NAND.
SysConf sysconf{fs};
sysconf.Save();
InitializeDeterministicWiiSaves(fs.get());
InitializeDeterministicWiiSaves(fs.get());
}
else if (save_redirect)
{
const u64 title_id = SConfig::GetInstance().GetTitleID();
std::string source_path = Common::GetTitleDataPath(title_id);
if (!File::IsDirectory(save_redirect->m_target_path))
{
File::CreateFullPath(save_redirect->m_target_path + "/");
if (save_redirect->m_clone)
{
File::CopyDir(Common::GetTitleDataPath(title_id, Common::FROM_CONFIGURED_ROOT),
save_redirect->m_target_path);
}
}
s_nand_redirects.emplace_back(IOS::HLE::FS::NandRedirect{
std::move(source_path), std::move(save_redirect->m_target_path)});
fs->SetNandRedirects(s_nand_redirects);
}
}
void CleanUpWiiFileSystemContents()
+14 -1
View File
@@ -3,6 +3,16 @@
#pragma once
#include <optional>
#include <vector>
#include "DiscIO/RiivolutionPatcher.h"
namespace IOS::HLE::FS
{
struct NandRedirect;
}
namespace Core
{
enum class RestoreReason
@@ -21,6 +31,9 @@ void BackupWiiSettings();
void RestoreWiiSettings(RestoreReason reason);
// Initialize or clean up the filesystem contents.
void InitializeWiiFileSystemContents();
void InitializeWiiFileSystemContents(
std::optional<DiscIO::Riivolution::SavegameRedirect> save_redirect);
void CleanUpWiiFileSystemContents();
const std::vector<IOS::HLE::FS::NandRedirect>& GetActiveNandRedirects();
} // namespace Core
+6
View File
@@ -23,11 +23,17 @@ add_library(discio
FileSystemGCWii.h
Filesystem.cpp
Filesystem.h
GameModDescriptor.cpp
GameModDescriptor.h
LaggedFibonacciGenerator.cpp
LaggedFibonacciGenerator.h
MultithreadedCompressor.h
NANDImporter.cpp
NANDImporter.h
RiivolutionParser.cpp
RiivolutionParser.h
RiivolutionPatcher.cpp
RiivolutionPatcher.h
ScrubbedBlob.cpp
ScrubbedBlob.h
TGCBlob.cpp
File diff suppressed because it is too large Load Diff
+143 -23
View File
@@ -5,6 +5,7 @@
#include <array>
#include <cstddef>
#include <functional>
#include <map>
#include <memory>
#include <optional>
@@ -16,6 +17,7 @@
#include "Common/CommonTypes.h"
#include "Common/FileUtil.h"
#include "DiscIO/Blob.h"
#include "DiscIO/Volume.h"
#include "DiscIO/WiiEncryptionCache.h"
namespace File
@@ -29,22 +31,108 @@ namespace DiscIO
enum class PartitionType : u32;
class DirectoryBlobReader;
class VolumeDisc;
// Returns true if the path is inside a DirectoryBlob and doesn't represent the DirectoryBlob itself
bool ShouldHideFromGameList(const std::string& volume_path);
// Content chunk that is loaded from a file in the host file system.
struct ContentFile
{
// Path where the file can be found.
std::string m_filename;
// Offset from the start of the file where the first byte of this content chunk is.
u64 m_offset;
};
// Content chunk that loads data from a DirectoryBlobReader.
// Intented for representing a partition within a disc.
struct ContentPartition
{
// The reader to read data from.
DirectoryBlobReader* m_reader;
// Offset from the start of the partition for the first byte represented by this chunk.
u64 m_offset;
// The value passed as partition_data_offset to EncryptPartitionData().
u64 m_partition_data_offset;
};
// Content chunk that loads data from a Volume.
struct ContentVolume
{
// Offset from the start of the volume for the first byte represented by this chunk.
u64 m_offset;
// The volume to read data from.
const Volume* m_volume;
// The partition passed to the Volume's Read() method.
Partition m_partition;
};
// Content chunk representing a run of identical bytes.
// Useful for padding between chunks within a file.
struct ContentFixedByte
{
u8 m_byte;
};
using ContentSource = std::variant<ContentFile, // File
const u8*, // Memory
ContentPartition, // Partition
ContentVolume, // Volume
ContentFixedByte // Fixed value padding
>;
struct BuilderContentSource
{
u64 m_offset;
u64 m_size;
ContentSource m_source;
};
struct FSTBuilderNode
{
std::string m_filename;
u64 m_size;
std::variant<std::vector<BuilderContentSource>, std::vector<FSTBuilderNode>> m_content;
void* m_user_data = nullptr;
bool IsFile() const
{
return std::holds_alternative<std::vector<BuilderContentSource>>(m_content);
}
std::vector<BuilderContentSource>& GetFileContent()
{
return std::get<std::vector<BuilderContentSource>>(m_content);
}
const std::vector<BuilderContentSource>& GetFileContent() const
{
return std::get<std::vector<BuilderContentSource>>(m_content);
}
bool IsFolder() const { return std::holds_alternative<std::vector<FSTBuilderNode>>(m_content); }
std::vector<FSTBuilderNode>& GetFolderContent()
{
return std::get<std::vector<FSTBuilderNode>>(m_content);
}
const std::vector<FSTBuilderNode>& GetFolderContent() const
{
return std::get<std::vector<FSTBuilderNode>>(m_content);
}
};
class DiscContent
{
public:
using ContentSource =
std::variant<std::string, // File
const u8*, // Memory
DirectoryBlobReader* // Partition (which one it is is determined by m_offset)
>;
DiscContent(u64 offset, u64 size, const std::string& path);
DiscContent(u64 offset, u64 size, const u8* data);
DiscContent(u64 offset, u64 size, DirectoryBlobReader* blob);
DiscContent(u64 offset, u64 size, ContentSource source);
// Provided because it's convenient when searching for DiscContent in an std::set
explicit DiscContent(u64 offset);
@@ -62,8 +150,13 @@ public:
bool operator>=(const DiscContent& other) const { return !(*this < other); }
private:
// Position of this content chunk within its parent DiscContentContainer.
u64 m_offset;
// Number of bytes this content chunk takes up.
u64 m_size = 0;
// Where and how to find the data for this content chunk.
ContentSource m_content_source;
};
@@ -71,13 +164,11 @@ class DiscContentContainer
{
public:
template <typename T>
void Add(u64 offset, const std::vector<T>& vector)
void AddReference(u64 offset, const std::vector<T>& vector)
{
return Add(offset, vector.size() * sizeof(T), reinterpret_cast<const u8*>(vector.data()));
}
void Add(u64 offset, u64 size, const std::string& path);
void Add(u64 offset, u64 size, const u8* data);
void Add(u64 offset, u64 size, DirectoryBlobReader* blob);
void Add(u64 offset, u64 size, ContentSource source);
u64 CheckSizeAndAdd(u64 offset, const std::string& path);
u64 CheckSizeAndAdd(u64 offset, u64 max_size, const std::string& path);
@@ -92,6 +183,10 @@ class DirectoryBlobPartition
public:
DirectoryBlobPartition() = default;
DirectoryBlobPartition(const std::string& root_directory, std::optional<bool> is_wii);
DirectoryBlobPartition(DiscIO::VolumeDisc* volume, const DiscIO::Partition& partition,
std::optional<bool> is_wii,
const std::function<void(std::vector<FSTBuilderNode>* fst_nodes,
FSTBuilderNode* dol_node)>& fst_callback);
// We do not allow copying, because it might mess up the pointers inside DiscContents
DirectoryBlobPartition(const DirectoryBlobPartition&) = delete;
@@ -104,27 +199,38 @@ public:
const std::string& GetRootDirectory() const { return m_root_directory; }
const std::vector<u8>& GetHeader() const { return m_disc_header; }
const DiscContentContainer& GetContents() const { return m_contents; }
const std::optional<DiscIO::Partition>& GetWrappedPartition() const
{
return m_wrapped_partition;
}
const std::array<u8, VolumeWii::AES_KEY_SIZE>& GetKey() const { return m_key; }
void SetKey(std::array<u8, VolumeWii::AES_KEY_SIZE> key) { m_key = key; }
private:
void SetDiscHeaderAndDiscType(std::optional<bool> is_wii);
void SetBI2();
void SetDiscHeaderFromFile(const std::string& boot_bin_path);
void SetDiscHeader(std::vector<u8> boot_bin);
void SetDiscType(std::optional<bool> is_wii);
void SetBI2FromFile(const std::string& bi2_path);
void SetBI2(std::vector<u8> bi2);
// Returns DOL address
u64 SetApploader();
u64 SetApploaderFromFile(const std::string& path);
u64 SetApploader(std::vector<u8> apploader, const std::string& log_path);
// Returns FST address
u64 SetDOL(u64 dol_address);
u64 SetDOLFromFile(const std::string& path, u64 dol_address);
u64 SetDOL(FSTBuilderNode dol_node, u64 dol_address);
void BuildFST(u64 fst_address);
void BuildFSTFromFolder(const std::string& fst_root_path, u64 fst_address);
void BuildFST(std::vector<FSTBuilderNode> root_nodes, 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);
void WriteDirectory(std::vector<FSTBuilderNode>* parent_entries, u32* fst_offset,
u32* name_offset, u64* data_offset, u32 parent_entry_index,
u64 name_table_offset);
DiscContentContainer m_contents;
std::vector<u8> m_disc_header;
@@ -140,6 +246,8 @@ private:
u32 m_address_shift = 0;
u64 m_data_size = 0;
std::optional<DiscIO::Partition> m_wrapped_partition = std::nullopt;
};
class DirectoryBlobReader : public BlobReader
@@ -148,6 +256,10 @@ class DirectoryBlobReader : public BlobReader
public:
static std::unique_ptr<DirectoryBlobReader> Create(const std::string& dol_path);
static std::unique_ptr<DirectoryBlobReader> Create(
std::unique_ptr<DiscIO::VolumeDisc> volume,
const std::function<void(std::vector<FSTBuilderNode>* fst_nodes, FSTBuilderNode* dol_node)>&
fst_callback);
// We do not allow copying, because it might mess up the pointers inside DiscContents
DirectoryBlobReader(const DirectoryBlobReader&) = delete;
@@ -183,15 +295,21 @@ private:
explicit DirectoryBlobReader(const std::string& game_partition_root,
const std::string& true_root);
explicit DirectoryBlobReader(std::unique_ptr<DiscIO::VolumeDisc> volume,
const std::function<void(std::vector<FSTBuilderNode>* fst_nodes,
FSTBuilderNode* dol_node)>& fst_callback);
const DirectoryBlobPartition* GetPartition(u64 offset, u64 size, u64 partition_data_offset) const;
bool EncryptPartitionData(u64 offset, u64 size, u8* buffer, u64 partition_data_offset,
u64 partition_data_decrypted_size);
void SetNonpartitionDiscHeaderFromFile(const std::vector<u8>& partition_header,
const std::string& game_partition_root);
void SetNonpartitionDiscHeader(const std::vector<u8>& partition_header,
const std::string& game_partition_root);
void SetWiiRegionData(const std::string& game_partition_root);
std::vector<u8> header_bin);
void SetWiiRegionDataFromFile(const std::string& game_partition_root);
void SetWiiRegionData(const std::vector<u8>& wii_region_data, const std::string& log_path);
void SetPartitions(std::vector<PartitionWithType>&& partitions);
void SetPartitionHeader(DirectoryBlobPartition* partition, u64 partition_address);
@@ -209,9 +327,11 @@ private:
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;
std::vector<std::vector<u8>> m_extra_data;
u64 m_data_size;
std::unique_ptr<DiscIO::VolumeDisc> m_wrapped_volume;
};
} // namespace DiscIO
+147
View File
@@ -0,0 +1,147 @@
// Copyright 2021 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "DiscIO/GameModDescriptor.h"
#include <picojson.h>
#include "Common/IOFile.h"
#include "Common/MathUtil.h"
#include "Common/StringUtil.h"
namespace DiscIO
{
static std::string MakeAbsolute(const std::string& directory, const std::string& path)
{
#ifdef _WIN32
return PathToString(StringToPath(directory) / StringToPath(path));
#else
if (StringBeginsWith(path, "/"))
return path;
return directory + "/" + path;
#endif
}
std::optional<GameModDescriptor> ParseGameModDescriptorFile(const std::string& filename)
{
::File::IOFile f(filename, "rb");
if (!f)
return std::nullopt;
std::vector<char> data;
data.resize(f.GetSize());
if (!f.ReadBytes(data.data(), data.size()))
return std::nullopt;
#ifdef _WIN32
std::string path = ReplaceAll(filename, "\\", "/");
#else
const std::string& path = filename;
#endif
return ParseGameModDescriptorString(std::string_view(data.data(), data.size()), path);
}
static std::vector<GameModDescriptorRiivolutionPatchOption>
ParseRiivolutionOptions(const picojson::array& array)
{
std::vector<GameModDescriptorRiivolutionPatchOption> options;
for (const auto& option_object : array)
{
if (!option_object.is<picojson::object>())
continue;
auto& option = options.emplace_back();
for (const auto& [key, value] : option_object.get<picojson::object>())
{
if (key == "section-name" && value.is<std::string>())
option.section_name = value.get<std::string>();
else if (key == "option-id" && value.is<std::string>())
option.option_id = value.get<std::string>();
else if (key == "option-name" && value.is<std::string>())
option.option_name = value.get<std::string>();
else if (key == "choice" && value.is<double>())
option.choice = MathUtil::SaturatingCast<u32>(value.get<double>());
}
}
return options;
}
static GameModDescriptorRiivolution ParseRiivolutionObject(const std::string& json_directory,
const picojson::object& object)
{
GameModDescriptorRiivolution r;
for (const auto& [element_key, element_value] : object)
{
if (element_key == "patches" && element_value.is<picojson::array>())
{
for (const auto& patch_object : element_value.get<picojson::array>())
{
if (!patch_object.is<picojson::object>())
continue;
auto& patch = r.patches.emplace_back();
for (const auto& [key, value] : patch_object.get<picojson::object>())
{
if (key == "xml" && value.is<std::string>())
patch.xml = MakeAbsolute(json_directory, value.get<std::string>());
else if (key == "root" && value.is<std::string>())
patch.root = MakeAbsolute(json_directory, value.get<std::string>());
else if (key == "options" && value.is<picojson::array>())
patch.options = ParseRiivolutionOptions(value.get<picojson::array>());
}
}
}
}
return r;
}
std::optional<GameModDescriptor> ParseGameModDescriptorString(std::string_view json,
std::string_view json_path)
{
std::string json_directory;
SplitPath(json_path, &json_directory, nullptr, nullptr);
picojson::value json_root;
std::string err;
picojson::parse(json_root, json.begin(), json.end(), &err);
if (!err.empty())
return std::nullopt;
if (!json_root.is<picojson::object>())
return std::nullopt;
GameModDescriptor descriptor;
bool is_valid_version = false;
for (const auto& [key, value] : json_root.get<picojson::object>())
{
if (key == "version" && value.is<double>())
{
is_valid_version = value.get<double>() == 1.0;
}
else if (key == "base-file" && value.is<std::string>())
{
descriptor.base_file = MakeAbsolute(json_directory, value.get<std::string>());
}
else if (key == "display-name" && value.is<std::string>())
{
descriptor.display_name = value.get<std::string>();
}
else if (key == "banner" && value.is<std::string>())
{
descriptor.banner = MakeAbsolute(json_directory, value.get<std::string>());
}
else if (key == "riivolution" && value.is<picojson::object>())
{
descriptor.riivolution =
ParseRiivolutionObject(json_directory, value.get<picojson::object>());
}
}
if (!is_valid_version)
return std::nullopt;
return descriptor;
}
} // namespace DiscIO

Some files were not shown because too many files have changed in this diff Show More