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