Files
AnyPS5/core/libs/tests/GuestAppInfo.cpp
T
Scott SchusterandClaude Opus 5.5 3f32c12fd8 fix(libkernel): read sceKernelGetAppInfo's title id through AppMetadata
The title id now comes from GetAppTitleId, so param.json has one parser and
its validation (a missing file or titleId throws). Another pid returns EPERM
and a null info returns 0 without writing, as shadPS4 does. app_id stays 1,
the running title's id in libSceSystemService, which is inferred, not
measured.

guest_app_info runs the call without param.json in a child process and
checks that the field between app_id and the title id is left alone.

AI-assisted (Claude Code).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 03:11:18 +02:00

69 lines
2.6 KiB
C++

#include "SceTypes.hpp"
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <string>
#include <string_view>
extern "C" {
int APS5_VABI sceKernelGetAppInfo(int pid, void* info);
int APS5_VABI getpid_nid_postfix(void);
}
static constexpr int SCE_KERNEL_ERROR_EPERM = static_cast<int>(0x80020001);
static void Require(bool value) { if (!value) std::abort(); }
struct AppInfo {
std::uint32_t appId;
std::uint64_t unknown1;
char titleId[14];
char unknown2[0x3c];
};
static std::filesystem::path EnterTemporaryRoot(std::string_view name) {
const auto root = std::filesystem::temp_directory_path() / (std::string(name) + std::to_string(getpid_nid_postfix()));
std::filesystem::remove_all(root);
std::filesystem::create_directories(root);
std::filesystem::current_path(root);
return root;
}
int main(int argc, char** argv) {
if (argc == 3 && std::string_view(argv[1]) == "--without-param-json") {
std::filesystem::current_path(argv[2]);
AppInfo info{};
sceKernelGetAppInfo(getpid_nid_postfix(), &info);
return 0;
}
const auto previous = std::filesystem::current_path();
const auto root = EnterTemporaryRoot("guest_app_info_");
std::filesystem::create_directories(root / "app0" / "sce_sys");
std::ofstream(root / "app0" / "sce_sys" / "param.json") << "{\n \"contentId\": \"IV0000-PPSA97358_00-XPSEMU0000000000\",\n \"localizedParameters\": {\"defaultLanguage\": \"en-US\", \"en-US\": {\"titleName\": \"XPSemu\"}},\n \"titleId\": \"PPSA97358\"\n}\n";
AppInfo info;
std::memset(&info, 0x5a, sizeof(info));
const int self = getpid_nid_postfix();
Require(sceKernelGetAppInfo(self, &info) == 0);
Require(info.appId == 1);
Require(std::strcmp(info.titleId, "PPSA97358") == 0);
Require(info.unknown1 == 0x5a5a5a5a5a5a5a5aull);
Require(info.unknown2[0] == 0x5a);
Require(sceKernelGetAppInfo(self + 1, &info) == SCE_KERNEL_ERROR_EPERM);
Require(sceKernelGetAppInfo(self, nullptr) == 0);
std::filesystem::current_path(previous);
std::filesystem::remove_all(root);
const auto empty = std::filesystem::temp_directory_path() / ("guest_app_info_empty_" + std::to_string(self));
std::filesystem::remove_all(empty);
std::filesystem::create_directories(empty);
const std::string arguments = "\"" + std::string(argv[0]) + "\" --without-param-json \"" + empty.string() + "\"";
#ifdef _WIN32
const std::string command = "\"" + arguments + "\"";
#else
const std::string command = arguments;
#endif
Require(std::system(command.c_str()) != 0);
std::filesystem::remove_all(empty);
}