CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/libretro/libretro.cpp
Views: 1401
#include "ppsspp_config.h"1#include <cstring>2#include <cassert>3#include <thread>4#include <atomic>5#include <vector>6#include <cstdlib>7#include <mutex>89#include "Common/CPUDetect.h"10#include "Common/Log.h"11#include "Common/Log/LogManager.h"12#include "Common/System/Display.h"13#include "Common/System/NativeApp.h"14#include "Common/System/System.h"15#include "Common/TimeUtil.h"16#include "Common/File/FileUtil.h"17#include "Common/Serialize/Serializer.h"18#include "Common/Log/StdioListener.h"19#include "Common/Input/InputState.h"20#include "Common/Thread/ThreadUtil.h"21#include "Common/Thread/ThreadManager.h"22#include "Common/File/VFS/VFS.h"23#include "Common/File/VFS/DirectoryReader.h"24#include "Common/Data/Text/I18n.h"25#include "Common/StringUtils.h"2627#include "Core/Config.h"28#include "Core/ConfigValues.h"29#include "Core/Core.h"30#include "Core/HLE/sceCtrl.h"31#include "Core/HLE/sceUtility.h"32#include "Core/HLE/__sceAudio.h"33#include "Core/HW/MemoryStick.h"34#include "Core/MemMap.h"35#include "Core/System.h"36#include "Core/CoreTiming.h"37#include "Core/HW/Display.h"38#include "Core/CwCheat.h"39#include "Core/ELF/ParamSFO.h"4041#include "GPU/GPUState.h"42#include "GPU/GPUInterface.h"43#include "GPU/Common/FramebufferManagerCommon.h"44#include "GPU/Common/TextureScalerCommon.h"45#include "GPU/Common/PresentationCommon.h"4647#include "UI/AudioCommon.h"4849#include "libretro/libretro.h"50#include "libretro/LibretroGraphicsContext.h"51#include "libretro/libretro_core_options.h"5253#if PPSSPP_PLATFORM(ANDROID)54#include <sys/system_properties.h>55#endif5657#define DIR_SEP "/"58#ifdef _WIN3259#define DIR_SEP_CHRS "/\\"60#else61#define DIR_SEP_CHRS "/"62#endif6364#ifdef HAVE_LIBRETRO_VFS65#include "streams/file_stream.h"66#endif6768#define SAMPLERATE 441006970/* AUDIO output buffer */71static struct {72int16_t *data;73int32_t size;74int32_t capacity;75} output_audio_buffer = {NULL, 0, 0};7677// Calculated swap interval is 'stable' if the same78// value is recorded for a number of retro_run()79// calls equal to VSYNC_SWAP_INTERVAL_FRAMES80#define VSYNC_SWAP_INTERVAL_FRAMES 681// Calculated swap interval is 'valid' if it is82// within VSYNC_SWAP_INTERVAL_THRESHOLD of an integer83// value84#define VSYNC_SWAP_INTERVAL_THRESHOLD 0.05f85// Swap interval detection is only enabled if the86// core is running at 'normal' speed - i.e. if87// run speed is within VSYNC_SWAP_INTERVAL_RUN_SPEED_THRESHOLD88// percent of 10089#define VSYNC_SWAP_INTERVAL_RUN_SPEED_THRESHOLD 5.0f9091static bool libretro_supports_bitmasks = false;92static bool libretro_supports_option_categories = false;93static bool show_ip_address_options = true;94static bool show_upnp_port_option = true;95static bool show_detect_frame_rate_option = true;96static std::string changeProAdhocServer;9798namespace Libretro99{100LibretroGraphicsContext *ctx;101retro_environment_t environ_cb;102retro_hw_context_type backend = RETRO_HW_CONTEXT_DUMMY;103static retro_audio_sample_batch_t audio_batch_cb;104static retro_input_poll_t input_poll_cb;105static retro_input_state_t input_state_cb;106static retro_log_printf_t log_cb;107108static bool detectVsyncSwapInterval = false;109static bool detectVsyncSwapIntervalOptShown = true;110static bool softwareRenderInitHack = false;111112static s64 expectedTimeUsPerRun = 0;113static uint32_t vsyncSwapInterval = 1;114static uint32_t vsyncSwapIntervalLast = 1;115static uint32_t vsyncSwapIntervalCounter = 0;116static int numVBlanksLast = 0;117static double fpsTimeLast = 0.0;118static float runSpeed = 0.0f;119static s64 runTicksLast = 0;120121static void ensure_output_audio_buffer_capacity(int32_t capacity)122{123if (capacity <= output_audio_buffer.capacity) {124return;125}126127output_audio_buffer.data = (int16_t*)realloc(output_audio_buffer.data, capacity * sizeof(*output_audio_buffer.data));128output_audio_buffer.capacity = capacity;129log_cb(RETRO_LOG_DEBUG, "Output audio buffer capacity set to %d\n", capacity);130}131132static void init_output_audio_buffer(int32_t capacity)133{134output_audio_buffer.data = NULL;135output_audio_buffer.size = 0;136output_audio_buffer.capacity = 0;137ensure_output_audio_buffer_capacity(capacity);138}139140static void free_output_audio_buffer()141{142free(output_audio_buffer.data);143output_audio_buffer.data = NULL;144output_audio_buffer.size = 0;145output_audio_buffer.capacity = 0;146}147148static void upload_output_audio_buffer()149{150audio_batch_cb(output_audio_buffer.data, output_audio_buffer.size / 2);151output_audio_buffer.size = 0;152}153154155/**156* Clamp a value to a given range.157*158* This implementation was taken from `RGBAUtil.cpp` to allow building when `std::clamp()` is unavailable.159*160* @param f The value to clamp.161* @param low The lower bound of the range.162* @param high The upper bound of the range.163* @return The clamped value.164*/165template <typename T>166static T clamp(T f, T low, T high) {167if (f < low)168return low;169if (f > high)170return high;171return f;172}173174static void VsyncSwapIntervalReset()175{176expectedTimeUsPerRun = (s64)(1000000.0f / (60.0f / 1.001f));177vsyncSwapInterval = 1;178vsyncSwapIntervalLast = 1;179vsyncSwapIntervalCounter = 0;180181numVBlanksLast = 0;182fpsTimeLast = 0.0;183runSpeed = 0.0f;184runTicksLast = 0;185186detectVsyncSwapIntervalOptShown = true;187}188189static void VsyncSwapIntervalDetect()190{191if (!detectVsyncSwapInterval)192return;193194// All bets are off if core is running at195// the 'wrong' speed (i.e. cycle count for196// this run will be meaningless if internal197// frame rate is dropping below expected198// value, or fast forward is enabled)199double fpsTime = time_now_d();200int numVBlanks = __DisplayGetNumVblanks();201int frames = numVBlanks - numVBlanksLast;202203if (frames >= VSYNC_SWAP_INTERVAL_FRAMES << 1)204{205double fps = (double)frames / (fpsTime - fpsTimeLast);206runSpeed = fps / ((60.0f / 1.001f) / 100.0f);207208fpsTimeLast = fpsTime;209numVBlanksLast = numVBlanks;210}211212float speedDelta = 100.0f - runSpeed;213speedDelta = (speedDelta < 0.0f) ? -speedDelta : speedDelta;214215// Speed is measured relative to a 60 Hz refresh216// rate. If we are transitioning from a low internal217// frame rate to a higher internal frame rate, then218// 'full speed' may actually equate to219// (100 / current_swap_interval)...220if ((vsyncSwapInterval > 1) &&221(speedDelta >= VSYNC_SWAP_INTERVAL_RUN_SPEED_THRESHOLD))222{223speedDelta = 100.0f - (runSpeed * (float)vsyncSwapInterval);224speedDelta = (speedDelta < 0.0f) ? -speedDelta : speedDelta;225}226227if (speedDelta >= VSYNC_SWAP_INTERVAL_RUN_SPEED_THRESHOLD)228{229// Swap interval detection is invalid - bail out230vsyncSwapIntervalCounter = 0;231return;232}233234// Get elapsed time (us) for this run235s64 runTicks = CoreTiming::GetTicks();236s64 runTimeUs = cyclesToUs(runTicks - runTicksLast);237238// Check if current internal frame rate is a239// factor of the default ~60 Hz240float swapRatio = (float)runTimeUs / (float)expectedTimeUsPerRun;241uint32_t swapInteger;242float swapRemainder;243244// If internal frame rate is equal to (within threshold)245// or higher than the default ~60 Hz, fall back to a246// swap interval of 1247if (swapRatio < (1.0f + VSYNC_SWAP_INTERVAL_THRESHOLD))248{249swapInteger = 1;250swapRemainder = 0.0f;251}252else253{254swapInteger = (uint32_t)(swapRatio + 0.5f);255swapRemainder = swapRatio - (float)swapInteger;256swapRemainder = (swapRemainder < 0.0f) ?257-swapRemainder : swapRemainder;258}259260// > Swap interval is considered 'valid' if it is261// within VSYNC_SWAP_INTERVAL_THRESHOLD of an integer262// value263// > If valid, check if new swap interval differs from264// previously logged value265if ((swapRemainder <= VSYNC_SWAP_INTERVAL_THRESHOLD) &&266(swapInteger != vsyncSwapInterval))267{268vsyncSwapIntervalCounter =269(swapInteger == vsyncSwapIntervalLast) ?270(vsyncSwapIntervalCounter + 1) : 0;271272// Check whether swap interval is 'stable'273if (vsyncSwapIntervalCounter >= VSYNC_SWAP_INTERVAL_FRAMES)274{275vsyncSwapInterval = swapInteger;276vsyncSwapIntervalCounter = 0;277278// Notify frontend279retro_system_av_info avInfo;280retro_get_system_av_info(&avInfo);281environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avInfo);282}283284vsyncSwapIntervalLast = swapInteger;285}286else287vsyncSwapIntervalCounter = 0;288289runTicksLast = runTicks;290}291} // namespace Libretro292293using namespace Libretro;294295class PrintfLogger : public LogListener296{297public:298PrintfLogger(retro_log_callback log) : log_(log.log) {}299void Log(const LogMessage &message)300{301switch (message.level)302{303case LogLevel::LVERBOSE:304case LogLevel::LDEBUG:305log_(RETRO_LOG_DEBUG, "[%s] %s",306message.log, message.msg.c_str());307break;308309case LogLevel::LERROR:310log_(RETRO_LOG_ERROR, "[%s] %s",311message.log, message.msg.c_str());312break;313case LogLevel::LNOTICE:314case LogLevel::LWARNING:315log_(RETRO_LOG_WARN, "[%s] %s",316message.log, message.msg.c_str());317break;318case LogLevel::LINFO:319default:320log_(RETRO_LOG_INFO, "[%s] %s",321message.log, message.msg.c_str());322break;323}324}325326private:327retro_log_printf_t log_;328};329static PrintfLogger *printfLogger;330331static bool set_variable_visibility(void)332{333struct retro_core_option_display option_display;334struct retro_variable var;335bool updated = false;336337// Show/hide IP address options338bool show_ip_address_options_prev = show_ip_address_options;339show_ip_address_options = true;340341var.key = "ppsspp_change_pro_ad_hoc_server_address";342if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && strcmp(var.value, "IP address"))343show_ip_address_options = false;344345if (show_ip_address_options != show_ip_address_options_prev)346{347option_display.visible = show_ip_address_options;348for (int i = 0; i < 12; i++)349{350char key[64] = {0};351option_display.key = key;352snprintf(key, sizeof(key), "ppsspp_pro_ad_hoc_server_address%02d", i + 1);353environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);354}355updated = true;356}357358// Show/hide 'UPnP Use Original Port' option359bool show_upnp_port_option_prev = show_upnp_port_option;360show_upnp_port_option = true;361362var.key = "ppsspp_enable_upnp";363if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && !strcmp(var.value, "disabled"))364show_upnp_port_option = false;365366if (show_upnp_port_option != show_upnp_port_option_prev)367{368option_display.visible = show_upnp_port_option;369option_display.key = "ppsspp_upnp_use_original_port";370environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);371updated = true;372}373374// Show/hide 'Detect Frame Rate Changes' option375bool show_detect_frame_rate_option_prev = show_detect_frame_rate_option;376int frameskip = 0;377bool auto_frameskip = false;378bool dupe_frames = false;379show_detect_frame_rate_option = true;380381var.key = "ppsspp_frameskip";382if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && strcmp(var.value, "disabled"))383frameskip = atoi(var.value);384var.key = "ppsspp_auto_frameskip";385if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && !strcmp(var.value, "enabled"))386auto_frameskip = true;387var.key = "ppsspp_frame_duplication";388if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && !strcmp(var.value, "enabled"))389dupe_frames = true;390391show_detect_frame_rate_option = (frameskip == 0) && !auto_frameskip && !dupe_frames;392if (show_detect_frame_rate_option != show_detect_frame_rate_option_prev)393{394option_display.visible = show_detect_frame_rate_option;395option_display.key = "ppsspp_detect_vsync_swap_interval";396environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);397updated = true;398}399400return updated;401}402403void retro_set_environment(retro_environment_t cb)404{405environ_cb = cb;406407libretro_set_core_options(environ_cb, &libretro_supports_option_categories);408struct retro_core_options_update_display_callback update_display_cb;409update_display_cb.callback = set_variable_visibility;410environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_UPDATE_DISPLAY_CALLBACK, &update_display_cb);411412#ifdef HAVE_LIBRETRO_VFS413struct retro_vfs_interface_info vfs_iface_info { 1, nullptr };414if (cb(RETRO_ENVIRONMENT_GET_VFS_INTERFACE, &vfs_iface_info))415filestream_vfs_init(&vfs_iface_info);416#endif417}418419static int get_language_auto(void)420{421retro_language val = RETRO_LANGUAGE_ENGLISH;422environ_cb(RETRO_ENVIRONMENT_GET_LANGUAGE, &val);423424switch (val)425{426default:427case RETRO_LANGUAGE_ENGLISH:428return PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;429case RETRO_LANGUAGE_JAPANESE:430return PSP_SYSTEMPARAM_LANGUAGE_JAPANESE;431case RETRO_LANGUAGE_FRENCH:432return PSP_SYSTEMPARAM_LANGUAGE_FRENCH;433case RETRO_LANGUAGE_GERMAN:434return PSP_SYSTEMPARAM_LANGUAGE_GERMAN;435case RETRO_LANGUAGE_SPANISH:436return PSP_SYSTEMPARAM_LANGUAGE_SPANISH;437case RETRO_LANGUAGE_ITALIAN:438return PSP_SYSTEMPARAM_LANGUAGE_ITALIAN;439case RETRO_LANGUAGE_PORTUGUESE_BRAZIL:440case RETRO_LANGUAGE_PORTUGUESE_PORTUGAL:441return PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE;442case RETRO_LANGUAGE_RUSSIAN:443return PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN;444case RETRO_LANGUAGE_DUTCH:445return PSP_SYSTEMPARAM_LANGUAGE_DUTCH;446case RETRO_LANGUAGE_KOREAN:447return PSP_SYSTEMPARAM_LANGUAGE_KOREAN;448case RETRO_LANGUAGE_CHINESE_TRADITIONAL:449return PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL;450case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:451return PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED;452}453}454455static std::string map_psp_language_to_i18n_locale(int val)456{457switch (val)458{459default:460case PSP_SYSTEMPARAM_LANGUAGE_ENGLISH:461return "en_US";462case PSP_SYSTEMPARAM_LANGUAGE_JAPANESE:463return "ja_JP";464case PSP_SYSTEMPARAM_LANGUAGE_FRENCH:465return "fr_FR";466case PSP_SYSTEMPARAM_LANGUAGE_GERMAN:467return "de_DE";468case PSP_SYSTEMPARAM_LANGUAGE_SPANISH:469return "es_ES";470case PSP_SYSTEMPARAM_LANGUAGE_ITALIAN:471return "it_IT";472case PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE:473return "pt_PT";474case PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN:475return "ru_RU";476case PSP_SYSTEMPARAM_LANGUAGE_DUTCH:477return "nl_NL";478case PSP_SYSTEMPARAM_LANGUAGE_KOREAN:479return "ko_KR";480case PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL:481return "zh_TW";482case PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED:483return "zh_CN";484}485}486487static void check_variables(CoreParameter &coreParam)488{489if (g_Config.bForceLagSync)490{491bool isFastForwarding;492if (environ_cb(RETRO_ENVIRONMENT_GET_FASTFORWARDING, &isFastForwarding))493coreParam.fastForward = isFastForwarding;494}495496bool updated = false;497498if ( coreState != CoreState::CORE_POWERUP499&& environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated)500&& !updated)501return;502503struct retro_variable var = {0};504std::string sTextureShaderName_prev;505int iInternalResolution_prev;506int iTexScalingType_prev;507int iTexScalingLevel_prev;508int iMultiSampleLevel_prev;509bool bDisplayCropTo16x9_prev;510511var.key = "ppsspp_language";512if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)513{514if (!strcmp(var.value, "Automatic"))515g_Config.iLanguage = -1;516else if (!strcmp(var.value, "English"))517g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;518else if (!strcmp(var.value, "Japanese"))519g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_JAPANESE;520else if (!strcmp(var.value, "French"))521g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_FRENCH;522else if (!strcmp(var.value, "Spanish"))523g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_SPANISH;524else if (!strcmp(var.value, "German"))525g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_GERMAN;526else if (!strcmp(var.value, "Italian"))527g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_ITALIAN;528else if (!strcmp(var.value, "Dutch"))529g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_DUTCH;530else if (!strcmp(var.value, "Portuguese"))531g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE;532else if (!strcmp(var.value, "Russian"))533g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN;534else if (!strcmp(var.value, "Korean"))535g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_KOREAN;536else if (!strcmp(var.value, "Chinese Traditional"))537g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL;538else if (!strcmp(var.value, "Chinese Simplified"))539g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED;540}541542#ifndef __EMSCRIPTEN__543var.key = "ppsspp_cpu_core";544if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)545{546if (!strcmp(var.value, "JIT"))547g_Config.iCpuCore = (int)CPUCore::JIT;548else if (!strcmp(var.value, "IR JIT"))549g_Config.iCpuCore = (int)CPUCore::IR_INTERPRETER;550else if (!strcmp(var.value, "Interpreter"))551g_Config.iCpuCore = (int)CPUCore::INTERPRETER;552}553554if (System_GetPropertyBool(SYSPROP_CAN_JIT) == false && g_Config.iCpuCore == (int)CPUCore::JIT) {555// Just gonna force it to the IR interpreter on startup.556// We don't hide the option, but we make sure it's off on bootup. In case someone wants557// to experiment in future iOS versions or something...558g_Config.iCpuCore = (int)CPUCore::IR_INTERPRETER;559}560#else561g_Config.iCpuCore = (int)CPUCore::INTERPRETER;562#endif563564var.key = "ppsspp_fast_memory";565if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)566{567if (!strcmp(var.value, "disabled"))568g_Config.bFastMemory = false;569else570g_Config.bFastMemory = true;571}572573var.key = "ppsspp_ignore_bad_memory_access";574if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)575{576if (!strcmp(var.value, "disabled"))577g_Config.bIgnoreBadMemAccess = false;578else579g_Config.bIgnoreBadMemAccess = true;580}581582var.key = "ppsspp_io_timing_method";583if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)584{585if (!strcmp(var.value, "Fast"))586g_Config.iIOTimingMethod = IOTIMING_FAST;587else if (!strcmp(var.value, "Host"))588g_Config.iIOTimingMethod = IOTIMING_HOST;589else if (!strcmp(var.value, "Simulate UMD delays"))590g_Config.iIOTimingMethod = IOTIMING_REALISTIC;591else if (!strcmp(var.value, "Simulate UMD slow reading speed"))592g_Config.iIOTimingMethod = IOTIMING_UMDSLOWREALISTIC;593}594595var.key = "ppsspp_force_lag_sync";596if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)597{598if (!strcmp(var.value, "disabled"))599g_Config.bForceLagSync = false;600else601g_Config.bForceLagSync = true;602}603604var.key = "ppsspp_locked_cpu_speed";605if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)606g_Config.iLockedCPUSpeed = atoi(var.value);607608var.key = "ppsspp_cache_iso";609if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)610{611if (!strcmp(var.value, "disabled"))612g_Config.bCacheFullIsoInRam = false;613else614g_Config.bCacheFullIsoInRam = true;615}616617var.key = "ppsspp_cheats";618if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)619{620if (!strcmp(var.value, "disabled"))621g_Config.bEnableCheats = false;622else623g_Config.bEnableCheats = true;624}625626var.key = "ppsspp_psp_model";627if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)628{629if (!strcmp(var.value, "psp_1000"))630g_Config.iPSPModel = PSP_MODEL_FAT;631else if (!strcmp(var.value, "psp_2000_3000"))632g_Config.iPSPModel = PSP_MODEL_SLIM;633}634635var.key = "ppsspp_button_preference";636if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)637{638if (!strcmp(var.value, "Cross"))639g_Config.iButtonPreference = PSP_SYSTEMPARAM_BUTTON_CROSS;640else if (!strcmp(var.value, "Circle"))641g_Config.iButtonPreference = PSP_SYSTEMPARAM_BUTTON_CIRCLE;642}643644var.key = "ppsspp_analog_is_circular";645if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)646{647if (!strcmp(var.value, "disabled"))648g_Config.bAnalogIsCircular = false;649else650g_Config.bAnalogIsCircular = true;651}652653var.key = "ppsspp_memstick_inserted";654if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)655{656if (!strcmp(var.value, "disabled"))657g_Config.bMemStickInserted = false;658else659g_Config.bMemStickInserted = true;660}661662var.key = "ppsspp_internal_resolution";663if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)664{665iInternalResolution_prev = g_Config.iInternalResolution;666667if (!strcmp(var.value, "480x272"))668g_Config.iInternalResolution = 1;669else if (!strcmp(var.value, "960x544"))670g_Config.iInternalResolution = 2;671else if (!strcmp(var.value, "1440x816"))672g_Config.iInternalResolution = 3;673else if (!strcmp(var.value, "1920x1088"))674g_Config.iInternalResolution = 4;675else if (!strcmp(var.value, "2400x1360"))676g_Config.iInternalResolution = 5;677else if (!strcmp(var.value, "2880x1632"))678g_Config.iInternalResolution = 6;679else if (!strcmp(var.value, "3360x1904"))680g_Config.iInternalResolution = 7;681else if (!strcmp(var.value, "3840x2176"))682g_Config.iInternalResolution = 8;683else if (!strcmp(var.value, "4320x2448"))684g_Config.iInternalResolution = 9;685else if (!strcmp(var.value, "4800x2720"))686g_Config.iInternalResolution = 10;687688// Force resolution to 1x without hardware context689if (backend == RETRO_HW_CONTEXT_NONE)690g_Config.iInternalResolution = 1;691}692693var.key = "ppsspp_software_rendering";694if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)695{696if (!PSP_IsInited())697{698if (!strcmp(var.value, "disabled") && backend != RETRO_HW_CONTEXT_NONE)699g_Config.bSoftwareRendering = false;700else701g_Config.bSoftwareRendering = true;702}703704// Force resolution to 1x with software rendering705if (g_Config.bSoftwareRendering)706g_Config.iInternalResolution = 1;707}708709#if 0 // see issue #16786710var.key = "ppsspp_mulitsample_level";711if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)712{713iMultiSampleLevel_prev = g_Config.iMultiSampleLevel;714715if (!strcmp(var.value, "Disabled"))716g_Config.iMultiSampleLevel = 0;717else if (!strcmp(var.value, "x2"))718g_Config.iMultiSampleLevel = 1;719else if (!strcmp(var.value, "x4"))720g_Config.iMultiSampleLevel = 2;721else if (!strcmp(var.value, "x8"))722g_Config.iMultiSampleLevel = 3;723}724#endif725726var.key = "ppsspp_cropto16x9";727if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)728{729bDisplayCropTo16x9_prev = g_Config.bDisplayCropTo16x9;730731if (!strcmp(var.value, "disabled"))732g_Config.bDisplayCropTo16x9 = false;733else734g_Config.bDisplayCropTo16x9 = true;735}736737var.key = "ppsspp_frameskip";738if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)739g_Config.iFrameSkip = atoi(var.value);740741var.key = "ppsspp_frameskiptype";742if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)743{744if (!strcmp(var.value, "Number of frames"))745g_Config.iFrameSkipType = 0;746else if (!strcmp(var.value, "Percent of FPS"))747g_Config.iFrameSkipType = 1;748}749750var.key = "ppsspp_auto_frameskip";751if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)752{753if (!strcmp(var.value, "disabled"))754g_Config.bAutoFrameSkip = false;755else756g_Config.bAutoFrameSkip = true;757}758759var.key = "ppsspp_frame_duplication";760if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)761{762if (!strcmp(var.value, "disabled"))763g_Config.bRenderDuplicateFrames = false;764else765g_Config.bRenderDuplicateFrames = true;766}767768var.key = "ppsspp_detect_vsync_swap_interval";769if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)770{771if (!strcmp(var.value, "disabled"))772detectVsyncSwapInterval = false;773else774detectVsyncSwapInterval = true;775}776777var.key = "ppsspp_inflight_frames";778if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)779{780if (!strcmp(var.value, "No buffer"))781g_Config.iInflightFrames = 0;782else if (!strcmp(var.value, "Up to 1"))783g_Config.iInflightFrames = 1;784else if (!strcmp(var.value, "Up to 2"))785g_Config.iInflightFrames = 2;786}787788var.key = "ppsspp_skip_buffer_effects";789if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)790{791if (!strcmp(var.value, "disabled"))792g_Config.bSkipBufferEffects = false;793else794g_Config.bSkipBufferEffects = true;795}796797var.key = "ppsspp_disable_range_culling";798if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)799{800if (!strcmp(var.value, "disabled"))801g_Config.bDisableRangeCulling = false;802else803g_Config.bDisableRangeCulling = true;804}805806var.key = "ppsspp_skip_gpu_readbacks";807if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)808{809if (!strcmp(var.value, "disabled"))810g_Config.iSkipGPUReadbackMode = (int)SkipGPUReadbackMode::NO_SKIP;811else812g_Config.iSkipGPUReadbackMode = (int)SkipGPUReadbackMode::SKIP;813}814815var.key = "ppsspp_lazy_texture_caching";816if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)817{818if (!strcmp(var.value, "disabled"))819g_Config.bTextureBackoffCache = false;820else821g_Config.bTextureBackoffCache = true;822}823824var.key = "ppsspp_spline_quality";825if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)826{827if (!strcmp(var.value, "Low"))828g_Config.iSplineBezierQuality = 0;829else if (!strcmp(var.value, "Medium"))830g_Config.iSplineBezierQuality = 1;831else if (!strcmp(var.value, "High"))832g_Config.iSplineBezierQuality = 2;833}834835var.key = "ppsspp_gpu_hardware_transform";836if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)837{838if (!strcmp(var.value, "disabled"))839g_Config.bHardwareTransform = false;840else841g_Config.bHardwareTransform = true;842}843844var.key = "ppsspp_software_skinning";845if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)846{847if (!strcmp(var.value, "disabled"))848g_Config.bSoftwareSkinning = false;849else850g_Config.bSoftwareSkinning = true;851}852853var.key = "ppsspp_hardware_tesselation";854if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)855{856if (!strcmp(var.value, "disabled"))857g_Config.bHardwareTessellation = false;858else859g_Config.bHardwareTessellation = true;860}861862var.key = "ppsspp_lower_resolution_for_effects";863if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)864{865if (!strcmp(var.value, "disabled"))866g_Config.iBloomHack = 0;867else if (!strcmp(var.value, "Safe"))868g_Config.iBloomHack = 1;869else if (!strcmp(var.value, "Balanced"))870g_Config.iBloomHack = 2;871else if (!strcmp(var.value, "Aggressive"))872g_Config.iBloomHack = 3;873}874875var.key = "ppsspp_texture_scaling_type";876if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)877{878iTexScalingType_prev = g_Config.iTexScalingType;879880if (!strcmp(var.value, "xbrz"))881g_Config.iTexScalingType = TextureScalerCommon::XBRZ;882else if (!strcmp(var.value, "hybrid"))883g_Config.iTexScalingType = TextureScalerCommon::HYBRID;884else if (!strcmp(var.value, "bicubic"))885g_Config.iTexScalingType = TextureScalerCommon::BICUBIC;886else if (!strcmp(var.value, "hybrid_bicubic"))887g_Config.iTexScalingType = TextureScalerCommon::HYBRID_BICUBIC;888}889890var.key = "ppsspp_texture_scaling_level";891if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)892{893iTexScalingLevel_prev = g_Config.iTexScalingLevel;894895if (!strcmp(var.value, "disabled"))896g_Config.iTexScalingLevel = 1;897else if (!strcmp(var.value, "2x"))898g_Config.iTexScalingLevel = 2;899else if (!strcmp(var.value, "3x"))900g_Config.iTexScalingLevel = 3;901else if (!strcmp(var.value, "4x"))902g_Config.iTexScalingLevel = 4;903else if (!strcmp(var.value, "5x"))904g_Config.iTexScalingLevel = 5;905}906907var.key = "ppsspp_texture_deposterize";908if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)909{910if (!strcmp(var.value, "disabled"))911g_Config.bTexDeposterize = false;912else913g_Config.bTexDeposterize = true;914}915916var.key = "ppsspp_texture_shader";917if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)918{919sTextureShaderName_prev = g_Config.sTextureShaderName;920921if (!strcmp(var.value, "disabled"))922g_Config.sTextureShaderName = "Off";923else if (!strcmp(var.value, "2xBRZ"))924g_Config.sTextureShaderName = "Tex2xBRZ";925else if (!strcmp(var.value, "4xBRZ"))926g_Config.sTextureShaderName = "Tex4xBRZ";927else if (!strcmp(var.value, "MMPX"))928g_Config.sTextureShaderName = "TexMMPX";929}930931var.key = "ppsspp_texture_anisotropic_filtering";932if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)933{934if (!strcmp(var.value, "disabled"))935g_Config.iAnisotropyLevel = 0;936else if (!strcmp(var.value, "2x"))937g_Config.iAnisotropyLevel = 1;938else if (!strcmp(var.value, "4x"))939g_Config.iAnisotropyLevel = 2;940else if (!strcmp(var.value, "8x"))941g_Config.iAnisotropyLevel = 3;942else if (!strcmp(var.value, "16x"))943g_Config.iAnisotropyLevel = 4;944}945946var.key = "ppsspp_texture_filtering";947if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)948{949if (!strcmp(var.value, "Auto"))950g_Config.iTexFiltering = 1;951else if (!strcmp(var.value, "Nearest"))952g_Config.iTexFiltering = 2;953else if (!strcmp(var.value, "Linear"))954g_Config.iTexFiltering = 3;955else if (!strcmp(var.value, "Auto max quality"))956g_Config.iTexFiltering = 4;957}958959var.key = "ppsspp_smart_2d_texture_filtering";960if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)961{962if (!strcmp(var.value, "disabled"))963g_Config.bSmart2DTexFiltering = false;964else965g_Config.bSmart2DTexFiltering = true;966}967968var.key = "ppsspp_texture_replacement";969if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)970{971if (!strcmp(var.value, "disabled"))972g_Config.bReplaceTextures = false;973else974g_Config.bReplaceTextures = true;975}976977var.key = "ppsspp_enable_wlan";978if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)979{980if (!strcmp(var.value, "disabled"))981g_Config.bEnableWlan = false;982else983g_Config.bEnableWlan = true;984}985986var.key = "ppsspp_wlan_channel";987if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)988g_Config.iWlanAdhocChannel = atoi(var.value);989990var.key = "ppsspp_enable_builtin_pro_ad_hoc_server";991if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)992{993if (!strcmp(var.value, "disabled"))994g_Config.bEnableAdhocServer = false;995else996g_Config.bEnableAdhocServer = true;997}998999var.key = "ppsspp_change_pro_ad_hoc_server_address";1000if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)1001changeProAdhocServer = var.value;10021003var.key = "ppsspp_enable_upnp";1004if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)1005{1006if (!strcmp(var.value, "disabled"))1007g_Config.bEnableUPnP = false;1008else1009g_Config.bEnableUPnP = true;1010}10111012var.key = "ppsspp_upnp_use_original_port";1013if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)1014{1015if (!strcmp(var.value, "disabled"))1016g_Config.bUPnPUseOriginalPort = false;1017else1018g_Config.bUPnPUseOriginalPort = true;1019}10201021var.key = "ppsspp_port_offset";1022if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)1023g_Config.iPortOffset = atoi(var.value);10241025var.key = "ppsspp_minimum_timeout";1026if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)1027g_Config.iMinTimeout = atoi(var.value);10281029var.key = "ppsspp_forced_first_connect";1030if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)1031{1032if (!strcmp(var.value, "disabled"))1033g_Config.bForcedFirstConnect = false;1034else1035g_Config.bForcedFirstConnect = true;1036}10371038std::string ppsspp_change_mac_address[12];1039int ppsspp_pro_ad_hoc_ipv4[12];1040char key[64] = {0};1041var.key = key;1042g_Config.sMACAddress = "";1043g_Config.proAdhocServer = "";1044for (int i = 0; i < 12; i++)1045{1046snprintf(key, sizeof(key), "ppsspp_change_mac_address%02d", i + 1);1047if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)1048{1049ppsspp_change_mac_address[i] = var.value;10501051if (i && i % 2 == 0)1052g_Config.sMACAddress += ":";10531054g_Config.sMACAddress += ppsspp_change_mac_address[i];1055}10561057snprintf(key, sizeof(key), "ppsspp_pro_ad_hoc_server_address%02d", i + 1);1058if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)1059ppsspp_pro_ad_hoc_ipv4[i] = atoi(var.value);1060}10611062if (g_Config.sMACAddress == "00:00:00:00:00:00")1063{1064g_Config.sMACAddress = CreateRandMAC();10651066for (int i = 0; i < 12; i++)1067{1068snprintf(key, sizeof(key), "ppsspp_change_mac_address%02d", i + 1);1069std::string digit = {g_Config.sMACAddress[i + i / 2]};1070var.value = digit.c_str();1071environ_cb(RETRO_ENVIRONMENT_SET_VARIABLE, &var);1072}1073}10741075if (changeProAdhocServer == "IP address")1076{1077g_Config.proAdhocServer = "";1078bool leadingZero = true;1079for (int i = 0; i < 12; i++)1080{1081if (i && i % 3 == 0)1082{1083g_Config.proAdhocServer += '.';1084leadingZero = true;1085}10861087int addressPt = ppsspp_pro_ad_hoc_ipv4[i];1088if (addressPt || i % 3 == 2)1089leadingZero = false; // We are either non-zero or the last digit of a byte10901091if (! leadingZero)1092g_Config.proAdhocServer += static_cast<char>('0' + addressPt);1093}1094}1095else1096g_Config.proAdhocServer = changeProAdhocServer;10971098g_Config.bTexHardwareScaling = g_Config.sTextureShaderName != "Off";10991100if (gpu && (g_Config.iTexScalingType != iTexScalingType_prev1101|| g_Config.iTexScalingLevel != iTexScalingLevel_prev1102|| g_Config.sTextureShaderName != sTextureShaderName_prev))1103{1104gpu->NotifyConfigChanged();1105}11061107if (g_Config.iLanguage < 0)1108g_Config.iLanguage = get_language_auto();11091110g_Config.sLanguageIni = map_psp_language_to_i18n_locale(g_Config.iLanguage);1111g_i18nrepo.LoadIni(g_Config.sLanguageIni);11121113// Cannot detect refresh rate changes if:1114// > Frame skipping is enabled1115// > Frame duplication is enabled1116detectVsyncSwapInterval &=1117!g_Config.bAutoFrameSkip &&1118(g_Config.iFrameSkip == 0) &&1119!g_Config.bRenderDuplicateFrames;11201121bool updateAvInfo = false;1122bool updateGeometry = false;11231124if (!detectVsyncSwapInterval && (vsyncSwapInterval != 1))1125{1126vsyncSwapInterval = 1;1127updateAvInfo = true;1128}11291130if (g_Config.iInternalResolution != iInternalResolution_prev && backend != RETRO_HW_CONTEXT_NONE)1131{1132coreParam.pixelWidth = coreParam.renderWidth = g_Config.iInternalResolution * NATIVEWIDTH;1133coreParam.pixelHeight = coreParam.renderHeight = g_Config.iInternalResolution * NATIVEHEIGHT;11341135if (gpu)1136{1137retro_system_av_info avInfo;1138retro_get_system_av_info(&avInfo);1139environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avInfo);1140updateAvInfo = false;1141gpu->NotifyDisplayResized();1142}1143}11441145if (g_Config.bDisplayCropTo16x9 != bDisplayCropTo16x9_prev && PSP_IsInited())1146{1147updateGeometry = true;1148if (gpu)1149gpu->NotifyDisplayResized();1150}11511152#if 0 // see issue #167861153if (g_Config.iMultiSampleLevel != iMultiSampleLevel_prev && PSP_IsInited())1154{1155if (gpu)1156{1157gpu->NotifyRenderResized();1158}1159}1160#endif11611162if (updateAvInfo)1163{1164retro_system_av_info avInfo;1165retro_get_system_av_info(&avInfo);1166environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avInfo);1167}1168else if (updateGeometry)1169{1170retro_system_av_info avInfo;1171retro_get_system_av_info(&avInfo);1172environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &avInfo);1173}11741175set_variable_visibility();1176}11771178void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }1179void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; }1180void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }1181void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }11821183static const struct retro_controller_description psp_controllers[] =1184{1185{ "PSP", RETRO_DEVICE_JOYPAD },1186{ NULL, 0 }1187};11881189static const struct retro_controller_info ports[] =1190{1191{ psp_controllers, 1 },1192{ NULL, 0 }1193};11941195void retro_init(void)1196{1197TimeInit();11981199struct retro_log_callback log;1200if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))1201{1202log_cb = log.log;1203LogManager::Init(&g_Config.bEnableLogging);1204printfLogger = new PrintfLogger(log);1205LogManager* logman = LogManager::GetInstance();1206logman->RemoveListener(logman->GetStdioListener());1207logman->RemoveListener(logman->GetDebuggerListener());1208logman->ChangeFileLog(nullptr);1209logman->AddListener(printfLogger);1210}12111212VsyncSwapIntervalReset();12131214struct retro_input_descriptor desc[] = {1215{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },1216{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },1217{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },1218{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },1219{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Cross" },1220{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Circle" },1221{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Triangle" },1222{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Square" },1223{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L" },1224{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" },1225{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },1226{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },1227{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },1228{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },1229{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },1230{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },1231{ 0 },1232};1233environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);1234environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);12351236if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL))1237libretro_supports_bitmasks = true;12381239g_Config.Load("", "");1240g_Config.iInternalResolution = 0;12411242// Log levels must be set after g_Config.Load1243LogManager::GetInstance()->SetAllLogLevels(LogLevel::LINFO);12441245const char* nickname = NULL;1246if (environ_cb(RETRO_ENVIRONMENT_GET_USERNAME, &nickname) && nickname)1247g_Config.sNickName = std::string(nickname);12481249Path retro_base_dir;1250Path retro_save_dir;1251const char* dir_ptr = NULL;1252if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir_ptr) && dir_ptr)1253retro_base_dir = Path(dir_ptr);12541255if (environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &dir_ptr) && dir_ptr)1256retro_save_dir = Path(dir_ptr);12571258retro_base_dir /= "PPSSPP";12591260g_Config.currentDirectory = retro_base_dir;1261g_Config.defaultCurrentDirectory = retro_base_dir;1262g_Config.memStickDirectory = retro_save_dir;1263g_Config.flash0Directory = retro_base_dir / "flash0";1264g_Config.internalDataDirectory = retro_base_dir;1265g_Config.bEnableNetworkChat = false;1266g_Config.bDiscordPresence = false;12671268g_VFS.Register("", new DirectoryReader(retro_base_dir));12691270g_threadManager.Init(cpu_info.num_cores, cpu_info.logical_cpu_count);12711272init_output_audio_buffer(2048);1273}12741275void retro_deinit(void)1276{1277g_threadManager.Teardown();1278LogManager::Shutdown();1279log_cb = NULL;12801281delete printfLogger;1282printfLogger = nullptr;12831284libretro_supports_bitmasks = false;1285libretro_supports_option_categories = false;12861287VsyncSwapIntervalReset();12881289free_output_audio_buffer();1290}12911292void retro_set_controller_port_device(unsigned port, unsigned device)1293{1294(void)port;1295(void)device;1296}12971298void retro_get_system_info(struct retro_system_info *info)1299{1300*info = {};1301info->library_name = "PPSSPP";1302info->library_version = PPSSPP_GIT_VERSION;1303info->need_fullpath = true;1304info->valid_extensions = "elf|iso|cso|prx|pbp|chd";1305}13061307void retro_get_system_av_info(struct retro_system_av_info *info)1308{1309*info = {};1310info->timing.fps = (60.0 / 1.001) / (double)vsyncSwapInterval;1311info->timing.sample_rate = SAMPLERATE;13121313info->geometry.base_width = g_Config.iInternalResolution * NATIVEWIDTH;1314info->geometry.base_height = g_Config.iInternalResolution * NATIVEHEIGHT;1315info->geometry.max_width = g_Config.iInternalResolution * NATIVEWIDTH;1316info->geometry.max_height = g_Config.iInternalResolution * NATIVEHEIGHT;13171318if (g_Config.bDisplayCropTo16x9)1319info->geometry.base_height -= g_Config.iInternalResolution * 2;13201321info->geometry.aspect_ratio = (float)info->geometry.base_width / (float)info->geometry.base_height;13221323PSP_CoreParameter().pixelWidth = PSP_CoreParameter().renderWidth = info->geometry.base_width;1324PSP_CoreParameter().pixelHeight = PSP_CoreParameter().renderHeight = info->geometry.base_height;13251326/* Must reset context to resize render area properly while running,1327* but not necessary with software, and not working with Vulkan.. (TODO) */1328if (PSP_IsInited() && ctx && backend != RETRO_HW_CONTEXT_NONE && ctx->GetGPUCore() != GPUCORE_VULKAN)1329((LibretroHWRenderContext *)Libretro::ctx)->ContextReset();1330}13311332unsigned retro_api_version(void) { return RETRO_API_VERSION; }13331334namespace Libretro1335{1336bool useEmuThread = false;1337std::atomic<EmuThreadState> emuThreadState(EmuThreadState::DISABLED);13381339static std::thread emuThread;1340static void EmuFrame()1341{1342ctx->SetRenderTarget();1343if (ctx->GetDrawContext())1344ctx->GetDrawContext()->BeginFrame(Draw::DebugFlags::NONE);13451346gpu->BeginHostFrame();13471348coreState = CORE_RUNNING;1349PSP_RunLoopUntil(UINT64_MAX);13501351gpu->EndHostFrame();13521353if (ctx->GetDrawContext()) {1354ctx->GetDrawContext()->EndFrame();1355ctx->GetDrawContext()->Present(Draw::PresentMode::FIFO, 1);1356}1357}13581359static void EmuThreadFunc()1360{1361SetCurrentThreadName("Emu");13621363for (;;)1364{1365switch ((EmuThreadState)emuThreadState)1366{1367case EmuThreadState::START_REQUESTED:1368emuThreadState = EmuThreadState::RUNNING;1369/* fallthrough */1370case EmuThreadState::RUNNING:1371EmuFrame();1372break;1373case EmuThreadState::PAUSE_REQUESTED:1374emuThreadState = EmuThreadState::PAUSED;1375/* fallthrough */1376case EmuThreadState::PAUSED:1377sleep_ms(1);1378break;1379default:1380case EmuThreadState::QUIT_REQUESTED:1381emuThreadState = EmuThreadState::STOPPED;1382ctx->StopThread();1383return;1384}1385}1386}13871388void EmuThreadStart()1389{1390bool wasPaused = emuThreadState == EmuThreadState::PAUSED;1391emuThreadState = EmuThreadState::START_REQUESTED;13921393if (!wasPaused)1394{1395ctx->ThreadStart();1396emuThread = std::thread(&EmuThreadFunc);1397}1398}13991400void EmuThreadStop()1401{1402if (emuThreadState != EmuThreadState::RUNNING)1403return;14041405emuThreadState = EmuThreadState::QUIT_REQUESTED;14061407// Need to keep eating frames to allow the EmuThread to exit correctly.1408while (ctx->ThreadFrame())1409;14101411emuThread.join();1412emuThread = std::thread();1413ctx->ThreadEnd();1414}14151416void EmuThreadPause()1417{1418if (emuThreadState != EmuThreadState::RUNNING)1419return;14201421emuThreadState = EmuThreadState::PAUSE_REQUESTED;14221423ctx->ThreadFrame(); // Eat 1 frame14241425while (emuThreadState != EmuThreadState::PAUSED)1426sleep_ms(1);1427}14281429} // namespace Libretro14301431static void retro_check_backend(void)1432{1433struct retro_variable var = {0};14341435var.key = "ppsspp_backend";1436if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)1437{1438if (!strcmp(var.value, "auto"))1439backend = RETRO_HW_CONTEXT_DUMMY;1440else if (!strcmp(var.value, "opengl"))1441backend = RETRO_HW_CONTEXT_OPENGL;1442else if (!strcmp(var.value, "vulkan"))1443backend = RETRO_HW_CONTEXT_VULKAN;1444else if (!strcmp(var.value, "d3d11"))1445backend = RETRO_HW_CONTEXT_DIRECT3D;1446else if (!strcmp(var.value, "none"))1447backend = RETRO_HW_CONTEXT_NONE;1448}1449}14501451bool retro_load_game(const struct retro_game_info *game)1452{1453retro_pixel_format fmt = retro_pixel_format::RETRO_PIXEL_FORMAT_XRGB8888;1454if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))1455{1456ERROR_LOG(Log::System, "XRGB8888 is not supported.\n");1457return false;1458}14591460retro_check_backend();14611462coreState = CORE_POWERUP;1463ctx = LibretroGraphicsContext::CreateGraphicsContext();1464INFO_LOG(Log::System, "Using %s backend", ctx->Ident());14651466Core_SetGraphicsContext(ctx);1467SetGPUBackend((GPUBackend)g_Config.iGPUBackend);14681469useEmuThread = ctx->GetGPUCore() == GPUCORE_GLES;14701471// default to interpreter to allow startup in platforms w/o JIT capability1472g_Config.iCpuCore = (int)CPUCore::INTERPRETER;14731474CoreParameter coreParam = {};1475coreParam.enableSound = true;1476coreParam.fileToStart = Path(std::string(game->path));1477coreParam.mountIso.clear();1478coreParam.startBreak = false;1479coreParam.headLess = true;1480coreParam.graphicsContext = ctx;1481coreParam.gpuCore = ctx->GetGPUCore();1482check_variables(coreParam);14831484// TODO: OpenGL goes black when inited with software rendering,1485// therefore start without, set back after init, and reset.1486softwareRenderInitHack = ctx->GetGPUCore() == GPUCORE_GLES && g_Config.bSoftwareRendering;1487if (softwareRenderInitHack)1488g_Config.bSoftwareRendering = false;14891490// set cpuCore from libretro setting variable1491coreParam.cpuCore = (CPUCore)g_Config.iCpuCore;14921493std::string error_string;1494if (!PSP_InitStart(coreParam, &error_string))1495{1496ERROR_LOG(Log::Boot, "%s", error_string.c_str());1497return false;1498}14991500struct retro_core_option_display option_display;15011502// Show/hide 'MSAA' and 'Texture Shader' options, Vulkan only1503option_display.visible = (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) ? true : false;1504#if 0 // see issue #167861505option_display.key = "ppsspp_mulitsample_level";1506environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);1507#endif1508option_display.key = "ppsspp_texture_shader";1509environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);15101511// Show/hide 'Buffered Frames' option, Vulkan/GL only1512option_display.visible = (g_Config.iGPUBackend == (int)GPUBackend::VULKAN ||1513g_Config.iGPUBackend == (int)GPUBackend::OPENGL) ? true : false;1514option_display.key = "ppsspp_inflight_frames";1515environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);15161517set_variable_visibility();15181519return true;1520}15211522void retro_unload_game(void)1523{1524if (Libretro::useEmuThread)1525Libretro::EmuThreadStop();15261527PSP_Shutdown();1528g_VFS.Clear();15291530delete ctx;1531ctx = nullptr;1532PSP_CoreParameter().graphicsContext = nullptr;1533}15341535void retro_reset(void)1536{1537std::string error_string;15381539PSP_Shutdown();15401541if (!PSP_Init(PSP_CoreParameter(), &error_string))1542{1543ERROR_LOG(Log::Boot, "%s", error_string.c_str());1544environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, nullptr);1545}1546}15471548static void retro_input(void)1549{1550unsigned i;1551int16_t ret = 0;1552// clang-format off1553static struct1554{1555u32 retro;1556u32 sceCtrl;1557} map[] = {1558{ RETRO_DEVICE_ID_JOYPAD_UP, CTRL_UP },1559{ RETRO_DEVICE_ID_JOYPAD_DOWN, CTRL_DOWN },1560{ RETRO_DEVICE_ID_JOYPAD_LEFT, CTRL_LEFT },1561{ RETRO_DEVICE_ID_JOYPAD_RIGHT, CTRL_RIGHT },1562{ RETRO_DEVICE_ID_JOYPAD_X, CTRL_TRIANGLE },1563{ RETRO_DEVICE_ID_JOYPAD_A, CTRL_CIRCLE },1564{ RETRO_DEVICE_ID_JOYPAD_B, CTRL_CROSS },1565{ RETRO_DEVICE_ID_JOYPAD_Y, CTRL_SQUARE },1566{ RETRO_DEVICE_ID_JOYPAD_L, CTRL_LTRIGGER },1567{ RETRO_DEVICE_ID_JOYPAD_R, CTRL_RTRIGGER },1568{ RETRO_DEVICE_ID_JOYPAD_START, CTRL_START },1569{ RETRO_DEVICE_ID_JOYPAD_SELECT, CTRL_SELECT },1570};1571// clang-format on15721573input_poll_cb();15741575if (libretro_supports_bitmasks)1576ret = input_state_cb(0, RETRO_DEVICE_JOYPAD,15770, RETRO_DEVICE_ID_JOYPAD_MASK);1578else1579{1580for (i = RETRO_DEVICE_ID_JOYPAD_B; i <= RETRO_DEVICE_ID_JOYPAD_R; i++)1581if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, i))1582ret |= (1 << i);1583}15841585for (i = 0; i < sizeof(map) / sizeof(*map); i++)1586{1587bool pressed = ret & (1 << map[i].retro);15881589if (pressed)1590{1591__CtrlUpdateButtons(map[i].sceCtrl, 0);1592}1593else1594{1595__CtrlUpdateButtons(0, map[i].sceCtrl);1596}1597}15981599float x_left = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X) / 32767.0f;1600float y_left = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y) / -32767.0f;1601float x_right = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X) / 32767.0f;1602float y_right = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y) / -32767.0f;16031604__CtrlSetAnalogXY(CTRL_STICK_LEFT, x_left, y_left);1605__CtrlSetAnalogXY(CTRL_STICK_RIGHT, x_right, y_right);16061607// Analog circle vs square gate compensation1608// copied from ControlMapper.cpp's ConvertAnalogStick function1609const bool isCircular = g_Config.bAnalogIsCircular;16101611float norm = std::max(fabsf(x_left), fabsf(y_left));16121613if (norm == 0.0f)1614return;16151616if (isCircular) {1617float newNorm = sqrtf(x_left * x_left + y_left * y_left);1618float factor = newNorm / norm;1619x_left *= factor;1620y_left *= factor;1621norm = newNorm;1622}16231624float mappedNorm = norm;1625x_left = Libretro::clamp(x_left / norm * mappedNorm, -1.0f, 1.0f);1626y_left = Libretro::clamp(y_left / norm * mappedNorm, -1.0f, 1.0f);16271628__CtrlSetAnalogXY(CTRL_STICK_LEFT, x_left, y_left);1629__CtrlSetAnalogXY(CTRL_STICK_RIGHT, x_right, y_right);1630}16311632void retro_run(void)1633{1634if (PSP_IsIniting())1635{1636std::string error_string;1637while (!PSP_InitUpdate(&error_string))1638sleep_ms(4);16391640if (!PSP_IsInited())1641{1642ERROR_LOG(Log::Boot, "%s", error_string.c_str());1643environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, nullptr);1644return;1645}16461647if (softwareRenderInitHack)1648{1649log_cb(RETRO_LOG_DEBUG, "Software rendering init hack for opengl triggered.\n");1650softwareRenderInitHack = false;1651g_Config.bSoftwareRendering = true;1652retro_reset();1653}1654}16551656check_variables(PSP_CoreParameter());16571658retro_input();16591660if (useEmuThread)1661{1662if ( emuThreadState == EmuThreadState::PAUSED ||1663emuThreadState == EmuThreadState::PAUSE_REQUESTED)1664{1665VsyncSwapIntervalDetect();1666ctx->SwapBuffers();1667return;1668}16691670if (emuThreadState != EmuThreadState::RUNNING)1671EmuThreadStart();16721673if (!ctx->ThreadFrame())1674{1675VsyncSwapIntervalDetect();1676return;1677}1678}1679else1680EmuFrame();16811682VsyncSwapIntervalDetect();1683ctx->SwapBuffers();1684upload_output_audio_buffer();1685}16861687unsigned retro_get_region(void) { return RETRO_REGION_NTSC; }16881689bool retro_load_game_special(unsigned type, const struct retro_game_info *info, size_t num) { return false; }16901691namespace SaveState1692{1693struct SaveStart1694{1695void DoState(PointerWrap &p);1696};1697} // namespace SaveState16981699size_t retro_serialize_size(void)1700{1701if (!gpu) // The HW renderer isn't ready on first pass.1702return 134217728; // 128MB ought to be enough for anybody.17031704SaveState::SaveStart state;1705// TODO: Libretro API extension to use the savestate queue1706if (useEmuThread)1707EmuThreadPause();17081709return (CChunkFileReader::MeasurePtr(state) + 0x800000) & ~0x7FFFFF;1710// We don't unpause intentionally1711}17121713bool retro_serialize(void *data, size_t size)1714{1715if (!gpu) // The HW renderer isn't ready on first pass.1716return false;17171718// TODO: Libretro API extension to use the savestate queue1719if (useEmuThread)1720EmuThreadPause(); // Does nothing if already paused17211722size_t measuredSize;1723SaveState::SaveStart state;1724auto err = CChunkFileReader::MeasureAndSavePtr(state, (u8 **)&data, &measuredSize);1725bool retVal = err == CChunkFileReader::ERROR_NONE;17261727if (useEmuThread)1728{1729EmuThreadStart();1730sleep_ms(4);1731}17321733return retVal;1734}17351736bool retro_unserialize(const void *data, size_t size)1737{1738if (!gpu) // The HW renderer isn't ready on first pass.1739return false;17401741// TODO: Libretro API extension to use the savestate queue1742if (useEmuThread)1743EmuThreadPause(); // Does nothing if already paused17441745std::string errorString;1746SaveState::SaveStart state;1747bool retVal = CChunkFileReader::LoadPtr((u8 *)data, state, &errorString)1748== CChunkFileReader::ERROR_NONE;17491750if (useEmuThread)1751{1752EmuThreadStart();1753sleep_ms(4);1754}17551756return retVal;1757}17581759void *retro_get_memory_data(unsigned id)1760{1761if ( id == RETRO_MEMORY_SYSTEM_RAM )1762return Memory::GetPointerWriteUnchecked(PSP_GetKernelMemoryBase()) ;1763return NULL;1764}17651766size_t retro_get_memory_size(unsigned id)1767{1768if ( id == RETRO_MEMORY_SYSTEM_RAM )1769return Memory::g_MemorySize ;1770return 0;1771}17721773void retro_cheat_reset(void) {1774// Init Cheat Engine1775CWCheatEngine *cheatEngine = new CWCheatEngine(g_paramSFO.GetDiscID());1776Path file=cheatEngine->CheatFilename();17771778// Output cheats to cheat file1779std::ofstream outFile;1780outFile.open(file.c_str());1781outFile << "_S " << g_paramSFO.GetDiscID() << std::endl;1782outFile.close();17831784g_Config.bReloadCheats = true;17851786// Parse and Run the Cheats1787cheatEngine->ParseCheats();1788if (cheatEngine->HasCheats()) {1789cheatEngine->Run();1790}17911792}17931794void retro_cheat_set(unsigned index, bool enabled, const char *code) {1795// Initialize Cheat Engine1796CWCheatEngine *cheatEngine = new CWCheatEngine(g_paramSFO.GetDiscID());1797cheatEngine->CreateCheatFile();1798Path file=cheatEngine->CheatFilename();17991800// Read cheats file1801std::vector<std::string> cheats;1802std::ifstream cheat_content(file.c_str());1803std::stringstream buffer;1804buffer << cheat_content.rdbuf();1805std::string existing_cheats=ReplaceAll(buffer.str(), std::string("\n_C"), std::string("|"));1806SplitString(existing_cheats, '|', cheats);18071808// Generate Cheat String1809std::stringstream cheat("");1810cheat << (enabled ? "1 " : "0 ") << index << std::endl;1811std::string code_str(code);1812std::vector<std::string> codes;1813code_str=ReplaceAll(code_str, std::string(" "), std::string("+"));1814SplitString(code_str, '+', codes);1815int part=0;1816for (int i=0; i < codes.size(); i++) {1817if (codes[i].size() <= 2) {1818// _L _M ..etc1819// Assume _L1820} else if (part == 0) {1821cheat << "_L " << codes[i] << " ";1822part++;1823} else {1824cheat << codes[i] << std::endl;1825part=0;1826}1827}18281829// Add or Replace the Cheat1830if (index + 1 < cheats.size()) {1831cheats[index + 1]=cheat.str();1832} else {1833cheats.push_back(cheat.str());1834}18351836// Output cheats to cheat file1837std::ofstream outFile;1838outFile.open(file.c_str());1839outFile << "_S " << g_paramSFO.GetDiscID() << std::endl;1840for (int i=1; i < cheats.size(); i++) {1841outFile << "_C" << cheats[i] << std::endl;1842}1843outFile.close();18441845g_Config.bReloadCheats = true;18461847// Parse and Run the Cheats1848cheatEngine->ParseCheats();1849if (cheatEngine->HasCheats()) {1850cheatEngine->Run();1851}1852}18531854int64_t System_GetPropertyInt(SystemProperty prop)1855{1856switch (prop)1857{1858case SYSPROP_AUDIO_SAMPLE_RATE:1859return SAMPLERATE;1860#if PPSSPP_PLATFORM(ANDROID)1861case SYSPROP_SYSTEMVERSION: {1862char sdk[PROP_VALUE_MAX] = {0};1863if (__system_property_get("ro.build.version.sdk", sdk) != 0) {1864return atoi(sdk);1865}1866return -1;1867}1868#endif1869default:1870break;1871}18721873return -1;1874}18751876float System_GetPropertyFloat(SystemProperty prop)1877{1878switch (prop)1879{1880case SYSPROP_DISPLAY_REFRESH_RATE:1881return 60.0f / 1.001f;1882case SYSPROP_DISPLAY_SAFE_INSET_LEFT:1883case SYSPROP_DISPLAY_SAFE_INSET_RIGHT:1884case SYSPROP_DISPLAY_SAFE_INSET_TOP:1885case SYSPROP_DISPLAY_SAFE_INSET_BOTTOM:1886return 0.0f;1887default:1888break;1889}18901891return -1;1892}18931894bool System_GetPropertyBool(SystemProperty prop)1895{1896switch (prop)1897{1898case SYSPROP_CAN_JIT:1899#if PPSSPP_PLATFORM(IOS)1900bool can_jit;1901return (environ_cb(RETRO_ENVIRONMENT_GET_JIT_CAPABLE, &can_jit) && can_jit);1902#else1903return true;1904#endif1905default:1906return false;1907}1908}19091910std::string System_GetProperty(SystemProperty prop) { return ""; }1911std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) { return std::vector<std::string>(); }19121913void System_Notify(SystemNotification notification) {1914switch (notification) {1915default:1916break;1917}1918}1919bool System_MakeRequest(SystemRequestType type, int requestId, const std::string ¶m1, const std::string ¶m2, int64_t param3, int64_t param4) { return false; }1920void System_PostUIMessage(UIMessage message, const std::string ¶m) {}1921void NativeFrame(GraphicsContext *graphicsContext) {}1922void NativeResized() {}19231924void System_Toast(std::string_view str) {}19251926inline int16_t Clamp16(int32_t sample) {1927if (sample < -32767) return -32767;1928if (sample > 32767) return 32767;1929return sample;1930}19311932void System_AudioPushSamples(const int32_t *audio, int numSamples) {1933// Convert to 16-bit audio for further processing.1934int16_t buffer[1024 * 2];1935int origSamples = numSamples * 2;19361937while (numSamples > 0) {1938int blockSize = std::min(1024, numSamples);1939for (int i = 0; i < blockSize; i++) {1940buffer[i * 2] = Clamp16(audio[i * 2]);1941buffer[i * 2 + 1] = Clamp16(audio[i * 2 + 1]);1942}19431944numSamples -= blockSize;1945}19461947if (output_audio_buffer.capacity - output_audio_buffer.size < origSamples)1948ensure_output_audio_buffer_capacity((output_audio_buffer.capacity + origSamples) * 1.5);1949memcpy(output_audio_buffer.data + output_audio_buffer.size, buffer, origSamples * sizeof(*output_audio_buffer.data));1950output_audio_buffer.size += origSamples;1951}19521953void System_AudioGetDebugStats(char *buf, size_t bufSize) { if (buf) buf[0] = '\0'; }1954void System_AudioClear() {}19551956#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)1957std::vector<std::string> System_GetCameraDeviceList() { return std::vector<std::string>(); }1958bool System_AudioRecordingIsAvailable() { return false; }1959bool System_AudioRecordingState() { return false; }19601961#endif19621963// TODO: To avoid having to define these here, these should probably be turned into system "requests".1964bool NativeSaveSecret(std::string_view nameOfSecret, std::string_view data) { return false; }1965std::string NativeLoadSecret(std::string_view nameOfSecret) {1966return "";1967}196819691970