mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-23 10:35:03 +00:00
* Custom Trophy images * text and button - settings * Description * + * plural * translation for 'Trophy earned!' * Revert: translation for 'Trophy earned!' * play audio * fixes crash due to having too many trophies The game 'My Name is Mayo' has so many trophies in sequence that when overlapping them, the emulator ended up crashing, so if there is something on the screen and a new trophies are achieved, it will clear and show the new one. * Animations, config: position, duration * - * TR * fix sdl/qt * clang \O/ * Side menu with filter options. Sorting * +TR * fix showHiddenCheck * Time Unlocked * Fixes ghost text, larger image, black text in light theme * Button - Delete Trophy * limits the width of Description - showMaximized * changing column positions * useEuropeanDateFormat en_US, zh_CN, zh_TW, ja_JP, ko_KR, lt_LT, nb_NO, nl_NL useEuropeanDateFormat = false
226 lines
7.5 KiB
C++
226 lines
7.5 KiB
C++
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include <unordered_map>
|
|
#include "common/logging/log.h"
|
|
#include "common/path_util.h"
|
|
#include "common/scope_exit.h"
|
|
|
|
#ifdef __APPLE__
|
|
#include <CoreFoundation/CFBundle.h>
|
|
#include <dlfcn.h>
|
|
#include <sys/param.h>
|
|
#endif
|
|
|
|
#ifndef MAX_PATH
|
|
#ifdef _WIN32
|
|
// This is the maximum number of UTF-16 code units permissible in Windows file paths
|
|
#define MAX_PATH 260
|
|
#else
|
|
// This is the maximum number of UTF-8 code units permissible in all other OSes' file paths
|
|
#define MAX_PATH 1024
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef ENABLE_QT_GUI
|
|
#include <QString>
|
|
#endif
|
|
|
|
namespace Common::FS {
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
#ifdef __APPLE__
|
|
using IsTranslocatedURLFunc = Boolean (*)(CFURLRef path, bool* isTranslocated,
|
|
CFErrorRef* __nullable error);
|
|
using CreateOriginalPathForURLFunc = CFURLRef __nullable (*)(CFURLRef translocatedPath,
|
|
CFErrorRef* __nullable error);
|
|
|
|
static CFURLRef UntranslocateBundlePath(const CFURLRef bundle_path) {
|
|
if (void* security_handle =
|
|
dlopen("/System/Library/Frameworks/Security.framework/Security", RTLD_LAZY)) {
|
|
SCOPE_EXIT {
|
|
dlclose(security_handle);
|
|
};
|
|
|
|
const auto IsTranslocatedURL = reinterpret_cast<IsTranslocatedURLFunc>(
|
|
dlsym(security_handle, "SecTranslocateIsTranslocatedURL"));
|
|
const auto CreateOriginalPathForURL = reinterpret_cast<CreateOriginalPathForURLFunc>(
|
|
dlsym(security_handle, "SecTranslocateCreateOriginalPathForURL"));
|
|
|
|
bool is_translocated = false;
|
|
if (IsTranslocatedURL && CreateOriginalPathForURL &&
|
|
IsTranslocatedURL(bundle_path, &is_translocated, nullptr) && is_translocated) {
|
|
return CreateOriginalPathForURL(bundle_path, nullptr);
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
static std::filesystem::path GetBundleParentDirectory() {
|
|
if (CFBundleRef bundle_ref = CFBundleGetMainBundle()) {
|
|
if (CFURLRef bundle_url_ref = CFBundleCopyBundleURL(bundle_ref)) {
|
|
SCOPE_EXIT {
|
|
CFRelease(bundle_url_ref);
|
|
};
|
|
|
|
CFURLRef untranslocated_url_ref = UntranslocateBundlePath(bundle_url_ref);
|
|
SCOPE_EXIT {
|
|
if (untranslocated_url_ref) {
|
|
CFRelease(untranslocated_url_ref);
|
|
}
|
|
};
|
|
|
|
char app_bundle_path[MAXPATHLEN];
|
|
if (CFURLGetFileSystemRepresentation(
|
|
untranslocated_url_ref ? untranslocated_url_ref : bundle_url_ref, true,
|
|
reinterpret_cast<u8*>(app_bundle_path), sizeof(app_bundle_path))) {
|
|
std::filesystem::path bundle_path{app_bundle_path};
|
|
return bundle_path.parent_path();
|
|
}
|
|
}
|
|
}
|
|
return std::filesystem::current_path();
|
|
}
|
|
#endif
|
|
|
|
static auto UserPaths = [] {
|
|
#ifdef __APPLE__
|
|
// Set the current path to the directory containing the app bundle.
|
|
std::filesystem::current_path(GetBundleParentDirectory());
|
|
#endif
|
|
|
|
// Try the portable user directory first.
|
|
auto user_dir = std::filesystem::current_path() / PORTABLE_DIR;
|
|
if (!std::filesystem::exists(user_dir)) {
|
|
// If it doesn't exist, use the standard path for the platform instead.
|
|
// NOTE: On Windows we currently just create the portable directory instead.
|
|
#ifdef __APPLE__
|
|
user_dir =
|
|
std::filesystem::path(getenv("HOME")) / "Library" / "Application Support" / "shadPS4";
|
|
#elif defined(__linux__)
|
|
const char* xdg_data_home = getenv("XDG_DATA_HOME");
|
|
if (xdg_data_home != nullptr && strlen(xdg_data_home) > 0) {
|
|
user_dir = std::filesystem::path(xdg_data_home) / "shadPS4";
|
|
} else {
|
|
user_dir = std::filesystem::path(getenv("HOME")) / ".local" / "share" / "shadPS4";
|
|
}
|
|
#endif
|
|
}
|
|
|
|
std::unordered_map<PathType, fs::path> paths;
|
|
|
|
const auto create_path = [&](PathType shad_path, const fs::path& new_path) {
|
|
std::filesystem::create_directory(new_path);
|
|
paths.insert_or_assign(shad_path, new_path);
|
|
};
|
|
|
|
create_path(PathType::UserDir, user_dir);
|
|
create_path(PathType::LogDir, user_dir / LOG_DIR);
|
|
create_path(PathType::ScreenshotsDir, user_dir / SCREENSHOTS_DIR);
|
|
create_path(PathType::ShaderDir, user_dir / SHADER_DIR);
|
|
create_path(PathType::SaveDataDir, user_dir / SAVEDATA_DIR);
|
|
create_path(PathType::GameDataDir, user_dir / GAMEDATA_DIR);
|
|
create_path(PathType::TempDataDir, user_dir / TEMPDATA_DIR);
|
|
create_path(PathType::SysModuleDir, user_dir / SYSMODULES_DIR);
|
|
create_path(PathType::DownloadDir, user_dir / DOWNLOAD_DIR);
|
|
create_path(PathType::CapturesDir, user_dir / CAPTURES_DIR);
|
|
create_path(PathType::CheatsDir, user_dir / CHEATS_DIR);
|
|
create_path(PathType::PatchesDir, user_dir / PATCHES_DIR);
|
|
create_path(PathType::MetaDataDir, user_dir / METADATA_DIR);
|
|
create_path(PathType::CustomTrophy, user_dir / CUSTOM_TROPHY);
|
|
|
|
return paths;
|
|
}();
|
|
|
|
bool ValidatePath(const fs::path& path) {
|
|
if (path.empty()) {
|
|
LOG_ERROR(Common_Filesystem, "Input path is empty, path={}", PathToUTF8String(path));
|
|
return false;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
if (path.u16string().size() >= MAX_PATH) {
|
|
LOG_ERROR(Common_Filesystem, "Input path is too long, path={}", PathToUTF8String(path));
|
|
return false;
|
|
}
|
|
#else
|
|
if (path.u8string().size() >= MAX_PATH) {
|
|
LOG_ERROR(Common_Filesystem, "Input path is too long, path={}", PathToUTF8String(path));
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string PathToUTF8String(const std::filesystem::path& path) {
|
|
const auto u8_string = path.u8string();
|
|
return std::string{u8_string.begin(), u8_string.end()};
|
|
}
|
|
|
|
const fs::path& GetUserPath(PathType shad_path) {
|
|
return UserPaths.at(shad_path);
|
|
}
|
|
|
|
std::string GetUserPathString(PathType shad_path) {
|
|
return PathToUTF8String(GetUserPath(shad_path));
|
|
}
|
|
|
|
void SetUserPath(PathType shad_path, const fs::path& new_path) {
|
|
if (!std::filesystem::is_directory(new_path)) {
|
|
LOG_ERROR(Common_Filesystem, "Filesystem object at new_path={} is not a directory",
|
|
PathToUTF8String(new_path));
|
|
return;
|
|
}
|
|
|
|
UserPaths.insert_or_assign(shad_path, new_path);
|
|
}
|
|
|
|
std::optional<fs::path> FindGameByID(const fs::path& dir, const std::string& game_id,
|
|
int max_depth) {
|
|
if (max_depth < 0) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Check if this is the game we're looking for
|
|
if (dir.filename() == game_id && fs::exists(dir / "sce_sys" / "param.sfo")) {
|
|
auto eboot_path = dir / "eboot.bin";
|
|
if (fs::exists(eboot_path)) {
|
|
return eboot_path;
|
|
}
|
|
}
|
|
|
|
// Recursively search subdirectories
|
|
std::error_code ec;
|
|
for (const auto& entry : fs::directory_iterator(dir, ec)) {
|
|
if (!entry.is_directory()) {
|
|
continue;
|
|
}
|
|
if (auto found = FindGameByID(entry.path(), game_id, max_depth - 1)) {
|
|
return found;
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
#ifdef ENABLE_QT_GUI
|
|
void PathToQString(QString& result, const std::filesystem::path& path) {
|
|
#ifdef _WIN32
|
|
result = QString::fromStdWString(path.wstring());
|
|
#else
|
|
result = QString::fromStdString(path.string());
|
|
#endif
|
|
}
|
|
|
|
std::filesystem::path PathFromQString(const QString& path) {
|
|
#ifdef _WIN32
|
|
return std::filesystem::path(path.toStdWString());
|
|
#else
|
|
return std::filesystem::path(path.toStdString());
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
} // namespace Common::FS
|