mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 13:30:02 +00:00
1719 lines
53 KiB
C++
1719 lines
53 KiB
C++
#include "ppsspp_config.h"
|
|
#include <cstring>
|
|
#include <cassert>
|
|
#include <thread>
|
|
#include <atomic>
|
|
#include <vector>
|
|
#include <cstdlib>
|
|
#include <mutex>
|
|
|
|
#include "Common/CPUDetect.h"
|
|
#include "Common/Log.h"
|
|
#include "Common/LogManager.h"
|
|
#include "Common/System/Display.h"
|
|
#include "Common/System/NativeApp.h"
|
|
#include "Common/System/System.h"
|
|
#include "Common/TimeUtil.h"
|
|
#include "Common/File/FileUtil.h"
|
|
#include "Common/Serialize/Serializer.h"
|
|
#include "Common/ConsoleListener.h"
|
|
#include "Common/Input/InputState.h"
|
|
#include "Common/Thread/ThreadUtil.h"
|
|
#include "Common/Thread/ThreadManager.h"
|
|
#include "Common/File/VFS/VFS.h"
|
|
#include "Common/File/VFS/AssetReader.h"
|
|
#include "Common/Data/Text/I18n.h"
|
|
|
|
#include "Core/Config.h"
|
|
#include "Core/ConfigValues.h"
|
|
#include "Core/Core.h"
|
|
#include "Core/HLE/sceCtrl.h"
|
|
#include "Core/HLE/sceUtility.h"
|
|
#include "Core/HLE/__sceAudio.h"
|
|
#include "Core/HW/MemoryStick.h"
|
|
#include "Core/Host.h"
|
|
#include "Core/MemMap.h"
|
|
#include "Core/System.h"
|
|
#include "Core/CoreTiming.h"
|
|
#include "Core/HW/Display.h"
|
|
|
|
#include "GPU/GPUState.h"
|
|
#include "GPU/GPUInterface.h"
|
|
#include "GPU/Common/FramebufferManagerCommon.h"
|
|
#include "GPU/Common/TextureScalerCommon.h"
|
|
#include "GPU/Common/PresentationCommon.h"
|
|
|
|
#include "libretro/libretro.h"
|
|
#include "libretro/LibretroGraphicsContext.h"
|
|
#include "libretro/libretro_core_options.h"
|
|
|
|
#if PPSSPP_PLATFORM(ANDROID)
|
|
#include <sys/system_properties.h>
|
|
#endif
|
|
|
|
#define DIR_SEP "/"
|
|
#ifdef _WIN32
|
|
#define DIR_SEP_CHRS "/\\"
|
|
#else
|
|
#define DIR_SEP_CHRS "/"
|
|
#endif
|
|
|
|
#define SAMPLERATE 44100
|
|
|
|
#define AUDIO_RING_BUFFER_SIZE (1 << 16)
|
|
#define AUDIO_RING_BUFFER_SIZE_MASK (AUDIO_RING_BUFFER_SIZE - 1)
|
|
// An alpha factor of 1/180 is *somewhat* equivalent
|
|
// to calculating the average for the last 180
|
|
// frames, or 3 seconds of runtime...
|
|
#define AUDIO_FRAMES_MOVING_AVG_ALPHA (1.0f / 180.0f)
|
|
|
|
// Calculated swap interval is 'stable' if the same
|
|
// value is recorded for a number of retro_run()
|
|
// calls equal to VSYNC_SWAP_INTERVAL_FRAMES
|
|
#define VSYNC_SWAP_INTERVAL_FRAMES 6
|
|
// Calculated swap interval is 'valid' if it is
|
|
// within VSYNC_SWAP_INTERVAL_THRESHOLD of an integer
|
|
// value
|
|
#define VSYNC_SWAP_INTERVAL_THRESHOLD 0.05f
|
|
// Swap interval detection is only enabled if the
|
|
// core is running at 'normal' speed - i.e. if
|
|
// run speed is within VSYNC_SWAP_INTERVAL_RUN_SPEED_THRESHOLD
|
|
// percent of 100
|
|
#define VSYNC_SWAP_INTERVAL_RUN_SPEED_THRESHOLD 5.0f
|
|
|
|
static bool libretro_supports_bitmasks = false;
|
|
static bool libretro_supports_option_categories = false;
|
|
static bool show_ip_address_options = true;
|
|
static bool show_upnp_port_option = true;
|
|
static bool show_detect_frame_rate_option = true;
|
|
static std::string changeProAdhocServer;
|
|
|
|
namespace Libretro
|
|
{
|
|
LibretroGraphicsContext *ctx;
|
|
retro_environment_t environ_cb;
|
|
static retro_audio_sample_batch_t audio_batch_cb;
|
|
static retro_input_poll_t input_poll_cb;
|
|
static retro_input_state_t input_state_cb;
|
|
} // namespace Libretro
|
|
|
|
namespace Libretro
|
|
{
|
|
static bool detectVsyncSwapInterval = false;
|
|
static bool detectVsyncSwapIntervalOptShown = true;
|
|
|
|
static s64 expectedTimeUsPerRun = 0;
|
|
static uint32_t vsyncSwapInterval = 1;
|
|
static uint32_t vsyncSwapIntervalLast = 1;
|
|
static uint32_t vsyncSwapIntervalCounter = 0;
|
|
static int numVBlanksLast = 0;
|
|
static double fpsTimeLast = 0.0;
|
|
static float runSpeed = 0.0f;
|
|
static s64 runTicksLast = 0;
|
|
|
|
static void VsyncSwapIntervalReset()
|
|
{
|
|
expectedTimeUsPerRun = (s64)(1000000.0f / (60.0f / 1.001f));
|
|
vsyncSwapInterval = 1;
|
|
vsyncSwapIntervalLast = 1;
|
|
vsyncSwapIntervalCounter = 0;
|
|
|
|
numVBlanksLast = 0;
|
|
fpsTimeLast = 0.0;
|
|
runSpeed = 0.0f;
|
|
runTicksLast = 0;
|
|
|
|
detectVsyncSwapIntervalOptShown = true;
|
|
}
|
|
|
|
static void VsyncSwapIntervalDetect()
|
|
{
|
|
if (!detectVsyncSwapInterval)
|
|
return;
|
|
|
|
// All bets are off if core is running at
|
|
// the 'wrong' speed (i.e. cycle count for
|
|
// this run will be meaningless if internal
|
|
// frame rate is dropping below expected
|
|
// value, or fast forward is enabled)
|
|
double fpsTime = time_now_d();
|
|
int numVBlanks = __DisplayGetNumVblanks();
|
|
int frames = numVBlanks - numVBlanksLast;
|
|
|
|
if (frames >= VSYNC_SWAP_INTERVAL_FRAMES << 1)
|
|
{
|
|
double fps = (double)frames / (fpsTime - fpsTimeLast);
|
|
runSpeed = fps / ((60.0f / 1.001f) / 100.0f);
|
|
|
|
fpsTimeLast = fpsTime;
|
|
numVBlanksLast = numVBlanks;
|
|
}
|
|
|
|
float speedDelta = 100.0f - runSpeed;
|
|
speedDelta = (speedDelta < 0.0f) ? -speedDelta : speedDelta;
|
|
|
|
// Speed is measured relative to a 60 Hz refresh
|
|
// rate. If we are transitioning from a low internal
|
|
// frame rate to a higher internal frame rate, then
|
|
// 'full speed' may actually equate to
|
|
// (100 / current_swap_interval)...
|
|
if ((vsyncSwapInterval > 1) &&
|
|
(speedDelta >= VSYNC_SWAP_INTERVAL_RUN_SPEED_THRESHOLD))
|
|
{
|
|
speedDelta = 100.0f - (runSpeed * (float)vsyncSwapInterval);
|
|
speedDelta = (speedDelta < 0.0f) ? -speedDelta : speedDelta;
|
|
}
|
|
|
|
if (speedDelta >= VSYNC_SWAP_INTERVAL_RUN_SPEED_THRESHOLD)
|
|
{
|
|
// Swap interval detection is invalid - bail out
|
|
vsyncSwapIntervalCounter = 0;
|
|
return;
|
|
}
|
|
|
|
// Get elapsed time (us) for this run
|
|
s64 runTicks = CoreTiming::GetTicks();
|
|
s64 runTimeUs = cyclesToUs(runTicks - runTicksLast);
|
|
|
|
// Check if current internal frame rate is a
|
|
// factor of the default ~60 Hz
|
|
float swapRatio = (float)runTimeUs / (float)expectedTimeUsPerRun;
|
|
uint32_t swapInteger;
|
|
float swapRemainder;
|
|
|
|
// If internal frame rate is equal to (within threshold)
|
|
// or higher than the default ~60 Hz, fall back to a
|
|
// swap interval of 1
|
|
if (swapRatio < (1.0f + VSYNC_SWAP_INTERVAL_THRESHOLD))
|
|
{
|
|
swapInteger = 1;
|
|
swapRemainder = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
swapInteger = (uint32_t)(swapRatio + 0.5f);
|
|
swapRemainder = swapRatio - (float)swapInteger;
|
|
swapRemainder = (swapRemainder < 0.0f) ?
|
|
-swapRemainder : swapRemainder;
|
|
}
|
|
|
|
// > Swap interval is considered 'valid' if it is
|
|
// within VSYNC_SWAP_INTERVAL_THRESHOLD of an integer
|
|
// value
|
|
// > If valid, check if new swap interval differs from
|
|
// previously logged value
|
|
if ((swapRemainder <= VSYNC_SWAP_INTERVAL_THRESHOLD) &&
|
|
(swapInteger != vsyncSwapInterval))
|
|
{
|
|
vsyncSwapIntervalCounter =
|
|
(swapInteger == vsyncSwapIntervalLast) ?
|
|
(vsyncSwapIntervalCounter + 1) : 0;
|
|
|
|
// Check whether swap interval is 'stable'
|
|
if (vsyncSwapIntervalCounter >= VSYNC_SWAP_INTERVAL_FRAMES)
|
|
{
|
|
vsyncSwapInterval = swapInteger;
|
|
vsyncSwapIntervalCounter = 0;
|
|
|
|
// Notify frontend
|
|
retro_system_av_info avInfo;
|
|
retro_get_system_av_info(&avInfo);
|
|
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avInfo);
|
|
}
|
|
|
|
vsyncSwapIntervalLast = swapInteger;
|
|
}
|
|
else
|
|
vsyncSwapIntervalCounter = 0;
|
|
|
|
runTicksLast = runTicks;
|
|
}
|
|
} // namespace Libretro
|
|
|
|
namespace Libretro
|
|
{
|
|
static std::mutex audioSampleLock_;
|
|
static int16_t audioRingBuffer[AUDIO_RING_BUFFER_SIZE] = {0};
|
|
static uint32_t audioRingBufferBase = 0;
|
|
static uint32_t audioRingBufferIndex = 0;
|
|
|
|
static int16_t *audioOutBuffer = NULL;
|
|
static uint32_t audioOutBufferSize = 0;
|
|
static float audioOutFramesAvg = 0.0f;
|
|
// Set this to an arbitrarily large value,
|
|
// it will be fine tuned in AudioUploadSamples()
|
|
static uint32_t audioBatchFramesMax = AUDIO_RING_BUFFER_SIZE >> 1;
|
|
|
|
static void AudioBufferFlush()
|
|
{
|
|
const std::lock_guard<std::mutex> lock(audioSampleLock_);
|
|
audioRingBufferBase = 0;
|
|
audioRingBufferIndex = 0;
|
|
audioOutFramesAvg = (float)SAMPLERATE / (60.0f / 1.001f);
|
|
}
|
|
|
|
static void AudioBufferInit()
|
|
{
|
|
audioOutFramesAvg = (float)SAMPLERATE / (60.0f / 1.001f);
|
|
audioOutBufferSize = ((uint32_t)audioOutFramesAvg + 1) * 2;
|
|
audioOutBuffer = (int16_t *)malloc(audioOutBufferSize * sizeof(int16_t));
|
|
audioBatchFramesMax = AUDIO_RING_BUFFER_SIZE >> 1;
|
|
|
|
AudioBufferFlush();
|
|
}
|
|
|
|
static void AudioBufferDeinit()
|
|
{
|
|
if (audioOutBuffer)
|
|
free(audioOutBuffer);
|
|
audioOutBuffer = NULL;
|
|
audioOutBufferSize = 0;
|
|
audioOutFramesAvg = 0.0f;
|
|
audioBatchFramesMax = AUDIO_RING_BUFFER_SIZE >> 1;
|
|
|
|
AudioBufferFlush();
|
|
}
|
|
|
|
static uint32_t AudioBufferOccupancy()
|
|
{
|
|
const std::lock_guard<std::mutex> lock(audioSampleLock_);
|
|
uint32_t occupancy = (audioRingBufferIndex - audioRingBufferBase) &
|
|
AUDIO_RING_BUFFER_SIZE_MASK;
|
|
return occupancy >> 1;
|
|
}
|
|
|
|
static void AudioBufferWrite(int16_t *audio, uint32_t frames)
|
|
{
|
|
const std::lock_guard<std::mutex> lock(audioSampleLock_);
|
|
uint32_t frameIndex;
|
|
uint32_t bufferIndex = audioRingBufferIndex;
|
|
|
|
for (frameIndex = 0; frameIndex < frames; frameIndex++)
|
|
{
|
|
audioRingBuffer[audioRingBufferIndex] = *(audio++);
|
|
audioRingBuffer[audioRingBufferIndex + 1] = *(audio++);
|
|
audioRingBufferIndex = (audioRingBufferIndex + 2) % AUDIO_RING_BUFFER_SIZE;
|
|
}
|
|
}
|
|
|
|
static uint32_t AudioBufferRead(int16_t *audio, uint32_t frames)
|
|
{
|
|
const std::lock_guard<std::mutex> lock(audioSampleLock_);
|
|
uint32_t framesAvailable = ((audioRingBufferIndex - audioRingBufferBase) &
|
|
AUDIO_RING_BUFFER_SIZE_MASK) >> 1;
|
|
uint32_t frameIndex;
|
|
|
|
if (frames > framesAvailable)
|
|
frames = framesAvailable;
|
|
|
|
for(frameIndex = 0; frameIndex < frames; frameIndex++)
|
|
{
|
|
uint32_t bufferIndex = (audioRingBufferBase + (frameIndex << 1)) &
|
|
AUDIO_RING_BUFFER_SIZE_MASK;
|
|
*(audio++) = audioRingBuffer[bufferIndex];
|
|
*(audio++) = audioRingBuffer[bufferIndex + 1];
|
|
}
|
|
|
|
audioRingBufferBase += frames << 1;
|
|
audioRingBufferBase &= AUDIO_RING_BUFFER_SIZE_MASK;
|
|
|
|
return frames;
|
|
}
|
|
|
|
static void AudioUploadSamples()
|
|
{
|
|
|
|
// - If 'Detect Frame Rate Changes' is disabled, then
|
|
// the core specifies a fixed frame rate of (60.0f / 1.001f)
|
|
// - At the audio sample rate of 44100, this means the
|
|
// frontend expects exactly 735.735 sample frames per call of
|
|
// retro_run()
|
|
// - If g_Config.bRenderDuplicateFrames is enabled and
|
|
// frameskip is disabled, the mean of the buffer occupancy
|
|
// willapproximate to this value in most cases
|
|
uint32_t framesAvailable = AudioBufferOccupancy();
|
|
|
|
if (framesAvailable > 0)
|
|
{
|
|
// Update 'running average' of buffer occupancy.
|
|
// Note that this is not a true running
|
|
// average, but just a leaky-integrator/
|
|
// exponential moving average, used because
|
|
// it is simple and fast (i.e. requires no
|
|
// window of samples).
|
|
audioOutFramesAvg = (AUDIO_FRAMES_MOVING_AVG_ALPHA * (float)framesAvailable) +
|
|
((1.0f - AUDIO_FRAMES_MOVING_AVG_ALPHA) * audioOutFramesAvg);
|
|
uint32_t frames = (uint32_t)audioOutFramesAvg;
|
|
|
|
if (audioOutBufferSize < (frames << 1))
|
|
{
|
|
audioOutBufferSize = (frames << 1);
|
|
audioOutBuffer = (int16_t *)realloc(audioOutBuffer,
|
|
audioOutBufferSize * sizeof(int16_t));
|
|
}
|
|
|
|
frames = AudioBufferRead(audioOutBuffer, frames);
|
|
|
|
int16_t *audioOutBufferPtr = audioOutBuffer;
|
|
while (frames > 0)
|
|
{
|
|
uint32_t framesToWrite = (frames > audioBatchFramesMax) ?
|
|
audioBatchFramesMax : frames;
|
|
uint32_t framesWritten = audio_batch_cb(audioOutBufferPtr,
|
|
framesToWrite);
|
|
|
|
if ((framesWritten < framesToWrite) &&
|
|
(framesWritten > 0))
|
|
audioBatchFramesMax = framesWritten;
|
|
|
|
frames -= framesToWrite;
|
|
audioOutBufferPtr += framesToWrite << 1;
|
|
}
|
|
}
|
|
}
|
|
} // namespace Libretro
|
|
|
|
using namespace Libretro;
|
|
|
|
class LibretroHost : public Host
|
|
{
|
|
public:
|
|
LibretroHost() {}
|
|
bool InitGraphics(std::string *error_message, GraphicsContext **ctx) override { return true; }
|
|
void ShutdownGraphics() override {}
|
|
void InitSound() override {}
|
|
void UpdateSound() override
|
|
{
|
|
int hostAttemptBlockSize = __AudioGetHostAttemptBlockSize();
|
|
const int blockSizeMax = 512;
|
|
static int16_t audio[blockSizeMax * 2];
|
|
assert(hostAttemptBlockSize <= blockSizeMax);
|
|
|
|
int samples = __AudioMix(audio, hostAttemptBlockSize, SAMPLERATE);
|
|
AudioBufferWrite(audio, samples);
|
|
}
|
|
void ShutdownSound() override {}
|
|
bool IsDebuggingEnabled() override { return false; }
|
|
bool AttemptLoadSymbolMap() override { return false; }
|
|
};
|
|
|
|
class PrintfLogger : public LogListener
|
|
{
|
|
public:
|
|
PrintfLogger(retro_log_callback log) : log_(log.log) {}
|
|
void Log(const LogMessage &message)
|
|
{
|
|
switch (message.level)
|
|
{
|
|
case LogTypes::LVERBOSE:
|
|
case LogTypes::LDEBUG:
|
|
log_(RETRO_LOG_DEBUG, "[%s] %s",
|
|
message.log, message.msg.c_str());
|
|
break;
|
|
|
|
case LogTypes::LERROR:
|
|
log_(RETRO_LOG_ERROR, "[%s] %s",
|
|
message.log, message.msg.c_str());
|
|
break;
|
|
case LogTypes::LNOTICE:
|
|
case LogTypes::LWARNING:
|
|
log_(RETRO_LOG_WARN, "[%s] %s",
|
|
message.log, message.msg.c_str());
|
|
break;
|
|
case LogTypes::LINFO:
|
|
default:
|
|
log_(RETRO_LOG_INFO, "[%s] %s",
|
|
message.log, message.msg.c_str());
|
|
break;
|
|
}
|
|
}
|
|
|
|
private:
|
|
retro_log_printf_t log_;
|
|
};
|
|
static PrintfLogger *printfLogger;
|
|
|
|
static bool set_variable_visibility(void)
|
|
{
|
|
struct retro_core_option_display option_display;
|
|
struct retro_variable var;
|
|
bool updated = false;
|
|
|
|
// Show/hide IP address options
|
|
bool show_ip_address_options_prev = show_ip_address_options;
|
|
show_ip_address_options = true;
|
|
|
|
var.key = "ppsspp_change_pro_ad_hoc_server_address";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && strcmp(var.value, "IP address"))
|
|
show_ip_address_options = false;
|
|
|
|
if (show_ip_address_options != show_ip_address_options_prev)
|
|
{
|
|
option_display.visible = show_ip_address_options;
|
|
for (int i = 0; i < 12; i++)
|
|
{
|
|
char key[64] = {0};
|
|
option_display.key = key;
|
|
snprintf(key, sizeof(key), "ppsspp_pro_ad_hoc_server_address%02d", i + 1);
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
}
|
|
updated = true;
|
|
}
|
|
|
|
// Show/hide 'UPnP Use Original Port' option
|
|
bool show_upnp_port_option_prev = show_upnp_port_option;
|
|
show_upnp_port_option = true;
|
|
|
|
var.key = "ppsspp_enable_upnp";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && !strcmp(var.value, "disabled"))
|
|
show_upnp_port_option = false;
|
|
|
|
if (show_upnp_port_option != show_upnp_port_option_prev)
|
|
{
|
|
option_display.visible = show_upnp_port_option;
|
|
option_display.key = "ppsspp_upnp_use_original_port";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
updated = true;
|
|
}
|
|
|
|
// Show/hide 'Detect Frame Rate Changes' option
|
|
bool show_detect_frame_rate_option_prev = show_detect_frame_rate_option;
|
|
int frameskip = 0;
|
|
bool auto_frameskip = false;
|
|
bool dupe_frames = false;
|
|
show_detect_frame_rate_option = true;
|
|
|
|
var.key = "ppsspp_frameskip";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && strcmp(var.value, "disabled"))
|
|
frameskip = atoi(var.value);
|
|
var.key = "ppsspp_auto_frameskip";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && !strcmp(var.value, "enabled"))
|
|
auto_frameskip = true;
|
|
var.key = "ppsspp_frame_duplication";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value && !strcmp(var.value, "enabled"))
|
|
dupe_frames = true;
|
|
|
|
show_detect_frame_rate_option = (frameskip == 0) && !auto_frameskip && !dupe_frames;
|
|
if (show_detect_frame_rate_option != show_detect_frame_rate_option_prev)
|
|
{
|
|
option_display.visible = show_detect_frame_rate_option;
|
|
option_display.key = "ppsspp_detect_vsync_swap_interval";
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
updated = true;
|
|
}
|
|
|
|
return updated;
|
|
}
|
|
|
|
void retro_set_environment(retro_environment_t cb)
|
|
{
|
|
environ_cb = cb;
|
|
|
|
libretro_set_core_options(environ_cb, &libretro_supports_option_categories);
|
|
struct retro_core_options_update_display_callback update_display_cb;
|
|
update_display_cb.callback = set_variable_visibility;
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_UPDATE_DISPLAY_CALLBACK, &update_display_cb);
|
|
}
|
|
|
|
static int get_language_auto(void)
|
|
{
|
|
retro_language val = RETRO_LANGUAGE_ENGLISH;
|
|
environ_cb(RETRO_ENVIRONMENT_GET_LANGUAGE, &val);
|
|
|
|
switch (val)
|
|
{
|
|
default:
|
|
case RETRO_LANGUAGE_ENGLISH:
|
|
return PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;
|
|
case RETRO_LANGUAGE_JAPANESE:
|
|
return PSP_SYSTEMPARAM_LANGUAGE_JAPANESE;
|
|
case RETRO_LANGUAGE_FRENCH:
|
|
return PSP_SYSTEMPARAM_LANGUAGE_FRENCH;
|
|
case RETRO_LANGUAGE_GERMAN:
|
|
return PSP_SYSTEMPARAM_LANGUAGE_GERMAN;
|
|
case RETRO_LANGUAGE_SPANISH:
|
|
return PSP_SYSTEMPARAM_LANGUAGE_SPANISH;
|
|
case RETRO_LANGUAGE_ITALIAN:
|
|
return PSP_SYSTEMPARAM_LANGUAGE_ITALIAN;
|
|
case RETRO_LANGUAGE_PORTUGUESE_BRAZIL:
|
|
case RETRO_LANGUAGE_PORTUGUESE_PORTUGAL:
|
|
return PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE;
|
|
case RETRO_LANGUAGE_RUSSIAN:
|
|
return PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN;
|
|
case RETRO_LANGUAGE_DUTCH:
|
|
return PSP_SYSTEMPARAM_LANGUAGE_DUTCH;
|
|
case RETRO_LANGUAGE_KOREAN:
|
|
return PSP_SYSTEMPARAM_LANGUAGE_KOREAN;
|
|
case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
|
|
return PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL;
|
|
case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
|
|
return PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED;
|
|
}
|
|
}
|
|
|
|
static std::string map_psp_language_to_i18n_locale(int val)
|
|
{
|
|
switch (val)
|
|
{
|
|
default:
|
|
case PSP_SYSTEMPARAM_LANGUAGE_ENGLISH:
|
|
return "en_US";
|
|
case PSP_SYSTEMPARAM_LANGUAGE_JAPANESE:
|
|
return "ja_JP";
|
|
case PSP_SYSTEMPARAM_LANGUAGE_FRENCH:
|
|
return "fr_FR";
|
|
case PSP_SYSTEMPARAM_LANGUAGE_GERMAN:
|
|
return "de_DE";
|
|
case PSP_SYSTEMPARAM_LANGUAGE_SPANISH:
|
|
return "es_ES";
|
|
case PSP_SYSTEMPARAM_LANGUAGE_ITALIAN:
|
|
return "it_IT";
|
|
case PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE:
|
|
return "pt_PT";
|
|
case PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN:
|
|
return "ru_RU";
|
|
case PSP_SYSTEMPARAM_LANGUAGE_DUTCH:
|
|
return "nl_NL";
|
|
case PSP_SYSTEMPARAM_LANGUAGE_KOREAN:
|
|
return "ko_KR";
|
|
case PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL:
|
|
return "zh_TW";
|
|
case PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED:
|
|
return "zh_CN";
|
|
}
|
|
}
|
|
|
|
static void check_variables(CoreParameter &coreParam)
|
|
{
|
|
bool updated = false;
|
|
|
|
if ( coreState != CoreState::CORE_POWERUP
|
|
&& environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated)
|
|
&& !updated)
|
|
return;
|
|
|
|
struct retro_variable var = {0};
|
|
std::string sTextureShaderName_prev;
|
|
int iInternalResolution_prev;
|
|
int iTexScalingType_prev;
|
|
int iTexScalingLevel_prev;
|
|
|
|
var.key = "ppsspp_language";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "Automatic"))
|
|
g_Config.iLanguage = -1;
|
|
else if (!strcmp(var.value, "English"))
|
|
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_ENGLISH;
|
|
else if (!strcmp(var.value, "Japanese"))
|
|
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_JAPANESE;
|
|
else if (!strcmp(var.value, "French"))
|
|
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_FRENCH;
|
|
else if (!strcmp(var.value, "Spanish"))
|
|
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_SPANISH;
|
|
else if (!strcmp(var.value, "German"))
|
|
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_GERMAN;
|
|
else if (!strcmp(var.value, "Italian"))
|
|
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_ITALIAN;
|
|
else if (!strcmp(var.value, "Dutch"))
|
|
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_DUTCH;
|
|
else if (!strcmp(var.value, "Portuguese"))
|
|
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_PORTUGUESE;
|
|
else if (!strcmp(var.value, "Russian"))
|
|
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_RUSSIAN;
|
|
else if (!strcmp(var.value, "Korean"))
|
|
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_KOREAN;
|
|
else if (!strcmp(var.value, "Chinese Traditional"))
|
|
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_CHINESE_TRADITIONAL;
|
|
else if (!strcmp(var.value, "Chinese Simplified"))
|
|
g_Config.iLanguage = PSP_SYSTEMPARAM_LANGUAGE_CHINESE_SIMPLIFIED;
|
|
}
|
|
|
|
var.key = "ppsspp_cpu_core";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "JIT"))
|
|
g_Config.iCpuCore = (int)CPUCore::JIT;
|
|
else if (!strcmp(var.value, "IR JIT"))
|
|
g_Config.iCpuCore = (int)CPUCore::IR_JIT;
|
|
else if (!strcmp(var.value, "Interpreter"))
|
|
g_Config.iCpuCore = (int)CPUCore::INTERPRETER;
|
|
}
|
|
|
|
var.key = "ppsspp_fast_memory";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bFastMemory = false;
|
|
else
|
|
g_Config.bFastMemory = true;
|
|
}
|
|
|
|
var.key = "ppsspp_ignore_bad_memory_access";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bIgnoreBadMemAccess = false;
|
|
else
|
|
g_Config.bIgnoreBadMemAccess = true;
|
|
}
|
|
|
|
var.key = "ppsspp_io_timing_method";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "Fast"))
|
|
g_Config.iIOTimingMethod = IOTIMING_FAST;
|
|
else if (!strcmp(var.value, "Host"))
|
|
g_Config.iIOTimingMethod = IOTIMING_HOST;
|
|
else if (!strcmp(var.value, "Simulate UMD delays"))
|
|
g_Config.iIOTimingMethod = IOTIMING_REALISTIC;
|
|
}
|
|
|
|
var.key = "ppsspp_force_lag_sync";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bForceLagSync = false;
|
|
else
|
|
g_Config.bForceLagSync = true;
|
|
}
|
|
|
|
var.key = "ppsspp_locked_cpu_speed";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
g_Config.iLockedCPUSpeed = atoi(var.value);
|
|
|
|
var.key = "ppsspp_cache_iso";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bCacheFullIsoInRam = false;
|
|
else
|
|
g_Config.bCacheFullIsoInRam = true;
|
|
}
|
|
|
|
var.key = "ppsspp_cheats";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bEnableCheats = false;
|
|
else
|
|
g_Config.bEnableCheats = true;
|
|
}
|
|
|
|
var.key = "ppsspp_psp_model";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "psp_1000"))
|
|
g_Config.iPSPModel = PSP_MODEL_FAT;
|
|
else if (!strcmp(var.value, "psp_2000_3000"))
|
|
g_Config.iPSPModel = PSP_MODEL_SLIM;
|
|
}
|
|
|
|
var.key = "ppsspp_button_preference";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "Cross"))
|
|
g_Config.iButtonPreference = PSP_SYSTEMPARAM_BUTTON_CROSS;
|
|
else if (!strcmp(var.value, "Circle"))
|
|
g_Config.iButtonPreference = PSP_SYSTEMPARAM_BUTTON_CIRCLE;
|
|
}
|
|
|
|
var.key = "ppsspp_internal_resolution";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
iInternalResolution_prev = g_Config.iInternalResolution;
|
|
|
|
if (!strcmp(var.value, "480x272"))
|
|
g_Config.iInternalResolution = 1;
|
|
else if (!strcmp(var.value, "960x544"))
|
|
g_Config.iInternalResolution = 2;
|
|
else if (!strcmp(var.value, "1440x816"))
|
|
g_Config.iInternalResolution = 3;
|
|
else if (!strcmp(var.value, "1920x1088"))
|
|
g_Config.iInternalResolution = 4;
|
|
else if (!strcmp(var.value, "2400x1360"))
|
|
g_Config.iInternalResolution = 5;
|
|
else if (!strcmp(var.value, "2880x1632"))
|
|
g_Config.iInternalResolution = 6;
|
|
else if (!strcmp(var.value, "3360x1904"))
|
|
g_Config.iInternalResolution = 7;
|
|
else if (!strcmp(var.value, "3840x2176"))
|
|
g_Config.iInternalResolution = 8;
|
|
else if (!strcmp(var.value, "4320x2448"))
|
|
g_Config.iInternalResolution = 9;
|
|
else if (!strcmp(var.value, "4800x2720"))
|
|
g_Config.iInternalResolution = 10;
|
|
}
|
|
|
|
var.key = "ppsspp_skip_buffer_effects";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bSkipBufferEffects = false;
|
|
else
|
|
g_Config.bSkipBufferEffects = true;
|
|
}
|
|
|
|
var.key = "ppsspp_skip_gpu_readbacks";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bSkipGPUReadbacks = false;
|
|
else
|
|
g_Config.bSkipGPUReadbacks = true;
|
|
}
|
|
|
|
var.key = "ppsspp_frameskip";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
g_Config.iFrameSkip = atoi(var.value);
|
|
|
|
var.key = "ppsspp_frameskiptype";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "Number of frames"))
|
|
g_Config.iFrameSkipType = 0;
|
|
else if (!strcmp(var.value, "Percent of FPS"))
|
|
g_Config.iFrameSkipType = 1;
|
|
}
|
|
|
|
var.key = "ppsspp_auto_frameskip";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bAutoFrameSkip = false;
|
|
else
|
|
g_Config.bAutoFrameSkip = true;
|
|
}
|
|
|
|
var.key = "ppsspp_frame_duplication";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bRenderDuplicateFrames = false;
|
|
else
|
|
g_Config.bRenderDuplicateFrames = true;
|
|
}
|
|
|
|
var.key = "ppsspp_detect_vsync_swap_interval";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
detectVsyncSwapInterval = false;
|
|
else
|
|
detectVsyncSwapInterval = true;
|
|
}
|
|
|
|
var.key = "ppsspp_inflight_frames";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "No buffer"))
|
|
g_Config.iInflightFrames = 0;
|
|
else if (!strcmp(var.value, "Up to 1"))
|
|
g_Config.iInflightFrames = 1;
|
|
else if (!strcmp(var.value, "Up to 2"))
|
|
g_Config.iInflightFrames = 2;
|
|
}
|
|
|
|
var.key = "ppsspp_gpu_hardware_transform";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bHardwareTransform = false;
|
|
else
|
|
g_Config.bHardwareTransform = true;
|
|
}
|
|
|
|
var.key = "ppsspp_software_skinning";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bSoftwareSkinning = false;
|
|
else
|
|
g_Config.bSoftwareSkinning = true;
|
|
}
|
|
|
|
var.key = "ppsspp_vertex_cache";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bVertexCache = false;
|
|
else
|
|
g_Config.bVertexCache = true;
|
|
}
|
|
|
|
var.key = "ppsspp_lazy_texture_caching";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bTextureBackoffCache = false;
|
|
else
|
|
g_Config.bTextureBackoffCache = true;
|
|
}
|
|
|
|
var.key = "ppsspp_spline_quality";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "Low"))
|
|
g_Config.iSplineBezierQuality = 0;
|
|
else if (!strcmp(var.value, "Medium"))
|
|
g_Config.iSplineBezierQuality = 1;
|
|
else if (!strcmp(var.value, "High"))
|
|
g_Config.iSplineBezierQuality = 2;
|
|
}
|
|
|
|
var.key = "ppsspp_hardware_tesselation";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bHardwareTessellation = false;
|
|
else
|
|
g_Config.bHardwareTessellation = true;
|
|
}
|
|
|
|
var.key = "ppsspp_lower_resolution_for_effects";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.iBloomHack = 0;
|
|
else if (!strcmp(var.value, "Safe"))
|
|
g_Config.iBloomHack = 1;
|
|
else if (!strcmp(var.value, "Balanced"))
|
|
g_Config.iBloomHack = 2;
|
|
else if (!strcmp(var.value, "Aggressive"))
|
|
g_Config.iBloomHack = 3;
|
|
}
|
|
|
|
var.key = "ppsspp_texture_scaling_type";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
iTexScalingType_prev = g_Config.iTexScalingType;
|
|
|
|
if (!strcmp(var.value, "xbrz"))
|
|
g_Config.iTexScalingType = TextureScalerCommon::XBRZ;
|
|
else if (!strcmp(var.value, "hybrid"))
|
|
g_Config.iTexScalingType = TextureScalerCommon::HYBRID;
|
|
else if (!strcmp(var.value, "bicubic"))
|
|
g_Config.iTexScalingType = TextureScalerCommon::BICUBIC;
|
|
else if (!strcmp(var.value, "hybrid_bicubic"))
|
|
g_Config.iTexScalingType = TextureScalerCommon::HYBRID_BICUBIC;
|
|
}
|
|
|
|
var.key = "ppsspp_texture_scaling_level";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
iTexScalingLevel_prev = g_Config.iTexScalingLevel;
|
|
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.iTexScalingLevel = 1;
|
|
else if (!strcmp(var.value, "2x"))
|
|
g_Config.iTexScalingLevel = 2;
|
|
else if (!strcmp(var.value, "3x"))
|
|
g_Config.iTexScalingLevel = 3;
|
|
else if (!strcmp(var.value, "4x"))
|
|
g_Config.iTexScalingLevel = 4;
|
|
else if (!strcmp(var.value, "5x"))
|
|
g_Config.iTexScalingLevel = 5;
|
|
}
|
|
|
|
var.key = "ppsspp_texture_deposterize";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bTexDeposterize = false;
|
|
else
|
|
g_Config.bTexDeposterize = true;
|
|
}
|
|
|
|
var.key = "ppsspp_texture_shader";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
sTextureShaderName_prev = g_Config.sTextureShaderName;
|
|
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.sTextureShaderName = "Off";
|
|
else if (!strcmp(var.value, "2xBRZ"))
|
|
g_Config.sTextureShaderName = "Tex2xBRZ";
|
|
else if (!strcmp(var.value, "4xBRZ"))
|
|
g_Config.sTextureShaderName = "Tex4xBRZ";
|
|
else if (!strcmp(var.value, "MMPX"))
|
|
g_Config.sTextureShaderName = "TexMMPX";
|
|
}
|
|
|
|
var.key = "ppsspp_texture_anisotropic_filtering";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.iAnisotropyLevel = 0;
|
|
else if (!strcmp(var.value, "2x"))
|
|
g_Config.iAnisotropyLevel = 1;
|
|
else if (!strcmp(var.value, "4x"))
|
|
g_Config.iAnisotropyLevel = 2;
|
|
else if (!strcmp(var.value, "8x"))
|
|
g_Config.iAnisotropyLevel = 3;
|
|
else if (!strcmp(var.value, "16x"))
|
|
g_Config.iAnisotropyLevel = 4;
|
|
}
|
|
|
|
var.key = "ppsspp_texture_filtering";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "Auto"))
|
|
g_Config.iTexFiltering = 1;
|
|
else if (!strcmp(var.value, "Nearest"))
|
|
g_Config.iTexFiltering = 2;
|
|
else if (!strcmp(var.value, "Linear"))
|
|
g_Config.iTexFiltering = 3;
|
|
else if (!strcmp(var.value, "Auto max quality"))
|
|
g_Config.iTexFiltering = 4;
|
|
}
|
|
|
|
var.key = "ppsspp_texture_replacement";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bReplaceTextures = false;
|
|
else
|
|
g_Config.bReplaceTextures = true;
|
|
}
|
|
|
|
var.key = "ppsspp_enable_wlan";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bEnableWlan = false;
|
|
else
|
|
g_Config.bEnableWlan = true;
|
|
}
|
|
|
|
var.key = "ppsspp_wlan_channel";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
g_Config.iWlanAdhocChannel = atoi(var.value);
|
|
|
|
var.key = "ppsspp_enable_builtin_pro_ad_hoc_server";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bEnableAdhocServer = false;
|
|
else
|
|
g_Config.bEnableAdhocServer = true;
|
|
}
|
|
|
|
var.key = "ppsspp_change_pro_ad_hoc_server_address";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
changeProAdhocServer = var.value;
|
|
|
|
var.key = "ppsspp_enable_upnp";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bEnableUPnP = false;
|
|
else
|
|
g_Config.bEnableUPnP = true;
|
|
}
|
|
|
|
var.key = "ppsspp_upnp_use_original_port";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bUPnPUseOriginalPort = false;
|
|
else
|
|
g_Config.bUPnPUseOriginalPort = true;
|
|
}
|
|
|
|
var.key = "ppsspp_port_offset";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
g_Config.iPortOffset = atoi(var.value);
|
|
|
|
var.key = "ppsspp_minimum_timeout";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
g_Config.iMinTimeout = atoi(var.value);
|
|
|
|
var.key = "ppsspp_forced_first_connect";
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "disabled"))
|
|
g_Config.bForcedFirstConnect = false;
|
|
else
|
|
g_Config.bForcedFirstConnect = true;
|
|
}
|
|
|
|
std::string ppsspp_change_mac_address[12];
|
|
int ppsspp_pro_ad_hoc_ipv4[12];
|
|
char key[64] = {0};
|
|
var.key = key;
|
|
g_Config.sMACAddress = "";
|
|
g_Config.proAdhocServer = "";
|
|
for (int i = 0; i < 12; i++)
|
|
{
|
|
snprintf(key, sizeof(key), "ppsspp_change_mac_address%02d", i + 1);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
ppsspp_change_mac_address[i] = var.value;
|
|
|
|
if (i && i % 2 == 0)
|
|
g_Config.sMACAddress += ":";
|
|
|
|
g_Config.sMACAddress += ppsspp_change_mac_address[i];
|
|
}
|
|
|
|
snprintf(key, sizeof(key), "ppsspp_pro_ad_hoc_server_address%02d", i + 1);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
ppsspp_pro_ad_hoc_ipv4[i] = atoi(var.value);
|
|
}
|
|
|
|
if (g_Config.sMACAddress == "00:00:00:00:00:00")
|
|
{
|
|
g_Config.sMACAddress = CreateRandMAC();
|
|
|
|
for (int i = 0; i < 12; i++)
|
|
{
|
|
snprintf(key, sizeof(key), "ppsspp_change_mac_address%02d", i + 1);
|
|
std::string digit = {g_Config.sMACAddress[i + i / 2]};
|
|
var.value = digit.c_str();
|
|
environ_cb(RETRO_ENVIRONMENT_SET_VARIABLE, &var);
|
|
}
|
|
}
|
|
|
|
if (changeProAdhocServer == "IP address")
|
|
{
|
|
g_Config.proAdhocServer = "";
|
|
for (int i = 0; i < 12; i++)
|
|
{
|
|
if (i && i % 3 == 0)
|
|
g_Config.proAdhocServer += '.';
|
|
|
|
int addressPt = ppsspp_pro_ad_hoc_ipv4[i];
|
|
g_Config.proAdhocServer += static_cast<char>('0' + addressPt);
|
|
}
|
|
}
|
|
else
|
|
g_Config.proAdhocServer = changeProAdhocServer;
|
|
|
|
g_Config.bTexHardwareScaling = g_Config.sTextureShaderName != "Off";
|
|
|
|
if (gpu && (g_Config.iTexScalingType != iTexScalingType_prev
|
|
|| g_Config.iTexScalingLevel != iTexScalingLevel_prev
|
|
|| g_Config.sTextureShaderName != sTextureShaderName_prev))
|
|
{
|
|
gpu->ClearCacheNextFrame();
|
|
gpu->Resized();
|
|
}
|
|
|
|
if (g_Config.iLanguage < 0)
|
|
g_Config.iLanguage = get_language_auto();
|
|
|
|
g_Config.sLanguageIni = map_psp_language_to_i18n_locale(g_Config.iLanguage);
|
|
i18nrepo.LoadIni(g_Config.sLanguageIni);
|
|
|
|
// Cannot detect refresh rate changes if:
|
|
// > Frame skipping is enabled
|
|
// > Frame duplication is enabled
|
|
detectVsyncSwapInterval &=
|
|
!g_Config.bAutoFrameSkip &&
|
|
(g_Config.iFrameSkip == 0) &&
|
|
!g_Config.bRenderDuplicateFrames;
|
|
|
|
bool updateAvInfo = false;
|
|
if (!detectVsyncSwapInterval && (vsyncSwapInterval != 1))
|
|
{
|
|
vsyncSwapInterval = 1;
|
|
updateAvInfo = true;
|
|
}
|
|
|
|
if (g_Config.iInternalResolution != iInternalResolution_prev && !PSP_IsInited())
|
|
{
|
|
coreParam.pixelWidth = coreParam.renderWidth = g_Config.iInternalResolution * 480;
|
|
coreParam.pixelHeight = coreParam.renderHeight = g_Config.iInternalResolution * 272;
|
|
|
|
if (gpu)
|
|
{
|
|
retro_system_av_info avInfo;
|
|
retro_get_system_av_info(&avInfo);
|
|
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avInfo);
|
|
updateAvInfo = false;
|
|
gpu->Resized();
|
|
}
|
|
}
|
|
|
|
if (updateAvInfo)
|
|
{
|
|
retro_system_av_info avInfo;
|
|
retro_get_system_av_info(&avInfo);
|
|
environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &avInfo);
|
|
}
|
|
|
|
bool isFastForwarding = environ_cb(RETRO_ENVIRONMENT_GET_FASTFORWARDING, &isFastForwarding);
|
|
coreParam.fastForward = isFastForwarding;
|
|
|
|
set_variable_visibility();
|
|
}
|
|
|
|
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; }
|
|
void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; }
|
|
void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; }
|
|
void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; }
|
|
|
|
void retro_init(void)
|
|
{
|
|
VsyncSwapIntervalReset();
|
|
AudioBufferInit();
|
|
|
|
g_threadManager.Init(cpu_info.num_cores, cpu_info.logical_cpu_count);
|
|
|
|
struct retro_input_descriptor desc[] = {
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Cross" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Circle" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Triangle" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Square" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
|
|
{ 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
|
|
{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "Right Analog X" },
|
|
{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "Right Analog Y" },
|
|
{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Left Analog X" },
|
|
{ 0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Left Analog Y" },
|
|
{ 0 },
|
|
};
|
|
environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL))
|
|
libretro_supports_bitmasks = true;
|
|
|
|
struct retro_log_callback log;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
|
|
{
|
|
LogManager::Init(&g_Config.bEnableLogging);
|
|
printfLogger = new PrintfLogger(log);
|
|
LogManager* logman = LogManager::GetInstance();
|
|
logman->RemoveListener(logman->GetConsoleListener());
|
|
logman->RemoveListener(logman->GetDebuggerListener());
|
|
logman->ChangeFileLog(nullptr);
|
|
logman->AddListener(printfLogger);
|
|
logman->SetAllLogLevels(LogTypes::LINFO);
|
|
}
|
|
|
|
g_Config.Load("", "");
|
|
g_Config.iInternalResolution = 0;
|
|
|
|
const char* nickname = NULL;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_USERNAME, &nickname) && nickname)
|
|
g_Config.sNickName = std::string(nickname);
|
|
|
|
Path retro_base_dir;
|
|
Path retro_save_dir;
|
|
const char* dir_ptr = NULL;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir_ptr) && dir_ptr)
|
|
retro_base_dir = Path(dir_ptr);
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &dir_ptr) && dir_ptr)
|
|
retro_save_dir = Path(dir_ptr);
|
|
|
|
retro_base_dir /= "PPSSPP";
|
|
|
|
g_Config.currentDirectory = retro_base_dir;
|
|
g_Config.defaultCurrentDirectory = retro_base_dir;
|
|
g_Config.memStickDirectory = retro_save_dir;
|
|
g_Config.flash0Directory = retro_base_dir / "flash0";
|
|
g_Config.internalDataDirectory = retro_base_dir;
|
|
g_Config.bEnableNetworkChat = false;
|
|
g_Config.bDiscordPresence = false;
|
|
|
|
VFSRegister("", new DirectoryAssetReader(retro_base_dir));
|
|
|
|
host = new LibretroHost();
|
|
}
|
|
|
|
void retro_deinit(void)
|
|
{
|
|
g_threadManager.Teardown();
|
|
LogManager::Shutdown();
|
|
|
|
delete printfLogger;
|
|
printfLogger = nullptr;
|
|
|
|
delete host;
|
|
host = nullptr;
|
|
|
|
libretro_supports_bitmasks = false;
|
|
libretro_supports_option_categories = false;
|
|
|
|
VsyncSwapIntervalReset();
|
|
AudioBufferDeinit();
|
|
}
|
|
|
|
void retro_set_controller_port_device(unsigned port, unsigned device)
|
|
{
|
|
(void)port;
|
|
(void)device;
|
|
}
|
|
|
|
void retro_get_system_info(struct retro_system_info *info)
|
|
{
|
|
*info = {};
|
|
info->library_name = "PPSSPP";
|
|
info->library_version = PPSSPP_GIT_VERSION;
|
|
info->need_fullpath = true;
|
|
info->valid_extensions = "elf|iso|cso|prx|pbp";
|
|
}
|
|
|
|
void retro_get_system_av_info(struct retro_system_av_info *info)
|
|
{
|
|
*info = {};
|
|
info->timing.fps = (60.0 / 1.001) / (double)vsyncSwapInterval;
|
|
info->timing.sample_rate = SAMPLERATE;
|
|
|
|
info->geometry.base_width = g_Config.iInternalResolution * 480;
|
|
info->geometry.base_height = g_Config.iInternalResolution * 272;
|
|
info->geometry.max_width = g_Config.iInternalResolution * 480;
|
|
info->geometry.max_height = g_Config.iInternalResolution * 272;
|
|
info->geometry.aspect_ratio = 480.0 / 272.0; // Not 16:9! But very, very close.
|
|
}
|
|
|
|
unsigned retro_api_version(void) { return RETRO_API_VERSION; }
|
|
|
|
namespace Libretro
|
|
{
|
|
bool useEmuThread = false;
|
|
std::atomic<EmuThreadState> emuThreadState(EmuThreadState::DISABLED);
|
|
|
|
static std::thread emuThread;
|
|
static void EmuFrame()
|
|
{
|
|
ctx->SetRenderTarget();
|
|
if (ctx->GetDrawContext())
|
|
ctx->GetDrawContext()->BeginFrame();
|
|
|
|
gpu->BeginHostFrame();
|
|
|
|
coreState = CORE_RUNNING;
|
|
PSP_RunLoopUntil(UINT64_MAX);
|
|
|
|
gpu->EndHostFrame();
|
|
|
|
if (ctx->GetDrawContext())
|
|
ctx->GetDrawContext()->EndFrame();
|
|
}
|
|
|
|
static void EmuThreadFunc()
|
|
{
|
|
SetCurrentThreadName("Emu");
|
|
|
|
for (;;)
|
|
{
|
|
switch ((EmuThreadState)emuThreadState)
|
|
{
|
|
case EmuThreadState::START_REQUESTED:
|
|
emuThreadState = EmuThreadState::RUNNING;
|
|
/* fallthrough */
|
|
case EmuThreadState::RUNNING:
|
|
EmuFrame();
|
|
break;
|
|
case EmuThreadState::PAUSE_REQUESTED:
|
|
emuThreadState = EmuThreadState::PAUSED;
|
|
/* fallthrough */
|
|
case EmuThreadState::PAUSED:
|
|
sleep_ms(1);
|
|
break;
|
|
default:
|
|
case EmuThreadState::QUIT_REQUESTED:
|
|
emuThreadState = EmuThreadState::STOPPED;
|
|
ctx->StopThread();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void EmuThreadStart()
|
|
{
|
|
bool wasPaused = emuThreadState == EmuThreadState::PAUSED;
|
|
emuThreadState = EmuThreadState::START_REQUESTED;
|
|
|
|
if (!wasPaused)
|
|
{
|
|
ctx->ThreadStart();
|
|
emuThread = std::thread(&EmuThreadFunc);
|
|
}
|
|
}
|
|
|
|
void EmuThreadStop()
|
|
{
|
|
if (emuThreadState != EmuThreadState::RUNNING)
|
|
return;
|
|
|
|
emuThreadState = EmuThreadState::QUIT_REQUESTED;
|
|
|
|
// Need to keep eating frames to allow the EmuThread to exit correctly.
|
|
while (ctx->ThreadFrame())
|
|
AudioBufferFlush();
|
|
|
|
emuThread.join();
|
|
emuThread = std::thread();
|
|
ctx->ThreadEnd();
|
|
}
|
|
|
|
void EmuThreadPause()
|
|
{
|
|
if (emuThreadState != EmuThreadState::RUNNING)
|
|
return;
|
|
|
|
emuThreadState = EmuThreadState::PAUSE_REQUESTED;
|
|
|
|
ctx->ThreadFrame(); // Eat 1 frame
|
|
AudioBufferFlush();
|
|
|
|
while (emuThreadState != EmuThreadState::PAUSED)
|
|
sleep_ms(1);
|
|
}
|
|
|
|
} // namespace Libretro
|
|
|
|
bool retro_load_game(const struct retro_game_info *game)
|
|
{
|
|
retro_pixel_format fmt = retro_pixel_format::RETRO_PIXEL_FORMAT_XRGB8888;
|
|
if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
|
|
{
|
|
ERROR_LOG(SYSTEM, "XRGB8888 is not supported.\n");
|
|
return false;
|
|
}
|
|
|
|
coreState = CORE_POWERUP;
|
|
ctx = LibretroGraphicsContext::CreateGraphicsContext();
|
|
INFO_LOG(SYSTEM, "Using %s backend", ctx->Ident());
|
|
|
|
Core_SetGraphicsContext(ctx);
|
|
SetGPUBackend((GPUBackend)g_Config.iGPUBackend);
|
|
|
|
useEmuThread = ctx->GetGPUCore() == GPUCORE_GLES;
|
|
|
|
CoreParameter coreParam = {};
|
|
coreParam.enableSound = true;
|
|
coreParam.fileToStart = Path(std::string(game->path));
|
|
coreParam.mountIso.clear();
|
|
coreParam.startBreak = false;
|
|
coreParam.printfEmuLog = true;
|
|
coreParam.headLess = true;
|
|
coreParam.graphicsContext = ctx;
|
|
coreParam.gpuCore = ctx->GetGPUCore();
|
|
coreParam.cpuCore = (CPUCore)g_Config.iCpuCore;
|
|
check_variables(coreParam);
|
|
|
|
std::string error_string;
|
|
if (!PSP_InitStart(coreParam, &error_string))
|
|
{
|
|
ERROR_LOG(BOOT, "%s", error_string.c_str());
|
|
return false;
|
|
}
|
|
|
|
set_variable_visibility();
|
|
|
|
return true;
|
|
}
|
|
|
|
void retro_unload_game(void)
|
|
{
|
|
if (Libretro::useEmuThread)
|
|
Libretro::EmuThreadStop();
|
|
|
|
PSP_Shutdown();
|
|
VFSShutdown();
|
|
|
|
delete ctx;
|
|
ctx = nullptr;
|
|
PSP_CoreParameter().graphicsContext = nullptr;
|
|
}
|
|
|
|
void retro_reset(void)
|
|
{
|
|
std::string error_string;
|
|
|
|
PSP_Shutdown();
|
|
|
|
if (!PSP_Init(PSP_CoreParameter(), &error_string))
|
|
{
|
|
ERROR_LOG(BOOT, "%s", error_string.c_str());
|
|
environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, nullptr);
|
|
}
|
|
}
|
|
|
|
static void retro_input(void)
|
|
{
|
|
unsigned i;
|
|
int16_t ret = 0;
|
|
// clang-format off
|
|
static struct
|
|
{
|
|
u32 retro;
|
|
u32 sceCtrl;
|
|
} map[] = {
|
|
{ RETRO_DEVICE_ID_JOYPAD_UP, CTRL_UP },
|
|
{ RETRO_DEVICE_ID_JOYPAD_DOWN, CTRL_DOWN },
|
|
{ RETRO_DEVICE_ID_JOYPAD_LEFT, CTRL_LEFT },
|
|
{ RETRO_DEVICE_ID_JOYPAD_RIGHT, CTRL_RIGHT },
|
|
{ RETRO_DEVICE_ID_JOYPAD_X, CTRL_TRIANGLE },
|
|
{ RETRO_DEVICE_ID_JOYPAD_A, CTRL_CIRCLE },
|
|
{ RETRO_DEVICE_ID_JOYPAD_B, CTRL_CROSS },
|
|
{ RETRO_DEVICE_ID_JOYPAD_Y, CTRL_SQUARE },
|
|
{ RETRO_DEVICE_ID_JOYPAD_L, CTRL_LTRIGGER },
|
|
{ RETRO_DEVICE_ID_JOYPAD_R, CTRL_RTRIGGER },
|
|
{ RETRO_DEVICE_ID_JOYPAD_START, CTRL_START },
|
|
{ RETRO_DEVICE_ID_JOYPAD_SELECT, CTRL_SELECT },
|
|
};
|
|
// clang-format on
|
|
|
|
input_poll_cb();
|
|
|
|
if (libretro_supports_bitmasks)
|
|
ret = input_state_cb(0, RETRO_DEVICE_JOYPAD,
|
|
0, RETRO_DEVICE_ID_JOYPAD_MASK);
|
|
else
|
|
{
|
|
for (i = RETRO_DEVICE_ID_JOYPAD_B; i <= RETRO_DEVICE_ID_JOYPAD_R; i++)
|
|
if (input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, i))
|
|
ret |= (1 << i);
|
|
}
|
|
|
|
for (i = 0; i < sizeof(map) / sizeof(*map); i++)
|
|
{
|
|
bool pressed = ret & (1 << map[i].retro);
|
|
|
|
if (pressed)
|
|
{
|
|
__CtrlButtonDown(map[i].sceCtrl);
|
|
}
|
|
else
|
|
{
|
|
__CtrlButtonUp(map[i].sceCtrl);
|
|
}
|
|
}
|
|
|
|
float x_left = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X) / 32767.0f;
|
|
float y_left = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y) / -32767.0f;
|
|
float x_right = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X) / 32767.0f;
|
|
float y_right = input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y) / -32767.0f;
|
|
__CtrlSetAnalogXY(CTRL_STICK_LEFT, x_left, y_left);
|
|
__CtrlSetAnalogXY(CTRL_STICK_RIGHT, x_right, y_right);
|
|
}
|
|
|
|
void retro_run(void)
|
|
{
|
|
if (PSP_IsIniting())
|
|
{
|
|
std::string error_string;
|
|
while (!PSP_InitUpdate(&error_string))
|
|
sleep_ms(4);
|
|
|
|
if (!PSP_IsInited())
|
|
{
|
|
ERROR_LOG(BOOT, "%s", error_string.c_str());
|
|
environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, nullptr);
|
|
return;
|
|
}
|
|
}
|
|
|
|
check_variables(PSP_CoreParameter());
|
|
|
|
retro_input();
|
|
|
|
if (useEmuThread)
|
|
{
|
|
if( emuThreadState == EmuThreadState::PAUSED ||
|
|
emuThreadState == EmuThreadState::PAUSE_REQUESTED)
|
|
{
|
|
VsyncSwapIntervalDetect();
|
|
AudioUploadSamples();
|
|
ctx->SwapBuffers();
|
|
return;
|
|
}
|
|
|
|
if (emuThreadState != EmuThreadState::RUNNING)
|
|
EmuThreadStart();
|
|
|
|
if (!ctx->ThreadFrame())
|
|
{
|
|
VsyncSwapIntervalDetect();
|
|
AudioUploadSamples();
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
EmuFrame();
|
|
|
|
VsyncSwapIntervalDetect();
|
|
AudioUploadSamples();
|
|
ctx->SwapBuffers();
|
|
}
|
|
|
|
unsigned retro_get_region(void) { return RETRO_REGION_NTSC; }
|
|
|
|
bool retro_load_game_special(unsigned type, const struct retro_game_info *info, size_t num) { return false; }
|
|
|
|
namespace SaveState
|
|
{
|
|
struct SaveStart
|
|
{
|
|
void DoState(PointerWrap &p);
|
|
};
|
|
} // namespace SaveState
|
|
|
|
size_t retro_serialize_size(void)
|
|
{
|
|
if(!gpu) { // The HW renderer isn't ready on first pass.
|
|
return 134217728; // 128MB ought to be enough for anybody.
|
|
}
|
|
|
|
SaveState::SaveStart state;
|
|
// TODO: Libretro API extension to use the savestate queue
|
|
if (useEmuThread)
|
|
EmuThreadPause();
|
|
|
|
return (CChunkFileReader::MeasurePtr(state) + 0x800000)
|
|
& ~0x7FFFFF; // We don't unpause intentionally
|
|
}
|
|
|
|
bool retro_serialize(void *data, size_t size)
|
|
{
|
|
if(!gpu) { // The HW renderer isn't ready on first pass.
|
|
return false;
|
|
}
|
|
|
|
bool retVal;
|
|
SaveState::SaveStart state;
|
|
// TODO: Libretro API extension to use the savestate queue
|
|
if (useEmuThread)
|
|
EmuThreadPause(); // Does nothing if already paused
|
|
|
|
size_t measured = CChunkFileReader::MeasurePtr(state);
|
|
assert(measured <= size);
|
|
auto err = CChunkFileReader::SavePtr((u8 *)data, state, measured);
|
|
retVal = err == CChunkFileReader::ERROR_NONE;
|
|
|
|
if (useEmuThread)
|
|
{
|
|
EmuThreadStart();
|
|
sleep_ms(4);
|
|
}
|
|
|
|
AudioBufferFlush();
|
|
|
|
return retVal;
|
|
}
|
|
|
|
bool retro_unserialize(const void *data, size_t size)
|
|
{
|
|
bool retVal;
|
|
SaveState::SaveStart state;
|
|
// TODO: Libretro API extension to use the savestate queue
|
|
if (useEmuThread)
|
|
EmuThreadPause(); // Does nothing if already paused
|
|
|
|
std::string errorString;
|
|
retVal = CChunkFileReader::LoadPtr((u8 *)data, state, &errorString)
|
|
== CChunkFileReader::ERROR_NONE;
|
|
|
|
if (useEmuThread)
|
|
{
|
|
EmuThreadStart();
|
|
sleep_ms(4);
|
|
}
|
|
|
|
AudioBufferFlush();
|
|
|
|
return retVal;
|
|
}
|
|
|
|
void *retro_get_memory_data(unsigned id)
|
|
{
|
|
if ( id == RETRO_MEMORY_SYSTEM_RAM )
|
|
return Memory::GetPointerWriteUnchecked(PSP_GetKernelMemoryBase()) ;
|
|
return NULL;
|
|
}
|
|
|
|
size_t retro_get_memory_size(unsigned id)
|
|
{
|
|
if ( id == RETRO_MEMORY_SYSTEM_RAM )
|
|
return Memory::g_MemorySize ;
|
|
return 0;
|
|
}
|
|
|
|
void retro_cheat_reset(void) {}
|
|
|
|
void retro_cheat_set(unsigned index, bool enabled, const char *code) { }
|
|
|
|
int System_GetPropertyInt(SystemProperty prop)
|
|
{
|
|
switch (prop)
|
|
{
|
|
case SYSPROP_AUDIO_SAMPLE_RATE:
|
|
return SAMPLERATE;
|
|
#if PPSSPP_PLATFORM(ANDROID)
|
|
case SYSPROP_SYSTEMVERSION: {
|
|
char sdk[PROP_VALUE_MAX] = {0};
|
|
if (__system_property_get("ro.build.version.sdk", sdk) != 0) {
|
|
return atoi(sdk);
|
|
}
|
|
return -1;
|
|
}
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
float System_GetPropertyFloat(SystemProperty prop)
|
|
{
|
|
switch (prop)
|
|
{
|
|
case SYSPROP_DISPLAY_REFRESH_RATE:
|
|
// Have to lie here and report 60 Hz instead
|
|
// of (60.0 / 1.001), otherwise the internal
|
|
// stereo resampler will output at the wrong
|
|
// frequency...
|
|
return 60.0f;
|
|
case SYSPROP_DISPLAY_SAFE_INSET_LEFT:
|
|
case SYSPROP_DISPLAY_SAFE_INSET_RIGHT:
|
|
case SYSPROP_DISPLAY_SAFE_INSET_TOP:
|
|
case SYSPROP_DISPLAY_SAFE_INSET_BOTTOM:
|
|
return 0.0f;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
bool System_GetPropertyBool(SystemProperty prop)
|
|
{
|
|
switch (prop)
|
|
{
|
|
case SYSPROP_CAN_JIT:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::string System_GetProperty(SystemProperty prop) { return ""; }
|
|
std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) { return std::vector<std::string>(); }
|
|
|
|
void System_SendMessage(const char *command, const char *parameter) {}
|
|
void NativeUpdate() {}
|
|
void NativeRender(GraphicsContext *graphicsContext) {}
|
|
void NativeResized() {}
|
|
|
|
void System_Toast(const char *str) {}
|
|
|
|
#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS)
|
|
std::vector<std::string> __cameraGetDeviceList() { return std::vector<std::string>(); }
|
|
bool audioRecording_Available() { return false; }
|
|
bool audioRecording_State() { return false; }
|
|
|
|
void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, std::function<void(bool, const std::string &)> cb) { cb(false, ""); }
|
|
#endif
|