coreinit: Handle SD mounting permission in FSGetMountSource

One Piece requires this to not get stuck in an infinite loop on boot.

This also sets up initial infrastructure for handling cos.xml permissions
This commit is contained in:
Exzap 2024-04-06 22:18:30 +02:00
parent fde7230191
commit 74e8d205b0
5 changed files with 157 additions and 22 deletions

View File

@ -914,6 +914,27 @@ namespace CafeSystem
return sGameInfo_ForegroundTitle.GetBase().GetArgStr();
}
CosCapabilityBits GetForegroundTitleCosCapabilities(CosCapabilityGroup group)
{
if (sLaunchModeIsStandalone)
return CosCapabilityBits::All;
auto& update = sGameInfo_ForegroundTitle.GetUpdate();
if (update.IsValid())
{
ParsedCosXml* cosXml = update.GetCosInfo();
if (cosXml)
return cosXml->GetCapabilityBits(group);
}
auto& base = sGameInfo_ForegroundTitle.GetBase();
if(base.IsValid())
{
ParsedCosXml* cosXml = base.GetCosInfo();
if (cosXml)
return cosXml->GetCapabilityBits(group);
}
return CosCapabilityBits::All;
}
// when switching titles custom parameters can be passed, returns true if override args are used
bool GetOverrideArgStr(std::vector<std::string>& args)
{

View File

@ -4,6 +4,9 @@
#include "Cafe/TitleList/TitleId.h"
#include "config/CemuConfig.h"
enum class CosCapabilityBits : uint64;
enum class CosCapabilityGroup : uint32;
namespace CafeSystem
{
class SystemImplementation
@ -41,6 +44,7 @@ namespace CafeSystem
std::string GetForegroundTitleName();
std::string GetForegroundTitleArgStr();
uint32 GetForegroundTitleOlvAccesskey();
CosCapabilityBits GetForegroundTitleCosCapabilities(CosCapabilityGroup group);
void ShutdownTitle();

View File

@ -11,6 +11,8 @@
#include "coreinit_IPC.h"
#include "Cafe/Filesystem/fsc.h"
#include "coreinit_IPCBuf.h"
#include "Cafe/CafeSystem.h"
#include "Cafe/TitleList/TitleInfo.h"
#define FS_CB_PLACEHOLDER_FINISHCMD (MPTR)(0xF122330E)
@ -94,6 +96,14 @@ namespace coreinit
// so we can just hard code it. Other mount types are not (yet) supported.
if (mountSourceType == MOUNT_TYPE::SD)
{
// check for SD card permissions (from cos.xml)
// One Piece relies on failing here, otherwise it will call FSGetMountSource in an infinite loop
CosCapabilityBitsFS perms = static_cast<CosCapabilityBitsFS>(CafeSystem::GetForegroundTitleCosCapabilities(CosCapabilityGroup::FS));
if(!HAS_FLAG(perms, CosCapabilityBitsFS::SDCARD_MOUNT))
{
cemuLog_logOnce(LogType::Force, "Title is trying to access SD card mount info without having SD card permissions. This may not be a bug");
return FS_RESULT::END_ITERATION;
}
mountSourceInfo->sourceType = 0;
strcpy(mountSourceInfo->path, "/sd");
return FS_RESULT::SUCCESS;

View File

@ -1,13 +1,11 @@
#include "TitleInfo.h"
#include "Cafe/Filesystem/fscDeviceHostFS.h"
#include "Cafe/Filesystem/FST/FST.h"
#include "pugixml.hpp"
#include "Common/FileStream.h"
#include <zarchive/zarchivereader.h>
#include "config/ActiveSettings.h"
#include "util/helpers/helpers.h"
// detect format by reading file header/footer
CafeTitleFileType DetermineCafeSystemFileType(fs::path filePath)
@ -709,10 +707,41 @@ std::string TitleInfo::GetInstallPath() const
{
TitleId titleId = GetAppTitleId();
TitleIdParser tip(titleId);
std::string tmp;
std::string tmp;
if (tip.IsSystemTitle())
tmp = fmt::format("sys/title/{:08x}/{:08x}", GetTitleIdHigh(titleId), GetTitleIdLow(titleId));
else
tmp = fmt::format("usr/title/{:08x}/{:08x}", GetTitleIdHigh(titleId), GetTitleIdLow(titleId));
tmp = fmt::format("sys/title/{:08x}/{:08x}", GetTitleIdHigh(titleId), GetTitleIdLow(titleId));
else
tmp = fmt::format("usr/title/{:08x}/{:08x}", GetTitleIdHigh(titleId), GetTitleIdLow(titleId));
return tmp;
}
ParsedCosXml* ParsedCosXml::Parse(uint8* xmlData, size_t xmlLen)
{
pugi::xml_document app_doc;
if (!app_doc.load_buffer_inplace(xmlData, xmlLen))
return nullptr;
const auto root = app_doc.child("app");
if (!root)
return nullptr;
ParsedCosXml* parsedCos = new ParsedCosXml();
auto node = root.child("argstr");
if (node)
parsedCos->argstr = node.text().as_string();
// parse permissions
auto permissionsNode = root.child("permissions");
for(uint32 permissionIndex = 0; permissionIndex < 19; ++permissionIndex)
{
std::string permissionName = fmt::format("p{}", permissionIndex);
auto permissionNode = permissionsNode.child(permissionName.c_str());
if (!permissionNode)
break;
parsedCos->permissions[permissionIndex].group = static_cast<CosCapabilityGroup>(ConvertString<uint32>(permissionNode.child("group").text().as_string(), 10));
parsedCos->permissions[permissionIndex].mask = static_cast<CosCapabilityBits>(ConvertString<uint64>(permissionNode.child("mask").text().as_string(), 16));
}
return parsedCos;
}

View File

@ -26,29 +26,95 @@ struct ParsedAppXml
uint32 sdk_version;
};
enum class CosCapabilityGroup : uint32
{
None = 0,
BSP = 1,
DK = 3,
USB = 9,
UHS = 12,
FS = 11,
MCP = 13,
NIM = 14,
ACT = 15,
FPD = 16,
BOSS = 17,
ACP = 18,
PDM = 19,
AC = 20,
NDM = 21,
NSEC = 22
};
enum class CosCapabilityBits : uint64
{
All = 0xFFFFFFFFFFFFFFFFull
};
enum class CosCapabilityBitsFS : uint64
{
ODD_READ = (1llu << 0),
ODD_WRITE = (1llu << 1),
ODD_RAW_OPEN = (1llu << 2),
ODD_MOUNT = (1llu << 3),
SLCCMPT_READ = (1llu << 4),
SLCCMPT_WRITE = (1llu << 5),
SLCCMPT_RAW_OPEN = (1llu << 6),
SLCCMPT_MOUNT = (1llu << 7),
SLC_READ = (1llu << 8),
SLC_WRITE = (1llu << 9),
SLC_RAW_OPEN = (1llu << 10),
SLC_MOUNT = (1llu << 11),
MLC_READ = (1llu << 12),
MLC_WRITE = (1llu << 13),
MLC_RAW_OPEN = (1llu << 14),
MLC_MOUNT = (1llu << 15),
SDCARD_READ = (1llu << 16),
SDCARD_WRITE = (1llu << 17),
SDCARD_RAW_OPEN = (1llu << 18),
SDCARD_MOUNT = (1llu << 19),
HFIO_READ = (1llu << 20),
HFIO_WRITE = (1llu << 21),
HFIO_RAW_OPEN = (1llu << 22),
HFIO_MOUNT = (1llu << 23),
RAMDISK_READ = (1llu << 24),
RAMDISK_WRITE = (1llu << 25),
RAMDISK_RAW_OPEN = (1llu << 26),
RAMDISK_MOUNT = (1llu << 27),
USB_READ = (1llu << 28),
USB_WRITE = (1llu << 29),
USB_RAW_OPEN = (1llu << 30),
USB_MOUNT = (1llu << 31),
OTHER_READ = (1llu << 32),
OTHER_WRITE = (1llu << 33),
OTHER_RAW_OPEN = (1llu << 34),
OTHER_MOUNT = (1llu << 35)
};
ENABLE_BITMASK_OPERATORS(CosCapabilityBitsFS);
struct ParsedCosXml
{
public:
std::string argstr;
static ParsedCosXml* Parse(uint8* xmlData, size_t xmlLen)
struct Permission
{
pugi::xml_document app_doc;
if (!app_doc.load_buffer_inplace(xmlData, xmlLen))
return nullptr;
CosCapabilityGroup group{CosCapabilityGroup::None};
CosCapabilityBits mask{CosCapabilityBits::All};
};
Permission permissions[19]{};
const auto root = app_doc.child("app");
if (!root)
return nullptr;
static ParsedCosXml* Parse(uint8* xmlData, size_t xmlLen);
ParsedCosXml* parsedCos = new ParsedCosXml();
for (const auto& child : root.children())
CosCapabilityBits GetCapabilityBits(CosCapabilityGroup group) const
{
for (const auto& perm : permissions)
{
std::string_view name = child.name();
if (name == "argstr")
parsedCos->argstr = child.text().as_string();
if (perm.group == group)
return perm.mask;
}
return parsedCos;
return CosCapabilityBits::All;
}
};
@ -151,7 +217,7 @@ public:
// cos.xml
std::string GetArgStr() const;
// meta.xml also contains a version which seems to match the one from app.xml
// meta.xml also contains a version field which seems to match the one from app.xml
// the titleId in meta.xml seems to be the title id of the base game for updates specifically. For AOC content it's the AOC's titleId
TitleIdParser::TITLE_TYPE GetTitleType();
@ -160,6 +226,11 @@ public:
return m_parsedMetaXml;
}
ParsedCosXml* GetCosInfo()
{
return m_parsedCosXml;
}
std::string GetPrintPath() const; // formatted path including type and WUA subpath. Intended for logging and user-facing information
std::string GetInstallPath() const; // installation subpath, relative to storage base. E.g. "usr/title/.../..." or "sys/title/.../..."