Big Picture: add settings configuration (#4255)

* prototype settings look

* put categories on top instead of on the side, plays nicer with input system

* added profiles + saving and loading framework

* add helper functions

* fix autofocusing

* add code for using embedded image files

* put settings in different files

* clang

* remove sdl_image submodule, unify functions when possible

* Always display selected profile in settings content areas

* Fix focusing, logs, highlight profile text

* persist highlight on focused buttons, add all categories placeholders

* All settings added

* navigate tabs with l1/r1
This commit is contained in:
rainmakerv2 2026-04-15 23:19:22 +08:00 committed by GitHub
parent 7759f4eb2e
commit be224a4556
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 953 additions and 79 deletions

3
.gitmodules vendored
View File

@ -125,9 +125,6 @@
[submodule "externals/openal-soft"]
path = externals/openal-soft
url = https://github.com/shadexternals/openal-soft.git
[submodule "externals/sdl3_image"]
path = externals/sdl3_image
url = https://github.com/libsdl-org/SDL_image
[submodule "externals/libusb"]
path = externals/libusb
url = https://github.com/shadexternals/libusb.git

View File

@ -233,7 +233,6 @@ find_package(nlohmann_json 3.12 CONFIG)
find_package(PNG 1.6 MODULE)
find_package(OpenAL CONFIG)
find_package(RenderDoc 1.6.0 MODULE)
find_package(SDL3_image CONFIG)
find_package(SDL3 3.1.2 CONFIG)
find_package(stb MODULE)
find_package(toml11 4.2.0 CONFIG)
@ -1107,6 +1106,8 @@ set(IMGUI src/imgui/imgui_config.h
src/imgui/renderer/texture_manager.h
src/imgui/big_picture.cpp
src/imgui/big_picture.h
src/imgui/settings_dialog_imgui.cpp
src/imgui/settings_dialog_imgui.h
)
set(INPUT src/input/controller.cpp
@ -1144,7 +1145,7 @@ add_executable(shadps4
create_target_directory_groups(shadps4)
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG minimp3)
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 SDL3_image::SDL3_image pugixml::pugixml)
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml)
target_link_libraries(shadps4 PRIVATE stb::headers lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json miniz::miniz fdk-aac CLI11::CLI11 OpenAL::OpenAL Cpp_Httplib)
if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
@ -1267,6 +1268,14 @@ include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/CMakeRC.cmake")
cmrc_add_resource_library(embedded-resources
ALIAS res::embedded
NAMESPACE res
src/images/big_picture/folder.png
src/images/big_picture/settings.png
src/images/big_picture/global-settings.png
src/images/big_picture/experimental.png
src/images/big_picture/graphics.png
src/images/big_picture/controller.png
src/images/big_picture/trophy.png
src/images/big_picture/log.png
src/images/trophy.wav
src/images/bronze.png
src/images/gold.png

View File

@ -76,6 +76,14 @@ path = [
"src/images/trophy.wav",
"src/images/hotkey.png",
"src/images/game_settings.png",
"src/images/big_picture/controller.png",
"src/images/big_picture/experimental.png",
"src/images/big_picture/folder.png",
"src/images/big_picture/global-settings.png",
"src/images/big_picture/graphics.png",
"src/images/big_picture/log.png",
"src/images/big_picture/settings.png",
"src/images/big_picture/trophy.png",
"src/shadps4.rc",
]
precedence = "aggregate"

View File

@ -63,29 +63,6 @@ if (NOT TARGET SDL3::SDL3)
add_subdirectory(sdl3)
endif()
# SDL3_image
if (NOT TARGET SDL3_image::SDL3_image)
set(SDLIMAGE_VENDORED OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_ANI OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_AVIF OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_BMP OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_GIF OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_JPG OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_JXL OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_LBM OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_PCX OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_PNM OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_QOI OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_SVG OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_TGA OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_TIF OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_WEBP OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_XCF OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_XPM OFF CACHE BOOL "" FORCE)
set(SDLIMAGE_XV OFF CACHE BOOL "" FORCE)
add_subdirectory(sdl3_image)
endif()
# vulkan-headers
if (NOT TARGET Vulkan::Headers)
set(VULKAN_HEADERS_ENABLE_MODULE OFF)

@ -1 +0,0 @@
Subproject commit 1aedddcbd205c4e1ea0f99fdb2c785acc8e2489b

View File

@ -484,11 +484,11 @@ void L::Draw() {
namespace Overlay {
void TextCentered(const std::string& text) {
float window_width = GetWindowSize().x;
float window_width = GetContentRegionAvail().x;
float text_width = CalcTextSize(text.c_str()).x;
float text_indentation = (window_width - text_width) * 0.5f;
SameLine(text_indentation);
SetCursorPosX(text_indentation);
Text("%s", text.c_str());
}

View File

@ -72,7 +72,6 @@ struct Setting {
}
/// Write v to the base layer.
/// Game-specific overrides are applied exclusively via Load(serial)
/// Set proper value as base or game_specific
void set(const T& v, bool game_specific = false) {
if (game_specific) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 993 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,35 +1,38 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <SDL3/SDL.h>
#include <fstream>
#include <cmrc/cmrc.hpp>
#include <imgui.h>
#include <stb_image.h>
#include "big_picture.h"
#include "common/logging/log.h"
#include "core/devtools/layer.h"
#include "core/file_format/psf.h"
#include "emulator.h"
#include "imgui/imgui_std.h"
#include "imgui/renderer/imgui_impl_sdl3_bpm.h"
#include "imgui/renderer/imgui_impl_sdlrenderer3.h"
#include "settings_dialog_imgui.h"
#include "imgui_fonts/notosansjp_regular.ttf.g.cpp"
#include "imgui_fonts/proggyvector_regular.ttf.g.cpp"
CMRC_DECLARE(res);
namespace BigPictureMode {
const float gameImageSize = 200.f;
static bool done = false;
static bool runGame = false;
static bool showSettings = false;
static std::filesystem::path runEbootPath = "";
static std::vector<Game> gameVec = {};
static std::vector<bool> focusState = {};
static float uiScale = 1.0f;
static int scaleSelected = 1;
static SDL_Window* window = nullptr;
static SDL_Renderer* renderer = nullptr;
static SDL_Renderer* renderer;
void Launch() {
if (!SDL_Init(SDL_INIT_VIDEO)) {
@ -44,8 +47,9 @@ void Launch() {
return;
}
window = SDL_CreateWindow("shadPS4 Big Picture Mode", EmulatorSettings.GetWindowWidth(),
EmulatorSettings.GetWindowHeight(), SDL_WINDOW_RESIZABLE);
SDL_Window* window =
SDL_CreateWindow("shadPS4 Big Picture Mode", EmulatorSettings.GetWindowWidth(),
EmulatorSettings.GetWindowHeight(), SDL_WINDOW_RESIZABLE);
renderer = SDL_CreateRenderer(window, nullptr);
if (EmulatorSettings.IsFullScreen()) {
@ -109,18 +113,21 @@ void Launch() {
colors[ImGuiCol_HeaderHovered] = ImVec4(0.25f, 0.50f, 0.85f, 1.00f); // lighter blue
style.WindowRounding = 0.0f;
style.FrameRounding = 5.0f;
style.FrameRounding = 5.0f * uiScale;
style.ItemSpacing = ImVec2(10.0f * uiScale, 10.0f * uiScale);
style.FramePadding = ImVec2(10.0f * uiScale, 10.0f * uiScale);
style.FrameBorderSize = 2.5f * uiScale;
style.WindowBorderSize = 0.0f;
style.WindowPadding = ImVec2(20.0f * uiScale, 20.0f * uiScale);
style.GrabMinSize = 20.0f * uiScale;
style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f);
style.Colors[ImGuiCol_SliderGrab] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f);
ImGui_ImplSDL3_InitForSDLRenderer(window, renderer);
ImGui_ImplSDLRenderer3_Init(renderer);
GetGameInfo();
GetGameInfo(gameVec, false);
uiScale = static_cast<float>(EmulatorSettings.GetBigPictureScale() / 1000.f);
float tempScale = uiScale;
while (!done) {
SDL_Event event;
@ -134,14 +141,15 @@ void Launch() {
ImGui_ImplSDLRenderer3_NewFrame();
ImGui_ImplSDL3_NewFrame();
ImGui::NewFrame();
ImGui::PushFont(myFont);
ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->WorkPos);
ImGui::SetNextWindowSize(viewport->WorkSize);
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration;
ImGui::PushFont(myFont);
ImGui::Begin("Game Window", &done, window_flags);
ImGui::DrawPrettyBackground();
ImGui::SetWindowFontScale(uiScale);
ImGuiWindowFlags child_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
@ -160,31 +168,35 @@ void Launch() {
ImGui::SetKeyboardFocusHere();
}
SetGameIcons();
SetGameIcons(gameVec);
ImGui::EndChild();
ImGui::Separator();
ImGui::SetNextItemWidth(300.0f * uiScale);
if (ImGui::SliderFloat("UI Scale", &tempScale, 0.25f, 3.0f)) {
// Dynamically changes UI scale
static float sliderScale = 1.0f;
if (ImGui::IsWindowAppearing()) {
sliderScale = uiScale;
}
ImGui::SliderFloat("UI Scale", &sliderScale, 0.25f, 3.0f);
// Only update when user is not interacting with slider
if (ImGui::IsItemDeactivatedAfterEdit()) {
uiScale = tempScale;
tempScale = uiScale;
uiScale = sliderScale;
}
ImGui::SameLine();
// Align buttons right
float buttonsWidth =
ImGui::CalcTextSize("Settings (Under Construction)").x + ImGui::CalcTextSize("Exit").x +
ImGui::GetStyle().FramePadding.x * 4.0f + ImGui::GetStyle().ItemSpacing.x;
float buttonsWidth = ImGui::CalcTextSize("Settings").x + ImGui::CalcTextSize("Exit").x +
ImGui::GetStyle().FramePadding.x * 4.0f +
ImGui::GetStyle().ItemSpacing.x;
ImGui::SetCursorPosX(ImGui::GetWindowContentRegionMax().x - buttonsWidth);
if (ImGui::Button("Settings (Under Construction)")) {
// Todo
if (ImGui::Button("Settings")) {
showSettings = true;
}
ImGui::SameLine();
if (ImGui::Button("Exit")) {
@ -214,6 +226,18 @@ void Launch() {
ImGui::EndPopup();
}
if (showSettings) {
EmulatorSettings.SetBigPictureScale(static_cast<int>(uiScale * 1000));
EmulatorSettings.Save();
DrawSettings(&showSettings);
// update when settings dialog closed
if (!showSettings) {
uiScale = static_cast<float>(EmulatorSettings.GetBigPictureScale() / 1000.f);
sliderScale = uiScale;
}
}
ImGui::PopFont();
ImGui::End();
ImGui::Render();
@ -233,44 +257,56 @@ void Launch() {
EmulatorSettings.SetBigPictureScale(static_cast<int>(uiScale * 1000));
EmulatorSettings.Save();
if (runGame) {
if (runEbootPath != "") {
auto* emulator = Common::Singleton<Core::Emulator>::Instance();
emulator->Run(runEbootPath);
}
}
void SetGameIcons() {
void SetGameIcons(std::vector<Game>& games) {
ImGuiStyle& style = ImGui::GetStyle();
const float maxAvailableWidth = ImGui::GetContentRegionAvail().x;
const float itemSpacing = style.ItemSpacing.x; // already scaled
const float padding = 10.0f * uiScale;
float rowContentWidth = gameImageSize * uiScale + itemSpacing;
// Use same line if content fits horizontally, move to next line if not
for (int i = 0; i < gameVec.size(); i++) {
for (int i = 0; i < games.size(); i++) {
ImGui::BeginGroup();
std::string ButtonName = "Button" + std::to_string(i);
const char* ButtonNameChar = ButtonName.c_str();
if (ImGui::ImageButton(ButtonNameChar, (ImTextureID)gameVec[i].iconTexture,
bool isNextItemFocused = (ImGui::GetID(ButtonNameChar) == ImGui::GetFocusID());
bool popColor = false;
if (isNextItemFocused) {
ImGui::PushStyleColor(ImGuiCol_Button,
ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered]);
popColor = true;
}
if (ImGui::ImageButton(ButtonNameChar, (ImTextureID)games[i].iconTexture,
ImVec2(gameImageSize * uiScale, gameImageSize * uiScale))) {
runGame = true;
done = true;
runEbootPath = gameVec[i].ebootPath;
runEbootPath = games[i].ebootPath;
}
if (popColor) {
ImGui::PopStyleColor();
}
// Scroll to item only when newly-focused
if (ImGui::IsItemFocused() && !focusState[i]) {
if (ImGui::IsItemFocused() && !games[i].focusState) {
ImGui::SetScrollHereY(0.5f);
}
focusState[i] = ImGui::IsItemFocused();
if (ImGui::IsWindowFocused())
games[i].focusState = ImGui::IsItemFocused();
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + gameImageSize * uiScale);
ImGui::TextWrapped("%s", gameVec[i].title.c_str());
ImGui::TextWrapped("%s", games[i].title.c_str());
ImGui::PopTextWrapPos();
ImGui::EndGroup();
// Use same line if content fits horizontally, move to next line if not
rowContentWidth += (gameImageSize * uiScale + itemSpacing * 2 + padding);
if (rowContentWidth < maxAvailableWidth) {
ImGui::SameLine(0.0f, padding);
@ -300,8 +336,16 @@ std::filesystem::path UpdateChecker(const std::string sceItem, std::filesystem::
return outputPath;
}
void GetGameInfo() {
gameVec.clear();
void GetGameInfo(std::vector<Game>& games, bool AddGlobalSettings, SDL_Texture* texture) {
games.clear();
if (AddGlobalSettings) {
Game global;
global.title = "Global";
global.iconTexture = texture;
global.focusState = false;
games.push_back(global);
}
for (const auto& installLoc : EmulatorSettings.GetAllGameInstallDirs()) {
if (installLoc.enabled && std::filesystem::exists(installLoc.path)) {
for (const auto& entry : std::filesystem::directory_iterator(installLoc.path)) {
@ -311,12 +355,6 @@ void GetGameInfo() {
}
Game game;
game.ebootPath = entry.path() / "eboot.bin";
const std::string iconFileName = "icon0.png";
std::filesystem::path iconPath = UpdateChecker(iconFileName, entry.path());
game.iconTexture = IMG_LoadTexture(renderer, iconPath.string().c_str());
PSF psf;
const std::string sfoFileName = "param.sfo";
std::filesystem::path sfoPath = UpdateChecker(sfoFileName, entry.path());
@ -325,15 +363,63 @@ void GetGameInfo() {
if (const auto title = psf.GetString("TITLE"); title.has_value()) {
game.title = *title;
}
if (const auto title_id = psf.GetString("TITLE_ID"); title_id.has_value()) {
game.serial = *title_id;
}
} else {
continue;
}
gameVec.push_back(game);
focusState.push_back(false);
const std::string iconFileName = "icon0.png";
std::filesystem::path iconPath = UpdateChecker(iconFileName, entry.path());
LoadTextureDataFromFile(iconPath, game.iconTexture, renderer);
game.ebootPath = entry.path() / "eboot.bin";
game.focusState = false;
games.push_back(game);
}
}
}
// Keep global settings at the start if it's added
auto start = AddGlobalSettings ? games.begin() + 1 : (games.begin());
std::sort(start, games.end(), [](const Game& a, const Game& b) {
return a.title < b.title; // Alphabetical order
});
}
void LoadTextureDataFromFile(std::filesystem::path filePath, SDL_Texture*& texture,
SDL_Renderer* m_renderer) {
std::ifstream file(filePath, std::ios::binary);
std::vector<char> data =
std::vector<char>(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
LoadTextureData(data, texture, m_renderer);
}
void LoadTextureData(std::vector<char> data, SDL_Texture*& texture, SDL_Renderer* m_renderer) {
int image_width = 0;
int image_height = 0;
int channels = 4;
unsigned char* image_data = stbi_load_from_memory(
(const unsigned char*)data.data(), (int)data.size(), &image_width, &image_height, NULL, 4);
if (image_data == nullptr) {
LOG_ERROR(ImGui, "Failed to load image: {}", stbi_failure_reason());
}
SDL_Surface* surface = SDL_CreateSurfaceFrom(image_width, image_height, SDL_PIXELFORMAT_RGBA32,
(void*)image_data, channels * image_width);
if (surface == nullptr) {
LOG_ERROR(ImGui, "Unable to create SDL surface: {}", SDL_GetError());
}
texture = SDL_CreateTextureFromSurface(m_renderer, surface);
if (texture == nullptr) {
LOG_ERROR(ImGui, "Unable to create SDL texture: {}", SDL_GetError());
}
SDL_DestroySurface(surface);
stbi_image_free(image_data);
}
} // namespace BigPictureMode

View File

@ -4,7 +4,7 @@
#pragma once
#include <filesystem>
#include <SDL3_image/SDL_image.h>
#include <SDL3/SDL.h>
namespace BigPictureMode {
@ -12,11 +12,17 @@ struct Game {
SDL_Texture* iconTexture;
std::filesystem::path ebootPath;
std::string title;
std::string serial;
bool focusState;
};
void Launch();
void SetGameIcons();
void GetGameInfo();
void SetGameIcons(std::vector<Game>& games);
void GetGameInfo(std::vector<Game>& games, bool AddGlobalSettings, SDL_Texture* texture = {});
std::filesystem::path UpdateChecker(const std::string sceItem, std::filesystem::path game_folder);
void LoadTextureDataFromFile(std::filesystem::path filePath, SDL_Texture*& texture,
SDL_Renderer* renderer);
void LoadTextureData(std::vector<char> data, SDL_Texture*& texture, SDL_Renderer* renderer);
} // namespace BigPictureMode

View File

@ -0,0 +1,602 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cmrc/cmrc.hpp>
#include <stb_image.h>
#include "core/devtools/layer.h"
#include "core/emulator_settings.h"
#include "imgui/imgui_std.h"
#include "settings_dialog_imgui.h"
#include "imgui_fonts/notosansjp_regular.ttf.g.cpp"
#include "imgui_fonts/proggyvector_regular.ttf.g.cpp"
CMRC_DECLARE(res);
namespace BigPictureMode {
const float gameImageSize = 200.f;
const float settingsIconSize = 125.f;
static std::vector<Game> settingsProfileVec = {};
static float uiScale = 1.0f;
static CurrentSettings currentSettings;
static Textures textures;
static SDL_Renderer* renderer;
static SettingsCategory currentCategory = SettingsCategory::Profiles;
static std::string currentProfile = "Global";
static bool closeOnSave = false;
void Init() {
currentProfile = "Global";
currentCategory = SettingsCategory::Profiles;
LoadSettings("Global");
SDL_Window* window = SDL_GetKeyboardFocus();
renderer = SDL_GetRenderer(window);
LoadEmbeddedTexture("src/images/big_picture/settings.png", textures.general);
LoadEmbeddedTexture("src/images/big_picture/folder.png", textures.profiles);
LoadEmbeddedTexture("src/images/big_picture/global-settings.png", textures.globalSettings);
LoadEmbeddedTexture("src/images/big_picture/experimental.png", textures.experimental);
LoadEmbeddedTexture("src/images/big_picture/graphics.png", textures.graphics);
LoadEmbeddedTexture("src/images/big_picture/controller.png", textures.input);
LoadEmbeddedTexture("src/images/big_picture/trophy.png", textures.trophy);
LoadEmbeddedTexture("src/images/big_picture/log.png", textures.log);
GetGameInfo(settingsProfileVec, true, textures.globalSettings);
uiScale = static_cast<float>(EmulatorSettings.GetBigPictureScale() / 1000.f);
}
void DeInit() {
EmulatorSettings.Load();
EmulatorSettings.SetBigPictureScale(static_cast<int>(uiScale * 1000));
EmulatorSettings.Save();
}
void DrawSettings(bool* open) {
if (!*open)
return;
const ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->WorkPos);
ImGui::SetNextWindowSize(viewport->WorkSize);
if (ImGui::Begin("Settings", nullptr, ImGuiWindowFlags_NoDecoration)) {
if (ImGui::IsWindowAppearing()) {
Init();
closeOnSave = false;
}
ImGui::DrawPrettyBackground();
ImGui::SetWindowFontScale(uiScale);
ImGuiWindowFlags child_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NavFlattened;
ImVec4 settingsColor = ImVec4(0.1f, 0.1f, 0.12f, 0.8f); // Darker gray
ImGui::PushStyleColor(ImGuiCol_ChildBg, settingsColor);
ImGui::BeginChild("Categories", ImVec2(0, 0), ImGuiChildFlags_AutoResizeY,
child_flags | ImGuiWindowFlags_HorizontalScrollbar);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(30.0f * uiScale, 0.0f));
// Must add categories in enum order for L1/R1 to work correctly, with experimental last
AddCategory("Profiles", textures.profiles, SettingsCategory::Profiles);
AddCategory("General", textures.general, SettingsCategory::General);
AddCategory("Graphics", textures.graphics, SettingsCategory::Graphics);
AddCategory("Input", textures.input, SettingsCategory::Input);
AddCategory("Trophy", textures.trophy, SettingsCategory::Trophy);
AddCategory("Log", textures.log, SettingsCategory::Log);
if (currentProfile != "Global")
AddCategory("Experimental", textures.experimental, SettingsCategory::Experimental);
ImGui::PopStyleVar();
ImGui::EndChild(); // Categories
if (ImGui::IsWindowAppearing()) {
ImGui::SetKeyboardFocusHere();
}
ImGui::BeginChild("ContentRegion", ImVec2(0, -ImGui::GetFrameHeightWithSpacing()), true,
child_flags);
ImGui::PopStyleColor();
LoadCategory(currentCategory);
ImGui::EndChild();
ImGui::Separator();
ImGui::SetNextItemWidth(300.0f * uiScale);
static float sliderScale2 = 1.0f;
if (ImGui::IsWindowAppearing()) {
sliderScale2 = uiScale;
}
ImGui::SliderFloat("UI Scale", &sliderScale2, 0.25f, 3.0f);
// Only update when user is not interacting with slider
if (ImGui::IsItemDeactivatedAfterEdit()) {
uiScale = sliderScale2;
}
ImGui::SameLine();
// Align buttons right
float buttonsWidth = ImGui::CalcTextSize("Save").x + ImGui::CalcTextSize("Cancel").x +
ImGui::CalcTextSize("Apply").x +
ImGui::GetStyle().FramePadding.x * 6.0f +
ImGui::GetStyle().ItemSpacing.x * 2;
ImGui::SetCursorPosX(ImGui::GetWindowContentRegionMax().x - buttonsWidth);
if (ImGui::Button("Save")) {
closeOnSave = true;
ImGui::OpenPopup("Save Confirmation");
}
ImGui::SameLine();
if (ImGui::Button("Apply")) {
ImGui::OpenPopup("Save Confirmation");
}
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginPopupModal("Save Confirmation", nullptr,
ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("%s", ("Profile Saved:\n" + currentProfile).c_str());
ImGui::Separator();
if (ImGui::Button("OK", ImVec2(250 * uiScale, 0))) {
std::string profile = currentProfile;
if (currentProfile != "Global") {
profile = currentProfile.substr(0, 9);
}
SaveSettings(profile);
if (closeOnSave) {
DeInit();
*open = false;
ImGui::CloseCurrentPopup();
} else {
ImGui::CloseCurrentPopup();
}
}
ImGui::EndPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel")) {
DeInit();
*open = false;
}
SettingsCategory lastCategory =
currentProfile != "Global" ? SettingsCategory::Experimental : SettingsCategory::Log;
// choose next category with R1
if (ImGui::IsKeyPressed(ImGuiKey_GamepadR1)) {
int currentIndex = static_cast<int>(currentCategory);
currentCategory == lastCategory
? currentCategory = static_cast<SettingsCategory>(0)
: currentCategory = static_cast<SettingsCategory>(currentIndex + 1);
}
// choose previous category with R1
if (ImGui::IsKeyPressed(ImGuiKey_GamepadL1)) {
int currentIndex = static_cast<int>(currentCategory);
currentIndex == 0 ? currentCategory = lastCategory
: currentCategory = static_cast<SettingsCategory>(currentIndex - 1);
}
}
ImGui::End();
}
void LoadCategory(SettingsCategory category) {
ImGui::TextColored(ImVec4(0.00f, 1.00f, 1.00f, 1.00f), "%s",
("Selected Profile: " + currentProfile).c_str()); // Dark Blue
ImGui::Dummy(ImVec2(0, 20.f * uiScale));
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(4.0f * uiScale, 10.0f * uiScale));
if (category == SettingsCategory::General) {
if (ImGui::BeginTable("SettingsTable", 2)) {
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale);
ImGui::TableSetupColumn("Value");
AddSettingCombo("Console Language", currentSettings.consoleLanguage);
AddSettingSliderInt("Volume", currentSettings.volume, 0, 500);
AddSettingBool("Show Splash Screen When Launching Game", currentSettings.showSplash);
AddSettingCombo("Audio Backend", currentSettings.audioBackend);
ImGui::EndTable();
}
} else if (category == SettingsCategory::Graphics) {
if (ImGui::BeginTable("SettingsTable", 2)) {
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale);
ImGui::TableSetupColumn("Value");
AddSettingCombo("Display Mode", currentSettings.fullscreenMode);
AddSettingCombo("Present Mode", currentSettings.presentMode);
AddSettingSliderInt("Window Width", currentSettings.windowWidth, 0, 8000);
AddSettingSliderInt("Window Height", currentSettings.windowHeight, 0, 7000);
AddSettingBool("Enable HDR", currentSettings.hdrAllowed);
AddSettingBool("Enable FSR", currentSettings.fsrEnabled);
if (currentSettings.fsrEnabled) {
AddSettingBool("Enable RCAS", currentSettings.rcasEnabled);
}
if (currentSettings.rcasEnabled && currentSettings.fsrEnabled) {
AddSettingSliderFloat("RCAS Attenuation", currentSettings.rcasAttenuation, 0.0f,
3.0f, 3);
}
ImGui::EndTable();
}
} else if (category == SettingsCategory::Input) {
if (ImGui::BeginTable("SettingsTable", 2)) {
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale);
ImGui::TableSetupColumn("Value");
AddSettingBool("Enable Motion Controls", currentSettings.motionControls);
AddSettingBool("Enable Background Controller Input",
currentSettings.backgroundController);
AddSettingCombo("Hide Cursor", currentSettings.cursorState);
if (currentSettings.cursorState == 1) {
AddSettingSliderInt("Hide Cursor Idle Timeout", currentSettings.cursorTimeout, 1,
10);
}
ImGui::EndTable();
}
} else if (category == SettingsCategory::Trophy) {
if (ImGui::BeginTable("SettingsTable", 2)) {
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale);
ImGui::TableSetupColumn("Value");
AddSettingBool("Disable Trophy Notification", currentSettings.trophyPopupDisabled);
if (!currentSettings.trophyPopupDisabled) {
AddSettingCombo("Trophy Notification Position", currentSettings.trophySide);
AddSettingSliderFloat("Trophy Notification Duration",
currentSettings.trophyDuration, 0.f, 10.f, 1);
}
ImGui::EndTable();
}
} else if (category == SettingsCategory::Log) {
if (ImGui::BeginTable("SettingsTable", 2)) {
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale);
ImGui::TableSetupColumn("Value");
AddSettingBool("Enable Logging", currentSettings.logEnabled);
if (currentSettings.logEnabled) {
AddSettingBool("Separate Log Files", currentSettings.separateLog);
AddSettingCombo("Log Type", currentSettings.logType);
}
ImGui::EndTable();
}
} else if (category == SettingsCategory::Experimental) {
if (ImGui::BeginTable("SettingsTable", 2)) {
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale);
ImGui::TableSetupColumn("Value");
AddSettingSliderInt("Additional DMem Allocation", currentSettings.extraDmem, 0, 20000);
AddSettingSliderInt("Vblank Frequency", currentSettings.vblankFrequency, 30, 360);
AddSettingCombo("Readbacks Mode", currentSettings.readbacksMode);
AddSettingBool("Enable Readback Linear Images", currentSettings.readbackLinearImages);
AddSettingBool("Enable Direct Memory Access", currentSettings.directMemoryAccess);
AddSettingBool("Enable Devkit Console Mode", currentSettings.devkitConsole);
AddSettingBool("Enable PS4 Neo Mode", currentSettings.neoMode);
AddSettingBool("Set PSN Sign-in to True", currentSettings.psnSignedIn);
AddSettingBool("Set Network Connected to True", currentSettings.connectedNetwork);
AddSettingBool("Enable Shader Cache", currentSettings.pipelineCacheEnabled);
if (currentSettings.pipelineCacheEnabled) {
AddSettingBool("Compress Shader Cache to Zip File",
currentSettings.pipelineCacheArchive);
}
ImGui::EndTable();
}
}
ImGui::PopStyleVar();
// Child Window if Needed
if (category == SettingsCategory::Profiles) {
ImGuiWindowFlags child_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NavFlattened;
ImGui::BeginChild("ProfileSelect", ImVec2(0, 0), true, child_flags);
Overlay::TextCentered("Select Global or Game-Specific Settings Profile");
SetProfileIcons(settingsProfileVec);
ImGui::EndChild();
}
}
void SaveSettings(std::string profile) {
const bool isSpecific = currentProfile != "Global";
/////////// General Tab
EmulatorSettings.SetConsoleLanguage(
languageMap.at(optionsLanguage.at(currentSettings.consoleLanguage)), isSpecific);
EmulatorSettings.SetVolumeSlider(currentSettings.volume, isSpecific);
EmulatorSettings.SetShowSplash(currentSettings.showSplash, isSpecific);
EmulatorSettings.SetAudioBackend(currentSettings.audioBackend, isSpecific);
/////////// Graphics Tab
bool isFullscreen = currentSettings.fullscreenMode != 0;
EmulatorSettings.SetFullScreen(isFullscreen);
EmulatorSettings.SetFullScreenMode(optionsFullscreenMode.at(currentSettings.fullscreenMode),
isSpecific);
EmulatorSettings.SetPresentMode(optionsPresentMode.at(currentSettings.presentMode), isSpecific);
EmulatorSettings.SetWindowHeight(currentSettings.windowHeight, isSpecific);
EmulatorSettings.SetWindowWidth(currentSettings.windowWidth, isSpecific);
EmulatorSettings.SetHdrAllowed(currentSettings.hdrAllowed, isSpecific);
EmulatorSettings.SetFsrEnabled(currentSettings.fsrEnabled, isSpecific);
EmulatorSettings.SetRcasEnabled(currentSettings.rcasEnabled, isSpecific);
EmulatorSettings.SetRcasAttenuation(static_cast<int>(currentSettings.rcasAttenuation * 1000),
isSpecific);
/////////// Input Tab
EmulatorSettings.SetMotionControlsEnabled(currentSettings.motionControls, isSpecific);
EmulatorSettings.SetBackgroundControllerInput(currentSettings.backgroundController, isSpecific);
EmulatorSettings.SetCursorState(currentSettings.cursorState, isSpecific);
EmulatorSettings.SetCursorHideTimeout(currentSettings.cursorTimeout, isSpecific);
/////////// Trophy Tab
EmulatorSettings.SetTrophyPopupDisabled(currentSettings.trophyPopupDisabled, isSpecific);
EmulatorSettings.SetTrophyNotificationSide(optionsTrophySide.at(currentSettings.trophySide),
isSpecific);
EmulatorSettings.SetTrophyNotificationDuration(
static_cast<double>(currentSettings.trophyDuration));
/////////// Log Tab
EmulatorSettings.SetLogEnabled(currentSettings.logEnabled, isSpecific);
EmulatorSettings.SetLogType(optionsLogType.at(currentSettings.logType), isSpecific);
EmulatorSettings.SetSeparateLoggingEnabled(currentSettings.separateLog, isSpecific);
/////////// Experimental Tab
if (isSpecific) {
EmulatorSettings.SetReadbacksMode(currentSettings.readbacksMode, true);
EmulatorSettings.SetReadbackLinearImagesEnabled(currentSettings.readbackLinearImages, true);
EmulatorSettings.SetDirectMemoryAccessEnabled(currentSettings.directMemoryAccess, true);
EmulatorSettings.SetDevKit(currentSettings.devkitConsole, true);
EmulatorSettings.SetNeo(currentSettings.neoMode, true);
EmulatorSettings.SetPSNSignedIn(currentSettings.psnSignedIn, true);
EmulatorSettings.SetConnectedToNetwork(currentSettings.connectedNetwork, true);
EmulatorSettings.SetPipelineCacheEnabled(currentSettings.pipelineCacheEnabled, true);
EmulatorSettings.SetPipelineCacheArchived(currentSettings.pipelineCacheArchive, true);
EmulatorSettings.SetExtraDmemInMBytes(currentSettings.extraDmem, true);
EmulatorSettings.SetVblankFrequency(currentSettings.vblankFrequency, true);
}
if (!isSpecific) {
EmulatorSettings.Save();
} else {
EmulatorSettings.Save(profile);
}
}
void LoadSettings(std::string profile) {
const bool isSpecific = currentProfile != "Global";
if (!isSpecific) {
EmulatorSettings.Load();
} else {
EmulatorSettings.Load(profile);
}
/////////// General Tab
int languageIndex = EmulatorSettings.GetConsoleLanguage();
std::string language;
for (const auto& [key, value] : languageMap) {
if (value == languageIndex) {
language = key;
}
}
currentSettings.consoleLanguage = GetComboIndex(language, optionsLanguage);
currentSettings.volume = EmulatorSettings.GetVolumeSlider();
currentSettings.showSplash = EmulatorSettings.IsShowSplash();
currentSettings.audioBackend = EmulatorSettings.GetAudioBackend();
/////////// Graphics Tab
currentSettings.fullscreenMode =
GetComboIndex(EmulatorSettings.GetFullScreenMode(), optionsFullscreenMode);
currentSettings.presentMode =
GetComboIndex(EmulatorSettings.GetPresentMode(), optionsPresentMode);
currentSettings.windowHeight = EmulatorSettings.GetWindowHeight();
currentSettings.windowWidth = EmulatorSettings.GetWindowWidth();
currentSettings.hdrAllowed = EmulatorSettings.IsHdrAllowed();
currentSettings.fsrEnabled = EmulatorSettings.IsFsrEnabled();
currentSettings.rcasEnabled = EmulatorSettings.IsRcasEnabled();
currentSettings.rcasAttenuation =
static_cast<float>(EmulatorSettings.GetRcasAttenuation() * 0.001f);
/////////// Input Tab
currentSettings.motionControls = EmulatorSettings.IsMotionControlsEnabled();
currentSettings.backgroundController = EmulatorSettings.IsBackgroundControllerInput();
currentSettings.cursorState = EmulatorSettings.GetCursorState();
currentSettings.cursorTimeout = EmulatorSettings.GetCursorHideTimeout();
/////////// Trophy Tab
currentSettings.trophyPopupDisabled = EmulatorSettings.IsTrophyPopupDisabled();
currentSettings.trophySide =
GetComboIndex(EmulatorSettings.GetTrophyNotificationSide(), optionsTrophySide);
currentSettings.trophyDuration =
static_cast<float>(EmulatorSettings.GetTrophyNotificationDuration());
/////////// Log Tab
currentSettings.logEnabled = EmulatorSettings.IsLogEnabled();
currentSettings.logType = GetComboIndex(EmulatorSettings.GetLogType(), optionsLogType);
currentSettings.separateLog = EmulatorSettings.IsSeparateLoggingEnabled();
/////////// Experimental Tab
if (isSpecific) {
currentSettings.readbacksMode = EmulatorSettings.GetReadbacksMode();
currentSettings.readbackLinearImages = EmulatorSettings.IsReadbackLinearImagesEnabled();
currentSettings.directMemoryAccess = EmulatorSettings.IsDirectMemoryAccessEnabled();
currentSettings.devkitConsole = EmulatorSettings.IsDevKit();
currentSettings.neoMode = EmulatorSettings.IsNeo();
currentSettings.psnSignedIn = EmulatorSettings.IsPSNSignedIn();
currentSettings.connectedNetwork = EmulatorSettings.IsConnectedToNetwork();
currentSettings.pipelineCacheEnabled = EmulatorSettings.IsPipelineCacheEnabled();
currentSettings.pipelineCacheArchive = EmulatorSettings.IsPipelineCacheArchived();
currentSettings.extraDmem = EmulatorSettings.GetExtraDmemInMBytes();
currentSettings.vblankFrequency = EmulatorSettings.GetVblankFrequency();
}
}
void AddCategory(std::string name, SDL_Texture* texture, SettingsCategory category) {
ImGui::SameLine();
ImGui::BeginGroup();
// make button appear hovered as long as category is selected, otherwise dull it's hovered color
currentCategory == category
? ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered])
: ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.235f, 0.392f, 0.624f, 1.00f));
if (ImGui::ImageButton(name.c_str(), ImTextureID(texture),
ImVec2(settingsIconSize * uiScale, settingsIconSize * uiScale))) {
currentCategory = category;
}
ImGui::PopStyleColor();
ImGui::SetCursorPosX(
(ImGui::GetCursorPosX() +
(settingsIconSize * uiScale - ImGui::CalcTextSize(name.c_str()).x) * 0.5f) +
ImGui::GetStyle().FramePadding.x);
ImGui::Text("%s", name.c_str());
ImGui::EndGroup();
}
void AddSettingBool(std::string name, bool& value) {
std::string label = "##" + name;
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextWrapped("%s", name.c_str());
ImGui::TableNextColumn();
ImGui::Checkbox(label.c_str(), &value);
}
void AddSettingSliderInt(std::string name, int& value, int min, int max) {
std::string label = "##" + name;
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextWrapped("%s", name.c_str());
ImGui::TableNextColumn();
ImGui::SliderInt(label.c_str(), &value, min, max);
}
void AddSettingSliderFloat(std::string name, float& value, int min, int max, int precision) {
std::string label = "##" + name;
std::string precisionString = "%." + std::to_string(precision) + "f";
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextWrapped("%s", name.c_str());
ImGui::TableNextColumn();
ImGui::SliderFloat(label.c_str(), &value, min, max, precisionString.c_str());
}
void AddSettingCombo(std::string name, int& value) {
std::string label = "##" + name;
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextWrapped("%s", name.c_str());
std::vector<std::string> options = optionsMap.at(name);
ImGui::TableNextColumn();
const char* combo_value = options[value].c_str();
if (ImGui::BeginCombo(label.c_str(), combo_value)) {
for (int i = 0; i < options.size(); i++) {
const bool selected = (i == value);
if (ImGui::Selectable(options[i].c_str(), selected))
value = i;
// Set the initial focus when opening the combo
if (selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
}
int GetComboIndex(std::string selection, std::vector<std::string> options) {
for (int i = 0; i < options.size(); i++) {
if (selection == options[i])
return i;
}
return 0;
}
void LoadEmbeddedTexture(std::string resourcePath, SDL_Texture*& texture) {
auto resource = cmrc::res::get_filesystem();
auto file = resource.open(resourcePath);
std::vector<char> texData = std::vector<char>(file.begin(), file.end());
BigPictureMode::LoadTextureData(texData, texture, renderer);
}
void SetProfileIcons(std::vector<Game>& games) {
ImGuiStyle& style = ImGui::GetStyle();
const float maxAvailableWidth = ImGui::GetContentRegionAvail().x;
const float itemSpacing = style.ItemSpacing.x; // already scaled
const float padding = 10.0f * uiScale;
float rowContentWidth = gameImageSize * uiScale + itemSpacing;
for (int i = 0; i < games.size(); i++) {
ImGui::BeginGroup();
std::string ButtonName = "Button" + std::to_string(i);
const char* ButtonNameChar = ButtonName.c_str();
bool isNextItemFocused = (ImGui::GetID(ButtonNameChar) == ImGui::GetFocusID());
bool popColor = false;
if (isNextItemFocused) {
ImGui::PushStyleColor(ImGuiCol_Button,
ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered]);
popColor = true;
}
if (ImGui::ImageButton(ButtonNameChar, (ImTextureID)games[i].iconTexture,
ImVec2(gameImageSize * uiScale, gameImageSize * uiScale))) {
currentProfile = i == 0 ? "Global" : games[i].serial + " - " + games[i].title;
LoadSettings(games[i].serial);
}
if (popColor) {
ImGui::PopStyleColor();
}
// Scroll to item only when newly-focused
if (ImGui::IsItemFocused() && !games[i].focusState) {
ImGui::SetScrollHereY(0.5f);
}
if (ImGui::IsWindowFocused()) {
games[i].focusState = ImGui::IsItemFocused();
}
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + gameImageSize * uiScale);
ImGui::TextWrapped("%s", games[i].title.c_str());
ImGui::PopTextWrapPos();
ImGui::EndGroup();
// Use same line if content fits horizontally, move to next line if not
rowContentWidth += (gameImageSize * uiScale + itemSpacing * 2 + padding);
if (rowContentWidth < maxAvailableWidth) {
ImGui::SameLine(0.0f, padding);
} else {
ImGui::Dummy(ImVec2(0.0f, padding));
rowContentWidth = gameImageSize * uiScale + itemSpacing;
}
}
}
} // namespace BigPictureMode

View File

@ -0,0 +1,191 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <map>
#include <string>
#include <vector>
#include <SDL3/SDL.h>
#include <imgui.h>
#include "big_picture.h"
namespace BigPictureMode {
enum class SettingsCategory {
Profiles,
General,
Graphics,
Input,
Trophy,
Log,
Experimental,
};
struct Textures {
SDL_Texture* profiles;
SDL_Texture* general;
SDL_Texture* globalSettings;
SDL_Texture* experimental;
SDL_Texture* graphics;
SDL_Texture* input;
SDL_Texture* trophy;
SDL_Texture* log;
};
void Init();
void DeInit();
void SetProfileIcons(std::vector<Game>& games);
void LoadEmbeddedTexture(std::string resourcePath, SDL_Texture*& texture);
void AddCategory(std::string name, SDL_Texture* texture, SettingsCategory category);
void DrawSettings(bool* open);
void SaveSettings(std::string profile);
void LoadSettings(std::string profile);
void LoadCategory(SettingsCategory);
void AddSettingBool(std::string name, bool& value);
void AddSettingSliderInt(std::string name, int& value, int min, int max);
void AddSettingSliderFloat(std::string name, float& value, int min, int max, int precision);
void AddSettingCombo(std::string name, int& value);
int GetComboIndex(std::string selection, std::vector<std::string> options);
//////////////////// Settings struct
// Note: use int instead of std::string for all combo settings as needed by ImGui
// then convert to string when saving/loading
struct CurrentSettings {
// General tab
int consoleLanguage;
int volume;
bool showSplash;
int audioBackend;
// Graphics tab
int fullscreenMode;
int presentMode;
int windowWidth;
int windowHeight;
bool hdrAllowed;
bool fsrEnabled;
bool rcasEnabled;
float rcasAttenuation;
// Input tab
bool motionControls;
bool backgroundController;
int cursorState;
int cursorTimeout;
// Trophy tab
bool trophyPopupDisabled;
int trophySide;
float trophyDuration;
// Log tab
bool logEnabled;
bool separateLog;
int logType;
// Experimental tab
int readbacksMode;
bool readbackLinearImages;
bool directMemoryAccess;
bool devkitConsole;
bool neoMode;
bool psnSignedIn;
bool connectedNetwork;
bool pipelineCacheEnabled;
bool pipelineCacheArchive;
int extraDmem;
int vblankFrequency;
};
//////////////////// option maps for comboboxes and other needed constants
const std::map<std::string, int> languageMap = {{"Arabic", 21},
{"Czech", 23},
{"Danish", 14},
{"Dutch", 6},
{"English (United Kingdom)", 18},
{"English (United States)", 1},
{"Finnish", 12},
{"French (Canada)", 22},
{"French (France)", 2},
{"German", 4},
{"Greek", 25},
{"Hungarian", 24},
{"Indonesian", 29},
{"Italian", 5},
{"Japanese", 0},
{"Korean", 9},
{"Norwegian (Bokmaal)", 15},
{"Polish", 16},
{"Portuguese (Brazil)", 17},
{"Portuguese (Portugal)", 7},
{"Romanian", 26},
{"Russian", 8},
{"Simplified Chinese", 11},
{"Spanish (Latin America)", 20},
{"Spanish (Spain)", 3},
{"Swedish", 13},
{"Thai", 27},
{"Traditional Chinese", 10},
{"Turkish", 19},
{"Ukrainian", 30},
{"Vietnamese", 28}};
const std::vector<std::string> optionsLanguage = {"Arabic",
"Czech",
"Danish",
"Dutch",
"English (United Kingdom)",
"English (United States)",
"Finnish",
"French (Canada)",
"French (France)",
"German",
"Greek",
"Hungarian",
"Indonesian",
"Italian",
"Japanese",
"Korean",
"Norwegian (Bokmaal)",
"Polish",
"Portuguese (Brazil)",
"Portuguese (Portugal)",
"Romanian",
"Russian",
"Simplified Chinese",
"Spanish (Latin America)",
"Spanish (Spain)",
"Swedish",
"Thai",
"Traditional Chinese",
"Turkish",
"Ukrainian",
"Vietnamese"};
const std::vector<std::string> optionsLogType = {"sync", "async"};
const std::vector<std::string> optionsFullscreenMode = {"Windowed", "Fullscreen",
"Fullscreen (Borderless)"};
const std::vector<std::string> optionsAudioBackend = {"SDL", "OpenAL"};
const std::vector<std::string> optionsPresentMode = {"Mailbox", "Fifo", "Immediate"};
const std::vector<std::string> optionsHideCursor = {"Never", "Idle", "Always"};
const std::vector<std::string> optionsTrophySide = {"left", "right", "top", "bottom"};
const std::vector<std::string> optionsReadbacksMode = {"Disabled", "Relaxed", "Precise"};
const std::map<std::string, std::vector<std::string>> optionsMap = {
{"Log Type", optionsLogType},
{"Console Language", optionsLanguage},
{"Audio Backend", optionsAudioBackend},
{"Display Mode", optionsFullscreenMode},
{"Present Mode", optionsPresentMode},
{"Hide Cursor", optionsHideCursor},
{"Trophy Notification Position", optionsTrophySide},
{"Readbacks Mode", optionsReadbacksMode},
};
} // namespace BigPictureMode