feat(libkernel): add app metadata reading module

This commit is contained in:
boykopovar 2026-09-23 15:09:31 +03:00
parent 282257fd98
commit cf1953058b
5 changed files with 131 additions and 1 deletions

1
.gitignore vendored
View File

@ -28,6 +28,7 @@
*.out
*.app
_SaveData/
build/
b/
Build/

View File

@ -1,5 +1,6 @@
#include "prx/libSceVideoOut/include/DisplayWindow.hpp"
#include "prx/libSceAgcDriver/Execution/include/AspectFit.hpp"
#include "prx/libkernel/AppMetadata/include/AppMetadata.hpp"
#include "SDL_vulkan.h"
#include <stdexcept>
#include <string>
@ -42,7 +43,8 @@ void DisplayWindow::create(std::uint32_t sourceWidth, std::uint32_t sourceHeight
initialWidth = fitted.width;
initialHeight = fitted.height;
}
window = SDL_CreateWindow("PS5", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, static_cast<int>(initialWidth), static_cast<int>(initialHeight), SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
const auto title = GetAppTitle_nid_postfix();
window = SDL_CreateWindow(title.value, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, static_cast<int>(initialWidth), static_cast<int>(initialHeight), SDL_WINDOW_SHOWN | SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
require(window != nullptr, SDL_GetError());
SDL_SetWindowMinimumSize(window, static_cast<int>(DisplayWindowMinimumWidth), static_cast<int>(DisplayWindowMinimumHeight));
installSubclass();

View File

@ -0,0 +1,28 @@
#ifndef CORE_LIBS_PRX_LIBKERNEL_APPMETADATA_INCLUDE_APPMETADATA_HPP
#define CORE_LIBS_PRX_LIBKERNEL_APPMETADATA_INCLUDE_APPMETADATA_HPP
#include <cstdint>
struct AppTitle {
char value[128];
};
struct AppTitleId {
char value[12];
};
struct AppIconData {
const std::uint8_t* bytes;
std::uint64_t size;
};
extern "C" {
AppTitle GetAppTitle_nid_postfix();
AppTitleId GetAppTitleId_nid_postfix();
bool HasAppIcon_nid_postfix();
AppIconData GetAppIconData_nid_postfix();
}
#endif

View File

@ -0,0 +1,98 @@
#include "prx/libkernel/AppMetadata/include/AppMetadata.hpp"
#include "prx/libc/include/General.hpp"
#include <cstdint>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <stdexcept>
#include <string>
#include <vector>
namespace {
bool g_titleLoaded = false;
bool g_iconAttempted = false;
bool g_iconLoaded = false;
char g_title[128] = {};
char g_titleId[12] = {};
std::vector<std::uint8_t> g_iconBytes;
constexpr const char* AppMetadataParamJsonGuestPath = "/app0/sce_sys/param.json";
constexpr const char* AppMetadataIconGuestPath = "/app0/sce_sys/icon0.png";
struct ParsedParamJson {
std::string title;
std::string titleId;
};
ParsedParamJson parseParamJson(const std::filesystem::path& paramJsonPath) {
static_cast<void>(paramJsonPath);
throw std::runtime_error("param.json parsing is not implemented");
}
void copyToFixedBuffer(char* destination, std::size_t destinationSize, const std::string& source) {
if (source.empty()) throw std::runtime_error("source string is empty");
if (source.size() + 1 > destinationSize) throw std::length_error("source string does not fit into destination buffer");
std::memcpy(destination, source.c_str(), source.size() + 1);
}
void ensureTitleLoaded() {
if (g_titleLoaded) return;
const auto resolvedPath = ResolvePath_nid_no_patch(AppMetadataParamJsonGuestPath);
if (!std::filesystem::exists(resolvedPath)) throw std::runtime_error("param.json not found");
const auto parsed = parseParamJson(resolvedPath);
copyToFixedBuffer(g_title, sizeof(g_title), parsed.title);
copyToFixedBuffer(g_titleId, sizeof(g_titleId), parsed.titleId);
g_titleLoaded = true;
}
void ensureIconLoaded() {
if (g_iconAttempted) return;
g_iconAttempted = true;
const auto resolvedPath = ResolvePath_nid_no_patch(AppMetadataIconGuestPath);
if (!std::filesystem::exists(resolvedPath)) return;
std::ifstream file(resolvedPath, std::ios::binary);
if (!file.is_open()) throw std::runtime_error("failed to open icon0.png");
file.seekg(0, std::ios::end);
const auto fileSize = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<std::uint8_t> bytes(static_cast<std::size_t>(fileSize));
if (!bytes.empty()) file.read(reinterpret_cast<char*>(bytes.data()), static_cast<std::streamsize>(bytes.size()));
if (bytes.empty()) throw std::runtime_error("icon0.png is empty");
constexpr std::uint8_t PngSignature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
if (bytes.size() < 8 || std::memcmp(bytes.data(), PngSignature, sizeof(PngSignature)) != 0) throw std::runtime_error("icon0.png is not a valid PNG file");
g_iconBytes = std::move(bytes);
g_iconLoaded = true;
}
}
extern "C" {
AppTitle GetAppTitle_nid_postfix() {
ensureTitleLoaded();
AppTitle result{};
std::memcpy(result.value, g_title, sizeof(result.value));
return result;
}
AppTitleId GetAppTitleId_nid_postfix() {
ensureTitleLoaded();
AppTitleId result{};
std::memcpy(result.value, g_titleId, sizeof(result.value));
return result;
}
bool HasAppIcon_nid_postfix() {
ensureIconLoaded();
return g_iconLoaded;
}
AppIconData GetAppIconData_nid_postfix() {
ensureIconLoaded();
if (!g_iconLoaded) throw std::runtime_error("icon not available");
return AppIconData{ g_iconBytes.data(), g_iconBytes.size() };
}
}

View File

@ -33,6 +33,7 @@ add_library(libkernel SHARED EXCLUDE_FROM_ALL
${CMAKE_CURRENT_SOURCE_DIR}/System/src/Rtld.cpp
${CMAKE_CURRENT_SOURCE_DIR}/System/src/Process.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Time/Time.cpp
${CMAKE_CURRENT_SOURCE_DIR}/AppMetadata/src/AppMetadata.cpp
)
target_include_directories(libkernel PRIVATE ${LIBS_INCLUDE_DIR})