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(); 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 // when switching titles custom parameters can be passed, returns true if override args are used
bool GetOverrideArgStr(std::vector<std::string>& args) bool GetOverrideArgStr(std::vector<std::string>& args)
{ {

View File

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

View File

@ -11,6 +11,8 @@
#include "coreinit_IPC.h" #include "coreinit_IPC.h"
#include "Cafe/Filesystem/fsc.h" #include "Cafe/Filesystem/fsc.h"
#include "coreinit_IPCBuf.h" #include "coreinit_IPCBuf.h"
#include "Cafe/CafeSystem.h"
#include "Cafe/TitleList/TitleInfo.h"
#define FS_CB_PLACEHOLDER_FINISHCMD (MPTR)(0xF122330E) #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. // so we can just hard code it. Other mount types are not (yet) supported.
if (mountSourceType == MOUNT_TYPE::SD) 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; mountSourceInfo->sourceType = 0;
strcpy(mountSourceInfo->path, "/sd"); strcpy(mountSourceInfo->path, "/sd");
return FS_RESULT::SUCCESS; return FS_RESULT::SUCCESS;

View File

@ -1,13 +1,11 @@
#include "TitleInfo.h" #include "TitleInfo.h"
#include "Cafe/Filesystem/fscDeviceHostFS.h" #include "Cafe/Filesystem/fscDeviceHostFS.h"
#include "Cafe/Filesystem/FST/FST.h" #include "Cafe/Filesystem/FST/FST.h"
#include "pugixml.hpp" #include "pugixml.hpp"
#include "Common/FileStream.h" #include "Common/FileStream.h"
#include <zarchive/zarchivereader.h> #include <zarchive/zarchivereader.h>
#include "config/ActiveSettings.h" #include "config/ActiveSettings.h"
#include "util/helpers/helpers.h"
// detect format by reading file header/footer // detect format by reading file header/footer
CafeTitleFileType DetermineCafeSystemFileType(fs::path filePath) CafeTitleFileType DetermineCafeSystemFileType(fs::path filePath)
@ -716,3 +714,34 @@ std::string TitleInfo::GetInstallPath() const
tmp = fmt::format("usr/title/{:08x}/{:08x}", GetTitleIdHigh(titleId), GetTitleIdLow(titleId)); tmp = fmt::format("usr/title/{:08x}/{:08x}", GetTitleIdHigh(titleId), GetTitleIdLow(titleId));
return tmp; 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; 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 struct ParsedCosXml
{ {
public:
std::string argstr; std::string argstr;
static ParsedCosXml* Parse(uint8* xmlData, size_t xmlLen) struct Permission
{ {
pugi::xml_document app_doc; CosCapabilityGroup group{CosCapabilityGroup::None};
if (!app_doc.load_buffer_inplace(xmlData, xmlLen)) CosCapabilityBits mask{CosCapabilityBits::All};
return nullptr; };
Permission permissions[19]{};
const auto root = app_doc.child("app"); static ParsedCosXml* Parse(uint8* xmlData, size_t xmlLen);
if (!root)
return nullptr;
ParsedCosXml* parsedCos = new ParsedCosXml(); CosCapabilityBits GetCapabilityBits(CosCapabilityGroup group) const
for (const auto& child : root.children())
{ {
std::string_view name = child.name(); for (const auto& perm : permissions)
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 // cos.xml
std::string GetArgStr() const; 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 // 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(); TitleIdParser::TITLE_TYPE GetTitleType();
@ -160,6 +226,11 @@ public:
return m_parsedMetaXml; 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 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/.../..." std::string GetInstallPath() const; // installation subpath, relative to storage base. E.g. "usr/title/.../..." or "sys/title/.../..."