Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/libretro/libretro.cpp
5651 views
1
#include "ppsspp_config.h"
2
#include <cstring>
3
#include <cassert>
4
#include <thread>
5
#include <atomic>
6
#include <vector>
7
#include <cstdlib>
8
#include <mutex>
9
10
#include "Common/CPUDetect.h"
11
#include "Common/Log.h"
12
#include "Common/Log/LogManager.h"
13
#include "Common/System/Display.h"
14
#include "Common/System/NativeApp.h"
15
#include "Common/System/System.h"
16
#include "Common/TimeUtil.h"
17
#include "Common/File/FileUtil.h"
18
#include "Common/Serialize/Serializer.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"
26
27
#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"
40
41
#include "GPU/GPUState.h"
42
#include "GPU/GPUCommon.h"
43
#include "GPU/Common/FramebufferManagerCommon.h"
44
#include "GPU/Common/TextureScalerCommon.h"
45
#include "GPU/Common/PresentationCommon.h"
46
47
#include "UI/AudioCommon.h"
48
49
#include <libretro.h>
50
#include "libretro/LibretroGraphicsContext.h"
51
#include "libretro/libretro_core_options.h"
52
53
#if PPSSPP_PLATFORM(ANDROID)
54
#include <sys/system_properties.h>
55
#endif
56
57
#define DIR_SEP "/"
58
#ifdef _WIN32
59
#define DIR_SEP_CHRS "/\\"
60
#else
61
#define DIR_SEP_CHRS "/"
62
#endif
63
64
#ifdef HAVE_LIBRETRO_VFS
65
#include "streams/file_stream.h"
66
#endif
67
68
#define SAMPLERATE 44100
69
70
/* AUDIO output buffer */
71
static struct {
72
int16_t *data;
73
int32_t size;
74
int32_t capacity;
75
} output_audio_buffer = {NULL, 0, 0};
76
77
// Calculated swap interval is 'stable' if the same
78
// value is recorded for a number of retro_run()
79
// calls equal to VSYNC_SWAP_INTERVAL_FRAMES
80
#define VSYNC_SWAP_INTERVAL_FRAMES 6
81
// Calculated swap interval is 'valid' if it is
82
// within VSYNC_SWAP_INTERVAL_THRESHOLD of an integer
83
// value
84
#define VSYNC_SWAP_INTERVAL_THRESHOLD 0.05f
85
// Swap interval detection is only enabled if the
86
// core is running at 'normal' speed - i.e. if
87
// run speed is within VSYNC_SWAP_INTERVAL_RUN_SPEED_THRESHOLD
88
// percent of 100
89
#define VSYNC_SWAP_INTERVAL_RUN_SPEED_THRESHOLD 5.0f
90
91
static bool libretro_supports_bitmasks = false;
92
static bool show_ip_address_options = true;
93
static bool show_upnp_port_option = true;
94
static bool show_detect_frame_rate_option = true;
95
static std::string changeProAdhocServer;
96
97
void* unserialize_data = NULL;
98
size_t unserialize_size = 0;
99
100
namespace Libretro
101
{
102
LibretroGraphicsContext *ctx;
103
retro_environment_t environ_cb;
104
retro_hw_context_type backend = RETRO_HW_CONTEXT_DUMMY;
105
static retro_audio_sample_batch_t audio_batch_cb;
106
static retro_input_poll_t input_poll_cb;
107
static retro_input_state_t input_state_cb;
108
static retro_log_printf_t log_cb;
109
110
bool g_pendingBoot = false;
111
std::string g_bootErrorString;
112
113
static bool detectVsyncSwapInterval = false;
114
static bool detectVsyncSwapIntervalOptShown = true;
115
static bool softwareRenderInitHack = false;
116
117
static s64 expectedTimeUsPerRun = 0;
118
static uint32_t vsyncSwapInterval = 1;
119
static uint32_t vsyncSwapIntervalLast = 1;
120
static uint32_t vsyncSwapIntervalCounter = 0;
121
static int numVBlanksLast = 0;
122
static double fpsTimeLast = 0.0;
123
static float runSpeed = 0.0f;
124
static s64 runTicksLast = 0;
125
126
static void ensure_output_audio_buffer_capacity(int32_t capacity)
127
{
128
if (capacity <= output_audio_buffer.capacity) {
129
return;
130
}
131
132
output_audio_buffer.data = (int16_t*)realloc(output_audio_buffer.data, capacity * sizeof(*output_audio_buffer.data));
133
output_audio_buffer.capacity = capacity;
134
log_cb(RETRO_LOG_DEBUG, "Output audio buffer capacity set to %d\n", capacity);
135
}
136
137
static void init_output_audio_buffer(int32_t capacity)
138
{
139
output_audio_buffer.data = NULL;
140
output_audio_buffer.size = 0;
141
output_audio_buffer.capacity = 0;
142
ensure_output_audio_buffer_capacity(capacity);
143
}
144
145
static void free_output_audio_buffer()
146
{
147
free(output_audio_buffer.data);
148
output_audio_buffer.data = NULL;
149
output_audio_buffer.size = 0;
150
output_audio_buffer.capacity = 0;
151
}
152
153
static void upload_output_audio_buffer()
154
{
155
audio_batch_cb(output_audio_buffer.data, output_audio_buffer.size / 2);
156
output_audio_buffer.size = 0;
157
}
158
159
160
/**
161
* Clamp a value to a given range.
162
*
163
* This implementation was taken from `RGBAUtil.cpp` to allow building when `std::clamp()` is unavailable.
164
*
165
* @param f The value to clamp.
166
* @param low The lower bound of the range.
167
* @param high The upper bound of the range.
168
* @return The clamped value.
169
*/
170
template <typename T>
171
static T clamp(T f, T low, T high) {
172
if (f < low)
173
return low;
174
if (f > high)
175
return high;
176
return f;
177
}
178
179
static void VsyncSwapIntervalReset()
180
{
181
expectedTimeUsPerRun = (s64)(1000000.0f / (60.0f / 1.001f));
182
vsyncSwapInterval = 1;
183
vsyncSwapIntervalLast = 1;
184
vsyncSwapIntervalCounter = 0;
185
186
numVBlanksLast = 0;
187
fpsTimeLast = 0.0;
188
runSpeed = 0.0f;
189
runTicksLast = 0;
190
191
detectVsyncSwapIntervalOptShown = true;
192
}
193
194
static void VsyncSwapIntervalDetect()
195
{
196
if (!detectVsyncSwapInterval)
197
return;
198
199
// All bets are off if core is running at
200
// the 'wrong' speed (i.e. cycle count for
201
// this run will be meaningless if internal
202
// frame rate is dropping below expected
203
// value, or fast forward is enabled)
204
double fpsTime = time_now_d();
205
int numVBlanks = __DisplayGetNumVblanks();
206
int frames = numVBlanks - numVBlanksLast;
207
208
if (frames >= VSYNC_SWAP_INTERVAL_FRAMES << 1)
209
{
210
double fps = (double)frames / (fpsTime - fpsTimeLast);
211
runSpeed = fps / ((60.0f / 1.001f) / 100.0f);
212
213
fpsTimeLast = fpsTime;
214
numVBlanksLast = numVBlanks;
215
}
216
217
float speedDelta = 100.0f - runSpeed;
218
speedDelta = (speedDelta < 0.0f) ? -speedDelta : speedDelta;
219
220
// Speed is measured relative to a 60 Hz refresh
221
// rate. If we are transitioning from a low internal
222
// frame rate to a higher internal frame rate, then
223
// 'full speed' may actually equate to
224
// (100 / current_swap_interval)...
225
if ((vsyncSwapInterval > 1) &&
226
(speedDelta >= VSYNC_SWAP_INTERVAL_RUN_SPEED_THRESHOLD))
227
{
228
speedDelta = 100.0f - (runSpeed * (float)vsyncSwapInterval);
229
speedDelta = (speedDelta < 0.0f) ? -speedDelta : speedDelta;
230
}
231
232
if (speedDelta >= VSYNC_SWAP_INTERVAL_RUN_SPEED_THRESHOLD)
233
{
234
// Swap interval detection is invalid - bail out
235
vsyncSwapIntervalCounter = 0;
236
return;
237
}
238
239
// Get elapsed time (us) for this run
240
s64 runTicks = CoreTiming::GetTicks();
241
s64 runTimeUs = cyclesToUs(runTicks - runTicksLast);
242
243
// Check if current internal frame rate is a
244
// factor of the default ~60 Hz
245
float swapRatio = (float)runTimeUs / (float)expectedTimeUsPerRun;
246
uint32_t swapInteger;
247
float swapRemainder;
248
249
// If internal frame rate is equal to (within threshold)
250
// or higher than the default ~60 Hz, fall back to a
251
// swap interval of 1
252
if (swapRatio < (1.0f + VSYNC_SWAP_INTERVAL_THRESHOLD))
253
{
254
swapInteger = 1;
255
swapRemainder = 0.0f;
256
}
257
else
258
{
259
swapInteger = (uint32_t)(swapRatio + 0.5f);
260
swapRemainder = swapRatio - (float)swapInteger;
261
swapRemainder = (swapRemainder < 0.0f) ?
262
-swapRemainder : swapRemainder;
263
}
264
265
// > Swap interval is considered 'valid' if it is
266
// within VSYNC_SWAP_INTERVAL_THRESHOLD of an integer
267
// value
268
// > If valid, check if new swap interval differs from
269
// previously logged value
270
if ((swapRemainder <= VSYNC_SWAP_INTERVAL_THRESHOLD) &&
271
(swapInteger != vsyncSwapInterval))
272
{
273
vsyncSwapIntervalCounter =
274
(swapInteger == vsyncSwapIntervalLast) ?
275
(vsyncSwapIntervalCounter + 1) : 0;
276
277
// Check whether swap interval is 'stable'
278
if (vsyncSwapIntervalCounter >= VSYNC_SWAP_INTERVAL_FRAMES)
279
{
280
vsyncSwapInterval = swapInteger;
281
vsyncSwapIntervalCounter = 0;
282
283
// Notify frontend
284
retro_system_av_info avInfo;
285
retro_get_system_av_info(&avInfo);
286
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avInfo);
287
}
288
289
vsyncSwapIntervalLast = swapInteger;
290
}
291
else
292
vsyncSwapIntervalCounter = 0;
293
294
runTicksLast = runTicks;
295
}
296
} // namespace Libretro
297
298
using namespace Libretro;
299
300
void RetroLogCallback(const LogMessage &message, void *userdata) {
301
retro_log_printf_t fn = (retro_log_printf_t)userdata;
302
switch (message.level) {
303
case LogLevel::LVERBOSE:
304
case LogLevel::LDEBUG:
305
(fn)(RETRO_LOG_DEBUG, "[%s] %s", message.log, message.msg.c_str());
306
break;
307
308
case LogLevel::LERROR:
309
(fn)(RETRO_LOG_ERROR, "[%s] %s", message.log, message.msg.c_str());
310
break;
311
case LogLevel::LNOTICE:
312
case LogLevel::LWARNING:
313
(fn)(RETRO_LOG_WARN, "[%s] %s", message.log, message.msg.c_str());
314
break;
315
case LogLevel::LINFO:
316
default:
317
(fn)(RETRO_LOG_INFO, "[%s] %s", message.log, message.msg.c_str());
318
break;
319
}
320
}
321
322
static bool set_variable_visibility(void)
323
{
324
struct retro_core_option_display option_display;
325
struct retro_variable var;
326
bool updated = false;
327
328
// Show/hide IP address options
329
bool show_ip_address_options_prev = show_ip_address_options;
330
show_ip_address_options = true;
331
332
var.key = "ppsspp_change_pro_ad_hoc_server_address";
333
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && strcmp(var.value, "IP address"))
334
show_ip_address_options = false;
335
336
if (show_ip_address_options != show_ip_address_options_prev)
337
{
338
option_display.visible = show_ip_address_options;
339
for (int i = 0; i < 12; i++)
340
{
341
char key[64] = {0};
342
option_display.key = key;
343
snprintf(key, sizeof(key), "ppsspp_pro_ad_hoc_server_address%02d", i + 1);
344
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
345
}
346
updated = true;
347
}
348
349
// Show/hide 'UPnP Use Original Port' option
350
bool show_upnp_port_option_prev = show_upnp_port_option;
351
show_upnp_port_option = true;
352
353
var.key = "ppsspp_enable_upnp";
354
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && !strcmp(var.value, "disabled"))
355
show_upnp_port_option = false;
356
357
if (show_upnp_port_option != show_upnp_port_option_prev)
358
{
359
option_display.visible = show_upnp_port_option;
360
option_display.key = "ppsspp_upnp_use_original_port";
361
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
362
updated = true;
363
}
364
365
// Show/hide 'Detect Frame Rate Changes' option
366
bool show_detect_frame_rate_option_prev = show_detect_frame_rate_option;
367
int frameskip = 0;
368
bool auto_frameskip = false;
369
bool dupe_frames = false;
370
show_detect_frame_rate_option = true;
371
372
var.key = "ppsspp_frameskip";
373
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && strcmp(var.value, "disabled"))
374
frameskip = atoi(var.value);
375
var.key = "ppsspp_auto_frameskip";
376
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && !strcmp(var.value, "enabled"))
377
auto_frameskip = true;
378
var.key = "ppsspp_frame_duplication";
379
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && !strcmp(var.value, "enabled"))
380
dupe_frames = true;
381
382
show_detect_frame_rate_option = (frameskip == 0) && !auto_frameskip && !dupe_frames;
383
if (show_detect_frame_rate_option != show_detect_frame_rate_option_prev)
384
{
385
option_display.visible = show_detect_frame_rate_option;
386
option_display.key = "ppsspp_detect_vsync_swap_interval";
387
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
388
updated = true;
389
}
390
391
return updated;
392
}
393
394
void retro_set_environment(retro_environment_t cb)
395
{
396
environ_cb = cb;
397
398
bool option_categories = false;
399
libretro_set_core_options(environ_cb, &option_categories);
400
struct retro_core_options_update_display_callback update_display_cb;
401
update_display_cb.callback = set_variable_visibility;
402
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_UPDATE_DISPLAY_CALLBACK, &update_display_cb);
403
404
#ifdef HAVE_LIBRETRO_VFS
405
struct retro_vfs_interface_info vfs_iface_info { 2, nullptr };
406
if (cb(RETRO_ENVIRONMENT_GET_VFS_INTERFACE, &vfs_iface_info))
407
File::InitLibretroVFS(&vfs_iface_info);
408
#endif
409
}
410
411
static int get_language_auto(void)
412
{
413
retro_language val = RETRO_LANGUAGE_ENGLISH;
414
environ_cb(RETRO_ENVIRONMENT_GET_LANGUAGE, &val);
415
416
switch (val)
417
{
418
default:
419
case RETRO_LANGUAGE_ENGLISH:
420
return PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;
421
case RETRO_LANGUAGE_JAPANESE:
422
return PSP_SYSTEMPARAM_LANGUAGE_JAPANESE;
423
case RETRO_LANGUAGE_FRENCH:
424
return PSP_SYSTEMPARAM_LANGUAGE_FRENCH;
425
case RETRO_LANGUAGE_GERMAN:
426
return PSP_SYSTEMPARAM_LANGUAGE_GERMAN;
427
case RETRO_LANGUAGE_SPANISH:
428
return PSP_SYSTEMPARAM_LANGUAGE_SPANISH;
429
case RETRO_LANGUAGE_ITALIAN:
430
return PSP_SYSTEMPARAM_LANGUAGE_ITALIAN;
431
case RETRO_LANGUAGE_PORTUGUESE_BRAZIL:
432
case RETRO_LANGUAGE_PORTUGUESE_PORTUGAL:
433
return PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE;
434
case RETRO_LANGUAGE_RUSSIAN:
435
return PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN;
436
case RETRO_LANGUAGE_DUTCH:
437
return PSP_SYSTEMPARAM_LANGUAGE_DUTCH;
438
case RETRO_LANGUAGE_KOREAN:
439
return PSP_SYSTEMPARAM_LANGUAGE_KOREAN;
440
case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
441
return PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL;
442
case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
443
return PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED;
444
}
445
}
446
447
static std::string map_psp_language_to_i18n_locale(int val)
448
{
449
switch (val)
450
{
451
default:
452
case PSP_SYSTEMPARAM_LANGUAGE_ENGLISH:
453
return "en_US";
454
case PSP_SYSTEMPARAM_LANGUAGE_JAPANESE:
455
return "ja_JP";
456
case PSP_SYSTEMPARAM_LANGUAGE_FRENCH:
457
return "fr_FR";
458
case PSP_SYSTEMPARAM_LANGUAGE_GERMAN:
459
return "de_DE";
460
case PSP_SYSTEMPARAM_LANGUAGE_SPANISH:
461
return "es_ES";
462
case PSP_SYSTEMPARAM_LANGUAGE_ITALIAN:
463
return "it_IT";
464
case PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE:
465
return "pt_PT";
466
case PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN:
467
return "ru_RU";
468
case PSP_SYSTEMPARAM_LANGUAGE_DUTCH:
469
return "nl_NL";
470
case PSP_SYSTEMPARAM_LANGUAGE_KOREAN:
471
return "ko_KR";
472
case PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL:
473
return "zh_TW";
474
case PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED:
475
return "zh_CN";
476
}
477
}
478
479
static void check_dynamic_variables(CoreParameter &coreParam) {
480
if (g_Config.bForceLagSync)
481
{
482
bool isFastForwarding;
483
if (environ_cb(RETRO_ENVIRONMENT_GET_FASTFORWARDING, &isFastForwarding))
484
coreParam.fastForward = isFastForwarding;
485
}
486
}
487
488
static void check_variables(CoreParameter &coreParam)
489
{
490
check_dynamic_variables(coreParam);
491
492
struct retro_variable var = {0};
493
std::string sTextureShaderName_prev;
494
int iInternalResolution_prev;
495
int iTexScalingType_prev;
496
int iTexScalingLevel_prev;
497
int iMultiSampleLevel_prev;
498
bool bDisplayCropTo16x9_prev;
499
500
var.key = "ppsspp_language";
501
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
502
{
503
if (!strcmp(var.value, "Automatic"))
504
g_Config.iLanguage = -1;
505
else if (!strcmp(var.value, "English"))
506
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;
507
else if (!strcmp(var.value, "Japanese"))
508
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_JAPANESE;
509
else if (!strcmp(var.value, "French"))
510
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_FRENCH;
511
else if (!strcmp(var.value, "Spanish"))
512
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_SPANISH;
513
else if (!strcmp(var.value, "German"))
514
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_GERMAN;
515
else if (!strcmp(var.value, "Italian"))
516
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_ITALIAN;
517
else if (!strcmp(var.value, "Dutch"))
518
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_DUTCH;
519
else if (!strcmp(var.value, "Portuguese"))
520
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE;
521
else if (!strcmp(var.value, "Russian"))
522
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN;
523
else if (!strcmp(var.value, "Korean"))
524
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_KOREAN;
525
else if (!strcmp(var.value, "Chinese Traditional"))
526
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL;
527
else if (!strcmp(var.value, "Chinese Simplified"))
528
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED;
529
}
530
531
#ifndef __EMSCRIPTEN__
532
var.key = "ppsspp_cpu_core";
533
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
534
{
535
if (!strcmp(var.value, "JIT"))
536
g_Config.iCpuCore = (int)CPUCore::JIT;
537
else if (!strcmp(var.value, "IR JIT"))
538
g_Config.iCpuCore = (int)CPUCore::IR_INTERPRETER;
539
else if (!strcmp(var.value, "Interpreter"))
540
g_Config.iCpuCore = (int)CPUCore::INTERPRETER;
541
}
542
543
if (System_GetPropertyBool(SYSPROP_CAN_JIT) == false && g_Config.iCpuCore == (int)CPUCore::JIT) {
544
// Just gonna force it to the IR interpreter on startup.
545
// We don't hide the option, but we make sure it's off on bootup. In case someone wants
546
// to experiment in future iOS versions or something...
547
g_Config.iCpuCore = (int)CPUCore::IR_INTERPRETER;
548
}
549
#else
550
g_Config.iCpuCore = (int)CPUCore::INTERPRETER;
551
#endif
552
553
var.key = "ppsspp_fast_memory";
554
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
555
{
556
if (!strcmp(var.value, "disabled"))
557
g_Config.bFastMemory = false;
558
else
559
g_Config.bFastMemory = true;
560
}
561
562
var.key = "ppsspp_ignore_bad_memory_access";
563
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
564
{
565
if (!strcmp(var.value, "disabled"))
566
g_Config.bIgnoreBadMemAccess = false;
567
else
568
g_Config.bIgnoreBadMemAccess = true;
569
}
570
571
var.key = "ppsspp_io_timing_method";
572
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
573
{
574
if (!strcmp(var.value, "Fast"))
575
g_Config.iIOTimingMethod = IOTIMING_FAST;
576
else if (!strcmp(var.value, "Host"))
577
g_Config.iIOTimingMethod = IOTIMING_HOST;
578
else if (!strcmp(var.value, "Simulate UMD delays"))
579
g_Config.iIOTimingMethod = IOTIMING_REALISTIC;
580
else if (!strcmp(var.value, "Simulate UMD slow reading speed"))
581
g_Config.iIOTimingMethod = IOTIMING_UMDSLOWREALISTIC;
582
}
583
584
var.key = "ppsspp_force_lag_sync";
585
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
586
{
587
if (!strcmp(var.value, "disabled"))
588
g_Config.bForceLagSync = false;
589
else
590
g_Config.bForceLagSync = true;
591
}
592
593
var.key = "ppsspp_locked_cpu_speed";
594
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
595
g_Config.iLockedCPUSpeed = atoi(var.value);
596
597
var.key = "ppsspp_cache_iso";
598
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
599
{
600
if (!strcmp(var.value, "disabled"))
601
g_Config.bCacheFullIsoInRam = false;
602
else
603
g_Config.bCacheFullIsoInRam = true;
604
}
605
606
var.key = "ppsspp_cheats";
607
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
608
{
609
if (!strcmp(var.value, "disabled"))
610
g_Config.bEnableCheats = false;
611
else
612
g_Config.bEnableCheats = true;
613
}
614
615
var.key = "ppsspp_psp_model";
616
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
617
{
618
if (!strcmp(var.value, "psp_1000"))
619
g_Config.iPSPModel = PSP_MODEL_FAT;
620
else if (!strcmp(var.value, "psp_2000_3000"))
621
g_Config.iPSPModel = PSP_MODEL_SLIM;
622
}
623
624
var.key = "ppsspp_button_preference";
625
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
626
{
627
if (!strcmp(var.value, "Cross"))
628
g_Config.iButtonPreference = PSP_SYSTEMPARAM_BUTTON_CROSS;
629
else if (!strcmp(var.value, "Circle"))
630
g_Config.iButtonPreference = PSP_SYSTEMPARAM_BUTTON_CIRCLE;
631
}
632
633
var.key = "ppsspp_analog_is_circular";
634
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
635
{
636
if (!strcmp(var.value, "disabled"))
637
g_Config.bAnalogIsCircular = false;
638
else
639
g_Config.bAnalogIsCircular = true;
640
}
641
642
var.key = "ppsspp_analog_deadzone";
643
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
644
g_Config.fAnalogDeadzone = atof(var.value);
645
646
var.key = "ppsspp_analog_sensitivity";
647
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
648
g_Config.fAnalogSensitivity = atof(var.value);
649
650
var.key = "ppsspp_memstick_inserted";
651
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
652
{
653
if (!strcmp(var.value, "disabled"))
654
g_Config.bMemStickInserted = false;
655
else
656
g_Config.bMemStickInserted = true;
657
}
658
659
var.key = "ppsspp_internal_resolution";
660
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
661
{
662
iInternalResolution_prev = g_Config.iInternalResolution;
663
664
if (!strcmp(var.value, "480x272"))
665
g_Config.iInternalResolution = 1;
666
else if (!strcmp(var.value, "960x544"))
667
g_Config.iInternalResolution = 2;
668
else if (!strcmp(var.value, "1440x816"))
669
g_Config.iInternalResolution = 3;
670
else if (!strcmp(var.value, "1920x1088"))
671
g_Config.iInternalResolution = 4;
672
else if (!strcmp(var.value, "2400x1360"))
673
g_Config.iInternalResolution = 5;
674
else if (!strcmp(var.value, "2880x1632"))
675
g_Config.iInternalResolution = 6;
676
else if (!strcmp(var.value, "3360x1904"))
677
g_Config.iInternalResolution = 7;
678
else if (!strcmp(var.value, "3840x2176"))
679
g_Config.iInternalResolution = 8;
680
else if (!strcmp(var.value, "4320x2448"))
681
g_Config.iInternalResolution = 9;
682
else if (!strcmp(var.value, "4800x2720"))
683
g_Config.iInternalResolution = 10;
684
685
// Force resolution to 1x without hardware context
686
if (backend == RETRO_HW_CONTEXT_NONE)
687
g_Config.iInternalResolution = 1;
688
}
689
690
var.key = "ppsspp_software_rendering";
691
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
692
{
693
if (!PSP_IsInited())
694
{
695
if (!strcmp(var.value, "disabled") && backend != RETRO_HW_CONTEXT_NONE)
696
g_Config.bSoftwareRendering = false;
697
else
698
g_Config.bSoftwareRendering = true;
699
}
700
701
// Force resolution to 1x with software rendering
702
if (g_Config.bSoftwareRendering)
703
g_Config.iInternalResolution = 1;
704
}
705
706
var.key = "ppsspp_mulitsample_level";
707
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
708
{
709
iMultiSampleLevel_prev = g_Config.iMultiSampleLevel;
710
711
if (!strcmp(var.value, "Disabled"))
712
g_Config.iMultiSampleLevel = 0;
713
else if (!strcmp(var.value, "x2"))
714
g_Config.iMultiSampleLevel = 1;
715
else if (!strcmp(var.value, "x4"))
716
g_Config.iMultiSampleLevel = 2;
717
else if (!strcmp(var.value, "x8"))
718
g_Config.iMultiSampleLevel = 3;
719
}
720
721
var.key = "ppsspp_cropto16x9";
722
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
723
{
724
bDisplayCropTo16x9_prev = g_Config.bDisplayCropTo16x9;
725
726
if (!strcmp(var.value, "disabled"))
727
g_Config.bDisplayCropTo16x9 = false;
728
else
729
g_Config.bDisplayCropTo16x9 = true;
730
}
731
732
var.key = "ppsspp_frameskip";
733
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
734
g_Config.iFrameSkip = atoi(var.value);
735
736
var.key = "ppsspp_auto_frameskip";
737
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
738
{
739
if (!strcmp(var.value, "disabled"))
740
g_Config.bAutoFrameSkip = false;
741
else
742
g_Config.bAutoFrameSkip = true;
743
}
744
745
var.key = "ppsspp_frame_duplication";
746
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
747
{
748
if (!strcmp(var.value, "disabled"))
749
g_Config.bRenderDuplicateFrames = false;
750
else
751
g_Config.bRenderDuplicateFrames = true;
752
}
753
754
var.key = "ppsspp_detect_vsync_swap_interval";
755
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
756
{
757
if (!strcmp(var.value, "disabled"))
758
detectVsyncSwapInterval = false;
759
else
760
detectVsyncSwapInterval = true;
761
}
762
763
var.key = "ppsspp_inflight_frames";
764
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
765
{
766
if (!strcmp(var.value, "No buffer"))
767
g_Config.iInflightFrames = 1;
768
else if (!strcmp(var.value, "Up to 1"))
769
g_Config.iInflightFrames = 2;
770
else if (!strcmp(var.value, "Up to 2"))
771
g_Config.iInflightFrames = 3;
772
}
773
774
var.key = "ppsspp_skip_buffer_effects";
775
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
776
{
777
if (!strcmp(var.value, "disabled"))
778
g_Config.bSkipBufferEffects = false;
779
else
780
g_Config.bSkipBufferEffects = true;
781
}
782
783
var.key = "ppsspp_disable_range_culling";
784
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
785
{
786
if (!strcmp(var.value, "disabled"))
787
g_Config.bDisableRangeCulling = false;
788
else
789
g_Config.bDisableRangeCulling = true;
790
}
791
792
var.key = "ppsspp_skip_gpu_readbacks";
793
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
794
{
795
if (!strcmp(var.value, "disabled"))
796
g_Config.iSkipGPUReadbackMode = (int)SkipGPUReadbackMode::NO_SKIP;
797
else
798
g_Config.iSkipGPUReadbackMode = (int)SkipGPUReadbackMode::SKIP;
799
}
800
801
var.key = "ppsspp_lazy_texture_caching";
802
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
803
{
804
if (!strcmp(var.value, "disabled"))
805
g_Config.bTextureBackoffCache = false;
806
else
807
g_Config.bTextureBackoffCache = true;
808
}
809
810
var.key = "ppsspp_spline_quality";
811
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
812
{
813
if (!strcmp(var.value, "Low"))
814
g_Config.iSplineBezierQuality = 0;
815
else if (!strcmp(var.value, "Medium"))
816
g_Config.iSplineBezierQuality = 1;
817
else if (!strcmp(var.value, "High"))
818
g_Config.iSplineBezierQuality = 2;
819
}
820
821
var.key = "ppsspp_gpu_hardware_transform";
822
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
823
{
824
if (!strcmp(var.value, "disabled"))
825
g_Config.bHardwareTransform = false;
826
else
827
g_Config.bHardwareTransform = true;
828
}
829
830
var.key = "ppsspp_software_skinning";
831
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
832
{
833
if (!strcmp(var.value, "disabled"))
834
g_Config.bSoftwareSkinning = false;
835
else
836
g_Config.bSoftwareSkinning = true;
837
}
838
839
var.key = "ppsspp_hardware_tesselation";
840
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
841
{
842
if (!strcmp(var.value, "disabled"))
843
g_Config.bHardwareTessellation = false;
844
else
845
g_Config.bHardwareTessellation = true;
846
}
847
848
var.key = "ppsspp_lower_resolution_for_effects";
849
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
850
{
851
if (!strcmp(var.value, "disabled"))
852
g_Config.iBloomHack = 0;
853
else if (!strcmp(var.value, "Safe"))
854
g_Config.iBloomHack = 1;
855
else if (!strcmp(var.value, "Balanced"))
856
g_Config.iBloomHack = 2;
857
else if (!strcmp(var.value, "Aggressive"))
858
g_Config.iBloomHack = 3;
859
}
860
861
var.key = "ppsspp_texture_scaling_type";
862
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
863
{
864
iTexScalingType_prev = g_Config.iTexScalingType;
865
866
if (!strcmp(var.value, "xbrz"))
867
g_Config.iTexScalingType = TextureScalerCommon::XBRZ;
868
else if (!strcmp(var.value, "hybrid"))
869
g_Config.iTexScalingType = TextureScalerCommon::HYBRID;
870
else if (!strcmp(var.value, "bicubic"))
871
g_Config.iTexScalingType = TextureScalerCommon::BICUBIC;
872
else if (!strcmp(var.value, "hybrid_bicubic"))
873
g_Config.iTexScalingType = TextureScalerCommon::HYBRID_BICUBIC;
874
}
875
876
var.key = "ppsspp_texture_scaling_level";
877
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
878
{
879
iTexScalingLevel_prev = g_Config.iTexScalingLevel;
880
881
if (!strcmp(var.value, "disabled"))
882
g_Config.iTexScalingLevel = 1;
883
else if (!strcmp(var.value, "2x"))
884
g_Config.iTexScalingLevel = 2;
885
else if (!strcmp(var.value, "3x"))
886
g_Config.iTexScalingLevel = 3;
887
else if (!strcmp(var.value, "4x"))
888
g_Config.iTexScalingLevel = 4;
889
else if (!strcmp(var.value, "5x"))
890
g_Config.iTexScalingLevel = 5;
891
}
892
893
var.key = "ppsspp_texture_deposterize";
894
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
895
{
896
if (!strcmp(var.value, "disabled"))
897
g_Config.bTexDeposterize = false;
898
else
899
g_Config.bTexDeposterize = true;
900
}
901
902
var.key = "ppsspp_texture_shader";
903
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
904
{
905
sTextureShaderName_prev = g_Config.sTextureShaderName;
906
907
if (!strcmp(var.value, "disabled"))
908
g_Config.sTextureShaderName = "Off";
909
else if (!strcmp(var.value, "2xBRZ"))
910
g_Config.sTextureShaderName = "Tex2xBRZ";
911
else if (!strcmp(var.value, "4xBRZ"))
912
g_Config.sTextureShaderName = "Tex4xBRZ";
913
else if (!strcmp(var.value, "MMPX"))
914
g_Config.sTextureShaderName = "TexMMPX";
915
}
916
917
var.key = "ppsspp_texture_anisotropic_filtering";
918
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
919
{
920
if (!strcmp(var.value, "disabled"))
921
g_Config.iAnisotropyLevel = 0;
922
else if (!strcmp(var.value, "2x"))
923
g_Config.iAnisotropyLevel = 1;
924
else if (!strcmp(var.value, "4x"))
925
g_Config.iAnisotropyLevel = 2;
926
else if (!strcmp(var.value, "8x"))
927
g_Config.iAnisotropyLevel = 3;
928
else if (!strcmp(var.value, "16x"))
929
g_Config.iAnisotropyLevel = 4;
930
}
931
932
var.key = "ppsspp_texture_filtering";
933
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
934
{
935
if (!strcmp(var.value, "Auto"))
936
g_Config.iTexFiltering = 1;
937
else if (!strcmp(var.value, "Nearest"))
938
g_Config.iTexFiltering = 2;
939
else if (!strcmp(var.value, "Linear"))
940
g_Config.iTexFiltering = 3;
941
else if (!strcmp(var.value, "Auto max quality"))
942
g_Config.iTexFiltering = 4;
943
}
944
945
var.key = "ppsspp_smart_2d_texture_filtering";
946
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
947
{
948
if (!strcmp(var.value, "disabled"))
949
g_Config.bSmart2DTexFiltering = false;
950
else
951
g_Config.bSmart2DTexFiltering = true;
952
}
953
954
var.key = "ppsspp_texture_replacement";
955
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
956
{
957
if (!strcmp(var.value, "disabled"))
958
g_Config.bReplaceTextures = false;
959
else
960
g_Config.bReplaceTextures = true;
961
}
962
963
var.key = "ppsspp_enable_wlan";
964
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
965
{
966
if (!strcmp(var.value, "disabled"))
967
g_Config.bEnableWlan = false;
968
else
969
g_Config.bEnableWlan = true;
970
}
971
972
var.key = "ppsspp_wlan_channel";
973
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
974
g_Config.iWlanAdhocChannel = atoi(var.value);
975
976
var.key = "ppsspp_enable_builtin_pro_ad_hoc_server";
977
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
978
{
979
if (!strcmp(var.value, "disabled"))
980
g_Config.bEnableAdhocServer = false;
981
else
982
g_Config.bEnableAdhocServer = true;
983
}
984
985
var.key = "ppsspp_change_pro_ad_hoc_server_address";
986
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
987
changeProAdhocServer = var.value;
988
989
var.key = "ppsspp_enable_upnp";
990
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
991
{
992
if (!strcmp(var.value, "disabled"))
993
g_Config.bEnableUPnP = false;
994
else
995
g_Config.bEnableUPnP = true;
996
}
997
998
var.key = "ppsspp_upnp_use_original_port";
999
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1000
{
1001
if (!strcmp(var.value, "disabled"))
1002
g_Config.bUPnPUseOriginalPort = false;
1003
else
1004
g_Config.bUPnPUseOriginalPort = true;
1005
}
1006
1007
var.key = "ppsspp_port_offset";
1008
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1009
g_Config.iPortOffset = atoi(var.value);
1010
1011
var.key = "ppsspp_minimum_timeout";
1012
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1013
g_Config.iMinTimeout = atoi(var.value);
1014
1015
var.key = "ppsspp_forced_first_connect";
1016
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1017
{
1018
if (!strcmp(var.value, "disabled"))
1019
g_Config.bForcedFirstConnect = false;
1020
else
1021
g_Config.bForcedFirstConnect = true;
1022
}
1023
1024
std::string ppsspp_change_mac_address[12];
1025
int ppsspp_pro_ad_hoc_ipv4[12];
1026
char key[64] = {0};
1027
var.key = key;
1028
g_Config.sMACAddress = "";
1029
g_Config.sProAdhocServer = "";
1030
for (int i = 0; i < 12; i++)
1031
{
1032
snprintf(key, sizeof(key), "ppsspp_change_mac_address%02d", i + 1);
1033
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1034
{
1035
ppsspp_change_mac_address[i] = var.value;
1036
1037
if (i && i % 2 == 0)
1038
g_Config.sMACAddress += ":";
1039
1040
g_Config.sMACAddress += ppsspp_change_mac_address[i];
1041
}
1042
1043
snprintf(key, sizeof(key), "ppsspp_pro_ad_hoc_server_address%02d", i + 1);
1044
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1045
ppsspp_pro_ad_hoc_ipv4[i] = atoi(var.value);
1046
}
1047
1048
if (g_Config.sMACAddress == "00:00:00:00:00:00")
1049
{
1050
g_Config.sMACAddress = CreateRandMAC();
1051
1052
for (int i = 0; i < 12; i++)
1053
{
1054
snprintf(key, sizeof(key), "ppsspp_change_mac_address%02d", i + 1);
1055
std::string digit = {g_Config.sMACAddress[i + i / 2]};
1056
var.value = digit.c_str();
1057
environ_cb(RETRO_ENVIRONMENT_SET_VARIABLE, &var);
1058
}
1059
}
1060
1061
if (changeProAdhocServer == "IP address")
1062
{
1063
g_Config.sProAdhocServer = "";
1064
bool leadingZero = true;
1065
for (int i = 0; i < 12; i++)
1066
{
1067
if (i && i % 3 == 0)
1068
{
1069
g_Config.sProAdhocServer += '.';
1070
leadingZero = true;
1071
}
1072
1073
int addressPt = ppsspp_pro_ad_hoc_ipv4[i];
1074
if (addressPt || i % 3 == 2)
1075
leadingZero = false; // We are either non-zero or the last digit of a byte
1076
1077
if (! leadingZero)
1078
g_Config.sProAdhocServer += static_cast<char>('0' + addressPt);
1079
}
1080
}
1081
else
1082
g_Config.sProAdhocServer = changeProAdhocServer;
1083
1084
g_Config.bTexHardwareScaling = g_Config.sTextureShaderName != "Off";
1085
1086
if (gpu && (g_Config.iTexScalingType != iTexScalingType_prev
1087
|| g_Config.iTexScalingLevel != iTexScalingLevel_prev
1088
|| g_Config.sTextureShaderName != sTextureShaderName_prev))
1089
{
1090
gpu->NotifyConfigChanged();
1091
}
1092
1093
if (g_Config.iLanguage < 0)
1094
g_Config.iLanguage = get_language_auto();
1095
1096
g_Config.sLanguageIni = map_psp_language_to_i18n_locale(g_Config.iLanguage);
1097
g_i18nrepo.LoadIni(g_Config.sLanguageIni);
1098
1099
// Cannot detect refresh rate changes if:
1100
// > Frame skipping is enabled
1101
// > Frame duplication is enabled
1102
detectVsyncSwapInterval &=
1103
!g_Config.bAutoFrameSkip &&
1104
(g_Config.iFrameSkip == 0) &&
1105
!g_Config.bRenderDuplicateFrames;
1106
1107
bool updateAvInfo = false;
1108
bool updateGeometry = false;
1109
1110
if (!detectVsyncSwapInterval && (vsyncSwapInterval != 1))
1111
{
1112
vsyncSwapInterval = 1;
1113
updateAvInfo = true;
1114
}
1115
1116
if (g_Config.iInternalResolution != iInternalResolution_prev && backend != RETRO_HW_CONTEXT_NONE)
1117
{
1118
coreParam.pixelWidth = coreParam.renderWidth = g_Config.iInternalResolution * NATIVEWIDTH;
1119
coreParam.pixelHeight = coreParam.renderHeight = g_Config.iInternalResolution * NATIVEHEIGHT;
1120
1121
if (gpu)
1122
{
1123
retro_system_av_info avInfo;
1124
retro_get_system_av_info(&avInfo);
1125
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avInfo);
1126
updateAvInfo = false;
1127
gpu->NotifyDisplayResized();
1128
}
1129
}
1130
1131
if (g_Config.bDisplayCropTo16x9 != bDisplayCropTo16x9_prev && PSP_IsInited())
1132
{
1133
updateGeometry = true;
1134
if (gpu)
1135
gpu->NotifyDisplayResized();
1136
}
1137
1138
if (g_Config.iMultiSampleLevel != iMultiSampleLevel_prev && PSP_IsInited())
1139
{
1140
if (gpu)
1141
{
1142
const DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(g_display.GetDeviceOrientation());
1143
gpu->NotifyRenderResized(config);
1144
}
1145
}
1146
1147
if (updateAvInfo)
1148
{
1149
retro_system_av_info avInfo;
1150
retro_get_system_av_info(&avInfo);
1151
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avInfo);
1152
}
1153
else if (updateGeometry)
1154
{
1155
retro_system_av_info avInfo;
1156
retro_get_system_av_info(&avInfo);
1157
environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &avInfo);
1158
}
1159
1160
set_variable_visibility();
1161
}
1162
1163
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }
1164
void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; }
1165
void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }
1166
void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }
1167
1168
static const struct retro_controller_description psp_controllers[] =
1169
{
1170
{ "PSP", RETRO_DEVICE_JOYPAD },
1171
{ NULL, 0 }
1172
};
1173
1174
static const struct retro_controller_info ports[] =
1175
{
1176
{ psp_controllers, 1 },
1177
{ NULL, 0 }
1178
};
1179
1180
void retro_init(void)
1181
{
1182
TimeInit();
1183
SetCurrentThreadName("Main");
1184
1185
struct retro_log_callback log;
1186
if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
1187
{
1188
log_cb = log.log;
1189
g_logManager.Init(&g_Config.bEnableLogging);
1190
g_logManager.SetOutputsEnabled(LogOutput::ExternalCallback);
1191
g_logManager.SetExternalLogCallback(&RetroLogCallback, (void *)log_cb);
1192
}
1193
1194
VsyncSwapIntervalReset();
1195
1196
struct retro_input_descriptor desc[] = {
1197
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
1198
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
1199
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
1200
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1201
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Cross" },
1202
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Circle" },
1203
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Triangle" },
1204
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Square" },
1205
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L" },
1206
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" },
1207
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
1208
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
1209
{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
1210
{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
1211
{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
1212
{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
1213
{ 0 },
1214
};
1215
environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
1216
environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
1217
1218
if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL))
1219
libretro_supports_bitmasks = true;
1220
1221
g_Config.Load("", "");
1222
g_Config.iInternalResolution = 0;
1223
1224
// Log levels must be set after g_Config.Load
1225
g_logManager.SetAllLogLevels(LogLevel::LINFO);
1226
1227
const char* nickname = NULL;
1228
if (environ_cb(RETRO_ENVIRONMENT_GET_USERNAME, &nickname) && nickname)
1229
g_Config.sNickName = std::string(nickname);
1230
1231
Path retro_base_dir;
1232
Path retro_save_dir;
1233
const char* dir_ptr = NULL;
1234
if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir_ptr) && dir_ptr)
1235
retro_base_dir = Path(dir_ptr);
1236
1237
if (environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &dir_ptr) && dir_ptr)
1238
retro_save_dir = Path(dir_ptr);
1239
1240
retro_base_dir /= "PPSSPP";
1241
1242
// Check if '<system_dir>/PPSSPP/compat.ini' exists, if not we can assume
1243
// the user is missing the assets entirely, so let's warn them about it.
1244
if (!File::Exists(Path(retro_base_dir / "compat.ini")))
1245
{
1246
const char* str = "Core system files missing, expect bugs.";
1247
unsigned msg_interface_version = 0;
1248
environ_cb(RETRO_ENVIRONMENT_GET_MESSAGE_INTERFACE_VERSION, &msg_interface_version);
1249
1250
if (msg_interface_version >= 1)
1251
{
1252
retro_message_ext msg = {
1253
str,
1254
3000,
1255
3,
1256
RETRO_LOG_WARN,
1257
RETRO_MESSAGE_TARGET_ALL,
1258
RETRO_MESSAGE_TYPE_NOTIFICATION,
1259
-1
1260
};
1261
environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE_EXT, &msg);
1262
}
1263
else
1264
{
1265
retro_message msg = {
1266
str,
1267
180
1268
};
1269
environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, &msg);
1270
}
1271
1272
// OSD messages should be kept pretty short, but
1273
// let's give the user a bit more info in logs.
1274
WARN_LOG(Log::System, "Please check the docs for more informations on how to install "
1275
"the PPSSPP assets: https://docs.libretro.com/library/ppsspp/");
1276
}
1277
1278
g_Config.currentDirectory = retro_base_dir;
1279
g_Config.defaultCurrentDirectory = retro_base_dir;
1280
g_Config.memStickDirectory = retro_save_dir;
1281
g_Config.flash0Directory = retro_base_dir / "flash0";
1282
g_Config.internalDataDirectory = retro_base_dir;
1283
g_Config.bEnableNetworkChat = false;
1284
g_Config.bDiscordRichPresence = false;
1285
1286
g_VFS.Register("", new DirectoryReader(retro_base_dir));
1287
1288
g_threadManager.Init(cpu_info.num_cores, cpu_info.logical_cpu_count);
1289
1290
init_output_audio_buffer(2048);
1291
}
1292
1293
void retro_deinit(void)
1294
{
1295
g_threadManager.Teardown();
1296
g_logManager.Shutdown();
1297
log_cb = NULL;
1298
1299
libretro_supports_bitmasks = false;
1300
1301
VsyncSwapIntervalReset();
1302
1303
free_output_audio_buffer();
1304
}
1305
1306
void retro_set_controller_port_device(unsigned port, unsigned device)
1307
{
1308
(void)port;
1309
(void)device;
1310
}
1311
1312
void retro_get_system_info(struct retro_system_info *info)
1313
{
1314
*info = {};
1315
info->library_name = "PPSSPP";
1316
info->library_version = PPSSPP_GIT_VERSION;
1317
info->need_fullpath = true;
1318
info->valid_extensions = "elf|iso|cso|prx|pbp|chd";
1319
}
1320
1321
void retro_get_system_av_info(struct retro_system_av_info *info)
1322
{
1323
*info = {};
1324
info->timing.fps = (60.0 / 1.001) / (double)vsyncSwapInterval;
1325
info->timing.sample_rate = SAMPLERATE;
1326
1327
_dbg_assert_(g_Config.iInternalResolution != 0);
1328
1329
info->geometry.base_width = g_Config.iInternalResolution * NATIVEWIDTH;
1330
info->geometry.base_height = g_Config.iInternalResolution * NATIVEHEIGHT;
1331
info->geometry.max_width = g_Config.iInternalResolution * NATIVEWIDTH;
1332
info->geometry.max_height = g_Config.iInternalResolution * NATIVEHEIGHT;
1333
1334
if (g_Config.bDisplayCropTo16x9)
1335
info->geometry.base_height -= g_Config.iInternalResolution * 2;
1336
1337
info->geometry.aspect_ratio = (float)info->geometry.base_width / (float)info->geometry.base_height;
1338
1339
PSP_CoreParameter().pixelWidth = PSP_CoreParameter().renderWidth = info->geometry.base_width;
1340
PSP_CoreParameter().pixelHeight = PSP_CoreParameter().renderHeight = info->geometry.base_height;
1341
1342
/* Must reset context to resize render area properly while running,
1343
* but not necessary with software, and not working with Vulkan.. (TODO) */
1344
if (PSP_IsInited() && ctx && backend != RETRO_HW_CONTEXT_NONE && ctx->GetGPUCore() != GPUCORE_VULKAN)
1345
((LibretroHWRenderContext *)Libretro::ctx)->ContextReset();
1346
}
1347
1348
unsigned retro_api_version(void) { return RETRO_API_VERSION; }
1349
1350
namespace Libretro
1351
{
1352
bool useEmuThread = false;
1353
std::atomic<EmuThreadState> emuThreadState(EmuThreadState::DISABLED);
1354
1355
static std::thread emuThread;
1356
static void EmuFrame()
1357
{
1358
ctx->SetRenderTarget();
1359
if (ctx->GetDrawContext()) {
1360
ctx->GetDrawContext()->BeginFrame(Draw::DebugFlags::NONE);
1361
}
1362
1363
if (gpu) {
1364
const DisplayLayoutConfig &config = g_Config.GetDisplayLayoutConfig(g_display.GetDeviceOrientation());
1365
gpu->BeginHostFrame(config);
1366
}
1367
1368
PSP_RunLoopWhileState();
1369
switch (coreState) {
1370
case CORE_NEXTFRAME:
1371
case CORE_POWERDOWN:
1372
// Reached the end of the frame while running at full blast, all good. Set back to running for the next frame
1373
coreState = CORE_RUNNING_CPU;
1374
break;
1375
default:
1376
// We're not handling the various states used for debugging in the libretro port.
1377
break;
1378
}
1379
1380
if (gpu)
1381
gpu->EndHostFrame();
1382
1383
if (ctx->GetDrawContext()) {
1384
ctx->GetDrawContext()->EndFrame();
1385
ctx->GetDrawContext()->Present(Draw::PresentMode::FIFO);
1386
}
1387
}
1388
1389
static void EmuThreadFunc()
1390
{
1391
SetCurrentThreadName("EmuThread");
1392
1393
for (;;)
1394
{
1395
switch ((EmuThreadState)emuThreadState)
1396
{
1397
case EmuThreadState::START_REQUESTED:
1398
emuThreadState = EmuThreadState::RUNNING;
1399
[[fallthrough]];
1400
case EmuThreadState::RUNNING:
1401
EmuFrame();
1402
break;
1403
case EmuThreadState::PAUSE_REQUESTED:
1404
emuThreadState = EmuThreadState::PAUSED;
1405
[[fallthrough]];
1406
case EmuThreadState::PAUSED:
1407
sleep_ms(1, "libretro-paused");
1408
break;
1409
default:
1410
case EmuThreadState::QUIT_REQUESTED:
1411
emuThreadState = EmuThreadState::STOPPED;
1412
return;
1413
}
1414
}
1415
}
1416
1417
void EmuThreadStart()
1418
{
1419
bool wasPaused = emuThreadState == EmuThreadState::PAUSED;
1420
emuThreadState = EmuThreadState::START_REQUESTED;
1421
1422
if (!wasPaused)
1423
{
1424
ctx->ThreadStart();
1425
emuThread = std::thread(&EmuThreadFunc);
1426
}
1427
}
1428
1429
void EmuThreadStop()
1430
{
1431
if (emuThreadState != EmuThreadState::RUNNING)
1432
return;
1433
1434
emuThreadState = EmuThreadState::QUIT_REQUESTED;
1435
1436
// Need to keep eating frames to allow the EmuThread to exit correctly.
1437
ctx->ThreadFrameUntilCondition([]() -> bool {
1438
return emuThreadState == EmuThreadState::STOPPED;
1439
});
1440
1441
emuThread.join();
1442
emuThread = std::thread();
1443
ctx->ThreadEnd();
1444
}
1445
1446
void EmuThreadPause()
1447
{
1448
if (emuThreadState != EmuThreadState::RUNNING)
1449
return;
1450
1451
emuThreadState = EmuThreadState::PAUSE_REQUESTED;
1452
1453
// Is this safe?
1454
ctx->ThreadFrame(true); // Eat 1 frame
1455
1456
while (emuThreadState != EmuThreadState::PAUSED)
1457
sleep_ms(1, "libretro-pause-poll");
1458
}
1459
1460
} // namespace Libretro
1461
1462
static void retro_check_backend(void)
1463
{
1464
struct retro_variable var = {0};
1465
1466
var.key = "ppsspp_backend";
1467
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1468
{
1469
if (!strcmp(var.value, "auto"))
1470
backend = RETRO_HW_CONTEXT_DUMMY;
1471
else if (!strcmp(var.value, "opengl"))
1472
backend = RETRO_HW_CONTEXT_OPENGL;
1473
else if (!strcmp(var.value, "vulkan"))
1474
backend = RETRO_HW_CONTEXT_VULKAN;
1475
else if (!strcmp(var.value, "d3d11"))
1476
backend = RETRO_HW_CONTEXT_D3D11;
1477
else if (!strcmp(var.value, "none"))
1478
backend = RETRO_HW_CONTEXT_NONE;
1479
}
1480
}
1481
1482
bool retro_load_game(const struct retro_game_info *game)
1483
{
1484
retro_pixel_format fmt = retro_pixel_format::RETRO_PIXEL_FORMAT_XRGB8888;
1485
if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
1486
{
1487
ERROR_LOG(Log::System, "XRGB8888 is not supported.\n");
1488
return false;
1489
}
1490
1491
retro_check_backend();
1492
1493
ctx = LibretroGraphicsContext::CreateGraphicsContext();
1494
1495
INFO_LOG(Log::System, "Using %s backend", ctx->Ident());
1496
1497
Core_SetGraphicsContext(ctx);
1498
SetGPUBackend((GPUBackend)g_Config.iGPUBackend);
1499
1500
useEmuThread = ctx->GetGPUCore() == GPUCORE_GLES;
1501
1502
// default to interpreter to allow startup in platforms w/o JIT capability
1503
// TODO: I guess we should auto detect? And also, default to IR Interpreter...
1504
g_Config.iCpuCore = (int)CPUCore::INTERPRETER;
1505
1506
CoreParameter coreParam = {};
1507
coreParam.enableSound = true;
1508
coreParam.fileToStart = Path(game->path);
1509
coreParam.startBreak = false;
1510
coreParam.headLess = true; // really?
1511
coreParam.graphicsContext = ctx;
1512
coreParam.gpuCore = ctx->GetGPUCore();
1513
check_variables(coreParam);
1514
1515
// TODO: OpenGL goes black when inited with software rendering,
1516
// therefore start without, set back after init, and reset.
1517
softwareRenderInitHack = ctx->GetGPUCore() == GPUCORE_GLES && g_Config.bSoftwareRendering;
1518
if (softwareRenderInitHack)
1519
g_Config.bSoftwareRendering = false;
1520
1521
// set cpuCore from libretro setting variable
1522
coreParam.cpuCore = (CPUCore)g_Config.iCpuCore;
1523
1524
g_pendingBoot = true;
1525
1526
struct retro_core_option_display option_display;
1527
1528
// Show/hide 'MSAA' and 'Texture Shader' options, Vulkan only
1529
option_display.visible = (g_Config.iGPUBackend == (int)GPUBackend::VULKAN);
1530
option_display.key = "ppsspp_mulitsample_level";
1531
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
1532
option_display.key = "ppsspp_texture_shader";
1533
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
1534
1535
// Show/hide 'Buffered Frames' option, Vulkan/GL only
1536
option_display.visible = (g_Config.iGPUBackend == (int)GPUBackend::VULKAN ||
1537
g_Config.iGPUBackend == (int)GPUBackend::OPENGL);
1538
option_display.key = "ppsspp_inflight_frames";
1539
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
1540
1541
set_variable_visibility();
1542
1543
// NOTE: At this point we haven't really booted yet, but "in-game" we'll just keep polling
1544
// PSP_InitUpdate until done.
1545
1546
// Launch the init process.
1547
if (!PSP_InitStart(coreParam)) {
1548
g_bootErrorString = coreParam.errorString;
1549
// Can't really fail, the errors normally happen later during InitUpdate
1550
ERROR_LOG(Log::Boot, "%s", g_bootErrorString.c_str());
1551
g_pendingBoot = false;
1552
return false;
1553
}
1554
1555
return true;
1556
}
1557
1558
void retro_unload_game(void)
1559
{
1560
if (Libretro::useEmuThread)
1561
Libretro::EmuThreadStop();
1562
1563
PSP_Shutdown(true);
1564
g_VFS.Clear();
1565
1566
delete ctx;
1567
ctx = nullptr;
1568
PSP_CoreParameter().graphicsContext = nullptr;
1569
}
1570
1571
void retro_reset(void)
1572
{
1573
PSP_Shutdown(true);
1574
1575
if (BootState::Complete != PSP_Init(PSP_CoreParameter(), &g_bootErrorString))
1576
{
1577
ERROR_LOG(Log::Boot, "%s", g_bootErrorString.c_str());
1578
environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, nullptr);
1579
}
1580
}
1581
1582
static void retro_input(void)
1583
{
1584
unsigned i;
1585
int16_t ret = 0;
1586
// clang-format off
1587
static struct
1588
{
1589
u32 retro;
1590
u32 sceCtrl;
1591
} map[] = {
1592
{ RETRO_DEVICE_ID_JOYPAD_UP, CTRL_UP },
1593
{ RETRO_DEVICE_ID_JOYPAD_DOWN, CTRL_DOWN },
1594
{ RETRO_DEVICE_ID_JOYPAD_LEFT, CTRL_LEFT },
1595
{ RETRO_DEVICE_ID_JOYPAD_RIGHT, CTRL_RIGHT },
1596
{ RETRO_DEVICE_ID_JOYPAD_X, CTRL_TRIANGLE },
1597
{ RETRO_DEVICE_ID_JOYPAD_A, CTRL_CIRCLE },
1598
{ RETRO_DEVICE_ID_JOYPAD_B, CTRL_CROSS },
1599
{ RETRO_DEVICE_ID_JOYPAD_Y, CTRL_SQUARE },
1600
{ RETRO_DEVICE_ID_JOYPAD_L, CTRL_LTRIGGER },
1601
{ RETRO_DEVICE_ID_JOYPAD_R, CTRL_RTRIGGER },
1602
{ RETRO_DEVICE_ID_JOYPAD_START, CTRL_START },
1603
{ RETRO_DEVICE_ID_JOYPAD_SELECT, CTRL_SELECT },
1604
};
1605
// clang-format on
1606
1607
input_poll_cb();
1608
1609
if (libretro_supports_bitmasks)
1610
ret = input_state_cb(0, RETRO_DEVICE_JOYPAD,
1611
0, RETRO_DEVICE_ID_JOYPAD_MASK);
1612
else
1613
{
1614
for (i = RETRO_DEVICE_ID_JOYPAD_B; i <= RETRO_DEVICE_ID_JOYPAD_R; i++)
1615
if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, i))
1616
ret |= (1 << i);
1617
}
1618
1619
for (i = 0; i < sizeof(map) / sizeof(*map); i++)
1620
{
1621
bool pressed = ret & (1 << map[i].retro);
1622
1623
if (pressed)
1624
{
1625
__CtrlUpdateButtons(map[i].sceCtrl, 0);
1626
}
1627
else
1628
{
1629
__CtrlUpdateButtons(0, map[i].sceCtrl);
1630
}
1631
}
1632
1633
float x_left = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X) / 32767.0f;
1634
float y_left = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y) / -32767.0f;
1635
float x_right = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X) / 32767.0f;
1636
float y_right = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y) / -32767.0f;
1637
1638
// Analog circle vs square gate compensation,
1639
// deadzone and sensitivity copied from ControlMapper.cpp's
1640
// ConvertAnalogStick and MapAxisValue functions
1641
const bool isCircular = g_Config.bAnalogIsCircular;
1642
1643
float norm = std::max(fabsf(x_left), fabsf(y_left));
1644
1645
if (norm == 0.0f)
1646
{
1647
__CtrlSetAnalogXY(CTRL_STICK_LEFT, x_left, y_left);
1648
__CtrlSetAnalogXY(CTRL_STICK_RIGHT, x_right, y_right);
1649
return;
1650
}
1651
1652
if (isCircular)
1653
{
1654
float newNorm = sqrtf(x_left * x_left + y_left * y_left);
1655
float factor = newNorm / norm;
1656
x_left *= factor;
1657
y_left *= factor;
1658
norm = newNorm;
1659
}
1660
1661
const float deadzone = g_Config.fAnalogDeadzone;
1662
const float sensitivity = g_Config.fAnalogSensitivity;
1663
const float sign = norm >= 0.0f ? 1.0f : -1.0f;
1664
float mappedNorm = norm;
1665
1666
// Apply deadzone
1667
mappedNorm = Libretro::clamp((fabsf(mappedNorm) - deadzone) / (1.0f - deadzone), 0.0f, 1.0f);
1668
1669
// Apply sensitivity
1670
if (mappedNorm != 0.0f)
1671
mappedNorm = Libretro::clamp(mappedNorm * sensitivity * sign, -1.0f, 1.0f);
1672
1673
x_left = Libretro::clamp(x_left / norm * mappedNorm, -1.0f, 1.0f);
1674
y_left = Libretro::clamp(y_left / norm * mappedNorm, -1.0f, 1.0f);
1675
1676
__CtrlSetAnalogXY(CTRL_STICK_LEFT, x_left, y_left);
1677
__CtrlSetAnalogXY(CTRL_STICK_RIGHT, x_right, y_right);
1678
}
1679
1680
void retro_run(void)
1681
{
1682
if (g_pendingBoot) {
1683
BootState state = PSP_InitUpdate(&g_bootErrorString);
1684
switch (state) {
1685
case BootState::Failed:
1686
g_pendingBoot = false;
1687
ERROR_LOG(Log::Boot, "%s", g_bootErrorString.c_str());
1688
environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, nullptr);
1689
return;
1690
case BootState::Booting:
1691
// Not done yet. Do maintenance stuff and bail.
1692
retro_input();
1693
ctx->SwapBuffers();
1694
return;
1695
case BootState::Off:
1696
// shouldn't happen.
1697
_dbg_assert_(false);
1698
return;
1699
case BootState::Complete:
1700
// done, continue.
1701
break;
1702
}
1703
1704
// BootState is BootState::Complete.
1705
// Here's where we finish the boot process.
1706
coreState = CORE_RUNNING_CPU;
1707
g_bootErrorString.clear();
1708
g_pendingBoot = false;
1709
1710
if (unserialize_data) {
1711
retro_unserialize(unserialize_data, unserialize_size);
1712
1713
free(unserialize_data);
1714
unserialize_data = NULL;
1715
}
1716
}
1717
1718
// TODO: This seems dubious.
1719
if (softwareRenderInitHack)
1720
{
1721
log_cb(RETRO_LOG_DEBUG, "Software rendering init hack for opengl triggered.\n");
1722
softwareRenderInitHack = false;
1723
g_Config.bSoftwareRendering = true;
1724
retro_reset();
1725
}
1726
1727
bool updated;
1728
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated)
1729
&& updated)
1730
check_variables(PSP_CoreParameter());
1731
else
1732
check_dynamic_variables(PSP_CoreParameter());
1733
1734
retro_input();
1735
1736
if (useEmuThread)
1737
{
1738
if ( emuThreadState == EmuThreadState::PAUSED ||
1739
emuThreadState == EmuThreadState::PAUSE_REQUESTED)
1740
{
1741
VsyncSwapIntervalDetect();
1742
ctx->SwapBuffers();
1743
return;
1744
}
1745
1746
if (emuThreadState != EmuThreadState::RUNNING)
1747
EmuThreadStart();
1748
1749
if (!ctx->ThreadFrame(true))
1750
{
1751
VsyncSwapIntervalDetect();
1752
return;
1753
}
1754
}
1755
else
1756
EmuFrame();
1757
1758
VsyncSwapIntervalDetect();
1759
ctx->SwapBuffers();
1760
upload_output_audio_buffer();
1761
}
1762
1763
unsigned retro_get_region(void) { return RETRO_REGION_NTSC; }
1764
1765
bool retro_load_game_special(unsigned type, const struct retro_game_info *info, size_t num) { return false; }
1766
1767
namespace SaveState
1768
{
1769
struct SaveStart
1770
{
1771
void DoState(PointerWrap &p);
1772
};
1773
} // namespace SaveState
1774
1775
size_t retro_serialize_size(void)
1776
{
1777
if (!gpu) // The HW renderer isn't ready on first pass.
1778
return 134217728; // 128MB ought to be enough for anybody.
1779
1780
SaveState::SaveStart state;
1781
// TODO: Libretro API extension to use the savestate queue
1782
if (useEmuThread)
1783
EmuThreadPause();
1784
1785
return (CChunkFileReader::MeasurePtr(state) + 0x800000) & ~0x7FFFFF;
1786
// We don't unpause intentionally
1787
}
1788
1789
bool retro_serialize(void *data, size_t size)
1790
{
1791
if (!gpu) // The HW renderer isn't ready on first pass.
1792
return false;
1793
1794
// TODO: Libretro API extension to use the savestate queue
1795
if (useEmuThread)
1796
EmuThreadPause(); // Does nothing if already paused
1797
1798
size_t measuredSize;
1799
SaveState::SaveStart state;
1800
auto err = CChunkFileReader::MeasureAndSavePtr(state, (u8 **)&data, &measuredSize);
1801
bool retVal = err == CChunkFileReader::ERROR_NONE;
1802
1803
if (useEmuThread)
1804
{
1805
EmuThreadStart();
1806
sleep_ms(4, "libretro-serialize");
1807
}
1808
1809
return retVal;
1810
}
1811
1812
bool retro_unserialize(const void *data, size_t size)
1813
{
1814
// The HW renderer isn't ready on first pass.
1815
// So we save the data until we are ready to use it.
1816
if (!gpu) {
1817
unserialize_data = malloc(size);
1818
memcpy(unserialize_data, data, size);
1819
return true;
1820
}
1821
1822
// TODO: Libretro API extension to use the savestate queue
1823
if (useEmuThread)
1824
EmuThreadPause(); // Does nothing if already paused
1825
1826
std::string errorString;
1827
SaveState::SaveStart state;
1828
bool retVal = CChunkFileReader::LoadPtr((u8 *)data, state, &errorString)
1829
== CChunkFileReader::ERROR_NONE;
1830
1831
if (useEmuThread)
1832
{
1833
EmuThreadStart();
1834
sleep_ms(4, "libretro-unserialize");
1835
}
1836
1837
return retVal;
1838
}
1839
1840
void *retro_get_memory_data(unsigned id)
1841
{
1842
if ( id == RETRO_MEMORY_SYSTEM_RAM )
1843
return Memory::GetPointerWriteUnchecked(PSP_GetKernelMemoryBase()) ;
1844
return NULL;
1845
}
1846
1847
size_t retro_get_memory_size(unsigned id)
1848
{
1849
if ( id == RETRO_MEMORY_SYSTEM_RAM )
1850
return Memory::g_MemorySize ;
1851
return 0;
1852
}
1853
1854
void retro_cheat_reset(void) {
1855
// Init Cheat Engine
1856
CWCheatEngine *cheatEngine = new CWCheatEngine(g_paramSFO.GetDiscID());
1857
Path file=cheatEngine->CheatFilename();
1858
1859
// Output cheats to cheat file
1860
FILE *outFile = File::OpenCFile(file, "wb");
1861
if (outFile != nullptr) {
1862
fprintf(outFile, "_S %s\n", g_paramSFO.GetDiscID().c_str());
1863
fclose(outFile);
1864
}
1865
1866
g_Config.bReloadCheats = true;
1867
1868
// Parse and Run the Cheats
1869
cheatEngine->ParseCheats();
1870
if (cheatEngine->HasCheats()) {
1871
cheatEngine->Run();
1872
}
1873
1874
}
1875
1876
void retro_cheat_set(unsigned index, bool enabled, const char *code) {
1877
// Initialize Cheat Engine
1878
CWCheatEngine *cheatEngine = new CWCheatEngine(g_paramSFO.GetDiscID());
1879
cheatEngine->CreateCheatFile();
1880
Path file=cheatEngine->CheatFilename();
1881
1882
// Read cheats file
1883
std::vector<std::string> cheats;
1884
std::string cheat_content;
1885
FILE *inFile = File::OpenCFile(file, "rb");
1886
if (inFile != nullptr) {
1887
std::array<uint8_t, 4096> buffer;
1888
for (;;) {
1889
size_t n = fread(buffer.data(), 1, buffer.size(), inFile);
1890
cheat_content.append((const char *)buffer.data(), n);
1891
if (n < buffer.size()) {
1892
break;
1893
}
1894
}
1895
fclose(inFile);
1896
}
1897
std::string existing_cheats=ReplaceAll(cheat_content, std::string("\n_C"), std::string("|"));
1898
SplitString(existing_cheats, '|', cheats);
1899
1900
// Generate Cheat String
1901
std::stringstream cheat("");
1902
cheat << (enabled ? "1 " : "0 ") << index << std::endl;
1903
std::string code_str(code);
1904
std::vector<std::string> codes;
1905
code_str=ReplaceAll(code_str, std::string(" "), std::string("+"));
1906
SplitString(code_str, '+', codes);
1907
int part=0;
1908
for (int i=0; i < codes.size(); i++) {
1909
if (codes[i].size() <= 2) {
1910
// _L _M ..etc
1911
// Assume _L
1912
} else if (part == 0) {
1913
cheat << "_L " << codes[i] << " ";
1914
part++;
1915
} else {
1916
cheat << codes[i] << std::endl;
1917
part=0;
1918
}
1919
}
1920
1921
// Add or Replace the Cheat
1922
if (index + 1 < cheats.size()) {
1923
cheats[index + 1]=cheat.str();
1924
} else {
1925
cheats.push_back(cheat.str());
1926
}
1927
1928
// Output cheats to cheat file
1929
FILE *outFile = File::OpenCFile(file, "wb");
1930
if (outFile != nullptr) {
1931
fprintf(outFile, "_S %s\n", g_paramSFO.GetDiscID().c_str());
1932
for (const std::string &cheat : cheats) {
1933
fprintf(outFile, "_C%s\n", cheat.c_str());
1934
}
1935
fclose(outFile);
1936
}
1937
1938
g_Config.bReloadCheats = true;
1939
1940
// Parse and Run the Cheats
1941
cheatEngine->ParseCheats();
1942
if (cheatEngine->HasCheats()) {
1943
cheatEngine->Run();
1944
}
1945
}
1946
1947
int64_t System_GetPropertyInt(SystemProperty prop) {
1948
switch (prop) {
1949
case SYSPROP_AUDIO_SAMPLE_RATE:
1950
return SAMPLERATE;
1951
#if PPSSPP_PLATFORM(ANDROID)
1952
case SYSPROP_SYSTEMVERSION: {
1953
char sdk[PROP_VALUE_MAX] = {0};
1954
if (__system_property_get("ro.build.version.sdk", sdk) != 0) {
1955
return atoi(sdk);
1956
}
1957
return -1;
1958
}
1959
#endif
1960
default:
1961
break;
1962
}
1963
1964
return -1;
1965
}
1966
1967
float System_GetPropertyFloat(SystemProperty prop)
1968
{
1969
switch (prop)
1970
{
1971
case SYSPROP_DISPLAY_REFRESH_RATE:
1972
return 60.0f / 1.001f;
1973
case SYSPROP_DISPLAY_SAFE_INSET_LEFT:
1974
case SYSPROP_DISPLAY_SAFE_INSET_RIGHT:
1975
case SYSPROP_DISPLAY_SAFE_INSET_TOP:
1976
case SYSPROP_DISPLAY_SAFE_INSET_BOTTOM:
1977
return 0.0f;
1978
default:
1979
break;
1980
}
1981
1982
return -1;
1983
}
1984
1985
bool System_GetPropertyBool(SystemProperty prop)
1986
{
1987
switch (prop)
1988
{
1989
case SYSPROP_CAN_JIT:
1990
#if PPSSPP_PLATFORM(IOS)
1991
bool can_jit;
1992
return (environ_cb(RETRO_ENVIRONMENT_GET_JIT_CAPABLE, &can_jit) && can_jit);
1993
#else
1994
return true;
1995
#endif
1996
default:
1997
return false;
1998
}
1999
}
2000
2001
std::string System_GetProperty(SystemProperty prop) { return ""; }
2002
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) { return std::vector<std::string>(); }
2003
2004
void System_Notify(SystemNotification notification) {
2005
switch (notification) {
2006
default:
2007
break;
2008
}
2009
}
2010
bool System_MakeRequest(SystemRequestType type, int requestId, const std::string &param1, const std::string &param2, int64_t param3, int64_t param4) { return false; }
2011
void System_PostUIMessage(UIMessage message, std::string_view param) {}
2012
void System_RunOnMainThread(std::function<void()>) {}
2013
void NativeFrame(GraphicsContext *graphicsContext) {}
2014
void NativeResized() {}
2015
2016
void System_Toast(std::string_view str) {}
2017
2018
inline int16_t Clamp16(int32_t sample) {
2019
if (sample < -32767) return -32767;
2020
if (sample > 32767) return 32767;
2021
return sample;
2022
}
2023
2024
void System_AudioPushSamples(const int32_t *audio, int numSamples, float volume) {
2025
// We ignore volume here, because it's handled by libretro presumably.
2026
2027
// Convert to 16-bit audio for further processing.
2028
int16_t buffer[1024 * 2];
2029
int origSamples = numSamples * 2;
2030
2031
while (numSamples > 0) {
2032
int blockSize = std::min(1024, numSamples);
2033
for (int i = 0; i < blockSize; i++) {
2034
buffer[i * 2] = Clamp16(audio[i * 2]);
2035
buffer[i * 2 + 1] = Clamp16(audio[i * 2 + 1]);
2036
}
2037
2038
numSamples -= blockSize;
2039
}
2040
2041
if (output_audio_buffer.capacity - output_audio_buffer.size < origSamples)
2042
ensure_output_audio_buffer_capacity((output_audio_buffer.capacity + origSamples) * 1.5);
2043
memcpy(output_audio_buffer.data + output_audio_buffer.size, buffer, origSamples * sizeof(*output_audio_buffer.data));
2044
output_audio_buffer.size += origSamples;
2045
}
2046
2047
void System_AudioGetDebugStats(char *buf, size_t bufSize) { if (buf) buf[0] = '\0'; }
2048
void System_AudioClear() {}
2049
2050
#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
2051
std::vector<std::string> System_GetCameraDeviceList() { return std::vector<std::string>(); }
2052
bool System_AudioRecordingIsAvailable() { return false; }
2053
bool System_AudioRecordingState() { return false; }
2054
#elif PPSSPP_PLATFORM(MAC)
2055
std::vector<std::string> __mac_getDeviceList() { return std::vector<std::string>(); }
2056
int __mac_startCapture(int width, int height) { return 0; }
2057
int __mac_stopCapture() { return 0; }
2058
#endif
2059
2060
// TODO: To avoid having to define these here, these should probably be turned into system "requests".
2061
bool NativeSaveSecret(std::string_view nameOfSecret, std::string_view data) { return false; }
2062
std::string NativeLoadSecret(std::string_view nameOfSecret) {
2063
return "";
2064
}
2065
2066