diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index 75cb1116..bde1611c 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -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& args) { diff --git a/src/Cafe/CafeSystem.h b/src/Cafe/CafeSystem.h index 336c2f40..c4043a59 100644 --- a/src/Cafe/CafeSystem.h +++ b/src/Cafe/CafeSystem.h @@ -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(); diff --git a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp index 1e6eb92b..a007f5ee 100644 --- a/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp +++ b/src/Cafe/OS/libs/coreinit/coreinit_FS.cpp @@ -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(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; diff --git a/src/Cafe/TitleList/TitleInfo.cpp b/src/Cafe/TitleList/TitleInfo.cpp index d23e1d0a..6d21929e 100644 --- a/src/Cafe/TitleList/TitleInfo.cpp +++ b/src/Cafe/TitleList/TitleInfo.cpp @@ -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 #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(ConvertString(permissionNode.child("group").text().as_string(), 10)); + parsedCos->permissions[permissionIndex].mask = static_cast(ConvertString(permissionNode.child("mask").text().as_string(), 16)); + } + + return parsedCos; +} \ No newline at end of file diff --git a/src/Cafe/TitleList/TitleInfo.h b/src/Cafe/TitleList/TitleInfo.h index eca6624d..e9347db7 100644 --- a/src/Cafe/TitleList/TitleInfo.h +++ b/src/Cafe/TitleList/TitleInfo.h @@ -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/.../..."