Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/libretro/libretro.cpp
3185 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/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 { 1, nullptr };
406
if (cb(RETRO_ENVIRONMENT_GET_VFS_INTERFACE, &vfs_iface_info))
407
filestream_vfs_init(&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_frameskiptype";
737
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
738
{
739
if (!strcmp(var.value, "Number of frames"))
740
g_Config.iFrameSkipType = 0;
741
else if (!strcmp(var.value, "Percent of FPS"))
742
g_Config.iFrameSkipType = 1;
743
}
744
745
var.key = "ppsspp_auto_frameskip";
746
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
747
{
748
if (!strcmp(var.value, "disabled"))
749
g_Config.bAutoFrameSkip = false;
750
else
751
g_Config.bAutoFrameSkip = true;
752
}
753
754
var.key = "ppsspp_frame_duplication";
755
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
756
{
757
if (!strcmp(var.value, "disabled"))
758
g_Config.bRenderDuplicateFrames = false;
759
else
760
g_Config.bRenderDuplicateFrames = true;
761
}
762
763
var.key = "ppsspp_detect_vsync_swap_interval";
764
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
765
{
766
if (!strcmp(var.value, "disabled"))
767
detectVsyncSwapInterval = false;
768
else
769
detectVsyncSwapInterval = true;
770
}
771
772
var.key = "ppsspp_inflight_frames";
773
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
774
{
775
if (!strcmp(var.value, "No buffer"))
776
g_Config.iInflightFrames = 1;
777
else if (!strcmp(var.value, "Up to 1"))
778
g_Config.iInflightFrames = 2;
779
else if (!strcmp(var.value, "Up to 2"))
780
g_Config.iInflightFrames = 3;
781
}
782
783
var.key = "ppsspp_skip_buffer_effects";
784
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
785
{
786
if (!strcmp(var.value, "disabled"))
787
g_Config.bSkipBufferEffects = false;
788
else
789
g_Config.bSkipBufferEffects = true;
790
}
791
792
var.key = "ppsspp_disable_range_culling";
793
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
794
{
795
if (!strcmp(var.value, "disabled"))
796
g_Config.bDisableRangeCulling = false;
797
else
798
g_Config.bDisableRangeCulling = true;
799
}
800
801
var.key = "ppsspp_skip_gpu_readbacks";
802
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
803
{
804
if (!strcmp(var.value, "disabled"))
805
g_Config.iSkipGPUReadbackMode = (int)SkipGPUReadbackMode::NO_SKIP;
806
else
807
g_Config.iSkipGPUReadbackMode = (int)SkipGPUReadbackMode::SKIP;
808
}
809
810
var.key = "ppsspp_lazy_texture_caching";
811
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
812
{
813
if (!strcmp(var.value, "disabled"))
814
g_Config.bTextureBackoffCache = false;
815
else
816
g_Config.bTextureBackoffCache = true;
817
}
818
819
var.key = "ppsspp_spline_quality";
820
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
821
{
822
if (!strcmp(var.value, "Low"))
823
g_Config.iSplineBezierQuality = 0;
824
else if (!strcmp(var.value, "Medium"))
825
g_Config.iSplineBezierQuality = 1;
826
else if (!strcmp(var.value, "High"))
827
g_Config.iSplineBezierQuality = 2;
828
}
829
830
var.key = "ppsspp_gpu_hardware_transform";
831
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
832
{
833
if (!strcmp(var.value, "disabled"))
834
g_Config.bHardwareTransform = false;
835
else
836
g_Config.bHardwareTransform = true;
837
}
838
839
var.key = "ppsspp_software_skinning";
840
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
841
{
842
if (!strcmp(var.value, "disabled"))
843
g_Config.bSoftwareSkinning = false;
844
else
845
g_Config.bSoftwareSkinning = true;
846
}
847
848
var.key = "ppsspp_hardware_tesselation";
849
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
850
{
851
if (!strcmp(var.value, "disabled"))
852
g_Config.bHardwareTessellation = false;
853
else
854
g_Config.bHardwareTessellation = true;
855
}
856
857
var.key = "ppsspp_lower_resolution_for_effects";
858
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
859
{
860
if (!strcmp(var.value, "disabled"))
861
g_Config.iBloomHack = 0;
862
else if (!strcmp(var.value, "Safe"))
863
g_Config.iBloomHack = 1;
864
else if (!strcmp(var.value, "Balanced"))
865
g_Config.iBloomHack = 2;
866
else if (!strcmp(var.value, "Aggressive"))
867
g_Config.iBloomHack = 3;
868
}
869
870
var.key = "ppsspp_texture_scaling_type";
871
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
872
{
873
iTexScalingType_prev = g_Config.iTexScalingType;
874
875
if (!strcmp(var.value, "xbrz"))
876
g_Config.iTexScalingType = TextureScalerCommon::XBRZ;
877
else if (!strcmp(var.value, "hybrid"))
878
g_Config.iTexScalingType = TextureScalerCommon::HYBRID;
879
else if (!strcmp(var.value, "bicubic"))
880
g_Config.iTexScalingType = TextureScalerCommon::BICUBIC;
881
else if (!strcmp(var.value, "hybrid_bicubic"))
882
g_Config.iTexScalingType = TextureScalerCommon::HYBRID_BICUBIC;
883
}
884
885
var.key = "ppsspp_texture_scaling_level";
886
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
887
{
888
iTexScalingLevel_prev = g_Config.iTexScalingLevel;
889
890
if (!strcmp(var.value, "disabled"))
891
g_Config.iTexScalingLevel = 1;
892
else if (!strcmp(var.value, "2x"))
893
g_Config.iTexScalingLevel = 2;
894
else if (!strcmp(var.value, "3x"))
895
g_Config.iTexScalingLevel = 3;
896
else if (!strcmp(var.value, "4x"))
897
g_Config.iTexScalingLevel = 4;
898
else if (!strcmp(var.value, "5x"))
899
g_Config.iTexScalingLevel = 5;
900
}
901
902
var.key = "ppsspp_texture_deposterize";
903
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
904
{
905
if (!strcmp(var.value, "disabled"))
906
g_Config.bTexDeposterize = false;
907
else
908
g_Config.bTexDeposterize = true;
909
}
910
911
var.key = "ppsspp_texture_shader";
912
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
913
{
914
sTextureShaderName_prev = g_Config.sTextureShaderName;
915
916
if (!strcmp(var.value, "disabled"))
917
g_Config.sTextureShaderName = "Off";
918
else if (!strcmp(var.value, "2xBRZ"))
919
g_Config.sTextureShaderName = "Tex2xBRZ";
920
else if (!strcmp(var.value, "4xBRZ"))
921
g_Config.sTextureShaderName = "Tex4xBRZ";
922
else if (!strcmp(var.value, "MMPX"))
923
g_Config.sTextureShaderName = "TexMMPX";
924
}
925
926
var.key = "ppsspp_texture_anisotropic_filtering";
927
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
928
{
929
if (!strcmp(var.value, "disabled"))
930
g_Config.iAnisotropyLevel = 0;
931
else if (!strcmp(var.value, "2x"))
932
g_Config.iAnisotropyLevel = 1;
933
else if (!strcmp(var.value, "4x"))
934
g_Config.iAnisotropyLevel = 2;
935
else if (!strcmp(var.value, "8x"))
936
g_Config.iAnisotropyLevel = 3;
937
else if (!strcmp(var.value, "16x"))
938
g_Config.iAnisotropyLevel = 4;
939
}
940
941
var.key = "ppsspp_texture_filtering";
942
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
943
{
944
if (!strcmp(var.value, "Auto"))
945
g_Config.iTexFiltering = 1;
946
else if (!strcmp(var.value, "Nearest"))
947
g_Config.iTexFiltering = 2;
948
else if (!strcmp(var.value, "Linear"))
949
g_Config.iTexFiltering = 3;
950
else if (!strcmp(var.value, "Auto max quality"))
951
g_Config.iTexFiltering = 4;
952
}
953
954
var.key = "ppsspp_smart_2d_texture_filtering";
955
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
956
{
957
if (!strcmp(var.value, "disabled"))
958
g_Config.bSmart2DTexFiltering = false;
959
else
960
g_Config.bSmart2DTexFiltering = true;
961
}
962
963
var.key = "ppsspp_texture_replacement";
964
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
965
{
966
if (!strcmp(var.value, "disabled"))
967
g_Config.bReplaceTextures = false;
968
else
969
g_Config.bReplaceTextures = true;
970
}
971
972
var.key = "ppsspp_enable_wlan";
973
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
974
{
975
if (!strcmp(var.value, "disabled"))
976
g_Config.bEnableWlan = false;
977
else
978
g_Config.bEnableWlan = true;
979
}
980
981
var.key = "ppsspp_wlan_channel";
982
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
983
g_Config.iWlanAdhocChannel = atoi(var.value);
984
985
var.key = "ppsspp_enable_builtin_pro_ad_hoc_server";
986
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
987
{
988
if (!strcmp(var.value, "disabled"))
989
g_Config.bEnableAdhocServer = false;
990
else
991
g_Config.bEnableAdhocServer = true;
992
}
993
994
var.key = "ppsspp_change_pro_ad_hoc_server_address";
995
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
996
changeProAdhocServer = var.value;
997
998
var.key = "ppsspp_enable_upnp";
999
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1000
{
1001
if (!strcmp(var.value, "disabled"))
1002
g_Config.bEnableUPnP = false;
1003
else
1004
g_Config.bEnableUPnP = true;
1005
}
1006
1007
var.key = "ppsspp_upnp_use_original_port";
1008
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1009
{
1010
if (!strcmp(var.value, "disabled"))
1011
g_Config.bUPnPUseOriginalPort = false;
1012
else
1013
g_Config.bUPnPUseOriginalPort = true;
1014
}
1015
1016
var.key = "ppsspp_port_offset";
1017
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1018
g_Config.iPortOffset = atoi(var.value);
1019
1020
var.key = "ppsspp_minimum_timeout";
1021
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1022
g_Config.iMinTimeout = atoi(var.value);
1023
1024
var.key = "ppsspp_forced_first_connect";
1025
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1026
{
1027
if (!strcmp(var.value, "disabled"))
1028
g_Config.bForcedFirstConnect = false;
1029
else
1030
g_Config.bForcedFirstConnect = true;
1031
}
1032
1033
std::string ppsspp_change_mac_address[12];
1034
int ppsspp_pro_ad_hoc_ipv4[12];
1035
char key[64] = {0};
1036
var.key = key;
1037
g_Config.sMACAddress = "";
1038
g_Config.proAdhocServer = "";
1039
for (int i = 0; i < 12; i++)
1040
{
1041
snprintf(key, sizeof(key), "ppsspp_change_mac_address%02d", i + 1);
1042
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1043
{
1044
ppsspp_change_mac_address[i] = var.value;
1045
1046
if (i && i % 2 == 0)
1047
g_Config.sMACAddress += ":";
1048
1049
g_Config.sMACAddress += ppsspp_change_mac_address[i];
1050
}
1051
1052
snprintf(key, sizeof(key), "ppsspp_pro_ad_hoc_server_address%02d", i + 1);
1053
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1054
ppsspp_pro_ad_hoc_ipv4[i] = atoi(var.value);
1055
}
1056
1057
if (g_Config.sMACAddress == "00:00:00:00:00:00")
1058
{
1059
g_Config.sMACAddress = CreateRandMAC();
1060
1061
for (int i = 0; i < 12; i++)
1062
{
1063
snprintf(key, sizeof(key), "ppsspp_change_mac_address%02d", i + 1);
1064
std::string digit = {g_Config.sMACAddress[i + i / 2]};
1065
var.value = digit.c_str();
1066
environ_cb(RETRO_ENVIRONMENT_SET_VARIABLE, &var);
1067
}
1068
}
1069
1070
if (changeProAdhocServer == "IP address")
1071
{
1072
g_Config.proAdhocServer = "";
1073
bool leadingZero = true;
1074
for (int i = 0; i < 12; i++)
1075
{
1076
if (i && i % 3 == 0)
1077
{
1078
g_Config.proAdhocServer += '.';
1079
leadingZero = true;
1080
}
1081
1082
int addressPt = ppsspp_pro_ad_hoc_ipv4[i];
1083
if (addressPt || i % 3 == 2)
1084
leadingZero = false; // We are either non-zero or the last digit of a byte
1085
1086
if (! leadingZero)
1087
g_Config.proAdhocServer += static_cast<char>('0' + addressPt);
1088
}
1089
}
1090
else
1091
g_Config.proAdhocServer = changeProAdhocServer;
1092
1093
g_Config.bTexHardwareScaling = g_Config.sTextureShaderName != "Off";
1094
1095
if (gpu && (g_Config.iTexScalingType != iTexScalingType_prev
1096
|| g_Config.iTexScalingLevel != iTexScalingLevel_prev
1097
|| g_Config.sTextureShaderName != sTextureShaderName_prev))
1098
{
1099
gpu->NotifyConfigChanged();
1100
}
1101
1102
if (g_Config.iLanguage < 0)
1103
g_Config.iLanguage = get_language_auto();
1104
1105
g_Config.sLanguageIni = map_psp_language_to_i18n_locale(g_Config.iLanguage);
1106
g_i18nrepo.LoadIni(g_Config.sLanguageIni);
1107
1108
// Cannot detect refresh rate changes if:
1109
// > Frame skipping is enabled
1110
// > Frame duplication is enabled
1111
detectVsyncSwapInterval &=
1112
!g_Config.bAutoFrameSkip &&
1113
(g_Config.iFrameSkip == 0) &&
1114
!g_Config.bRenderDuplicateFrames;
1115
1116
bool updateAvInfo = false;
1117
bool updateGeometry = false;
1118
1119
if (!detectVsyncSwapInterval && (vsyncSwapInterval != 1))
1120
{
1121
vsyncSwapInterval = 1;
1122
updateAvInfo = true;
1123
}
1124
1125
if (g_Config.iInternalResolution != iInternalResolution_prev && backend != RETRO_HW_CONTEXT_NONE)
1126
{
1127
coreParam.pixelWidth = coreParam.renderWidth = g_Config.iInternalResolution * NATIVEWIDTH;
1128
coreParam.pixelHeight = coreParam.renderHeight = g_Config.iInternalResolution * NATIVEHEIGHT;
1129
1130
if (gpu)
1131
{
1132
retro_system_av_info avInfo;
1133
retro_get_system_av_info(&avInfo);
1134
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avInfo);
1135
updateAvInfo = false;
1136
gpu->NotifyDisplayResized();
1137
}
1138
}
1139
1140
if (g_Config.bDisplayCropTo16x9 != bDisplayCropTo16x9_prev && PSP_IsInited())
1141
{
1142
updateGeometry = true;
1143
if (gpu)
1144
gpu->NotifyDisplayResized();
1145
}
1146
1147
if (g_Config.iMultiSampleLevel != iMultiSampleLevel_prev && PSP_IsInited())
1148
{
1149
if (gpu)
1150
{
1151
gpu->NotifyRenderResized();
1152
}
1153
}
1154
1155
if (updateAvInfo)
1156
{
1157
retro_system_av_info avInfo;
1158
retro_get_system_av_info(&avInfo);
1159
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avInfo);
1160
}
1161
else if (updateGeometry)
1162
{
1163
retro_system_av_info avInfo;
1164
retro_get_system_av_info(&avInfo);
1165
environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &avInfo);
1166
}
1167
1168
set_variable_visibility();
1169
}
1170
1171
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }
1172
void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; }
1173
void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }
1174
void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }
1175
1176
static const struct retro_controller_description psp_controllers[] =
1177
{
1178
{ "PSP", RETRO_DEVICE_JOYPAD },
1179
{ NULL, 0 }
1180
};
1181
1182
static const struct retro_controller_info ports[] =
1183
{
1184
{ psp_controllers, 1 },
1185
{ NULL, 0 }
1186
};
1187
1188
void retro_init(void)
1189
{
1190
TimeInit();
1191
SetCurrentThreadName("Main");
1192
1193
struct retro_log_callback log;
1194
if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
1195
{
1196
log_cb = log.log;
1197
g_logManager.Init(&g_Config.bEnableLogging);
1198
g_logManager.SetOutputsEnabled(LogOutput::ExternalCallback);
1199
g_logManager.SetExternalLogCallback(&RetroLogCallback, (void *)log_cb);
1200
}
1201
1202
VsyncSwapIntervalReset();
1203
1204
struct retro_input_descriptor desc[] = {
1205
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
1206
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
1207
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
1208
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
1209
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Cross" },
1210
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Circle" },
1211
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Triangle" },
1212
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Square" },
1213
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L" },
1214
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" },
1215
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
1216
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
1217
{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
1218
{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
1219
{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
1220
{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
1221
{ 0 },
1222
};
1223
environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
1224
environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
1225
1226
if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL))
1227
libretro_supports_bitmasks = true;
1228
1229
g_Config.Load("", "");
1230
g_Config.iInternalResolution = 0;
1231
1232
// Log levels must be set after g_Config.Load
1233
g_logManager.SetAllLogLevels(LogLevel::LINFO);
1234
1235
const char* nickname = NULL;
1236
if (environ_cb(RETRO_ENVIRONMENT_GET_USERNAME, &nickname) && nickname)
1237
g_Config.sNickName = std::string(nickname);
1238
1239
Path retro_base_dir;
1240
Path retro_save_dir;
1241
const char* dir_ptr = NULL;
1242
if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir_ptr) && dir_ptr)
1243
retro_base_dir = Path(dir_ptr);
1244
1245
if (environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &dir_ptr) && dir_ptr)
1246
retro_save_dir = Path(dir_ptr);
1247
1248
retro_base_dir /= "PPSSPP";
1249
1250
g_Config.currentDirectory = retro_base_dir;
1251
g_Config.defaultCurrentDirectory = retro_base_dir;
1252
g_Config.memStickDirectory = retro_save_dir;
1253
g_Config.flash0Directory = retro_base_dir / "flash0";
1254
g_Config.internalDataDirectory = retro_base_dir;
1255
g_Config.bEnableNetworkChat = false;
1256
g_Config.bDiscordRichPresence = false;
1257
1258
g_VFS.Register("", new DirectoryReader(retro_base_dir));
1259
1260
g_threadManager.Init(cpu_info.num_cores, cpu_info.logical_cpu_count);
1261
1262
init_output_audio_buffer(2048);
1263
}
1264
1265
void retro_deinit(void)
1266
{
1267
g_threadManager.Teardown();
1268
g_logManager.Shutdown();
1269
log_cb = NULL;
1270
1271
libretro_supports_bitmasks = false;
1272
1273
VsyncSwapIntervalReset();
1274
1275
free_output_audio_buffer();
1276
}
1277
1278
void retro_set_controller_port_device(unsigned port, unsigned device)
1279
{
1280
(void)port;
1281
(void)device;
1282
}
1283
1284
void retro_get_system_info(struct retro_system_info *info)
1285
{
1286
*info = {};
1287
info->library_name = "PPSSPP";
1288
info->library_version = PPSSPP_GIT_VERSION;
1289
info->need_fullpath = true;
1290
info->valid_extensions = "elf|iso|cso|prx|pbp|chd";
1291
}
1292
1293
void retro_get_system_av_info(struct retro_system_av_info *info)
1294
{
1295
*info = {};
1296
info->timing.fps = (60.0 / 1.001) / (double)vsyncSwapInterval;
1297
info->timing.sample_rate = SAMPLERATE;
1298
1299
_dbg_assert_(g_Config.iInternalResolution != 0);
1300
1301
info->geometry.base_width = g_Config.iInternalResolution * NATIVEWIDTH;
1302
info->geometry.base_height = g_Config.iInternalResolution * NATIVEHEIGHT;
1303
info->geometry.max_width = g_Config.iInternalResolution * NATIVEWIDTH;
1304
info->geometry.max_height = g_Config.iInternalResolution * NATIVEHEIGHT;
1305
1306
if (g_Config.bDisplayCropTo16x9)
1307
info->geometry.base_height -= g_Config.iInternalResolution * 2;
1308
1309
info->geometry.aspect_ratio = (float)info->geometry.base_width / (float)info->geometry.base_height;
1310
1311
PSP_CoreParameter().pixelWidth = PSP_CoreParameter().renderWidth = info->geometry.base_width;
1312
PSP_CoreParameter().pixelHeight = PSP_CoreParameter().renderHeight = info->geometry.base_height;
1313
1314
/* Must reset context to resize render area properly while running,
1315
* but not necessary with software, and not working with Vulkan.. (TODO) */
1316
if (PSP_IsInited() && ctx && backend != RETRO_HW_CONTEXT_NONE && ctx->GetGPUCore() != GPUCORE_VULKAN)
1317
((LibretroHWRenderContext *)Libretro::ctx)->ContextReset();
1318
}
1319
1320
unsigned retro_api_version(void) { return RETRO_API_VERSION; }
1321
1322
namespace Libretro
1323
{
1324
bool useEmuThread = false;
1325
std::atomic<EmuThreadState> emuThreadState(EmuThreadState::DISABLED);
1326
1327
static std::thread emuThread;
1328
static void EmuFrame()
1329
{
1330
ctx->SetRenderTarget();
1331
if (ctx->GetDrawContext()) {
1332
ctx->GetDrawContext()->BeginFrame(Draw::DebugFlags::NONE);
1333
}
1334
1335
if (gpu)
1336
gpu->BeginHostFrame();
1337
1338
PSP_RunLoopWhileState();
1339
switch (coreState) {
1340
case CORE_NEXTFRAME:
1341
case CORE_POWERDOWN:
1342
// Reached the end of the frame while running at full blast, all good. Set back to running for the next frame
1343
coreState = CORE_RUNNING_CPU;
1344
break;
1345
default:
1346
// We're not handling the various states used for debugging in the libretro port.
1347
break;
1348
}
1349
1350
if (gpu)
1351
gpu->EndHostFrame();
1352
1353
if (ctx->GetDrawContext()) {
1354
ctx->GetDrawContext()->EndFrame();
1355
ctx->GetDrawContext()->Present(Draw::PresentMode::FIFO, 1);
1356
}
1357
}
1358
1359
static void EmuThreadFunc()
1360
{
1361
SetCurrentThreadName("EmuThread");
1362
1363
for (;;)
1364
{
1365
switch ((EmuThreadState)emuThreadState)
1366
{
1367
case EmuThreadState::START_REQUESTED:
1368
emuThreadState = EmuThreadState::RUNNING;
1369
[[fallthrough]];
1370
case EmuThreadState::RUNNING:
1371
EmuFrame();
1372
break;
1373
case EmuThreadState::PAUSE_REQUESTED:
1374
emuThreadState = EmuThreadState::PAUSED;
1375
[[fallthrough]];
1376
case EmuThreadState::PAUSED:
1377
sleep_ms(1, "libretro-paused");
1378
break;
1379
default:
1380
case EmuThreadState::QUIT_REQUESTED:
1381
emuThreadState = EmuThreadState::STOPPED;
1382
ctx->StopThread();
1383
return;
1384
}
1385
}
1386
}
1387
1388
void EmuThreadStart()
1389
{
1390
bool wasPaused = emuThreadState == EmuThreadState::PAUSED;
1391
emuThreadState = EmuThreadState::START_REQUESTED;
1392
1393
if (!wasPaused)
1394
{
1395
ctx->ThreadStart();
1396
emuThread = std::thread(&EmuThreadFunc);
1397
}
1398
}
1399
1400
void EmuThreadStop()
1401
{
1402
if (emuThreadState != EmuThreadState::RUNNING)
1403
return;
1404
1405
emuThreadState = EmuThreadState::QUIT_REQUESTED;
1406
1407
// Need to keep eating frames to allow the EmuThread to exit correctly.
1408
while (ctx->ThreadFrame())
1409
;
1410
1411
emuThread.join();
1412
emuThread = std::thread();
1413
ctx->ThreadEnd();
1414
}
1415
1416
void EmuThreadPause()
1417
{
1418
if (emuThreadState != EmuThreadState::RUNNING)
1419
return;
1420
1421
emuThreadState = EmuThreadState::PAUSE_REQUESTED;
1422
1423
ctx->ThreadFrame(); // Eat 1 frame
1424
1425
while (emuThreadState != EmuThreadState::PAUSED)
1426
sleep_ms(1, "libretro-pause-poll");
1427
}
1428
1429
} // namespace Libretro
1430
1431
static void retro_check_backend(void)
1432
{
1433
struct retro_variable var = {0};
1434
1435
var.key = "ppsspp_backend";
1436
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
1437
{
1438
if (!strcmp(var.value, "auto"))
1439
backend = RETRO_HW_CONTEXT_DUMMY;
1440
else if (!strcmp(var.value, "opengl"))
1441
backend = RETRO_HW_CONTEXT_OPENGL;
1442
else if (!strcmp(var.value, "vulkan"))
1443
backend = RETRO_HW_CONTEXT_VULKAN;
1444
else if (!strcmp(var.value, "d3d11"))
1445
backend = RETRO_HW_CONTEXT_DIRECT3D;
1446
else if (!strcmp(var.value, "none"))
1447
backend = RETRO_HW_CONTEXT_NONE;
1448
}
1449
}
1450
1451
bool retro_load_game(const struct retro_game_info *game)
1452
{
1453
retro_pixel_format fmt = retro_pixel_format::RETRO_PIXEL_FORMAT_XRGB8888;
1454
if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
1455
{
1456
ERROR_LOG(Log::System, "XRGB8888 is not supported.\n");
1457
return false;
1458
}
1459
1460
retro_check_backend();
1461
1462
ctx = LibretroGraphicsContext::CreateGraphicsContext();
1463
1464
INFO_LOG(Log::System, "Using %s backend", ctx->Ident());
1465
1466
Core_SetGraphicsContext(ctx);
1467
SetGPUBackend((GPUBackend)g_Config.iGPUBackend);
1468
1469
useEmuThread = ctx->GetGPUCore() == GPUCORE_GLES;
1470
1471
// default to interpreter to allow startup in platforms w/o JIT capability
1472
// TODO: I guess we should auto detect? And also, default to IR Interpreter...
1473
g_Config.iCpuCore = (int)CPUCore::INTERPRETER;
1474
1475
CoreParameter coreParam = {};
1476
coreParam.enableSound = true;
1477
coreParam.fileToStart = Path(std::string(game->path));
1478
coreParam.startBreak = false;
1479
coreParam.headLess = true; // really?
1480
coreParam.graphicsContext = ctx;
1481
coreParam.gpuCore = ctx->GetGPUCore();
1482
check_variables(coreParam);
1483
1484
// TODO: OpenGL goes black when inited with software rendering,
1485
// therefore start without, set back after init, and reset.
1486
softwareRenderInitHack = ctx->GetGPUCore() == GPUCORE_GLES && g_Config.bSoftwareRendering;
1487
if (softwareRenderInitHack)
1488
g_Config.bSoftwareRendering = false;
1489
1490
// set cpuCore from libretro setting variable
1491
coreParam.cpuCore = (CPUCore)g_Config.iCpuCore;
1492
1493
g_pendingBoot = true;
1494
1495
struct retro_core_option_display option_display;
1496
1497
// Show/hide 'MSAA' and 'Texture Shader' options, Vulkan only
1498
option_display.visible = (g_Config.iGPUBackend == (int)GPUBackend::VULKAN);
1499
option_display.key = "ppsspp_mulitsample_level";
1500
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
1501
option_display.key = "ppsspp_texture_shader";
1502
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
1503
1504
// Show/hide 'Buffered Frames' option, Vulkan/GL only
1505
option_display.visible = (g_Config.iGPUBackend == (int)GPUBackend::VULKAN ||
1506
g_Config.iGPUBackend == (int)GPUBackend::OPENGL);
1507
option_display.key = "ppsspp_inflight_frames";
1508
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
1509
1510
set_variable_visibility();
1511
1512
// NOTE: At this point we haven't really booted yet, but "in-game" we'll just keep polling
1513
// PSP_InitUpdate until done.
1514
1515
// Launch the init process.
1516
if (!PSP_InitStart(coreParam)) {
1517
g_bootErrorString = coreParam.errorString;
1518
// Can't really fail, the errors normally happen later during InitUpdate
1519
ERROR_LOG(Log::Boot, "%s", g_bootErrorString.c_str());
1520
g_pendingBoot = false;
1521
return false;
1522
}
1523
1524
return true;
1525
}
1526
1527
void retro_unload_game(void)
1528
{
1529
if (Libretro::useEmuThread)
1530
Libretro::EmuThreadStop();
1531
1532
PSP_Shutdown(true);
1533
g_VFS.Clear();
1534
1535
delete ctx;
1536
ctx = nullptr;
1537
PSP_CoreParameter().graphicsContext = nullptr;
1538
}
1539
1540
void retro_reset(void)
1541
{
1542
PSP_Shutdown(true);
1543
1544
if (BootState::Complete != PSP_Init(PSP_CoreParameter(), &g_bootErrorString))
1545
{
1546
ERROR_LOG(Log::Boot, "%s", g_bootErrorString.c_str());
1547
environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, nullptr);
1548
}
1549
}
1550
1551
static void retro_input(void)
1552
{
1553
unsigned i;
1554
int16_t ret = 0;
1555
// clang-format off
1556
static struct
1557
{
1558
u32 retro;
1559
u32 sceCtrl;
1560
} map[] = {
1561
{ RETRO_DEVICE_ID_JOYPAD_UP, CTRL_UP },
1562
{ RETRO_DEVICE_ID_JOYPAD_DOWN, CTRL_DOWN },
1563
{ RETRO_DEVICE_ID_JOYPAD_LEFT, CTRL_LEFT },
1564
{ RETRO_DEVICE_ID_JOYPAD_RIGHT, CTRL_RIGHT },
1565
{ RETRO_DEVICE_ID_JOYPAD_X, CTRL_TRIANGLE },
1566
{ RETRO_DEVICE_ID_JOYPAD_A, CTRL_CIRCLE },
1567
{ RETRO_DEVICE_ID_JOYPAD_B, CTRL_CROSS },
1568
{ RETRO_DEVICE_ID_JOYPAD_Y, CTRL_SQUARE },
1569
{ RETRO_DEVICE_ID_JOYPAD_L, CTRL_LTRIGGER },
1570
{ RETRO_DEVICE_ID_JOYPAD_R, CTRL_RTRIGGER },
1571
{ RETRO_DEVICE_ID_JOYPAD_START, CTRL_START },
1572
{ RETRO_DEVICE_ID_JOYPAD_SELECT, CTRL_SELECT },
1573
};
1574
// clang-format on
1575
1576
input_poll_cb();
1577
1578
if (libretro_supports_bitmasks)
1579
ret = input_state_cb(0, RETRO_DEVICE_JOYPAD,
1580
0, RETRO_DEVICE_ID_JOYPAD_MASK);
1581
else
1582
{
1583
for (i = RETRO_DEVICE_ID_JOYPAD_B; i <= RETRO_DEVICE_ID_JOYPAD_R; i++)
1584
if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, i))
1585
ret |= (1 << i);
1586
}
1587
1588
for (i = 0; i < sizeof(map) / sizeof(*map); i++)
1589
{
1590
bool pressed = ret & (1 << map[i].retro);
1591
1592
if (pressed)
1593
{
1594
__CtrlUpdateButtons(map[i].sceCtrl, 0);
1595
}
1596
else
1597
{
1598
__CtrlUpdateButtons(0, map[i].sceCtrl);
1599
}
1600
}
1601
1602
float x_left = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X) / 32767.0f;
1603
float y_left = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y) / -32767.0f;
1604
float x_right = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X) / 32767.0f;
1605
float y_right = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y) / -32767.0f;
1606
1607
// Analog circle vs square gate compensation,
1608
// deadzone and sensitivity copied from ControlMapper.cpp's
1609
// ConvertAnalogStick and MapAxisValue functions
1610
const bool isCircular = g_Config.bAnalogIsCircular;
1611
1612
float norm = std::max(fabsf(x_left), fabsf(y_left));
1613
1614
if (norm == 0.0f)
1615
{
1616
__CtrlSetAnalogXY(CTRL_STICK_LEFT, x_left, y_left);
1617
__CtrlSetAnalogXY(CTRL_STICK_RIGHT, x_right, y_right);
1618
return;
1619
}
1620
1621
if (isCircular)
1622
{
1623
float newNorm = sqrtf(x_left * x_left + y_left * y_left);
1624
float factor = newNorm / norm;
1625
x_left *= factor;
1626
y_left *= factor;
1627
norm = newNorm;
1628
}
1629
1630
const float deadzone = g_Config.fAnalogDeadzone;
1631
const float sensitivity = g_Config.fAnalogSensitivity;
1632
const float sign = norm >= 0.0f ? 1.0f : -1.0f;
1633
float mappedNorm = norm;
1634
1635
// Apply deadzone
1636
mappedNorm = Libretro::clamp((fabsf(mappedNorm) - deadzone) / (1.0f - deadzone), 0.0f, 1.0f);
1637
1638
// Apply sensitivity
1639
if (mappedNorm != 0.0f)
1640
mappedNorm = Libretro::clamp(mappedNorm * sensitivity * sign, -1.0f, 1.0f);
1641
1642
x_left = Libretro::clamp(x_left / norm * mappedNorm, -1.0f, 1.0f);
1643
y_left = Libretro::clamp(y_left / norm * mappedNorm, -1.0f, 1.0f);
1644
1645
__CtrlSetAnalogXY(CTRL_STICK_LEFT, x_left, y_left);
1646
__CtrlSetAnalogXY(CTRL_STICK_RIGHT, x_right, y_right);
1647
}
1648
1649
void retro_run(void)
1650
{
1651
if (g_pendingBoot) {
1652
BootState state = PSP_InitUpdate(&g_bootErrorString);
1653
switch (state) {
1654
case BootState::Failed:
1655
g_pendingBoot = false;
1656
ERROR_LOG(Log::Boot, "%s", g_bootErrorString.c_str());
1657
environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, nullptr);
1658
return;
1659
case BootState::Booting:
1660
// Not done yet. Do maintenance stuff and bail.
1661
retro_input();
1662
ctx->SwapBuffers();
1663
return;
1664
case BootState::Off:
1665
// shouldn't happen.
1666
_dbg_assert_(false);
1667
return;
1668
}
1669
1670
// BootState is BootState::Complete.
1671
// Here's where we finish the boot process.
1672
coreState = CORE_RUNNING_CPU;
1673
g_bootErrorString.clear();
1674
g_pendingBoot = false;
1675
1676
if (unserialize_data) {
1677
retro_unserialize(unserialize_data, unserialize_size);
1678
1679
free(unserialize_data);
1680
unserialize_data = NULL;
1681
}
1682
}
1683
1684
// TODO: This seems dubious.
1685
if (softwareRenderInitHack)
1686
{
1687
log_cb(RETRO_LOG_DEBUG, "Software rendering init hack for opengl triggered.\n");
1688
softwareRenderInitHack = false;
1689
g_Config.bSoftwareRendering = true;
1690
retro_reset();
1691
}
1692
1693
bool updated;
1694
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated)
1695
&& updated)
1696
check_variables(PSP_CoreParameter());
1697
else
1698
check_dynamic_variables(PSP_CoreParameter());
1699
1700
retro_input();
1701
1702
if (useEmuThread)
1703
{
1704
if ( emuThreadState == EmuThreadState::PAUSED ||
1705
emuThreadState == EmuThreadState::PAUSE_REQUESTED)
1706
{
1707
VsyncSwapIntervalDetect();
1708
ctx->SwapBuffers();
1709
return;
1710
}
1711
1712
if (emuThreadState != EmuThreadState::RUNNING)
1713
EmuThreadStart();
1714
1715
if (!ctx->ThreadFrame())
1716
{
1717
VsyncSwapIntervalDetect();
1718
return;
1719
}
1720
}
1721
else
1722
EmuFrame();
1723
1724
VsyncSwapIntervalDetect();
1725
ctx->SwapBuffers();
1726
upload_output_audio_buffer();
1727
}
1728
1729
unsigned retro_get_region(void) { return RETRO_REGION_NTSC; }
1730
1731
bool retro_load_game_special(unsigned type, const struct retro_game_info *info, size_t num) { return false; }
1732
1733
namespace SaveState
1734
{
1735
struct SaveStart
1736
{
1737
void DoState(PointerWrap &p);
1738
};
1739
} // namespace SaveState
1740
1741
size_t retro_serialize_size(void)
1742
{
1743
if (!gpu) // The HW renderer isn't ready on first pass.
1744
return 134217728; // 128MB ought to be enough for anybody.
1745
1746
SaveState::SaveStart state;
1747
// TODO: Libretro API extension to use the savestate queue
1748
if (useEmuThread)
1749
EmuThreadPause();
1750
1751
return (CChunkFileReader::MeasurePtr(state) + 0x800000) & ~0x7FFFFF;
1752
// We don't unpause intentionally
1753
}
1754
1755
bool retro_serialize(void *data, size_t size)
1756
{
1757
if (!gpu) // The HW renderer isn't ready on first pass.
1758
return false;
1759
1760
// TODO: Libretro API extension to use the savestate queue
1761
if (useEmuThread)
1762
EmuThreadPause(); // Does nothing if already paused
1763
1764
size_t measuredSize;
1765
SaveState::SaveStart state;
1766
auto err = CChunkFileReader::MeasureAndSavePtr(state, (u8 **)&data, &measuredSize);
1767
bool retVal = err == CChunkFileReader::ERROR_NONE;
1768
1769
if (useEmuThread)
1770
{
1771
EmuThreadStart();
1772
sleep_ms(4, "libretro-serialize");
1773
}
1774
1775
return retVal;
1776
}
1777
1778
bool retro_unserialize(const void *data, size_t size)
1779
{
1780
// The HW renderer isn't ready on first pass.
1781
// So we save the data until we are ready to use it.
1782
if (!gpu) {
1783
unserialize_data = malloc(size);
1784
memcpy(unserialize_data, data, size);
1785
return true;
1786
}
1787
1788
// TODO: Libretro API extension to use the savestate queue
1789
if (useEmuThread)
1790
EmuThreadPause(); // Does nothing if already paused
1791
1792
std::string errorString;
1793
SaveState::SaveStart state;
1794
bool retVal = CChunkFileReader::LoadPtr((u8 *)data, state, &errorString)
1795
== CChunkFileReader::ERROR_NONE;
1796
1797
if (useEmuThread)
1798
{
1799
EmuThreadStart();
1800
sleep_ms(4, "libretro-unserialize");
1801
}
1802
1803
return retVal;
1804
}
1805
1806
void *retro_get_memory_data(unsigned id)
1807
{
1808
if ( id == RETRO_MEMORY_SYSTEM_RAM )
1809
return Memory::GetPointerWriteUnchecked(PSP_GetKernelMemoryBase()) ;
1810
return NULL;
1811
}
1812
1813
size_t retro_get_memory_size(unsigned id)
1814
{
1815
if ( id == RETRO_MEMORY_SYSTEM_RAM )
1816
return Memory::g_MemorySize ;
1817
return 0;
1818
}
1819
1820
void retro_cheat_reset(void) {
1821
// Init Cheat Engine
1822
CWCheatEngine *cheatEngine = new CWCheatEngine(g_paramSFO.GetDiscID());
1823
Path file=cheatEngine->CheatFilename();
1824
1825
// Output cheats to cheat file
1826
std::ofstream outFile;
1827
outFile.open(file.c_str());
1828
outFile << "_S " << g_paramSFO.GetDiscID() << std::endl;
1829
outFile.close();
1830
1831
g_Config.bReloadCheats = true;
1832
1833
// Parse and Run the Cheats
1834
cheatEngine->ParseCheats();
1835
if (cheatEngine->HasCheats()) {
1836
cheatEngine->Run();
1837
}
1838
1839
}
1840
1841
void retro_cheat_set(unsigned index, bool enabled, const char *code) {
1842
// Initialize Cheat Engine
1843
CWCheatEngine *cheatEngine = new CWCheatEngine(g_paramSFO.GetDiscID());
1844
cheatEngine->CreateCheatFile();
1845
Path file=cheatEngine->CheatFilename();
1846
1847
// Read cheats file
1848
std::vector<std::string> cheats;
1849
std::ifstream cheat_content(file.c_str());
1850
std::stringstream buffer;
1851
buffer << cheat_content.rdbuf();
1852
std::string existing_cheats=ReplaceAll(buffer.str(), std::string("\n_C"), std::string("|"));
1853
SplitString(existing_cheats, '|', cheats);
1854
1855
// Generate Cheat String
1856
std::stringstream cheat("");
1857
cheat << (enabled ? "1 " : "0 ") << index << std::endl;
1858
std::string code_str(code);
1859
std::vector<std::string> codes;
1860
code_str=ReplaceAll(code_str, std::string(" "), std::string("+"));
1861
SplitString(code_str, '+', codes);
1862
int part=0;
1863
for (int i=0; i < codes.size(); i++) {
1864
if (codes[i].size() <= 2) {
1865
// _L _M ..etc
1866
// Assume _L
1867
} else if (part == 0) {
1868
cheat << "_L " << codes[i] << " ";
1869
part++;
1870
} else {
1871
cheat << codes[i] << std::endl;
1872
part=0;
1873
}
1874
}
1875
1876
// Add or Replace the Cheat
1877
if (index + 1 < cheats.size()) {
1878
cheats[index + 1]=cheat.str();
1879
} else {
1880
cheats.push_back(cheat.str());
1881
}
1882
1883
// Output cheats to cheat file
1884
std::ofstream outFile;
1885
outFile.open(file.c_str());
1886
outFile << "_S " << g_paramSFO.GetDiscID() << std::endl;
1887
for (int i=1; i < cheats.size(); i++) {
1888
outFile << "_C" << cheats[i] << std::endl;
1889
}
1890
outFile.close();
1891
1892
g_Config.bReloadCheats = true;
1893
1894
// Parse and Run the Cheats
1895
cheatEngine->ParseCheats();
1896
if (cheatEngine->HasCheats()) {
1897
cheatEngine->Run();
1898
}
1899
}
1900
1901
int64_t System_GetPropertyInt(SystemProperty prop)
1902
{
1903
switch (prop)
1904
{
1905
case SYSPROP_AUDIO_SAMPLE_RATE:
1906
return SAMPLERATE;
1907
#if PPSSPP_PLATFORM(ANDROID)
1908
case SYSPROP_SYSTEMVERSION: {
1909
char sdk[PROP_VALUE_MAX] = {0};
1910
if (__system_property_get("ro.build.version.sdk", sdk) != 0) {
1911
return atoi(sdk);
1912
}
1913
return -1;
1914
}
1915
#endif
1916
default:
1917
break;
1918
}
1919
1920
return -1;
1921
}
1922
1923
float System_GetPropertyFloat(SystemProperty prop)
1924
{
1925
switch (prop)
1926
{
1927
case SYSPROP_DISPLAY_REFRESH_RATE:
1928
return 60.0f / 1.001f;
1929
case SYSPROP_DISPLAY_SAFE_INSET_LEFT:
1930
case SYSPROP_DISPLAY_SAFE_INSET_RIGHT:
1931
case SYSPROP_DISPLAY_SAFE_INSET_TOP:
1932
case SYSPROP_DISPLAY_SAFE_INSET_BOTTOM:
1933
return 0.0f;
1934
default:
1935
break;
1936
}
1937
1938
return -1;
1939
}
1940
1941
bool System_GetPropertyBool(SystemProperty prop)
1942
{
1943
switch (prop)
1944
{
1945
case SYSPROP_CAN_JIT:
1946
#if PPSSPP_PLATFORM(IOS)
1947
bool can_jit;
1948
return (environ_cb(RETRO_ENVIRONMENT_GET_JIT_CAPABLE, &can_jit) && can_jit);
1949
#else
1950
return true;
1951
#endif
1952
default:
1953
return false;
1954
}
1955
}
1956
1957
std::string System_GetProperty(SystemProperty prop) { return ""; }
1958
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) { return std::vector<std::string>(); }
1959
1960
void System_Notify(SystemNotification notification) {
1961
switch (notification) {
1962
default:
1963
break;
1964
}
1965
}
1966
bool System_MakeRequest(SystemRequestType type, int requestId, const std::string &param1, const std::string &param2, int64_t param3, int64_t param4) { return false; }
1967
void System_PostUIMessage(UIMessage message, const std::string &param) {}
1968
void System_RunOnMainThread(std::function<void()>) {}
1969
void NativeFrame(GraphicsContext *graphicsContext) {}
1970
void NativeResized() {}
1971
1972
void System_Toast(std::string_view str) {}
1973
1974
inline int16_t Clamp16(int32_t sample) {
1975
if (sample < -32767) return -32767;
1976
if (sample > 32767) return 32767;
1977
return sample;
1978
}
1979
1980
void System_AudioPushSamples(const int32_t *audio, int numSamples, float volume) {
1981
// We ignore volume here, because it's handled by libretro presumably.
1982
1983
// Convert to 16-bit audio for further processing.
1984
int16_t buffer[1024 * 2];
1985
int origSamples = numSamples * 2;
1986
1987
while (numSamples > 0) {
1988
int blockSize = std::min(1024, numSamples);
1989
for (int i = 0; i < blockSize; i++) {
1990
buffer[i * 2] = Clamp16(audio[i * 2]);
1991
buffer[i * 2 + 1] = Clamp16(audio[i * 2 + 1]);
1992
}
1993
1994
numSamples -= blockSize;
1995
}
1996
1997
if (output_audio_buffer.capacity - output_audio_buffer.size < origSamples)
1998
ensure_output_audio_buffer_capacity((output_audio_buffer.capacity + origSamples) * 1.5);
1999
memcpy(output_audio_buffer.data + output_audio_buffer.size, buffer, origSamples * sizeof(*output_audio_buffer.data));
2000
output_audio_buffer.size += origSamples;
2001
}
2002
2003
void System_AudioGetDebugStats(char *buf, size_t bufSize) { if (buf) buf[0] = '\0'; }
2004
void System_AudioClear() {}
2005
2006
#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
2007
std::vector<std::string> System_GetCameraDeviceList() { return std::vector<std::string>(); }
2008
bool System_AudioRecordingIsAvailable() { return false; }
2009
bool System_AudioRecordingState() { return false; }
2010
2011
#endif
2012
2013
// TODO: To avoid having to define these here, these should probably be turned into system "requests".
2014
bool NativeSaveSecret(std::string_view nameOfSecret, std::string_view data) { return false; }
2015
std::string NativeLoadSecret(std::string_view nameOfSecret) {
2016
return "";
2017
}
2018
2019