7 #if defined(SDL_LIBRETRO_IMPLEMENTATION) && !defined(SDL_LIBRETRO_ENV_IMPL_ONCE)
8#define SDL_LIBRETRO_ENV_IMPL_ONCE
15static void SDL_Libretro_Logger(
enum retro_log_level level,
const char* fmt, ...) {
16 if (SDL_Libretro_active && level < SDL_Libretro_active->logLevel) {
23 SDL_vsnprintf(buf,
sizeof(buf), fmt, args);
27 size_t len = SDL_strlen(buf);
28 if (len > 0 && buf[len - 1] ==
'\n') buf[len - 1] =
'\0';
31 case RETRO_LOG_INFO: SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"[SDL_Libretro] %s", buf);
break;
32 case RETRO_LOG_WARN: SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"[SDL_Libretro] %s", buf);
break;
33 case RETRO_LOG_ERROR: SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"[SDL_Libretro] %s", buf);
break;
35 default: SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
"[SDL_Libretro] %s", buf);
break;
39static retro_time_t SDL_Libretro_GetTimeUSEC(
void) {
40 return (SDL_GetPerformanceCounter() * (Uint64)1000000) / SDL_GetPerformanceFrequency();
43static uint64_t SDL_Libretro_GetCPUFeatures(
void) {
44 uint64_t features = 0;
45 if (SDL_HasSSE()) features |= RETRO_SIMD_SSE;
46 if (SDL_HasSSE2()) features |= RETRO_SIMD_SSE2;
47 if (SDL_HasSSE3()) features |= RETRO_SIMD_SSE3;
48 if (SDL_HasSSE41()) features |= RETRO_SIMD_SSE4;
49 if (SDL_HasSSE42()) features |= RETRO_SIMD_SSE42;
50 if (SDL_HasAVX()) features |= RETRO_SIMD_AVX;
51 if (SDL_HasAVX2()) features |= RETRO_SIMD_AVX2;
55static retro_perf_tick_t SDL_Libretro_GetPerfCounter(
void) {
56 return (retro_perf_tick_t)SDL_GetPerformanceCounter();
59static void SDL_Libretro_PerfRegister(
struct retro_perf_counter* counter) {
60 SDL_Libretro* lr = SDL_Libretro_active;
61 if (!lr || !counter)
return;
64 for (
unsigned i = 0; i < lr->core.perfCounterCount; i++) {
65 if (lr->core.perfCounters[i] == counter) {
66 counter->registered =
true;
71 if (lr->core.perfCounterCount >= SDL_arraysize(lr->core.perfCounters)) {
72 SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
73 "PERF: counter registry full (%u), dropping '%s'",
74 lr->core.perfCounterCount, counter->ident ? counter->ident :
"?");
78 lr->core.perfCounters[lr->core.perfCounterCount++] = counter;
79 counter->registered =
true;
82static void SDL_Libretro_PerfStart(
struct retro_perf_counter* counter) {
83 if (counter) counter->start = (retro_perf_tick_t)SDL_GetPerformanceCounter();
86static void SDL_Libretro_PerfStop(
struct retro_perf_counter* counter) {
87 if (counter) counter->total += (retro_perf_tick_t)SDL_GetPerformanceCounter() - counter->start;
93static void SDL_Libretro_PerfLog(
void) {
94 SDL_Libretro* lr = SDL_Libretro_active;
98 Uint64 freq = SDL_GetPerformanceFrequency();
100 for (
unsigned i = 0; i < lr->core.perfCounterCount; i++) {
101 const struct retro_perf_counter* counter = lr->core.perfCounters[i];
102 if (!counter)
continue;
104 double ms = freq ? ((double)counter->total / (
double)freq) * 1000.0 : 0.0;
105 SDL_Log(
"[PERF] %s: %llu calls, %llu ticks (%.4f ms)",
106 counter->ident ? counter->ident :
"?",
107 (unsigned long long)counter->call_cnt,
108 (unsigned long long)counter->total,
113static bool SDL_Libretro_SetRumbleState(
unsigned port,
enum retro_rumble_effect effect, uint16_t strength) {
114 SDL_Libretro* lr = SDL_Libretro_active;
115 if (!lr || port >= SDL_LIBRETRO_RUMBLE_PORTS)
return false;
117 float normalized = (float)strength / 65535.0f;
118 if (effect == RETRO_RUMBLE_STRONG) {
119 lr->core.rumbleStrong[port] = normalized;
121 lr->core.rumbleWeak[port] = normalized;
124 if (port < 16 && lr->gamepads[port]) {
125 Uint16 lo = (Uint16)(lr->core.rumbleWeak[port] * 65535.0f);
126 Uint16 hi = (Uint16)(lr->core.rumbleStrong[port] * 65535.0f);
127 SDL_RumbleGamepad(lr->gamepads[port], lo, hi, 100);
133static const char* SDL_Libretro_GetDirectory(SDL_Libretro* lr,
unsigned cmd) {
135 case RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY:
136 return lr->systemDirectory[0] ? lr->systemDirectory : NULL;
137 case RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY:
138 return lr->saveDirectory[0] ? lr->saveDirectory : NULL;
139 case RETRO_ENVIRONMENT_GET_CORE_ASSETS_DIRECTORY:
140 return lr->coreAssetsDirectory[0] ? lr->coreAssetsDirectory : NULL;
141 case RETRO_ENVIRONMENT_GET_PLAYLIST_DIRECTORY:
142 return lr->playlistDirectory[0] ? lr->playlistDirectory : NULL;
143 case RETRO_ENVIRONMENT_GET_FILE_BROWSER_START_DIRECTORY:
144 return lr->fileBrowserStartDirectory[0] ? lr->fileBrowserStartDirectory : NULL;
154static float SDL_Libretro_GetTargetRefreshRate(SDL_Libretro* lr) {
157 SDL_DisplayID display = SDL_GetDisplayForWindow(lr->window);
159 const SDL_DisplayMode* mode = SDL_GetCurrentDisplayMode(display);
160 if (mode && mode->refresh_rate > 0.0f) {
161 rate = mode->refresh_rate;
173static const char* SDL_Libretro_PickCoreOptionText(
const char* base,
const char* categorized,
const char* categoryKey) {
174 if (categoryKey && categoryKey[0] && categorized && categorized[0]) {
180static bool SDL_Libretro_EnvironmentCallback(
unsigned cmd,
void* data) {
181 SDL_Libretro* lr = SDL_Libretro_active;
182 if (!lr)
return false;
185 case RETRO_ENVIRONMENT_SET_ROTATION: {
186 if (!data)
return false;
187 lr->core.rotation = (int)*(
const unsigned*)data;
188 SDL_Log(
"[SDL_Libretro] SET_ROTATION: %d (%d deg)", lr->core.rotation, lr->core.rotation * 90);
192 case RETRO_ENVIRONMENT_GET_OVERSCAN: {
196 case RETRO_ENVIRONMENT_GET_CAN_DUPE: {
197 if (!data)
return false;
202 case RETRO_ENVIRONMENT_SET_MESSAGE: {
203 const struct retro_message* msg = (
const struct retro_message*)data;
204 if (!msg || !msg->msg || msg->msg[0] ==
'\0')
return msg != NULL;
205 double seconds = msg->frames / (lr->core.fps > 0 ? lr->core.fps : 60.0);
210 case RETRO_ENVIRONMENT_SHUTDOWN: {
211 SDL_Log(
"[SDL_Libretro] Shutdown requested");
212 lr->core.shutdown =
true;
216 case RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL: {
217 if (!data)
return false;
218 lr->core.performanceLevel = *(
const unsigned*)data;
222 case RETRO_ENVIRONMENT_GET_CORE_ASSETS_DIRECTORY:
223 case RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY:
224 case RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY:
225 case RETRO_ENVIRONMENT_GET_PLAYLIST_DIRECTORY:
226 case RETRO_ENVIRONMENT_GET_FILE_BROWSER_START_DIRECTORY: {
228 if (!data)
return true;
229 *(
const char**)data = SDL_Libretro_GetDirectory(lr, cmd);
233 case RETRO_ENVIRONMENT_SET_PIXEL_FORMAT: {
234 if (!data)
return false;
235 enum retro_pixel_format fmt = *(
const enum retro_pixel_format*)data;
237 case RETRO_PIXEL_FORMAT_0RGB1555:
238 case RETRO_PIXEL_FORMAT_XRGB8888:
239 case RETRO_PIXEL_FORMAT_RGB565: {
240 bool changed = (fmt != lr->core.pixelFormat);
241 lr->core.pixelFormat = fmt;
243 if (changed && lr->core.texture) {
244 lr->core.videoReinitPending =
true;
254 case RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS: {
255 const struct retro_input_descriptor* desc = (
const struct retro_input_descriptor*)data;
256 if (!desc)
return true;
258 for (
const struct retro_input_descriptor* d = desc; d->description; d++) count++;
259 SDL_free(lr->core.inputDescriptors);
260 lr->core.inputDescriptors = NULL;
261 lr->core.inputDescriptorCount = 0;
263 lr->core.inputDescriptors = (
struct retro_input_descriptor*)SDL_malloc(count *
sizeof(*desc));
264 if (lr->core.inputDescriptors) {
265 SDL_memcpy(lr->core.inputDescriptors, desc, count *
sizeof(*desc));
266 lr->core.inputDescriptorCount = count;
272 case RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK: {
273 if (!data)
return true;
274 const struct retro_keyboard_callback* cb = (
const struct retro_keyboard_callback*)data;
275 lr->core.keyboard_event = cb->callback;
279 case RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE: {
280 const struct retro_disk_control_callback* cb = (
const struct retro_disk_control_callback*)data;
281 if (!cb)
return true;
282 SDL_memset(&lr->core.disk_control, 0,
sizeof(lr->core.disk_control));
283 lr->core.disk_control.set_eject_state = cb->set_eject_state;
284 lr->core.disk_control.get_eject_state = cb->get_eject_state;
285 lr->core.disk_control.get_image_index = cb->get_image_index;
286 lr->core.disk_control.set_image_index = cb->set_image_index;
287 lr->core.disk_control.get_num_images = cb->get_num_images;
288 lr->core.disk_control.replace_image_index = cb->replace_image_index;
289 lr->core.disk_control.add_image_index = cb->add_image_index;
290 lr->core.diskControlActive =
true;
294 case RETRO_ENVIRONMENT_SET_HW_RENDER: {
295 SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"[SDL_Libretro] SET_HW_RENDER not supported");
299 case RETRO_ENVIRONMENT_GET_VARIABLE: {
301 if (!data)
return true;
302 struct retro_variable* var = (
struct retro_variable*)data;
303 if (!var->key)
return false;
306 var->value = o->
value;
312 case RETRO_ENVIRONMENT_SET_VARIABLE: {
314 if (!data)
return true;
315 const struct retro_variable* var = (
const struct retro_variable*)data;
316 if (!var->key || !var->value)
return false;
320 case RETRO_ENVIRONMENT_SET_VARIABLES: {
322 if (!data)
return true;
323 const struct retro_variable* var = (
const struct retro_variable*)data;
324 for (; var->key; var++) {
325 const char* label =
"";
326 const char* defaultVal =
"";
329 char* valueBuffer = var->value ? SDL_strdup(var->value) : NULL;
330 struct retro_core_option_value* values = NULL;
333 char* semicolon = SDL_strchr(valueBuffer,
';');
337 char* labelEnd = semicolon;
338 while (labelEnd > valueBuffer && labelEnd[-1] ==
' ') {
339 *(--labelEnd) =
'\0';
343 char* opts = semicolon + 1;
344 while (*opts ==
' ') {
349 unsigned count = *opts ? 1 : 0;
350 for (
const char* p = opts; *p; p++) {
351 if (*p ==
'|') count++;
355 values = (
struct retro_core_option_value*)SDL_calloc(count + 1,
sizeof(*values));
360 while (tok && *tok) {
361 char* nextPipe = SDL_strchr(tok,
'|');
362 if (nextPipe) *nextPipe =
'\0';
363 values[vcount].value = tok;
364 values[vcount].label = NULL;
366 tok = nextPipe ? nextPipe + 1 : NULL;
370 char* firstPipe = SDL_strchr(opts,
'|');
371 if (firstPipe) *firstPipe =
'\0';
375 SDL_Libretro_InitCoreOption(lr, var->key,
381 SDL_free(valueBuffer);
386 case RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE: {
387 if (!data)
return true;
388 *(
bool*)data = lr->core.optionsDirty;
389 lr->core.optionsDirty =
false;
393 case RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME: {
394 if (!data)
return true;
395 lr->core.supportNoGame = *(
const bool*)data;
399 case RETRO_ENVIRONMENT_GET_LIBRETRO_PATH: {
400 if (!data)
return true;
401 *(
const char**)data = lr->core.corePath[0] ? lr->core.corePath : NULL;
405 case RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK: {
406 if (!data)
return true;
407 lr->core.runloop_frame_time = *(
const struct retro_frame_time_callback*)data;
411 case RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK: {
413 if (!data)
return true;
414 lr->core.audio_callback = *(
const struct retro_audio_callback*)data;
418 case RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE: {
419 if (!data)
return true;
420 struct retro_rumble_interface* rumble = (
struct retro_rumble_interface*)data;
421 rumble->set_rumble_state = SDL_Libretro_SetRumbleState;
425 case RETRO_ENVIRONMENT_GET_INPUT_DEVICE_CAPABILITIES: {
426 if (!data)
return true;
428 (1 << RETRO_DEVICE_JOYPAD) |
429 (1 << RETRO_DEVICE_MOUSE) |
430 (1 << RETRO_DEVICE_KEYBOARD) |
431 (1 << RETRO_DEVICE_LIGHTGUN) |
432 (1 << RETRO_DEVICE_ANALOG) |
433 (1 << RETRO_DEVICE_POINTER);
437 case RETRO_ENVIRONMENT_GET_LOG_INTERFACE: {
438 if (!data)
return true;
439 struct retro_log_callback* cb = (
struct retro_log_callback*)data;
440 cb->log = &SDL_Libretro_Logger;
444 case RETRO_ENVIRONMENT_GET_PERF_INTERFACE: {
445 if (!data)
return true;
446 struct retro_perf_callback* perf = (
struct retro_perf_callback*)data;
447 perf->get_time_usec = &SDL_Libretro_GetTimeUSEC;
448 perf->get_cpu_features = &SDL_Libretro_GetCPUFeatures;
449 perf->get_perf_counter = &SDL_Libretro_GetPerfCounter;
450 perf->perf_register = &SDL_Libretro_PerfRegister;
451 perf->perf_start = &SDL_Libretro_PerfStart;
452 perf->perf_stop = &SDL_Libretro_PerfStop;
453 perf->perf_log = &SDL_Libretro_PerfLog;
457 case RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO: {
458 if (!data)
return false;
459 const struct retro_system_av_info* av = (
const struct retro_system_av_info*)data;
460 lr->core.fps = av->timing.fps;
461 lr->core.aspectRatio = av->geometry.aspect_ratio;
464 if (av->timing.sample_rate != lr->core.sampleRate) {
465 lr->core.sampleRate = av->timing.sample_rate;
466 if (lr->core.audioStream) {
467 lr->core.audioReinitPending =
true;
473 case RETRO_ENVIRONMENT_SET_GEOMETRY: {
474 if (!data)
return true;
475 const struct retro_game_geometry* geom = (
const struct retro_game_geometry*)data;
477 lr->core.aspectRatio = geom->aspect_ratio;
481 case RETRO_ENVIRONMENT_GET_USERNAME: {
482 if (!data)
return true;
483 *(
const char**)data = lr->username;
487 case RETRO_ENVIRONMENT_GET_LANGUAGE: {
488 if (!data)
return true;
489 *(
unsigned*)data = RETRO_LANGUAGE_ENGLISH;
493 case RETRO_ENVIRONMENT_SET_SERIALIZATION_QUIRKS: {
494 if (!data)
return true;
495 lr->core.serializationQuirks = *(
const uint64_t*)data;
500 case RETRO_ENVIRONMENT_GET_SENSOR_INTERFACE: {
501 if (!data)
return true;
502 struct retro_sensor_interface* sensor = (
struct retro_sensor_interface*)data;
503 sensor->set_sensor_state = SDL_Libretro_SetSensorState;
504 sensor->get_sensor_input = SDL_Libretro_GetSensorInput;
509 case RETRO_ENVIRONMENT_GET_AUDIO_VIDEO_ENABLE: {
510 if (!data)
return true;
511 *(
int*)data = RETRO_AV_ENABLE_VIDEO | RETRO_AV_ENABLE_AUDIO;
516 case RETRO_ENVIRONMENT_GET_FASTFORWARDING: {
517 if (!data)
return true;
518 *(
bool*)data = SDL_Libretro_GetSpeed(lr) > 1.0f;
523 case RETRO_ENVIRONMENT_GET_TARGET_REFRESH_RATE: {
524 if (!data)
return true;
525 *(
float*)data = SDL_Libretro_GetTargetRefreshRate(lr);
530 case RETRO_ENVIRONMENT_GET_INPUT_BITMASKS: {
534 case RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION: {
535 if (!data)
return false;
536 *(
unsigned*)data = 1;
541 case RETRO_ENVIRONMENT_GET_THROTTLE_STATE: {
542 if (!data)
return false;
543 struct retro_throttle_state* throttle = (
struct retro_throttle_state*)data;
545 double fps = lr->core.fps > 0.0 ? lr->core.fps : 60.0;
546 float speed = lr->speed;
548 throttle->mode = RETRO_THROTTLE_FRAME_STEPPING;
549 throttle->rate = 0.0f;
551 else if (speed > 1.0f) {
552 throttle->mode = RETRO_THROTTLE_FAST_FORWARD;
553 throttle->rate = (float)(fps * speed);
555 else if (speed > 0.0f && speed < 1.0f) {
556 throttle->mode = RETRO_THROTTLE_SLOW_MOTION;
557 throttle->rate = (float)(fps * speed);
559 else if (speed < 0.0f) {
560 throttle->mode = RETRO_THROTTLE_REWINDING;
561 throttle->rate = (float)(fps * -speed);
564 throttle->mode = RETRO_THROTTLE_NONE;
565 throttle->rate = (float)fps;
568 int vsync = SDL_RENDERER_VSYNC_DISABLED;
569 if (lr->renderer && SDL_GetRenderVSync(lr->renderer, &vsync) && vsync != SDL_RENDERER_VSYNC_DISABLED) {
570 float interval = (vsync == SDL_RENDERER_VSYNC_ADAPTIVE) ? 1.0f : (float)vsync;
571 float vsyncRate = SDL_Libretro_GetTargetRefreshRate(lr) / interval;
572 if (vsyncRate < fps) {
573 throttle->mode = RETRO_THROTTLE_VSYNC;
574 throttle->rate = vsyncRate;
582 case RETRO_ENVIRONMENT_SET_CONTROLLER_INFO: {
583 const struct retro_controller_info* info = (
const struct retro_controller_info*)data;
584 if (!info)
return true;
585 SDL_free(lr->core.controllerInfo);
586 lr->core.controllerInfo = NULL;
587 lr->core.controllerPortCount = 0;
589 for (
unsigned i = 0; info[i].types; i++) count++;
591 lr->core.controllerInfo = (
struct retro_controller_info*)SDL_malloc(count *
sizeof(*info));
592 if (lr->core.controllerInfo) {
593 SDL_memcpy(lr->core.controllerInfo, info, count *
sizeof(*info));
594 lr->core.controllerPortCount = count;
601 case RETRO_ENVIRONMENT_SET_MEMORY_MAPS: {
602 if (!data)
return true;
603 const struct retro_memory_map* map = (
const struct retro_memory_map*)data;
604 SDL_Libretro_FreeMemoryMap(lr);
605 if (map->num_descriptors > 0) {
606 size_t sz = map->num_descriptors *
sizeof(
struct retro_memory_descriptor);
607 lr->core.memoryMapDescriptors = (
struct retro_memory_descriptor*)SDL_malloc(sz);
608 if (lr->core.memoryMapDescriptors) {
609 SDL_memcpy(lr->core.memoryMapDescriptors, map->descriptors, sz);
610 lr->core.memoryMapDescriptorCount = map->num_descriptors;
612 for (
unsigned i = 0; i < lr->core.memoryMapDescriptorCount; i++) {
613 const char* as = lr->core.memoryMapDescriptors[i].addrspace;
614 if (as) lr->core.memoryMapDescriptors[i].addrspace = SDL_strdup(as);
621 case RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK: {
624 const struct retro_audio_buffer_status_callback* cb = (
const struct retro_audio_buffer_status_callback*)data;
625 lr->core.audio_buffer_status.callback = cb ? cb->callback : NULL;
629 case RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY: {
630 if (!data)
return true;
631 lr->core.minimumAudioLatencyMs = *(
const unsigned*)data;
633 if (lr->core.audioStream) {
634 SDL_Libretro_UpdateAudioThreshold(lr);
639 case RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE: {
640 if (!data)
return false;
641 const struct retro_system_content_info_override* overrides =
642 (
const struct retro_system_content_info_override*)data;
643 SDL_Libretro_FreeContentInfoOverrides(lr);
645 while (overrides[count].extensions) count++;
647 lr->core.contentInfoOverrides = (
struct retro_system_content_info_override*)
648 SDL_calloc(count,
sizeof(
struct retro_system_content_info_override));
649 if (lr->core.contentInfoOverrides) {
650 for (
unsigned i = 0; i < count; i++) {
651 lr->core.contentInfoOverrides[i] = overrides[i];
653 lr->core.contentInfoOverrides[i].extensions = SDL_strdup(overrides[i].extensions);
655 lr->core.contentInfoOverrideCount = count;
661 case RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION: {
662 if (!data)
return true;
663 *(
unsigned*)data = 2;
667 case RETRO_ENVIRONMENT_SET_CORE_OPTIONS: {
668 if (!data)
return true;
669 const struct retro_core_option_definition* defs = (
const struct retro_core_option_definition*)data;
671 while (defs[count].key) count++;
672 struct retro_core_option_v2_definition* v2defs =
673 (
struct retro_core_option_v2_definition*)SDL_calloc(count + 1,
sizeof(*v2defs));
674 if (!v2defs)
return false;
675 for (
unsigned i = 0; i < count; i++) {
676 v2defs[i].key = defs[i].key;
677 v2defs[i].desc = defs[i].desc;
678 v2defs[i].info = defs[i].info;
679 v2defs[i].default_value = defs[i].default_value;
680 SDL_memcpy(v2defs[i].values, defs[i].values,
sizeof(defs[i].values));
682 struct retro_core_options_v2 opts = { NULL, v2defs };
683 bool result = SDL_Libretro_EnvironmentCallback(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2, &opts);
688 case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL: {
689 if (!data)
return false;
690 const struct retro_core_options_intl* intl = (
const struct retro_core_options_intl*)data;
691 const struct retro_core_option_definition* defs = intl->us;
692 if (!defs)
return false;
694 while (defs[count].key) count++;
695 struct retro_core_option_v2_definition* v2defs =
696 (
struct retro_core_option_v2_definition*)SDL_calloc(count + 1,
sizeof(*v2defs));
697 if (!v2defs)
return false;
698 for (
unsigned i = 0; i < count; i++) {
699 v2defs[i].key = defs[i].key;
700 v2defs[i].desc = defs[i].desc;
701 v2defs[i].info = defs[i].info;
702 v2defs[i].default_value = defs[i].default_value;
703 SDL_memcpy(v2defs[i].values, defs[i].values,
sizeof(defs[i].values));
705 struct retro_core_options_v2 opts = { NULL, v2defs };
706 bool result = SDL_Libretro_EnvironmentCallback(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2, &opts);
711 case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY: {
712 if (!data)
return false;
713 const struct retro_core_option_display* opt = (
const struct retro_core_option_display*)data;
714 if (!opt->key)
return false;
715 for (
unsigned i = 0; i < lr->core.optionCount; i++) {
716 if (lr->core.options[i].key && SDL_strcmp(lr->core.options[i].key, opt->key) == 0) {
717 lr->core.options[i].visible = opt->visible;
718 lr->core.optionsDirty =
true;
725 case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2: {
726 if (!data)
return false;
727 const struct retro_core_options_v2* opts = (
const struct retro_core_options_v2*)data;
728 if (!opts->definitions) {
733 if (opts->categories) {
734 for (
unsigned c = 0; opts->categories[c].key; c++) {
735 SDL_Libretro_InitCoreOptionCategory(lr, opts->categories[c].key,
736 opts->categories[c].desc, opts->categories[c].info);
740 for (
unsigned i = 0; opts->definitions[i].key; i++) {
741 const struct retro_core_option_v2_definition* def = &opts->definitions[i];
742 const char* defaultVal = def->default_value ? def->default_value :
"";
744 const char* desc = SDL_Libretro_PickCoreOptionText(def->desc, def->desc_categorized, def->category_key);
745 const char* info = SDL_Libretro_PickCoreOptionText(def->info, def->info_categorized, def->category_key);
746 SDL_Libretro_InitCoreOption(lr, def->key,
756 case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL: {
757 if (!data)
return false;
758 const struct retro_core_options_v2_intl* intl = (
const struct retro_core_options_v2_intl*)data;
760 if (!intl->us)
return false;
761 return SDL_Libretro_EnvironmentCallback(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2, intl->us);
764 case RETRO_ENVIRONMENT_GET_MESSAGE_INTERFACE_VERSION: {
765 if (!data)
return true;
766 *(
unsigned*)data = 1;
770 case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_UPDATE_DISPLAY_CALLBACK: {
772 lr->core.optionsUpdateDisplayCallback = NULL;
775 const struct retro_core_options_update_display_callback* cb =
776 (
const struct retro_core_options_update_display_callback*)data;
777 lr->core.optionsUpdateDisplayCallback = cb->callback;
781 case RETRO_ENVIRONMENT_SET_MESSAGE_EXT: {
782 const struct retro_message_ext* msg = (
const struct retro_message_ext*)data;
783 if (!msg || !msg->msg)
return true;
784 double seconds = msg->duration / 1000.0;
786 if (msg->target == RETRO_MESSAGE_TARGET_LOG || msg->target == RETRO_MESSAGE_TARGET_ALL) {
787 switch (msg->level) {
788 case RETRO_LOG_DEBUG: SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION,
"[Core] %s", msg->msg);
break;
789 case RETRO_LOG_WARN: SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"[Core] %s", msg->msg);
break;
790 case RETRO_LOG_ERROR: SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"[Core] %s", msg->msg);
break;
791 default: SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"[Core] %s", msg->msg);
break;
795 if (msg->target != RETRO_MESSAGE_TARGET_LOG) {
796 SDL_Libretro_OsdPush(lr, msg->msg, seconds, msg->priority, msg->type, msg->progress);
801 case RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE: {
802 if (!data)
return true;
803 lr->core.disk_control = *(
const struct retro_disk_control_ext_callback*)data;
804 lr->core.diskControlActive =
true;
808 case RETRO_ENVIRONMENT_GET_GAME_INFO_EXT: {
810 if (!data || !lr->core.gameInfoExt.full_path)
return false;
811 *(
const struct retro_game_info_ext**)data = &lr->core.gameInfoExt;
816 case RETRO_ENVIRONMENT_GET_CURRENT_SOFTWARE_FRAMEBUFFER: {
817 if (!data || !lr->core.texture)
return false;
818 struct retro_framebuffer* fb = (
struct retro_framebuffer*)data;
819 if (fb->width != lr->core.width || fb->height != lr->core.height)
return false;
821 if (fb->access_flags & RETRO_MEMORY_ACCESS_READ)
return false;
824 SDL_Libretro_ReleaseSoftwareFramebuffer(lr);
828 if (!SDL_LockTexture(lr->core.texture, NULL, &pixels, &pitch))
return false;
831 fb->pitch = (size_t)pitch;
832 fb->format = lr->core.pixelFormat;
833 fb->memory_flags = RETRO_MEMORY_TYPE_CACHED;
834 lr->core.softwareFramebufferPixels = pixels;
839 case RETRO_ENVIRONMENT_GET_VFS_INTERFACE: {
840 if (!data)
return true;
841 struct retro_vfs_interface_info* info = (
struct retro_vfs_interface_info*)data;
842 if (info->required_interface_version > 4) {
843 SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
844 "[SDL_Libretro] GET_VFS_INTERFACE: unsupported required_interface_version %u",
845 info->required_interface_version);
850 info->required_interface_version = 4;
851 info->iface = &lr->vfs_interface;
856 case RETRO_ENVIRONMENT_GET_MICROPHONE_INTERFACE: {
857 if (!data)
return false;
858 struct retro_microphone_interface* mic = (
struct retro_microphone_interface*)data;
859 mic->interface_version = RETRO_MICROPHONE_INTERFACE_VERSION;
860 mic->open_mic = SDL_Libretro_MicOpen;
861 mic->close_mic = SDL_Libretro_MicClose;
862 mic->get_params = SDL_Libretro_MicGetParams;
863 mic->set_mic_state = SDL_Libretro_MicSetState;
864 mic->get_mic_state = SDL_Libretro_MicGetState;
865 mic->read_mic = SDL_Libretro_MicRead;
870 case RETRO_ENVIRONMENT_GET_DEVICE_POWER: {
871 if (!data)
return true;
872 struct retro_device_power* power = (
struct retro_device_power*)data;
874 SDL_PowerState sdl_state = SDL_GetPowerInfo(&power->seconds, &percent);
876 case SDL_POWERSTATE_ON_BATTERY:
877 power->state = RETRO_POWERSTATE_DISCHARGING;
879 case SDL_POWERSTATE_CHARGING:
880 power->state = RETRO_POWERSTATE_CHARGING;
882 case SDL_POWERSTATE_CHARGED:
883 power->state = RETRO_POWERSTATE_CHARGED;
885 case SDL_POWERSTATE_NO_BATTERY:
886 power->state = RETRO_POWERSTATE_PLUGGED_IN;
891 power->percent = (int8_t)percent;
896 case RETRO_ENVIRONMENT_GET_TARGET_SAMPLE_RATE: {
897 if (!data)
return true;
899 if (lr->core.audioStream && SDL_GetAudioStreamFormat(lr->core.audioStream, NULL, &spec) && spec.freq > 0) {
901 *(
unsigned*)data = (
unsigned)spec.freq;
902 }
else if (SDL_GetAudioDeviceFormat(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, NULL) && spec.freq > 0) {
904 *(
unsigned*)data = (
unsigned)spec.freq;
914 case RETRO_ENVIRONMENT_GET_CAMERA_INTERFACE:
915 case RETRO_ENVIRONMENT_GET_LOCATION_INTERFACE:
916 case RETRO_ENVIRONMENT_SET_PROC_ADDRESS_CALLBACK:
918 case RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE:
920 case RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS:
922 case RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE:
924 case RETRO_ENVIRONMENT_SET_HW_SHARED_CONTEXT:
926 case RETRO_ENVIRONMENT_GET_LED_INTERFACE:
930 SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"[SDL_Libretro] Unhandled environment callback: %u", cmd);
#define SDL_LIBRETRO_AUDIO_DEFAULT_SAMPLE_RATE
Fallback rate used when a core reports no audio sample rate (<= 0).
void SDL_Libretro_SetMessage(SDL_Libretro *lr, const char *msg, double duration)
Add a message to be displayed within the libretro context.
bool SDL_Libretro_SetOptionValue(SDL_Libretro *lr, const char *key, const char *value)
Set a core option's value; fails if the value isn't one the core declared.
const SDL_LibretroOption * SDL_Libretro_GetOption(const SDL_Libretro *lr, const char *key)
Retrieve details about a given core option.
A core option and its current state.
const char * value
Description / help text; may be empty.