mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
c481b6cd4e
recomp-ui takes a host-supplied icon path (GameInfo.window_icon_path) so the launcher window carries the same art as the game window. Publish the path psx_apply_window_icon() already resolves, rather than letting the launcher repeat the search — repeating it is what would let the two drift apart the next time the candidate list changes. Resolved once and cached: both the game window and the launcher ask, and the answer cannot change within a run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0194TYJAqgyG9a9PJe9uUcgQ
144 lines
3.8 KiB
C++
144 lines
3.8 KiB
C++
/* psx_window_icon.cpp — stage-time PNG → SDL_SetWindowIcon (Win/Linux/macOS). */
|
|
|
|
#include "psx_window_icon.h"
|
|
#include "psx_sdl.h"
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <filesystem>
|
|
#include <string>
|
|
|
|
#if defined(_WIN32)
|
|
#ifndef NOMINMAX
|
|
#define NOMINMAX
|
|
#endif
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
#define STBI_ONLY_PNG
|
|
#define STBI_NO_STDIO
|
|
#include "../third_party/stb_image.h"
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
static fs::path exe_dir_from_argv0(const char *argv0)
|
|
{
|
|
fs::path exe_dir;
|
|
std::error_code ec;
|
|
#if defined(_WIN32)
|
|
wchar_t buf[MAX_PATH];
|
|
DWORD n = GetModuleFileNameW(NULL, buf, (DWORD)(sizeof(buf) / sizeof(buf[0])));
|
|
if (n > 0 && n < (DWORD)(sizeof(buf) / sizeof(buf[0])))
|
|
exe_dir = fs::path(std::wstring(buf, buf + n)).parent_path();
|
|
#endif
|
|
if (exe_dir.empty()) {
|
|
const char *appimg = std::getenv("APPIMAGE");
|
|
if (appimg && appimg[0]) {
|
|
exe_dir = fs::absolute(appimg, ec).parent_path();
|
|
if (ec) exe_dir.clear();
|
|
}
|
|
}
|
|
if (exe_dir.empty() && argv0 && argv0[0]) {
|
|
exe_dir = fs::absolute(argv0, ec).parent_path();
|
|
if (ec) exe_dir.clear();
|
|
}
|
|
if (exe_dir.empty()) {
|
|
const char *base = SDL_GetBasePath();
|
|
if (base) {
|
|
exe_dir = fs::path(base);
|
|
SDL_free((void *)base);
|
|
}
|
|
}
|
|
if (exe_dir.empty()) exe_dir = fs::path(".");
|
|
return exe_dir;
|
|
}
|
|
|
|
static fs::path find_icon_png(const char *argv0)
|
|
{
|
|
const fs::path exe_dir = exe_dir_from_argv0(argv0);
|
|
const fs::path candidates[] = {
|
|
exe_dir / "assets" / "psxrecomp.png",
|
|
exe_dir / "psxrecomp.png",
|
|
exe_dir / ".." / "assets" / "psxrecomp.png",
|
|
};
|
|
for (const fs::path &p : candidates) {
|
|
std::error_code ec;
|
|
if (fs::is_regular_file(p, ec)) return p;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/* Resolve once and keep it: both the game window and the launcher ask, and
|
|
* the answer cannot change within a run. */
|
|
extern "C" const char *psx_window_icon_path(const char *argv0)
|
|
{
|
|
static std::string cached;
|
|
static bool resolved = false;
|
|
if (!resolved) {
|
|
cached = find_icon_png(argv0).string();
|
|
resolved = true;
|
|
}
|
|
return cached.c_str();
|
|
}
|
|
|
|
extern "C" void psx_apply_window_icon(SDL_Window *window, const char *argv0)
|
|
{
|
|
if (!window) return;
|
|
const fs::path png(psx_window_icon_path(argv0));
|
|
if (png.empty()) return;
|
|
|
|
FILE *f = std::fopen(png.string().c_str(), "rb");
|
|
if (!f) return;
|
|
if (std::fseek(f, 0, SEEK_END) != 0) {
|
|
std::fclose(f);
|
|
return;
|
|
}
|
|
long sz = std::ftell(f);
|
|
if (sz <= 0) {
|
|
std::fclose(f);
|
|
return;
|
|
}
|
|
if (std::fseek(f, 0, SEEK_SET) != 0) {
|
|
std::fclose(f);
|
|
return;
|
|
}
|
|
std::string buf(static_cast<size_t>(sz), '\0');
|
|
if (std::fread(buf.data(), 1, buf.size(), f) != buf.size()) {
|
|
std::fclose(f);
|
|
return;
|
|
}
|
|
std::fclose(f);
|
|
|
|
int w = 0, h = 0, comp = 0;
|
|
stbi_uc *pixels = stbi_load_from_memory(
|
|
reinterpret_cast<const stbi_uc *>(buf.data()), (int)buf.size(), &w, &h, &comp, 4);
|
|
if (!pixels || w <= 0 || h <= 0) {
|
|
if (pixels) stbi_image_free(pixels);
|
|
return;
|
|
}
|
|
|
|
#if defined(PSX_SDL3)
|
|
SDL_Surface *surf =
|
|
SDL_CreateSurfaceFrom(w, h, SDL_PIXELFORMAT_RGBA32, pixels, w * 4);
|
|
if (surf) {
|
|
SDL_SetWindowIcon(window, surf);
|
|
SDL_DestroySurface(surf);
|
|
}
|
|
#else
|
|
SDL_Surface *surf = SDL_CreateRGBSurfaceFrom(
|
|
pixels, w, h, 32, w * 4,
|
|
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
|
0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff
|
|
#else
|
|
0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
|
|
#endif
|
|
);
|
|
if (surf) {
|
|
SDL_SetWindowIcon(window, surf);
|
|
SDL_FreeSurface(surf);
|
|
}
|
|
#endif
|
|
stbi_image_free(pixels);
|
|
}
|