mirror of
https://github.com/libretro/parallel-gx1.git
synced 2026-11-07 04:26:01 -10:00
Every file the core touches goes through libretro-common's filestream, path and dirent layers, taken from RetroArch: vfs_hybrid opens local paths with libretro-common's own implementation and hands URI paths (and, on Android, anything a local open cannot reach) to the frontend's VFS. File::IOFile and File::DirectIOFile sit on RFILE, and FileUtil, FileSearch and the directory walks on path_stat, path_mkdir and retro_opendir. Dolphin's fopen, POSIX and Win32 file code, std::filesystem and std::fstream are gone, with the dual VFS paths and the core option that chose between them. Files opened for reading alone are memory-mapped where the platform can: reads copy straight out of the page cache, DirectIOFile's offset reads on one mapped object are thread safe, and GetMappedPtr(), View() and Prefetch() lend and warm without a copy. Text files are read a line at a time through File::LineReader, lent from the mapping or a window refilled a block at a time, never a byte at a time; JSON is parsed straight from the mapping. Ini files and the cheat file are built in memory and written once, an ini only when it changed and then atomically.
367 lines
12 KiB
C++
367 lines
12 KiB
C++
// Copyright 2014 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include "UICommon/UICommon.h"
|
|
|
|
#include <algorithm>
|
|
#include <clocale>
|
|
#include <cmath>
|
|
#include <locale>
|
|
#include <memory>
|
|
|
|
#include <fmt/format.h>
|
|
|
|
#include "Common/Common.h"
|
|
#include "Common/CommonPaths.h"
|
|
#include "Common/Config/Config.h"
|
|
#include "Common/FileUtil.h"
|
|
#include "Common/Logging/LogManager.h"
|
|
#include "Common/MathUtil.h"
|
|
#include "Common/MsgHandler.h"
|
|
|
|
#include "Core/Config/MainSettings.h"
|
|
#include "Core/ConfigLoaders/BaseConfigLoader.h"
|
|
#include "Core/ConfigManager.h"
|
|
#include "Core/Core.h"
|
|
#include "Core/FreeLookManager.h"
|
|
#include "Core/HW/GBAPad.h"
|
|
#include "Core/HW/GCKeyboard.h"
|
|
#include "Core/HW/GCPad.h"
|
|
#include "Core/HW/ProcessorInterface.h"
|
|
#include "Core/HW/Wiimote.h"
|
|
#include "Core/HotkeyManager.h"
|
|
#include "Core/IOS/IOS.h"
|
|
#include "Core/IOS/STM/STM.h"
|
|
#include "Core/System.h"
|
|
#include "Core/WiiRoot.h"
|
|
|
|
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
|
#include "InputCommon/GCAdapter.h"
|
|
|
|
|
|
#include "VideoCommon/Statistics.h"
|
|
#include "VideoCommon/VideoBackendBase.h"
|
|
#include "VideoCommon/VideoConfig.h"
|
|
|
|
#if defined(__APPLE__) && !defined(__LIBRETRO__)
|
|
#include <IOKit/pwr_mgt/IOPMLib.h>
|
|
#endif
|
|
|
|
namespace UICommon
|
|
{
|
|
static Config::ConfigChangedCallbackID s_config_changed_callback_id;
|
|
static Common::HookableEvent<> s_flush_unsaved_data_event_hook;
|
|
|
|
static void CreateDumpPath(std::string path)
|
|
{
|
|
if (!path.empty())
|
|
File::SetUserPath(D_DUMP_IDX, std::move(path));
|
|
File::CreateFullPath(File::GetUserPath(D_DUMPAUDIO_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_DUMPDSP_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_DUMPSSL_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_DUMPFRAMES_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_DUMPOBJECTS_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_DUMPTEXTURES_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_DUMPDEBUG_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_DUMPDEBUG_BRANCHWATCH_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_DUMPDEBUG_JITBLOCKS_IDX));
|
|
}
|
|
|
|
static void CreateLoadPath(std::string path)
|
|
{
|
|
if (!path.empty())
|
|
File::SetUserPath(D_LOAD_IDX, std::move(path));
|
|
File::CreateFullPath(File::GetUserPath(D_HIRESTEXTURES_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_RIIVOLUTION_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_GRAPHICSMOD_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_DYNAMICINPUT_IDX));
|
|
}
|
|
|
|
static void CreateResourcePackPath(std::string path)
|
|
{
|
|
if (!path.empty())
|
|
File::SetUserPath(D_RESOURCEPACK_IDX, std::move(path));
|
|
}
|
|
|
|
static void CreateWFSPath(const std::string& path)
|
|
{
|
|
if (!path.empty())
|
|
File::SetUserPath(D_WFSROOT_IDX, path + '/');
|
|
}
|
|
|
|
static void InitCustomPaths()
|
|
{
|
|
File::SetUserPath(D_WIIROOT_IDX, Config::Get(Config::MAIN_FS_PATH));
|
|
CreateLoadPath(Config::Get(Config::MAIN_LOAD_PATH));
|
|
CreateDumpPath(Config::Get(Config::MAIN_DUMP_PATH));
|
|
CreateResourcePackPath(Config::Get(Config::MAIN_RESOURCEPACK_PATH));
|
|
CreateWFSPath(Config::Get(Config::MAIN_WFS_PATH));
|
|
File::SetUserPath(F_WIISDCARDIMAGE_IDX, Config::Get(Config::MAIN_WII_SD_CARD_IMAGE_PATH));
|
|
File::SetUserPath(D_WIISDCARDSYNCFOLDER_IDX,
|
|
Config::Get(Config::MAIN_WII_SD_CARD_SYNC_FOLDER_PATH));
|
|
File::CreateFullPath(File::GetUserPath(D_WIISDCARDSYNCFOLDER_IDX));
|
|
#ifdef HAS_LIBMGBA
|
|
File::SetUserPath(F_GBABIOS_IDX, Config::Get(Config::MAIN_GBA_BIOS_PATH));
|
|
File::SetUserPath(D_GBASAVES_IDX, Config::Get(Config::MAIN_GBA_SAVES_PATH));
|
|
File::CreateFullPath(File::GetUserPath(D_GBASAVES_IDX));
|
|
#endif
|
|
}
|
|
|
|
static void RefreshConfig()
|
|
{
|
|
Common::SetEnableAlert(Config::Get(Config::MAIN_USE_PANIC_HANDLERS));
|
|
Common::SetAbortOnPanicAlert(Config::Get(Config::MAIN_ABORT_ON_PANIC_ALERT));
|
|
}
|
|
|
|
void Init()
|
|
{
|
|
Core::RestoreWiiSettings(Core::RestoreReason::CrashRecovery);
|
|
|
|
Config::Init();
|
|
const auto config_changed_callback = [] {
|
|
InitCustomPaths();
|
|
RefreshConfig();
|
|
};
|
|
s_config_changed_callback_id = Config::AddConfigChangedCallback(config_changed_callback);
|
|
Config::AddLayer(ConfigLoaders::GenerateBaseConfigLoader());
|
|
SConfig::Init();
|
|
g_Config.Init();
|
|
Common::Log::LogManager::Init();
|
|
VideoBackendBase::ActivateBackend(Config::Get(Config::MAIN_GFX_BACKEND));
|
|
Statistics::Init();
|
|
|
|
RefreshConfig();
|
|
}
|
|
|
|
void Shutdown()
|
|
{
|
|
Config::RemoveConfigChangedCallback(s_config_changed_callback_id);
|
|
|
|
Statistics::Shutdown();
|
|
GCAdapter::Shutdown();
|
|
WiimoteReal::Shutdown();
|
|
Common::Log::LogManager::Shutdown();
|
|
g_Config.Shutdown();
|
|
SConfig::Shutdown();
|
|
Config::Shutdown();
|
|
}
|
|
|
|
[[nodiscard]] Common::EventHook AddFlushUnsavedDataCallback(std::function<void()> callback)
|
|
{
|
|
return s_flush_unsaved_data_event_hook.Register(std::move(callback));
|
|
}
|
|
|
|
void FlushUnsavedData()
|
|
{
|
|
INFO_LOG_FMT(CORE, "Flushing unsaved data...");
|
|
s_flush_unsaved_data_event_hook.Trigger();
|
|
}
|
|
|
|
void InitControllers(const WindowSystemInfo& wsi)
|
|
{
|
|
if (g_controller_interface.IsInit())
|
|
return;
|
|
|
|
g_controller_interface.Initialize(wsi);
|
|
|
|
if (!g_controller_interface.HasDefaultDevice())
|
|
{
|
|
// Note that the CI default device could be still temporarily removed at any time
|
|
WARN_LOG_FMT(CONTROLLERINTERFACE, "No default device has been added in time. Premade control "
|
|
"mappings intended for the default device may not work.");
|
|
}
|
|
|
|
GCAdapter::Init();
|
|
Pad::Initialize();
|
|
Pad::InitializeGBA();
|
|
Keyboard::Initialize();
|
|
Wiimote::Initialize(Wiimote::InitializeMode::DO_NOT_WAIT_FOR_WIIMOTES);
|
|
HotkeyManagerEmu::Initialize();
|
|
FreeLook::Initialize();
|
|
}
|
|
|
|
void ShutdownControllers()
|
|
{
|
|
#ifndef __LIBRETRO__
|
|
Pad::Shutdown();
|
|
Pad::ShutdownGBA();
|
|
Keyboard::Shutdown();
|
|
Wiimote::Shutdown();
|
|
HotkeyManagerEmu::Shutdown();
|
|
FreeLook::Shutdown();
|
|
#endif
|
|
|
|
g_controller_interface.Shutdown();
|
|
}
|
|
|
|
void SetLocale(std::string locale_name)
|
|
{
|
|
auto set_locale = [](const std::string& locale) {
|
|
#ifdef __linux__
|
|
std::string adjusted_locale = locale;
|
|
if (!locale.empty())
|
|
adjusted_locale += ".UTF-8";
|
|
#else
|
|
const std::string& adjusted_locale = locale;
|
|
#endif
|
|
|
|
// setlocale sets the C locale, and global sets the C and C++ locales, so the call to setlocale
|
|
// would be redundant if it wasn't for not having any other good way to check whether
|
|
// the locale name is valid. (Constructing a std::locale object for an unsupported
|
|
// locale name throws std::runtime_error, and exception handling is disabled in Dolphin.)
|
|
if (!std::setlocale(LC_ALL, adjusted_locale.c_str()))
|
|
return false;
|
|
std::locale::global(std::locale(adjusted_locale));
|
|
return true;
|
|
};
|
|
|
|
#ifdef _WIN32
|
|
constexpr char PREFERRED_SEPARATOR = '-';
|
|
constexpr char OTHER_SEPARATOR = '_';
|
|
#else
|
|
constexpr char PREFERRED_SEPARATOR = '_';
|
|
constexpr char OTHER_SEPARATOR = '-';
|
|
#endif
|
|
|
|
// Users who use a system language other than English are unlikely to prefer American date and
|
|
// time formats, so let's explicitly request "en_GB" if Dolphin's language is set to "en".
|
|
// (The settings window only allows setting "en", not anything like "en_US" or "en_GB".)
|
|
// Users who prefer the American formats are likely to have their system language set to en_US,
|
|
// and are thus likely to leave Dolphin's language as the default value "" (<System Language>).
|
|
if (locale_name == "en")
|
|
locale_name = "en_GB";
|
|
|
|
std::ranges::replace(locale_name, OTHER_SEPARATOR, PREFERRED_SEPARATOR);
|
|
|
|
// Use the specified locale if supported.
|
|
if (set_locale(locale_name))
|
|
return;
|
|
|
|
// Remove subcodes until we get a supported locale. If that doesn't give us a supported locale,
|
|
// "" is passed to set_locale in order to get the system default locale.
|
|
while (!locale_name.empty())
|
|
{
|
|
const size_t separator_index = locale_name.rfind(PREFERRED_SEPARATOR);
|
|
locale_name.erase(separator_index == std::string::npos ? 0 : separator_index);
|
|
if (set_locale(locale_name))
|
|
return;
|
|
}
|
|
|
|
// If none of the locales tried above are supported, we just keep using whatever locale is set
|
|
// (which is the classic locale by default).
|
|
}
|
|
|
|
void CreateDirectories()
|
|
{
|
|
File::CreateFullPath(File::GetUserPath(D_RESOURCEPACK_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_USER_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_CACHE_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_COVERCACHE_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_CONFIG_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_CONFIG_IDX) + GRAPHICSMOD_CONFIG_DIR DIR_SEP);
|
|
File::CreateFullPath(File::GetUserPath(D_DUMPDSP_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_DUMPSSL_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_DUMPTEXTURES_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_DUMPDEBUG_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_DUMPDEBUG_BRANCHWATCH_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_DUMPDEBUG_JITBLOCKS_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_GAMESETTINGS_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX) + USA_DIR DIR_SEP);
|
|
File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX) + EUR_DIR DIR_SEP);
|
|
File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX) + JAP_DIR DIR_SEP);
|
|
File::CreateFullPath(File::GetUserPath(D_TRIUSER_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_HIRESTEXTURES_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_GRAPHICSMOD_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_LOGS_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_MAPS_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_SCREENSHOTS_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_RETROACHIEVEMENTSCACHE_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_STATESAVES_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_ASM_ROOT_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_WFSROOT_IDX));
|
|
#ifndef ANDROID
|
|
File::CreateFullPath(File::GetUserPath(D_THEMES_IDX));
|
|
File::CreateFullPath(File::GetUserPath(D_STYLES_IDX));
|
|
#else
|
|
// Disable media scanning in directories with a .nomedia file
|
|
File::CreateEmptyFile(File::GetUserPath(D_COVERCACHE_IDX) + DIR_SEP NOMEDIA_FILE);
|
|
#endif
|
|
}
|
|
|
|
void SetUserDirectory(std::string custom_path)
|
|
{
|
|
File::CreateFullPath(custom_path + DIR_SEP);
|
|
File::SetUserPath(D_USER_IDX, std::move(custom_path));
|
|
}
|
|
|
|
bool TriggerSTMPowerEvent()
|
|
{
|
|
const auto ios = Core::System::GetInstance().GetIOS();
|
|
if (!ios)
|
|
return false;
|
|
|
|
const auto stm = ios->GetDeviceByName("/dev/stm/eventhook");
|
|
if (!stm || !std::static_pointer_cast<IOS::HLE::STMEventHookDevice>(stm)->HasHookInstalled())
|
|
return false;
|
|
|
|
Core::DisplayMessage("Shutting down", 30000);
|
|
ios->GetSystem().GetProcessorInterface().PowerButton_Tap();
|
|
|
|
return true;
|
|
}
|
|
|
|
void InhibitScreenSaver(bool inhibit)
|
|
{
|
|
// Inhibit the screensaver. Depending on the operating system this may also
|
|
// disable low-power states and/or screen dimming.
|
|
|
|
#ifdef _WIN32
|
|
// Prevents Windows from sleeping, turning off the display, or idling
|
|
SetThreadExecutionState(ES_CONTINUOUS |
|
|
(inhibit ? (ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED) : 0));
|
|
#endif
|
|
|
|
#if defined(__APPLE__) && !defined(__LIBRETRO__)
|
|
static IOPMAssertionID s_power_assertion = kIOPMNullAssertionID;
|
|
if (inhibit)
|
|
{
|
|
CFStringRef reason_for_activity = CFSTR("Emulation Running");
|
|
if (IOPMAssertionCreateWithName(kIOPMAssertionTypePreventUserIdleDisplaySleep,
|
|
kIOPMAssertionLevelOn, reason_for_activity,
|
|
&s_power_assertion) != kIOReturnSuccess)
|
|
{
|
|
s_power_assertion = kIOPMNullAssertionID;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (s_power_assertion != kIOPMNullAssertionID)
|
|
{
|
|
IOPMAssertionRelease(s_power_assertion);
|
|
s_power_assertion = kIOPMNullAssertionID;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
std::string FormatSize(u64 bytes, int decimals)
|
|
{
|
|
// i18n: The symbol for the unit "bytes"
|
|
const char* const unit_symbols[] = {_trans("B"), _trans("KiB"), _trans("MiB"), _trans("GiB"),
|
|
_trans("TiB"), _trans("PiB"), _trans("EiB")};
|
|
|
|
// Find largest power of 2 less than size.
|
|
// div 10 to get largest named unit less than size
|
|
// 10 == log2(1024) (number of B in a KiB, KiB in a MiB, etc)
|
|
// Max value is 63 / 10 = 6
|
|
const int unit = MathUtil::IntLog2(std::max<u64>(bytes, 1)) / 10;
|
|
|
|
// Don't need exact values, only 5 most significant digits
|
|
const double unit_size = std::pow(2, unit * 10);
|
|
return fmt::format("{:.{}Lf} {}", bytes / unit_size, decimals,
|
|
Common::GetStringT(unit_symbols[unit]));
|
|
}
|
|
|
|
} // namespace UICommon
|