1 #include "ppsspp_config.h"
2 #include <cstring>
3 #include <cassert>
4 #include <thread>
5 #include <atomic>
6 #include <vector>
7 #include <cstdlib>
8 
9 #include "Common/CPUDetect.h"
10 #include "Common/Log.h"
11 #include "Common/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/ConsoleListener.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/AssetReader.h"
24 #include "Common/Data/Text/I18n.h"
25 
26 #include "Core/Config.h"
27 #include "Core/ConfigValues.h"
28 #include "Core/Core.h"
29 #include "Core/HLE/sceCtrl.h"
30 #include "Core/HLE/sceUtility.h"
31 #include "Core/HLE/__sceAudio.h"
32 #include "Core/HW/MemoryStick.h"
33 #include "Core/Host.h"
34 #include "Core/MemMap.h"
35 #include "Core/System.h"
36 
37 #include "GPU/GPUState.h"
38 #include "GPU/GPUInterface.h"
39 #include "GPU/Common/FramebufferManagerCommon.h"
40 #include "GPU/Common/TextureScalerCommon.h"
41 #include "GPU/Common/PresentationCommon.h"
42 
43 #include "libretro/libretro.h"
44 #include "libretro/LibretroGraphicsContext.h"
45 
46 #if PPSSPP_PLATFORM(ANDROID)
47 #include <sys/system_properties.h>
48 #endif
49 
50 #define DIR_SEP "/"
51 #ifdef _WIN32
52 #define DIR_SEP_CHRS "/\\"
53 #else
54 #define DIR_SEP_CHRS "/"
55 #endif
56 
57 #define SAMPLERATE 44100
58 
59 static bool libretro_supports_bitmasks = false;
60 
61 namespace Libretro
62 {
63    LibretroGraphicsContext *ctx;
64    retro_environment_t environ_cb;
65    static retro_audio_sample_batch_t audio_batch_cb;
66    static retro_input_poll_t input_poll_cb;
67    static retro_input_state_t input_state_cb;
68 } // namespace Libretro
69 
70 using namespace Libretro;
71 
72 class LibretroHost : public Host
73 {
74    public:
LibretroHost()75       LibretroHost() {}
InitGraphics(std::string * error_message,GraphicsContext ** ctx)76       bool InitGraphics(std::string *error_message, GraphicsContext **ctx) override { return true; }
ShutdownGraphics()77       void ShutdownGraphics() override {}
InitSound()78       void InitSound() override {}
UpdateSound()79       void UpdateSound() override
80       {
81          extern int hostAttemptBlockSize;
82          const int blockSizeMax = 512;
83          static int16_t audio[blockSizeMax * 2];
84          assert(hostAttemptBlockSize <= blockSizeMax);
85 
86          int samples = __AudioMix(audio, hostAttemptBlockSize, SAMPLERATE);
87          audio_batch_cb(audio, samples);
88       }
ShutdownSound()89       void ShutdownSound() override {}
IsDebuggingEnabled()90       bool IsDebuggingEnabled() override { return false; }
AttemptLoadSymbolMap()91       bool AttemptLoadSymbolMap() override { return false; }
92 };
93 
94 class PrintfLogger : public LogListener
95 {
96    public:
PrintfLogger(retro_log_callback log)97       PrintfLogger(retro_log_callback log) : log_(log.log) {}
Log(const LogMessage & message)98       void Log(const LogMessage &message)
99       {
100          switch (message.level)
101          {
102             case LogTypes::LVERBOSE:
103             case LogTypes::LDEBUG:
104                log_(RETRO_LOG_DEBUG, "[%s] %s",
105                      message.log, message.msg.c_str());
106                break;
107 
108             case LogTypes::LERROR:
109                log_(RETRO_LOG_ERROR, "[%s] %s",
110                      message.log, message.msg.c_str());
111                break;
112             case LogTypes::LNOTICE:
113             case LogTypes::LWARNING:
114                log_(RETRO_LOG_WARN, "[%s] %s",
115                      message.log, message.msg.c_str());
116                break;
117             case LogTypes::LINFO:
118             default:
119                log_(RETRO_LOG_INFO, "[%s] %s",
120                      message.log, message.msg.c_str());
121                break;
122          }
123       }
124 
125    private:
126       retro_log_printf_t log_;
127 };
128 static PrintfLogger *printfLogger;
129 
130 template <typename T> class RetroOption
131 {
132    public:
RetroOption(const char * id,const char * name,std::initializer_list<std::pair<const char *,T>> list)133       RetroOption(const char *id, const char *name, std::initializer_list<std::pair<const char *, T>> list) : id_(id), name_(name), list_(list.begin(), list.end()) {}
RetroOption(const char * id,const char * name,std::initializer_list<const char * > list)134       RetroOption(const char *id, const char *name, std::initializer_list<const char *> list) : id_(id), name_(name) {
135          for (auto option : list)
136             list_.push_back({ option, (T)list_.size() });
137       }
RetroOption(const char * id,const char * name,T first,std::initializer_list<const char * > list)138       RetroOption(const char *id, const char *name, T first, std::initializer_list<const char *> list) : id_(id), name_(name) {
139          for (auto option : list)
140             list_.push_back({ option, first + (int)list_.size() });
141       }
RetroOption(const char * id,const char * name,T first,int count,int step=1)142       RetroOption(const char *id, const char *name, T first, int count, int step = 1) : id_(id), name_(name) {
143          for (T i = first; i < first + count; i += step)
144             list_.push_back({ std::to_string(i), i });
145       }
RetroOption(const char * id,const char * name,bool initial)146       RetroOption(const char *id, const char *name, bool initial) : id_(id), name_(name) {
147          list_.push_back({ initial ? "enabled" : "disabled", initial });
148          list_.push_back({ !initial ? "enabled" : "disabled", !initial });
149       }
150 
GetOptions()151       retro_variable GetOptions()
152       {
153          if (options_.empty())
154          {
155             options_ = name_;
156             options_.push_back(';');
157             for (auto &option : list_)
158             {
159                if (option.first == list_.begin()->first)
160                   options_ += std::string(" ") + option.first;
161                else
162                   options_ += std::string("|") + option.first;
163             }
164          }
165          return { id_, options_.c_str() };
166       }
167 
Update(T * dest)168       bool Update(T *dest)
169       {
170          retro_variable var{ id_ };
171          T val = list_.front().second;
172 
173          if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
174          {
175             for (auto option : list_)
176             {
177                if (option.first == var.value)
178                {
179                   val = option.second;
180                   break;
181                }
182             }
183          }
184 
185          if (*dest != val)
186          {
187             *dest = val;
188             return true;
189          }
190 
191          return false;
192       }
193 
194    private:
195       const char *id_;
196       const char *name_;
197       std::string options_;
198       std::vector<std::pair<std::string, T>> list_;
199 };
200 
201 static RetroOption<CPUCore> ppsspp_cpu_core("ppsspp_cpu_core", "CPU Core", { { "JIT", CPUCore::JIT }, { "IR JIT", CPUCore::IR_JIT }, { "Interpreter", CPUCore::INTERPRETER } });
202 static RetroOption<int> ppsspp_locked_cpu_speed("ppsspp_locked_cpu_speed", "Locked CPU Speed", { { "off", 0 }, { "222MHz", 222 }, { "266MHz", 266 }, { "333MHz", 333 } });
203 static RetroOption<int> ppsspp_language("ppsspp_language", "Language", { { "Automatic", -1 }, { "English", PSP_SYSTEMPARAM_LANGUAGE_ENGLISH }, { "Japanese", PSP_SYSTEMPARAM_LANGUAGE_JAPANESE }, { "French", PSP_SYSTEMPARAM_LANGUAGE_FRENCH }, { "Spanish", PSP_SYSTEMPARAM_LANGUAGE_SPANISH }, { "German", PSP_SYSTEMPARAM_LANGUAGE_GERMAN }, { "Italian", PSP_SYSTEMPARAM_LANGUAGE_ITALIAN }, { "Dutch", PSP_SYSTEMPARAM_LANGUAGE_DUTCH }, { "Portuguese", PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE }, { "Russian", PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN }, { "Korean", PSP_SYSTEMPARAM_LANGUAGE_KOREAN }, { "Chinese Traditional", PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL }, { "Chinese Simplified", PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED } });
204 static RetroOption<int> ppsspp_rendering_mode("ppsspp_rendering_mode", "Rendering Mode", { { "Buffered", FB_BUFFERED_MODE }, { "Skip Buffer Effects", FB_NON_BUFFERED_MODE } });
205 static RetroOption<bool> ppsspp_auto_frameskip("ppsspp_auto_frameskip", "Auto Frameskip", false);
206 static RetroOption<int> ppsspp_frameskip("ppsspp_frameskip", "Frameskip", { "Off", "1", "2", "3", "4", "5", "6", "7", "8" });
207 static RetroOption<int> ppsspp_frameskiptype("ppsspp_frameskiptype", "Frameskip Type", { {"Number of frames", 0}, {"Percent of FPS", 1} });
208 static RetroOption<int> ppsspp_internal_resolution("ppsspp_internal_resolution", "Internal Resolution (Restart)", 1, { "480x272", "960x544", "1440x816", "1920x1088", "2400x1360", "2880x1632", "3360x1904", "3840x2176", "4320x2448", "4800x2720" });
209 static RetroOption<int> ppsspp_button_preference("ppsspp_button_preference", "Confirmation Button", { { "Cross", PSP_SYSTEMPARAM_BUTTON_CROSS }, { "Circle", PSP_SYSTEMPARAM_BUTTON_CIRCLE } });
210 static RetroOption<bool> ppsspp_fast_memory("ppsspp_fast_memory", "Fast Memory (Speedhack)", true);
211 static RetroOption<bool> ppsspp_block_transfer_gpu("ppsspp_block_transfer_gpu", "Block Transfer GPU", true);
212 static RetroOption<int> ppsspp_inflight_frames("ppsspp_inflight_frames", "Buffered frames (Slower, less lag, restart)", { { "Up to 2", 2 }, { "Up to 1", 1 }, { "No buffer", 0 }, });
213 static RetroOption<int> ppsspp_texture_scaling_level("ppsspp_texture_scaling_level", "Texture Scaling Level", { { "Off", 1 }, { "2x", 2 }, { "3x", 3 }, { "4x", 4 }, { "5x", 5 } });
214 static RetroOption<int> ppsspp_texture_scaling_type("ppsspp_texture_scaling_type", "Texture Scaling Type", { { "xbrz", TextureScalerCommon::XBRZ }, { "hybrid", TextureScalerCommon::HYBRID }, { "bicubic", TextureScalerCommon::BICUBIC }, { "hybrid_bicubic", TextureScalerCommon::HYBRID_BICUBIC } });
215 static RetroOption<std::string> ppsspp_texture_shader("ppsspp_texture_shader", "Texture Shader (Vulkan only, overrides Texture Scaling Type)", { {"Off", "Off"},  {"4xBRZ", "Tex4xBRZ"}, {"MMPX", "TexMMPX"} });
216 static RetroOption<int> ppsspp_texture_filtering("ppsspp_texture_filtering", "Texture Filtering", { { "Auto", 1 }, { "Nearest", 2 }, { "Linear", 3 }, {"Auto max quality", 4}});
217 static RetroOption<int> ppsspp_texture_anisotropic_filtering("ppsspp_texture_anisotropic_filtering", "Anisotropic Filtering", { "off", "2x", "4x", "8x", "16x" });
218 static RetroOption<int> ppsspp_lower_resolution_for_effects("ppsspp_lower_resolution_for_effects", "Lower resolution for effects", { {"Off", 0}, {"Safe", 1}, {"Balanced", 2}, {"Aggressive", 3} });
219 static RetroOption<bool> ppsspp_texture_deposterize("ppsspp_texture_deposterize", "Texture Deposterize", false);
220 static RetroOption<bool> ppsspp_texture_replacement("ppsspp_texture_replacement", "Texture Replacement", false);
221 static RetroOption<bool> ppsspp_gpu_hardware_transform("ppsspp_gpu_hardware_transform", "GPU Hardware T&L", true);
222 static RetroOption<bool> ppsspp_vertex_cache("ppsspp_vertex_cache", "Vertex Cache (Speedhack)", false);
223 static RetroOption<bool> ppsspp_cheats("ppsspp_cheats", "Internal Cheats Support", false);
224 static RetroOption<bool> ppsspp_io_threading("ppsspp_io_threading", "I/O on thread (Experimental)", true);
225 static RetroOption<IOTimingMethods> ppsspp_io_timing_method("ppsspp_io_timing_method", "IO Timing Method", { { "Fast", IOTimingMethods::IOTIMING_FAST }, { "Host", IOTimingMethods::IOTIMING_HOST }, { "Simulate UMD delays", IOTimingMethods::IOTIMING_REALISTIC } });
226 static RetroOption<bool> ppsspp_frame_duplication("ppsspp_frame_duplication", "Duplicate frames in 30hz games", false);
227 static RetroOption<bool> ppsspp_software_skinning("ppsspp_software_skinning", "Software Skinning", true);
228 static RetroOption<bool> ppsspp_ignore_bad_memory_access("ppsspp_ignore_bad_memory_access", "Ignore bad memory accesses", true);
229 static RetroOption<bool> ppsspp_lazy_texture_caching("ppsspp_lazy_texture_caching", "Lazy texture caching (Speedup)", false);
230 static RetroOption<bool> ppsspp_retain_changed_textures("ppsspp_retain_changed_textures", "Retain changed textures (Speedup, mem hog)", false);
231 static RetroOption<bool> ppsspp_force_lag_sync("ppsspp_force_lag_sync", "Force real clock sync (Slower, less lag)", false);
232 static RetroOption<int> ppsspp_spline_quality("ppsspp_spline_quality", "Spline/Bezier curves quality", { {"Low", 0}, {"Medium", 1}, {"High", 2} });
233 static RetroOption<bool> ppsspp_disable_slow_framebuffer_effects("ppsspp_disable_slow_framebuffer_effects", "Disable slower effects (Speedup)", false);
234 
retro_set_environment(retro_environment_t cb)235 void retro_set_environment(retro_environment_t cb)
236 {
237    std::vector<retro_variable> vars;
238    vars.push_back(ppsspp_internal_resolution.GetOptions());
239    vars.push_back(ppsspp_cpu_core.GetOptions());
240    vars.push_back(ppsspp_locked_cpu_speed.GetOptions());
241    vars.push_back(ppsspp_language.GetOptions());
242    vars.push_back(ppsspp_button_preference.GetOptions());
243    vars.push_back(ppsspp_rendering_mode.GetOptions());
244    vars.push_back(ppsspp_gpu_hardware_transform.GetOptions());
245    vars.push_back(ppsspp_texture_anisotropic_filtering.GetOptions());
246    vars.push_back(ppsspp_spline_quality.GetOptions());
247    vars.push_back(ppsspp_auto_frameskip.GetOptions());
248    vars.push_back(ppsspp_frameskip.GetOptions());
249    vars.push_back(ppsspp_frameskiptype.GetOptions());
250    vars.push_back(ppsspp_frame_duplication.GetOptions());
251    vars.push_back(ppsspp_vertex_cache.GetOptions());
252    vars.push_back(ppsspp_fast_memory.GetOptions());
253    vars.push_back(ppsspp_block_transfer_gpu.GetOptions());
254    vars.push_back(ppsspp_inflight_frames.GetOptions());
255    vars.push_back(ppsspp_software_skinning.GetOptions());
256    vars.push_back(ppsspp_lazy_texture_caching.GetOptions());
257    vars.push_back(ppsspp_retain_changed_textures.GetOptions());
258    vars.push_back(ppsspp_force_lag_sync.GetOptions());
259    vars.push_back(ppsspp_disable_slow_framebuffer_effects.GetOptions());
260    vars.push_back(ppsspp_lower_resolution_for_effects.GetOptions());
261    vars.push_back(ppsspp_texture_scaling_level.GetOptions());
262    vars.push_back(ppsspp_texture_scaling_type.GetOptions());
263    vars.push_back(ppsspp_texture_shader.GetOptions());
264    vars.push_back(ppsspp_texture_filtering.GetOptions());
265    vars.push_back(ppsspp_texture_deposterize.GetOptions());
266    vars.push_back(ppsspp_texture_replacement.GetOptions());
267    vars.push_back(ppsspp_io_threading.GetOptions());
268    vars.push_back(ppsspp_io_timing_method.GetOptions());
269    vars.push_back(ppsspp_ignore_bad_memory_access.GetOptions());
270    vars.push_back(ppsspp_cheats.GetOptions());
271    vars.push_back({});
272 
273    environ_cb = cb;
274 
275    cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void *)vars.data());
276 }
277 
get_language_auto(void)278 static int get_language_auto(void)
279 {
280    retro_language val = RETRO_LANGUAGE_ENGLISH;
281    environ_cb(RETRO_ENVIRONMENT_GET_LANGUAGE, &val);
282 
283    switch (val)
284    {
285       default:
286       case RETRO_LANGUAGE_ENGLISH:
287          return PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;
288       case RETRO_LANGUAGE_JAPANESE:
289          return PSP_SYSTEMPARAM_LANGUAGE_JAPANESE;
290       case RETRO_LANGUAGE_FRENCH:
291          return PSP_SYSTEMPARAM_LANGUAGE_FRENCH;
292       case RETRO_LANGUAGE_GERMAN:
293          return PSP_SYSTEMPARAM_LANGUAGE_GERMAN;
294       case RETRO_LANGUAGE_SPANISH:
295          return PSP_SYSTEMPARAM_LANGUAGE_SPANISH;
296       case RETRO_LANGUAGE_ITALIAN:
297          return PSP_SYSTEMPARAM_LANGUAGE_ITALIAN;
298       case RETRO_LANGUAGE_PORTUGUESE_BRAZIL:
299       case RETRO_LANGUAGE_PORTUGUESE_PORTUGAL:
300          return PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE;
301       case RETRO_LANGUAGE_RUSSIAN:
302          return PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN;
303       case RETRO_LANGUAGE_DUTCH:
304          return PSP_SYSTEMPARAM_LANGUAGE_DUTCH;
305       case RETRO_LANGUAGE_KOREAN:
306          return PSP_SYSTEMPARAM_LANGUAGE_KOREAN;
307       case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
308          return PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL;
309       case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
310          return PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED;
311    }
312 }
313 
map_psp_language_to_i18n_locale(int val)314 static std::string map_psp_language_to_i18n_locale(int val)
315 {
316    switch (val)
317    {
318       default:
319       case PSP_SYSTEMPARAM_LANGUAGE_ENGLISH:
320          return "en_US";
321       case PSP_SYSTEMPARAM_LANGUAGE_JAPANESE:
322          return "ja_JP";
323       case PSP_SYSTEMPARAM_LANGUAGE_FRENCH:
324          return "fr_FR";
325       case PSP_SYSTEMPARAM_LANGUAGE_GERMAN:
326          return "de_DE";
327       case PSP_SYSTEMPARAM_LANGUAGE_SPANISH:
328          return "es_ES";
329       case PSP_SYSTEMPARAM_LANGUAGE_ITALIAN:
330          return "it_IT";
331       case PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE:
332          return "pt_PT";
333       case PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN:
334          return "ru_RU";
335       case PSP_SYSTEMPARAM_LANGUAGE_DUTCH:
336          return "nl_NL";
337       case PSP_SYSTEMPARAM_LANGUAGE_KOREAN:
338          return "ko_KR";
339       case PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL:
340          return "zh_TW";
341       case PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED:
342          return "zh_CN";
343    }
344 }
345 
check_variables(CoreParameter & coreParam)346 static void check_variables(CoreParameter &coreParam)
347 {
348    bool updated = false;
349 
350    if (     coreState != CoreState::CORE_POWERUP
351          && environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated)
352          && !updated)
353       return;
354 
355    ppsspp_button_preference.Update(&g_Config.iButtonPreference);
356    ppsspp_fast_memory.Update(&g_Config.bFastMemory);
357    ppsspp_vertex_cache.Update(&g_Config.bVertexCache);
358    ppsspp_gpu_hardware_transform.Update(&g_Config.bHardwareTransform);
359    ppsspp_frameskip.Update(&g_Config.iFrameSkip);
360    ppsspp_frameskiptype.Update(&g_Config.iFrameSkipType);
361    ppsspp_auto_frameskip.Update(&g_Config.bAutoFrameSkip);
362    ppsspp_block_transfer_gpu.Update(&g_Config.bBlockTransferGPU);
363    ppsspp_texture_filtering.Update(&g_Config.iTexFiltering);
364    ppsspp_texture_anisotropic_filtering.Update(&g_Config.iAnisotropyLevel);
365    ppsspp_texture_deposterize.Update(&g_Config.bTexDeposterize);
366    ppsspp_texture_replacement.Update(&g_Config.bReplaceTextures);
367    ppsspp_cheats.Update(&g_Config.bEnableCheats);
368    ppsspp_locked_cpu_speed.Update(&g_Config.iLockedCPUSpeed);
369    ppsspp_rendering_mode.Update(&g_Config.iRenderingMode);
370    ppsspp_cpu_core.Update((CPUCore *)&g_Config.iCpuCore);
371    ppsspp_io_threading.Update(&g_Config.bSeparateIOThread);
372    ppsspp_io_timing_method.Update((IOTimingMethods *)&g_Config.iIOTimingMethod);
373    ppsspp_lower_resolution_for_effects.Update(&g_Config.iBloomHack);
374    ppsspp_frame_duplication.Update(&g_Config.bRenderDuplicateFrames);
375    ppsspp_software_skinning.Update(&g_Config.bSoftwareSkinning);
376    ppsspp_ignore_bad_memory_access.Update(&g_Config.bIgnoreBadMemAccess);
377    ppsspp_lazy_texture_caching.Update(&g_Config.bTextureBackoffCache);
378    ppsspp_retain_changed_textures.Update(&g_Config.bTextureSecondaryCache);
379    ppsspp_force_lag_sync.Update(&g_Config.bForceLagSync);
380    ppsspp_spline_quality.Update(&g_Config.iSplineBezierQuality);
381    ppsspp_disable_slow_framebuffer_effects.Update(&g_Config.bDisableSlowFramebufEffects);
382    ppsspp_inflight_frames.Update(&g_Config.iInflightFrames);
383    const bool do_scaling_type_update = ppsspp_texture_scaling_type.Update(&g_Config.iTexScalingType);
384    const bool do_scaling_level_update = ppsspp_texture_scaling_level.Update(&g_Config.iTexScalingLevel);
385    const bool do_texture_shader_update = ppsspp_texture_shader.Update(&g_Config.sTextureShaderName);
386 
387    g_Config.bTexHardwareScaling = "Off" != g_Config.sTextureShaderName;
388 
389    if (gpu && (do_scaling_type_update || do_scaling_level_update || do_texture_shader_update))
390    {
391       gpu->ClearCacheNextFrame();
392       gpu->Resized();
393    }
394 
395    ppsspp_language.Update(&g_Config.iLanguage);
396    if (g_Config.iLanguage < 0)
397       g_Config.iLanguage = get_language_auto();
398 
399    g_Config.sLanguageIni = map_psp_language_to_i18n_locale(g_Config.iLanguage);
400    i18nrepo.LoadIni(g_Config.sLanguageIni);
401 
402    if (ppsspp_internal_resolution.Update(&g_Config.iInternalResolution) && !PSP_IsInited())
403    {
404       coreParam.pixelWidth  = coreParam.renderWidth  = g_Config.iInternalResolution * 480;
405       coreParam.pixelHeight = coreParam.renderHeight = g_Config.iInternalResolution * 272;
406 
407       if (gpu)
408       {
409          retro_system_av_info av_info;
410          retro_get_system_av_info(&av_info);
411          environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &av_info);
412          gpu->Resized();
413       }
414    }
415 
416    bool isFastForwarding = environ_cb(RETRO_ENVIRONMENT_GET_FASTFORWARDING, &isFastForwarding);
417    coreParam.fastForward = isFastForwarding;
418 }
419 
retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)420 void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }
retro_set_audio_sample(retro_audio_sample_t cb)421 void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; }
retro_set_input_poll(retro_input_poll_t cb)422 void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }
retro_set_input_state(retro_input_state_t cb)423 void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }
424 
retro_init(void)425 void retro_init(void)
426 {
427    g_threadManager.Init(cpu_info.num_cores, cpu_info.logical_cpu_count);
428 
429    struct retro_input_descriptor desc[] = {
430       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
431       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
432       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
433       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
434       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Cross" },
435       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Circle" },
436       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Triangle" },
437       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Square" },
438       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L" },
439       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" },
440       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
441       { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
442       { 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
443       { 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
444       { 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
445       { 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
446       { 0 },
447    };
448    environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
449 
450    if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL))
451       libretro_supports_bitmasks = true;
452 
453    struct retro_log_callback log;
454    if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
455    {
456       LogManager::Init(&g_Config.bEnableLogging);
457       printfLogger = new PrintfLogger(log);
458       LogManager* logman = LogManager::GetInstance();
459       logman->RemoveListener(logman->GetConsoleListener());
460       logman->RemoveListener(logman->GetDebuggerListener());
461       logman->ChangeFileLog(nullptr);
462       logman->AddListener(printfLogger);
463       logman->SetAllLogLevels(LogTypes::LINFO);
464    }
465 
466    g_Config.Load("", "");
467    g_Config.iInternalResolution = 0;
468    g_Config.sMACAddress = "12:34:56:78:9A:BC";
469 
470    const char* nickname = NULL;
471    if (environ_cb(RETRO_ENVIRONMENT_GET_USERNAME, &nickname) && nickname)
472       g_Config.sNickName = std::string(nickname);
473 
474    Path retro_base_dir;
475    Path retro_save_dir;
476    const char* dir_ptr = NULL;
477    if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir_ptr) && dir_ptr)
478       retro_base_dir = Path(dir_ptr);
479 
480    if (environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &dir_ptr) && dir_ptr)
481       retro_save_dir = Path(dir_ptr);
482 
483    retro_base_dir /= "PPSSPP";
484 
485    g_Config.currentDirectory = retro_base_dir;
486    g_Config.defaultCurrentDirectory = retro_base_dir;
487    g_Config.memStickDirectory = retro_save_dir;
488    g_Config.flash0Directory = retro_base_dir / "flash0";
489    g_Config.internalDataDirectory = retro_base_dir;
490 
491    VFSRegister("", new DirectoryAssetReader(retro_base_dir));
492 
493    host = new LibretroHost();
494 }
495 
retro_deinit(void)496 void retro_deinit(void)
497 {
498    g_threadManager.Teardown();
499    LogManager::Shutdown();
500 
501    delete printfLogger;
502    printfLogger = nullptr;
503 
504    delete host;
505    host = nullptr;
506 
507    libretro_supports_bitmasks = false;
508 }
509 
retro_set_controller_port_device(unsigned port,unsigned device)510 void retro_set_controller_port_device(unsigned port, unsigned device)
511 {
512 	(void)port;
513 	(void)device;
514 }
515 
retro_get_system_info(struct retro_system_info * info)516 void retro_get_system_info(struct retro_system_info *info)
517 {
518    *info = {};
519    info->library_name     = "PPSSPP";
520    info->library_version  = PPSSPP_GIT_VERSION;
521    info->need_fullpath    = true;
522    info->valid_extensions = "elf|iso|cso|prx|pbp";
523 }
524 
retro_get_system_av_info(struct retro_system_av_info * info)525 void retro_get_system_av_info(struct retro_system_av_info *info)
526 {
527    *info = {};
528    info->timing.fps            = 60.0f / 1.001f;
529    info->timing.sample_rate    = SAMPLERATE;
530 
531    info->geometry.base_width   = g_Config.iInternalResolution * 480;
532    info->geometry.base_height  = g_Config.iInternalResolution * 272;
533    info->geometry.max_width    = g_Config.iInternalResolution * 480;
534    info->geometry.max_height   = g_Config.iInternalResolution * 272;
535    info->geometry.aspect_ratio = 480.0 / 272.0;  // Not 16:9! But very, very close.
536 }
537 
retro_api_version(void)538 unsigned retro_api_version(void) { return RETRO_API_VERSION; }
539 
540 namespace Libretro
541 {
542    bool useEmuThread = false;
543    std::atomic<EmuThreadState> emuThreadState(EmuThreadState::DISABLED);
544 
545    static std::thread emuThread;
EmuFrame()546    static void EmuFrame()
547    {
548       ctx->SetRenderTarget();
549       if (ctx->GetDrawContext())
550          ctx->GetDrawContext()->BeginFrame();
551 
552       gpu->BeginHostFrame();
553 
554       coreState = CORE_RUNNING;
555       PSP_RunLoopUntil(UINT64_MAX);
556 
557       gpu->EndHostFrame();
558 
559       if (ctx->GetDrawContext())
560          ctx->GetDrawContext()->EndFrame();
561    }
562 
EmuThreadFunc()563    static void EmuThreadFunc()
564    {
565       SetCurrentThreadName("Emu");
566 
567       for (;;)
568       {
569          switch ((EmuThreadState)emuThreadState)
570          {
571             case EmuThreadState::START_REQUESTED:
572                emuThreadState = EmuThreadState::RUNNING;
573                /* fallthrough */
574             case EmuThreadState::RUNNING:
575                EmuFrame();
576                break;
577             case EmuThreadState::PAUSE_REQUESTED:
578                emuThreadState = EmuThreadState::PAUSED;
579                /* fallthrough */
580             case EmuThreadState::PAUSED:
581                sleep_ms(1);
582                break;
583             default:
584             case EmuThreadState::QUIT_REQUESTED:
585                emuThreadState = EmuThreadState::STOPPED;
586                ctx->StopThread();
587                return;
588          }
589       }
590    }
591 
EmuThreadStart()592    void EmuThreadStart()
593    {
594       bool wasPaused = emuThreadState == EmuThreadState::PAUSED;
595       emuThreadState = EmuThreadState::START_REQUESTED;
596 
597       if (!wasPaused)
598       {
599          ctx->ThreadStart();
600          emuThread = std::thread(&EmuThreadFunc);
601       }
602    }
603 
EmuThreadStop()604    void EmuThreadStop()
605    {
606       if (emuThreadState != EmuThreadState::RUNNING)
607          return;
608 
609       emuThreadState = EmuThreadState::QUIT_REQUESTED;
610 
611       // Need to keep eating frames to allow the EmuThread to exit correctly.
612       while (ctx->ThreadFrame())
613          continue;
614 
615       emuThread.join();
616       emuThread = std::thread();
617       ctx->ThreadEnd();
618    }
619 
EmuThreadPause()620    void EmuThreadPause()
621    {
622       if (emuThreadState != EmuThreadState::RUNNING)
623          return;
624 
625       emuThreadState = EmuThreadState::PAUSE_REQUESTED;
626 
627       ctx->ThreadFrame(); // Eat 1 frame
628 
629       while (emuThreadState != EmuThreadState::PAUSED)
630          sleep_ms(1);
631    }
632 
633 } // namespace Libretro
634 
retro_load_game(const struct retro_game_info * game)635 bool retro_load_game(const struct retro_game_info *game)
636 {
637    retro_pixel_format fmt = retro_pixel_format::RETRO_PIXEL_FORMAT_XRGB8888;
638    if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
639    {
640       ERROR_LOG(SYSTEM, "XRGB8888 is not supported.\n");
641       return false;
642    }
643 
644    coreState = CORE_POWERUP;
645    ctx       = LibretroGraphicsContext::CreateGraphicsContext();
646    INFO_LOG(SYSTEM, "Using %s backend", ctx->Ident());
647 
648    Core_SetGraphicsContext(ctx);
649    SetGPUBackend((GPUBackend)g_Config.iGPUBackend);
650 
651    useEmuThread              = ctx->GetGPUCore() == GPUCORE_GLES;
652 
653    CoreParameter coreParam   = {};
654    coreParam.enableSound     = true;
655    coreParam.fileToStart     = Path(std::string(game->path));
656    coreParam.mountIso.clear();
657    coreParam.startBreak      = false;
658    coreParam.printfEmuLog    = true;
659    coreParam.headLess        = true;
660    coreParam.graphicsContext = ctx;
661    coreParam.gpuCore         = ctx->GetGPUCore();
662    coreParam.cpuCore         = (CPUCore)g_Config.iCpuCore;
663    check_variables(coreParam);
664 
665    std::string error_string;
666    if (!PSP_InitStart(coreParam, &error_string))
667    {
668       ERROR_LOG(BOOT, "%s", error_string.c_str());
669       return false;
670    }
671 
672    return true;
673 }
674 
retro_unload_game(void)675 void retro_unload_game(void)
676 {
677 	if (Libretro::useEmuThread)
678 		Libretro::EmuThreadStop();
679 
680 	PSP_Shutdown();
681 	VFSShutdown();
682 
683 	delete ctx;
684 	ctx = nullptr;
685 	PSP_CoreParameter().graphicsContext = nullptr;
686 }
687 
retro_reset(void)688 void retro_reset(void)
689 {
690    std::string error_string;
691 
692    PSP_Shutdown();
693 
694    if (!PSP_Init(PSP_CoreParameter(), &error_string))
695    {
696       ERROR_LOG(BOOT, "%s", error_string.c_str());
697       environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, nullptr);
698    }
699 }
700 
retro_input(void)701 static void retro_input(void)
702 {
703    unsigned i;
704    int16_t ret = 0;
705    // clang-format off
706    static struct
707    {
708       u32 retro;
709       u32 sceCtrl;
710    } map[] = {
711       { RETRO_DEVICE_ID_JOYPAD_UP,     CTRL_UP },
712       { RETRO_DEVICE_ID_JOYPAD_DOWN,   CTRL_DOWN },
713       { RETRO_DEVICE_ID_JOYPAD_LEFT,   CTRL_LEFT },
714       { RETRO_DEVICE_ID_JOYPAD_RIGHT,  CTRL_RIGHT },
715       { RETRO_DEVICE_ID_JOYPAD_X,      CTRL_TRIANGLE },
716       { RETRO_DEVICE_ID_JOYPAD_A,      CTRL_CIRCLE },
717       { RETRO_DEVICE_ID_JOYPAD_B,      CTRL_CROSS },
718       { RETRO_DEVICE_ID_JOYPAD_Y,      CTRL_SQUARE },
719       { RETRO_DEVICE_ID_JOYPAD_L,      CTRL_LTRIGGER },
720       { RETRO_DEVICE_ID_JOYPAD_R,      CTRL_RTRIGGER },
721       { RETRO_DEVICE_ID_JOYPAD_START,  CTRL_START },
722       { RETRO_DEVICE_ID_JOYPAD_SELECT, CTRL_SELECT },
723    };
724    // clang-format on
725 
726    input_poll_cb();
727 
728    if (libretro_supports_bitmasks)
729       ret = input_state_cb(0, RETRO_DEVICE_JOYPAD,
730             0, RETRO_DEVICE_ID_JOYPAD_MASK);
731    else
732    {
733       for (i = RETRO_DEVICE_ID_JOYPAD_B; i <= RETRO_DEVICE_ID_JOYPAD_R; i++)
734          if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, i))
735             ret |= (1 << i);
736    }
737 
738    for (i = 0; i < sizeof(map) / sizeof(*map); i++)
739    {
740       bool pressed = ret & (1 << map[i].retro);
741 
742       if (pressed)
743       {
744          __CtrlButtonDown(map[i].sceCtrl);
745       }
746       else
747       {
748          __CtrlButtonUp(map[i].sceCtrl);
749       }
750    }
751 
752    float x_left = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X) / 32767.0f;
753    float y_left = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y) / -32767.0f;
754    float x_right = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X) / 32767.0f;
755    float y_right = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y) / -32767.0f;
756    __CtrlSetAnalogXY(CTRL_STICK_LEFT, x_left, y_left);
757    __CtrlSetAnalogXY(CTRL_STICK_RIGHT, x_right, y_right);
758 }
759 
retro_run(void)760 void retro_run(void)
761 {
762    if (PSP_IsIniting())
763    {
764       std::string error_string;
765       while (!PSP_InitUpdate(&error_string))
766          sleep_ms(4);
767 
768       if (!PSP_IsInited())
769       {
770          ERROR_LOG(BOOT, "%s", error_string.c_str());
771          environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, nullptr);
772          return;
773       }
774    }
775 
776    check_variables(PSP_CoreParameter());
777 
778    retro_input();
779 
780    if (useEmuThread)
781    {
782       if(   emuThreadState == EmuThreadState::PAUSED ||
783             emuThreadState == EmuThreadState::PAUSE_REQUESTED)
784       {
785          ctx->SwapBuffers();
786          return;
787       }
788 
789       if (emuThreadState != EmuThreadState::RUNNING)
790          EmuThreadStart();
791 
792       if (!ctx->ThreadFrame())
793          return;
794    }
795    else
796       EmuFrame();
797 
798    ctx->SwapBuffers();
799 }
800 
retro_get_region(void)801 unsigned retro_get_region(void) { return RETRO_REGION_NTSC; }
802 
retro_load_game_special(unsigned type,const struct retro_game_info * info,size_t num)803 bool retro_load_game_special(unsigned type, const struct retro_game_info *info, size_t num) { return false; }
804 
805 namespace SaveState
806 {
807    struct SaveStart
808    {
809       void DoState(PointerWrap &p);
810    };
811 } // namespace SaveState
812 
retro_serialize_size(void)813 size_t retro_serialize_size(void)
814 {
815    if(!gpu) { // The HW renderer isn't ready on first pass.
816       return 134217728; // 128MB ought to be enough for anybody.
817    }
818 
819    SaveState::SaveStart state;
820    // TODO: Libretro API extension to use the savestate queue
821    if (useEmuThread)
822       EmuThreadPause();
823 
824    return (CChunkFileReader::MeasurePtr(state) + 0x800000)
825       & ~0x7FFFFF; // We don't unpause intentionally
826 }
827 
retro_serialize(void * data,size_t size)828 bool retro_serialize(void *data, size_t size)
829 {
830    if(!gpu) { // The HW renderer isn't ready on first pass.
831       return false;
832    }
833 
834    bool retVal;
835    SaveState::SaveStart state;
836    // TODO: Libretro API extension to use the savestate queue
837    if (useEmuThread)
838       EmuThreadPause(); // Does nothing if already paused
839 
840    size_t measured = CChunkFileReader::MeasurePtr(state);
841    assert(measured <= size);
842    auto err = CChunkFileReader::SavePtr((u8 *)data, state, measured);
843    retVal = err == CChunkFileReader::ERROR_NONE;
844 
845    if (useEmuThread)
846    {
847       EmuThreadStart();
848       sleep_ms(4);
849    }
850 
851    return retVal;
852 }
853 
retro_unserialize(const void * data,size_t size)854 bool retro_unserialize(const void *data, size_t size)
855 {
856    bool retVal;
857    SaveState::SaveStart state;
858    // TODO: Libretro API extension to use the savestate queue
859    if (useEmuThread)
860       EmuThreadPause(); // Does nothing if already paused
861 
862    std::string errorString;
863    retVal = CChunkFileReader::LoadPtr((u8 *)data, state, &errorString)
864       == CChunkFileReader::ERROR_NONE;
865 
866    if (useEmuThread)
867    {
868       EmuThreadStart();
869       sleep_ms(4);
870    }
871 
872    return retVal;
873 }
874 
retro_get_memory_data(unsigned id)875 void *retro_get_memory_data(unsigned id)
876 {
877    if ( id == RETRO_MEMORY_SYSTEM_RAM )
878       return Memory::GetPointerUnchecked(PSP_GetKernelMemoryBase()) ;
879    return NULL;
880 }
881 
retro_get_memory_size(unsigned id)882 size_t retro_get_memory_size(unsigned id)
883 {
884 	if ( id == RETRO_MEMORY_SYSTEM_RAM )
885 		return Memory::g_MemorySize ;
886 	return 0;
887 }
888 
retro_cheat_reset(void)889 void retro_cheat_reset(void) {}
890 
retro_cheat_set(unsigned index,bool enabled,const char * code)891 void retro_cheat_set(unsigned index, bool enabled, const char *code) { }
892 
System_GetPropertyInt(SystemProperty prop)893 int System_GetPropertyInt(SystemProperty prop)
894 {
895    switch (prop)
896    {
897       case SYSPROP_AUDIO_SAMPLE_RATE:
898          return SAMPLERATE;
899 #if PPSSPP_PLATFORM(ANDROID)
900       case SYSPROP_SYSTEMVERSION: {
901          char sdk[PROP_VALUE_MAX] = {0};
902          if (__system_property_get("ro.build.version.sdk", sdk) != 0) {
903             return atoi(sdk);
904          }
905          return -1;
906       }
907 #endif
908       default:
909          break;
910    }
911 
912    return -1;
913 }
914 
System_GetPropertyFloat(SystemProperty prop)915 float System_GetPropertyFloat(SystemProperty prop)
916 {
917    switch (prop)
918    {
919       case SYSPROP_DISPLAY_REFRESH_RATE:
920          return 60.f;
921       case SYSPROP_DISPLAY_SAFE_INSET_LEFT:
922       case SYSPROP_DISPLAY_SAFE_INSET_RIGHT:
923       case SYSPROP_DISPLAY_SAFE_INSET_TOP:
924       case SYSPROP_DISPLAY_SAFE_INSET_BOTTOM:
925          return 0.0f;
926       default:
927          break;
928    }
929 
930    return -1;
931 }
932 
System_GetProperty(SystemProperty prop)933 std::string System_GetProperty(SystemProperty prop) { return ""; }
System_GetPropertyStringVec(SystemProperty prop)934 std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) { return std::vector<std::string>(); }
935 
System_SendMessage(const char * command,const char * parameter)936 void System_SendMessage(const char *command, const char *parameter) {}
NativeUpdate()937 void NativeUpdate() {}
NativeRender(GraphicsContext * graphicsContext)938 void NativeRender(GraphicsContext *graphicsContext) {}
NativeResized()939 void NativeResized() {}
940 
941 #if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
__cameraGetDeviceList()942 std::vector<std::string> __cameraGetDeviceList() { return std::vector<std::string>(); }
audioRecording_Available()943 bool audioRecording_Available() { return false; }
audioRecording_State()944 bool audioRecording_State() { return false; }
945 
System_InputBoxGetString(const std::string & title,const std::string & defaultValue,std::function<void (bool,const std::string &)> cb)946 void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, std::function<void(bool, const std::string &)> cb) { cb(false, ""); }
947 #endif
948