CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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