NandPaths: Return paths that are relative to Wii NAND
Since all FS access will go through the new FS interface (PR #6421) in order to keep track of metadata properly, there is no need to return absolute paths anymore. In fact, returning host paths is a roadblock to using the FS interface. This starts the migration work by adding a way to get paths that are relative to the Wii NAND instead of always getting absolute paths on the host FS. To prepare for future changes, this commit also makes returned paths canonical by removing the trailing slash when it's unneeded. Eventually, once everything has been migrated to the new interface, we can remove the "from" parameter.
This commit is contained in:
parent
00de41b583
commit
8317a66ea5
|
@ -21,41 +21,46 @@ std::string RootUserPath(FromWhichRoot from)
|
||||||
return File::GetUserPath(idx);
|
return File::GetUserPath(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetImportTitlePath(u64 title_id, FromWhichRoot from)
|
static std::string RootUserPath(std::optional<FromWhichRoot> from)
|
||||||
|
{
|
||||||
|
return from ? RootUserPath(*from) : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetImportTitlePath(u64 title_id, std::optional<FromWhichRoot> from)
|
||||||
{
|
{
|
||||||
return RootUserPath(from) + StringFromFormat("/import/%08x/%08x",
|
return RootUserPath(from) + StringFromFormat("/import/%08x/%08x",
|
||||||
static_cast<u32>(title_id >> 32),
|
static_cast<u32>(title_id >> 32),
|
||||||
static_cast<u32>(title_id));
|
static_cast<u32>(title_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetTicketFileName(u64 title_id, FromWhichRoot from)
|
std::string GetTicketFileName(u64 title_id, std::optional<FromWhichRoot> from)
|
||||||
{
|
{
|
||||||
return StringFromFormat("%s/ticket/%08x/%08x.tik", RootUserPath(from).c_str(),
|
return StringFromFormat("%s/ticket/%08x/%08x.tik", RootUserPath(from).c_str(),
|
||||||
static_cast<u32>(title_id >> 32), static_cast<u32>(title_id));
|
static_cast<u32>(title_id >> 32), static_cast<u32>(title_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetTitlePath(u64 title_id, FromWhichRoot from)
|
std::string GetTitlePath(u64 title_id, std::optional<FromWhichRoot> from)
|
||||||
{
|
{
|
||||||
return StringFromFormat("%s/title/%08x/%08x/", RootUserPath(from).c_str(),
|
return StringFromFormat("%s/title/%08x/%08x", RootUserPath(from).c_str(),
|
||||||
static_cast<u32>(title_id >> 32), static_cast<u32>(title_id));
|
static_cast<u32>(title_id >> 32), static_cast<u32>(title_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetTitleDataPath(u64 title_id, FromWhichRoot from)
|
std::string GetTitleDataPath(u64 title_id, std::optional<FromWhichRoot> from)
|
||||||
{
|
{
|
||||||
return GetTitlePath(title_id, from) + "data/";
|
return GetTitlePath(title_id, from) + "/data";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetTitleContentPath(u64 title_id, FromWhichRoot from)
|
std::string GetTitleContentPath(u64 title_id, std::optional<FromWhichRoot> from)
|
||||||
{
|
{
|
||||||
return GetTitlePath(title_id, from) + "content/";
|
return GetTitlePath(title_id, from) + "/content";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetTMDFileName(u64 title_id, FromWhichRoot from)
|
std::string GetTMDFileName(u64 title_id, std::optional<FromWhichRoot> from)
|
||||||
{
|
{
|
||||||
return GetTitleContentPath(title_id, from) + "title.tmd";
|
return GetTitleContentPath(title_id, from) + "/title.tmd";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsTitlePath(const std::string& path, FromWhichRoot from, u64* title_id)
|
bool IsTitlePath(const std::string& path, std::optional<FromWhichRoot> from, u64* title_id)
|
||||||
{
|
{
|
||||||
std::string expected_prefix = RootUserPath(from) + "/title/";
|
std::string expected_prefix = RootUserPath(from) + "/title/";
|
||||||
if (!StringBeginsWith(path, expected_prefix))
|
if (!StringBeginsWith(path, expected_prefix))
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
@ -18,17 +19,22 @@ enum FromWhichRoot
|
||||||
|
|
||||||
std::string RootUserPath(FromWhichRoot from);
|
std::string RootUserPath(FromWhichRoot from);
|
||||||
|
|
||||||
// Returns /import/%08x/%08x. Intended for use by ES.
|
// The following functions return paths relative to the NAND root.
|
||||||
std::string GetImportTitlePath(u64 title_id, FromWhichRoot from = FROM_SESSION_ROOT);
|
// If a FromWhichRoot is passed, the NAND root on the host filesystem will be prepended to the path.
|
||||||
|
// TODO: remove the from parameter after all code is migrated off direct FS access.
|
||||||
|
|
||||||
std::string GetTicketFileName(u64 title_id, FromWhichRoot from);
|
// Returns /import/%08x/%08x. Intended for use by ES.
|
||||||
std::string GetTitlePath(u64 title_id, FromWhichRoot from);
|
std::string GetImportTitlePath(u64 title_id, std::optional<FromWhichRoot> from = {});
|
||||||
std::string GetTitleDataPath(u64 title_id, FromWhichRoot from);
|
|
||||||
std::string GetTitleContentPath(u64 title_id, FromWhichRoot from);
|
std::string GetTicketFileName(u64 title_id, std::optional<FromWhichRoot> from = {});
|
||||||
std::string GetTMDFileName(u64 title_id, FromWhichRoot from);
|
std::string GetTitlePath(u64 title_id, std::optional<FromWhichRoot> from = {});
|
||||||
|
std::string GetTitleDataPath(u64 title_id, std::optional<FromWhichRoot> from = {});
|
||||||
|
std::string GetTitleContentPath(u64 title_id, std::optional<FromWhichRoot> from = {});
|
||||||
|
std::string GetTMDFileName(u64 title_id, std::optional<FromWhichRoot> from = {});
|
||||||
|
|
||||||
// Returns whether a path is within an installed title's directory.
|
// Returns whether a path is within an installed title's directory.
|
||||||
bool IsTitlePath(const std::string& path, FromWhichRoot from, u64* title_id = nullptr);
|
bool IsTitlePath(const std::string& path, std::optional<FromWhichRoot> from = {},
|
||||||
|
u64* title_id = nullptr);
|
||||||
|
|
||||||
// Escapes characters that are invalid or have special meanings in the host file system
|
// Escapes characters that are invalid or have special meanings in the host file system
|
||||||
std::string EscapeFileName(const std::string& filename);
|
std::string EscapeFileName(const std::string& filename);
|
||||||
|
|
|
@ -472,7 +472,7 @@ void StateFlags::UpdateChecksum()
|
||||||
void UpdateStateFlags(std::function<void(StateFlags*)> update_function)
|
void UpdateStateFlags(std::function<void(StateFlags*)> update_function)
|
||||||
{
|
{
|
||||||
const std::string file_path =
|
const std::string file_path =
|
||||||
Common::GetTitleDataPath(Titles::SYSTEM_MENU, Common::FROM_SESSION_ROOT) + WII_STATE;
|
Common::GetTitleDataPath(Titles::SYSTEM_MENU, Common::FROM_SESSION_ROOT) + "/" WII_STATE;
|
||||||
|
|
||||||
File::IOFile file;
|
File::IOFile file;
|
||||||
StateFlags state;
|
StateFlags state;
|
||||||
|
|
|
@ -227,7 +227,7 @@ bool CBoot::SetupWiiMemory()
|
||||||
SettingsHandler gen;
|
SettingsHandler gen;
|
||||||
std::string serno;
|
std::string serno;
|
||||||
const std::string settings_file_path(
|
const std::string settings_file_path(
|
||||||
Common::GetTitleDataPath(Titles::SYSTEM_MENU, Common::FROM_SESSION_ROOT) + WII_SETTING);
|
Common::GetTitleDataPath(Titles::SYSTEM_MENU, Common::FROM_SESSION_ROOT) + "/" WII_SETTING);
|
||||||
if (File::Exists(settings_file_path) && gen.Open(settings_file_path))
|
if (File::Exists(settings_file_path) && gen.Open(settings_file_path))
|
||||||
{
|
{
|
||||||
serno = gen.GetValue("SERNO");
|
serno = gen.GetValue("SERNO");
|
||||||
|
@ -328,7 +328,7 @@ bool CBoot::SetupWiiMemory()
|
||||||
static void WriteEmptyPlayRecord()
|
static void WriteEmptyPlayRecord()
|
||||||
{
|
{
|
||||||
const std::string file_path =
|
const std::string file_path =
|
||||||
Common::GetTitleDataPath(Titles::SYSTEM_MENU, Common::FROM_SESSION_ROOT) + "play_rec.dat";
|
Common::GetTitleDataPath(Titles::SYSTEM_MENU, Common::FROM_SESSION_ROOT) + "/play_rec.dat";
|
||||||
File::IOFile playrec_file(file_path, "r+b");
|
File::IOFile playrec_file(file_path, "r+b");
|
||||||
std::vector<u8> empty_record(0x80);
|
std::vector<u8> empty_record(0x80);
|
||||||
playrec_file.WriteBytes(empty_record.data(), empty_record.size());
|
playrec_file.WriteBytes(empty_record.data(), empty_record.size());
|
||||||
|
|
|
@ -181,7 +181,7 @@ void CWiiSaveCrypted::ReadHDR()
|
||||||
m_valid = false;
|
m_valid = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::string banner_file_path = m_wii_title_path + "banner.bin";
|
std::string banner_file_path = m_wii_title_path + "/banner.bin";
|
||||||
if (!File::Exists(banner_file_path) ||
|
if (!File::Exists(banner_file_path) ||
|
||||||
AskYesNoT("%s already exists. Consider making a backup of the current save files before "
|
AskYesNoT("%s already exists. Consider making a backup of the current save files before "
|
||||||
"overwriting.\nOverwrite now?",
|
"overwriting.\nOverwrite now?",
|
||||||
|
@ -203,7 +203,7 @@ void CWiiSaveCrypted::WriteHDR()
|
||||||
return;
|
return;
|
||||||
memset(&m_header, 0, HEADER_SZ);
|
memset(&m_header, 0, HEADER_SZ);
|
||||||
|
|
||||||
std::string banner_file_path = m_wii_title_path + "banner.bin";
|
std::string banner_file_path = m_wii_title_path + "/banner.bin";
|
||||||
u32 banner_size = static_cast<u32>(File::GetSize(banner_file_path));
|
u32 banner_size = static_cast<u32>(File::GetSize(banner_file_path));
|
||||||
m_header.hdr.BannerSize = Common::swap32(banner_size);
|
m_header.hdr.BannerSize = Common::swap32(banner_size);
|
||||||
|
|
||||||
|
@ -347,7 +347,7 @@ void CWiiSaveCrypted::ImportWiiSaveFiles()
|
||||||
// Special characters in path components will be escaped such as /../
|
// Special characters in path components will be escaped such as /../
|
||||||
std::string file_path = Common::EscapePath(reinterpret_cast<const char*>(file_hdr_tmp.name));
|
std::string file_path = Common::EscapePath(reinterpret_cast<const char*>(file_hdr_tmp.name));
|
||||||
|
|
||||||
std::string file_path_full = m_wii_title_path + file_path;
|
std::string file_path_full = m_wii_title_path + '/' + file_path;
|
||||||
File::CreateFullPath(file_path_full);
|
File::CreateFullPath(file_path_full);
|
||||||
const File::FileInfo file_info(file_path_full);
|
const File::FileInfo file_info(file_path_full);
|
||||||
if (file_hdr_tmp.type == 1)
|
if (file_hdr_tmp.type == 1)
|
||||||
|
@ -585,7 +585,7 @@ bool CWiiSaveCrypted::getPaths(bool for_export)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!File::Exists(m_wii_title_path + "banner.bin"))
|
if (!File::Exists(m_wii_title_path + "/banner.bin"))
|
||||||
{
|
{
|
||||||
m_valid = false;
|
m_valid = false;
|
||||||
ERROR_LOG(CONSOLE, "No banner file found for title %s", game_id);
|
ERROR_LOG(CONSOLE, "No banner file found for title %s", game_id);
|
||||||
|
|
|
@ -42,7 +42,8 @@ static IOS::ES::TMDReader FindTMD(u64 title_id, const std::string& tmd_path)
|
||||||
|
|
||||||
IOS::ES::TMDReader ES::FindImportTMD(u64 title_id) const
|
IOS::ES::TMDReader ES::FindImportTMD(u64 title_id) const
|
||||||
{
|
{
|
||||||
return FindTMD(title_id, Common::GetImportTitlePath(title_id) + "/content/title.tmd");
|
return FindTMD(title_id, Common::GetImportTitlePath(title_id, Common::FROM_SESSION_ROOT) +
|
||||||
|
"/content/title.tmd");
|
||||||
}
|
}
|
||||||
|
|
||||||
IOS::ES::TMDReader ES::FindInstalledTMD(u64 title_id) const
|
IOS::ES::TMDReader ES::FindInstalledTMD(u64 title_id) const
|
||||||
|
@ -218,7 +219,8 @@ bool ES::InitImport(u64 title_id)
|
||||||
// IOS moves the title content directory to /import if the TMD exists during an import.
|
// IOS moves the title content directory to /import if the TMD exists during an import.
|
||||||
if (File::Exists(Common::GetTMDFileName(title_id, Common::FROM_SESSION_ROOT)))
|
if (File::Exists(Common::GetTMDFileName(title_id, Common::FROM_SESSION_ROOT)))
|
||||||
{
|
{
|
||||||
const std::string import_content_dir = Common::GetImportTitlePath(title_id) + "/content";
|
const std::string import_content_dir =
|
||||||
|
Common::GetImportTitlePath(title_id, Common::FROM_SESSION_ROOT) + "/content";
|
||||||
File::CreateFullPath(import_content_dir);
|
File::CreateFullPath(import_content_dir);
|
||||||
if (!File::Rename(content_dir, import_content_dir))
|
if (!File::Rename(content_dir, import_content_dir))
|
||||||
{
|
{
|
||||||
|
@ -233,7 +235,8 @@ bool ES::InitImport(u64 title_id)
|
||||||
bool ES::FinishImport(const IOS::ES::TMDReader& tmd)
|
bool ES::FinishImport(const IOS::ES::TMDReader& tmd)
|
||||||
{
|
{
|
||||||
const u64 title_id = tmd.GetTitleId();
|
const u64 title_id = tmd.GetTitleId();
|
||||||
const std::string import_content_dir = Common::GetImportTitlePath(title_id) + "/content";
|
const std::string import_content_dir =
|
||||||
|
Common::GetImportTitlePath(title_id, Common::FROM_SESSION_ROOT) + "/content";
|
||||||
|
|
||||||
// Remove everything not listed in the TMD.
|
// Remove everything not listed in the TMD.
|
||||||
std::unordered_set<std::string> expected_entries = {"title.tmd"};
|
std::unordered_set<std::string> expected_entries = {"title.tmd"};
|
||||||
|
@ -274,7 +277,8 @@ bool ES::WriteImportTMD(const IOS::ES::TMDReader& tmd)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string dest = Common::GetImportTitlePath(tmd.GetTitleId()) + "/content/title.tmd";
|
const std::string dest = Common::GetImportTitlePath(tmd.GetTitleId(), Common::FROM_SESSION_ROOT) +
|
||||||
|
"/content/title.tmd";
|
||||||
return File::Rename(tmd_path, dest);
|
return File::Rename(tmd_path, dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,7 +286,8 @@ void ES::FinishStaleImport(u64 title_id)
|
||||||
{
|
{
|
||||||
const auto import_tmd = FindImportTMD(title_id);
|
const auto import_tmd = FindImportTMD(title_id);
|
||||||
if (!import_tmd.IsValid())
|
if (!import_tmd.IsValid())
|
||||||
File::DeleteDirRecursively(Common::GetImportTitlePath(title_id) + "/content");
|
File::DeleteDirRecursively(Common::GetImportTitlePath(title_id, Common::FROM_SESSION_ROOT) +
|
||||||
|
"/content");
|
||||||
else
|
else
|
||||||
FinishImport(import_tmd);
|
FinishImport(import_tmd);
|
||||||
}
|
}
|
||||||
|
@ -305,7 +310,7 @@ std::string ES::GetContentPath(const u64 title_id, const IOS::ES::Content& conte
|
||||||
return content_map.GetFilenameFromSHA1(content.sha1).value_or("");
|
return content_map.GetFilenameFromSHA1(content.sha1).value_or("");
|
||||||
|
|
||||||
return Common::GetTitleContentPath(title_id, Common::FROM_SESSION_ROOT) +
|
return Common::GetTitleContentPath(title_id, Common::FROM_SESSION_ROOT) +
|
||||||
StringFromFormat("%08x.app", content.id);
|
StringFromFormat("/%08x.app", content.id);
|
||||||
}
|
}
|
||||||
} // namespace Device
|
} // namespace Device
|
||||||
} // namespace HLE
|
} // namespace HLE
|
||||||
|
|
|
@ -342,7 +342,8 @@ static bool CheckIfContentHashMatches(const std::vector<u8>& content, const IOS:
|
||||||
|
|
||||||
static std::string GetImportContentPath(u64 title_id, u32 content_id)
|
static std::string GetImportContentPath(u64 title_id, u32 content_id)
|
||||||
{
|
{
|
||||||
return Common::GetImportTitlePath(title_id) + StringFromFormat("/content/%08x.app", content_id);
|
return Common::GetImportTitlePath(title_id, Common::FROM_SESSION_ROOT) +
|
||||||
|
StringFromFormat("/content/%08x.app", content_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnCode ES::ImportContentEnd(Context& context, u32 content_fd)
|
ReturnCode ES::ImportContentEnd(Context& context, u32 content_fd)
|
||||||
|
@ -604,7 +605,7 @@ ReturnCode ES::DeleteContent(u64 title_id, u32 content_id) const
|
||||||
return ES_EINVAL;
|
return ES_EINVAL;
|
||||||
|
|
||||||
if (!File::Delete(Common::GetTitleContentPath(title_id, Common::FROM_SESSION_ROOT) +
|
if (!File::Delete(Common::GetTitleContentPath(title_id, Common::FROM_SESSION_ROOT) +
|
||||||
StringFromFormat("%08x.app", content_id)))
|
StringFromFormat("/%08x.app", content_id)))
|
||||||
{
|
{
|
||||||
return FS_ENOENT;
|
return FS_ENOENT;
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,8 @@ IPCCommandResult NetKDRequest::IOCtl(const IOCtlRequest& request)
|
||||||
if (config.CreationStage() == NWC24::NWC24Config::NWC24_IDCS_INITIAL)
|
if (config.CreationStage() == NWC24::NWC24Config::NWC24_IDCS_INITIAL)
|
||||||
{
|
{
|
||||||
const std::string settings_file_path(
|
const std::string settings_file_path(
|
||||||
Common::GetTitleDataPath(Titles::SYSTEM_MENU, Common::FROM_SESSION_ROOT) + WII_SETTING);
|
Common::GetTitleDataPath(Titles::SYSTEM_MENU, Common::FROM_SESSION_ROOT) +
|
||||||
|
"/" WII_SETTING);
|
||||||
SettingsHandler gen;
|
SettingsHandler gen;
|
||||||
std::string area, model;
|
std::string area, model;
|
||||||
bool got_settings = false;
|
bool got_settings = false;
|
||||||
|
|
|
@ -1384,8 +1384,8 @@ void GetSettings()
|
||||||
if (SConfig::GetInstance().bWii)
|
if (SConfig::GetInstance().bWii)
|
||||||
{
|
{
|
||||||
u64 title_id = SConfig::GetInstance().GetTitleID();
|
u64 title_id = SConfig::GetInstance().GetTitleID();
|
||||||
s_bClearSave =
|
s_bClearSave = !File::Exists(Common::GetTitleDataPath(title_id, Common::FROM_SESSION_ROOT) +
|
||||||
!File::Exists(Common::GetTitleDataPath(title_id, Common::FROM_SESSION_ROOT) + "banner.bin");
|
"/banner.bin");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,7 +36,7 @@ static void InitializeDeterministicWiiSaves()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO: Check for the actual save data
|
// TODO: Check for the actual save data
|
||||||
Movie::SetClearSave(!File::Exists(user_save_path + "banner.bin"));
|
Movie::SetClearSave(!File::Exists(user_save_path + "/banner.bin"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ static void InitializeDeterministicWiiSaves()
|
||||||
(Movie::IsMovieActive() && !Movie::IsStartingFromClearSave()))
|
(Movie::IsMovieActive() && !Movie::IsStartingFromClearSave()))
|
||||||
{
|
{
|
||||||
// Copy the current user's save to the Blank NAND
|
// Copy the current user's save to the Blank NAND
|
||||||
if (File::Exists(user_save_path + "banner.bin"))
|
if (File::Exists(user_save_path + "/banner.bin"))
|
||||||
{
|
{
|
||||||
File::CopyDir(user_save_path, save_path);
|
File::CopyDir(user_save_path, save_path);
|
||||||
}
|
}
|
||||||
|
@ -86,10 +86,10 @@ void ShutdownWiiRoot()
|
||||||
std::string user_backup_path = File::GetUserPath(D_BACKUP_IDX) +
|
std::string user_backup_path = File::GetUserPath(D_BACKUP_IDX) +
|
||||||
StringFromFormat("%08x/%08x/", static_cast<u32>(title_id >> 32),
|
StringFromFormat("%08x/%08x/", static_cast<u32>(title_id >> 32),
|
||||||
static_cast<u32>(title_id));
|
static_cast<u32>(title_id));
|
||||||
if (File::Exists(save_path + "banner.bin") && SConfig::GetInstance().bEnableMemcardSdWriting)
|
if (File::Exists(save_path + "/banner.bin") && SConfig::GetInstance().bEnableMemcardSdWriting)
|
||||||
{
|
{
|
||||||
// Backup the existing save just in case it's still needed.
|
// Backup the existing save just in case it's still needed.
|
||||||
if (File::Exists(user_save_path + "banner.bin"))
|
if (File::Exists(user_save_path + "/banner.bin"))
|
||||||
{
|
{
|
||||||
if (File::Exists(user_backup_path))
|
if (File::Exists(user_backup_path))
|
||||||
File::DeleteDirRecursively(user_backup_path);
|
File::DeleteDirRecursively(user_backup_path);
|
||||||
|
|
|
@ -25,7 +25,8 @@ constexpr unsigned int ICON_HEIGHT = 48;
|
||||||
constexpr unsigned int ICON_SIZE = ICON_WIDTH * ICON_HEIGHT * 2;
|
constexpr unsigned int ICON_SIZE = ICON_WIDTH * ICON_HEIGHT * 2;
|
||||||
|
|
||||||
WiiSaveBanner::WiiSaveBanner(u64 title_id)
|
WiiSaveBanner::WiiSaveBanner(u64 title_id)
|
||||||
: WiiSaveBanner(Common::GetTitleDataPath(title_id, Common::FROM_CONFIGURED_ROOT) + "banner.bin")
|
: WiiSaveBanner(Common::GetTitleDataPath(title_id, Common::FROM_CONFIGURED_ROOT) +
|
||||||
|
"/banner.bin")
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue