diff --git a/platform/libretro/libretro.cpp b/platform/libretro/libretro.cpp index d95ccb6c..b4fc1d50 100644 --- a/platform/libretro/libretro.cpp +++ b/platform/libretro/libretro.cpp @@ -3,6 +3,7 @@ #include #include "libretro.h" +#include "libretro_core_options.h" #include "../../source/vm.h" #include "../../source/PicoRam.h" @@ -40,10 +41,18 @@ const int PicoScreenHeight = 128; const int BytesPerPixel = 2; -const size_t screenBufferSize = PicoScreenWidth*PicoScreenHeight; +static int scale = 1; +static int crop_h_left = 0; +static int crop_h_right = 0; +static int crop_v_top = 0; +static int crop_v_bottom = 0; +const size_t screenBufferSize = PicoScreenWidth*PicoScreenHeight; uint16_t screenBuffer[screenBufferSize]; +const size_t screenBufferSize2x = PicoScreenWidth*PicoScreenHeight*4; +uint16_t screenBuffer2x[screenBufferSize2x]; + uint16_t _rgb565Colors[144]; Vm* _vm; @@ -60,6 +69,90 @@ static void frame_time_cb(retro_usec_t usec) frame_time = usec / 1000000.0; } +static void check_variables(bool startup) +{ + struct retro_variable var = {0}; + char key[256]; + int video_updated = 0; + + var.key = "fake08_video_scale"; + if (enviro_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + int oldScale = scale; + int newScale = 0; + if (!strcmp(var.value, "1x")) { + newScale = 1; + } + else if (!strcmp(var.value, "2x")) { + newScale = 2; + } + if (newScale != 0 && newScale != oldScale) + { + scale = newScale; + video_updated = 2; + } + } + + + var.key = "fake08_crop_h_left"; + + if (enviro_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + int newval = atoi(var.value); + if (newval != crop_h_left) + { + crop_h_left = newval; + video_updated = 1; + } + } + + var.key = "fake08_crop_h_right"; + + if (enviro_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + int newval = atoi(var.value); + if (newval != crop_h_right) + { + crop_h_right = newval; + video_updated = 1; + } + } + + var.key = "fake08_crop_v_top"; + + if (enviro_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + int newval = atoi(var.value); + if (newval != crop_v_top) + { + crop_v_top = newval; + video_updated = 1; + } + } + + var.key = "fake08_crop_v_bottom"; + + if (enviro_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + int newval = atoi(var.value); + if (newval != crop_v_bottom) + { + crop_v_bottom = newval; + video_updated = 1; + } + } + + if (video_updated && !startup) + { + struct retro_system_av_info av_info; + retro_get_system_av_info(&av_info); + if (video_updated == 2) + enviro_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &av_info); + else + enviro_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &av_info); + } +} + EXPORT void retro_set_environment(retro_environment_t cb) { @@ -74,6 +167,8 @@ EXPORT void retro_set_environment(retro_environment_t cb) struct retro_frame_time_callback frame_cb = { frame_time_cb, 1000000 / 60 }; enviro_cb(RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK, &frame_cb); + + libretro_set_core_options(cb); } EXPORT void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; } @@ -173,12 +268,14 @@ EXPORT void retro_get_system_info(struct retro_system_info *info) EXPORT void retro_get_system_av_info(struct retro_system_av_info *info) { + unsigned width = (PicoScreenWidth - crop_h_left - crop_h_right) * scale; + unsigned height = (PicoScreenHeight - crop_v_top - crop_v_bottom) * scale; memset(info, 0, sizeof(*info)); - info->geometry.base_width = PicoScreenWidth; - info->geometry.base_height = PicoScreenHeight; - info->geometry.max_width = PicoScreenWidth; - info->geometry.max_height = PicoScreenHeight; - info->geometry.aspect_ratio = 1.f; + info->geometry.base_width = width; + info->geometry.max_width = PicoScreenWidth * scale; + info->geometry.base_height = height; + info->geometry.max_height = PicoScreenHeight * scale; + info->geometry.aspect_ratio = 1.0f; info->timing.fps = 60.f; info->timing.sample_rate = 22050.f; @@ -223,6 +320,11 @@ int flip = 0; EXPORT void retro_run() { + bool updated = false; + + if (enviro_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated) + check_variables(false); + //TODO: improve this so slower hardware can play 30fps games at full speed if (_vm->getTargetFps() == 60 || frame % 2 == 0) { @@ -367,6 +469,24 @@ EXPORT void retro_run() break; } + #ifdef TWO_X_SCALE + + for(int scry = 0; scry < PicoScreenHeight; scry++) { + for (int scrx = 0; scrx < PicoScreenWidth; scrx++) { + int picox = scrx / drawModeScaleX; + int picoy = scry / drawModeScaleY; + uint16_t color = _rgb565Colors[screenPaletteMap[getPixelNibble(picox, picoy, picoFb)]]; + + for (int y = 0; y < SCALE; y++) { + for (int x = 0; x < SCALE; x++) { + screenBuffer[(scry*SCALE+y)*PicoScreenWidthScaled+scrx*SCALE+x] = color; + } + } + } + } + + + #else //TODO: handle rotation/flip/mirroring for(int scry = 0; scry < PicoScreenHeight; scry++) { for (int scrx = 0; scrx < PicoScreenWidth; scrx++) { @@ -378,6 +498,8 @@ EXPORT void retro_run() video_cb(&screenBuffer, PicoScreenWidth, PicoScreenHeight, PicoScreenWidth * BytesPerPixel); + #endif + frame++; } @@ -618,6 +740,8 @@ EXPORT void retro_cheat_set(unsigned index, bool enabled, const char *code) EXPORT bool retro_load_game(struct retro_game_info const *info) { + check_variables(true); + if (!info) { _vm->QueueCartChange("__FAKE08-BIOS.p8"); return true; diff --git a/platform/libretro/libretro_core_options.h b/platform/libretro/libretro_core_options.h new file mode 100644 index 00000000..02c3a1a4 --- /dev/null +++ b/platform/libretro/libretro_core_options.h @@ -0,0 +1,321 @@ +#ifndef LIBRETRO_CORE_OPTIONS_H__ +#define LIBRETRO_CORE_OPTIONS_H__ + +#include +#include + +#include +#include + +#ifndef HAVE_NO_LANGEXTRA +#include "libretro_core_options_intl.h" +#endif + +/* + ******************************** + * VERSION: 1.3 + ******************************** + * + * - 1.3: Move translations to libretro_core_options_intl.h + * - libretro_core_options_intl.h includes BOM and utf-8 + * fix for MSVC 2010-2013 + * - Added HAVE_NO_LANGEXTRA flag to disable translations + * on platforms/compilers without BOM support + * - 1.2: Use core options v1 interface when + * RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION is >= 1 + * (previously required RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION == 1) + * - 1.1: Support generation of core options v0 retro_core_option_value + * arrays containing options with a single value + * - 1.0: First commit +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + ******************************** + * Core Option Definitions + ******************************** +*/ + +/* RETRO_LANGUAGE_ENGLISH */ + +/* Default language: + * - All other languages must include the same keys and values + * - Will be used as a fallback in the event that frontend language + * is not available + * - Will be used as a fallback for any missing entries in + * frontend language definition */ + +struct retro_core_option_definition option_defs_us[] = { + { + "fake08_video_scale", + "Video Scale", + "Set internal video scale factor.", + { + { "1x", NULL }, /* If value itself is human-readable (e.g. a number) */ + { "2x", NULL }, /* and can displayed directly, the value_label should */ + { NULL, NULL }, /* be set to NULL */ + }, + "1x" + }, + { + "fake08_crop_h_left", + "Crop Horizontal Left", + "Removes pixels from left side of the screen.", + { + { "0", NULL }, + { "4", NULL }, + { "8", NULL }, + { "12", NULL }, + { "16", NULL }, + { NULL, NULL }, + }, + "0", + }, + { + "fake08_crop_h_right", + "Crop Horizontal Right ", + "Removes pixels from right side of the screen.", + { + { "0", NULL }, + { "4", NULL }, + { "8", NULL }, + { "12", NULL }, + { "16", NULL }, + { NULL, NULL }, + }, + "0", + }, + { + "fake08_crop_v_top", + "Crop Vertical Top", + "Removes pixels from the top of the screen.", + { + { "0", NULL }, + { "4", NULL }, + { "8", NULL }, + { "12", NULL }, + { "16", NULL }, + { NULL, NULL }, + }, + "0", + }, + { + "fake08_crop_v_bottom", + "Crop Vertical Bottom", + "Removes pixels from the bottom of the screen.", + { + { "0", NULL }, + { "4", NULL }, + { "8", NULL }, + { "12", NULL }, + { "16", NULL }, + { NULL, NULL }, + }, + "0", + }, + { NULL, NULL, NULL, {{0}}, NULL }, +}; + +/* + ******************************** + * Language Mapping + ******************************** +*/ + +#ifndef HAVE_NO_LANGEXTRA +struct retro_core_option_definition *option_defs_intl[RETRO_LANGUAGE_LAST] = { + option_defs_us, /* RETRO_LANGUAGE_ENGLISH */ + NULL, /* RETRO_LANGUAGE_JAPANESE */ + NULL, /* RETRO_LANGUAGE_FRENCH */ + NULL, /* RETRO_LANGUAGE_SPANISH */ + NULL, /* RETRO_LANGUAGE_GERMAN */ + NULL, /* RETRO_LANGUAGE_ITALIAN */ + NULL, /* RETRO_LANGUAGE_DUTCH */ + NULL, /* RETRO_LANGUAGE_PORTUGUESE_BRAZIL */ + NULL, /* RETRO_LANGUAGE_PORTUGUESE_PORTUGAL */ + NULL, /* RETRO_LANGUAGE_RUSSIAN */ + NULL, /* RETRO_LANGUAGE_KOREAN */ + NULL, /* RETRO_LANGUAGE_CHINESE_TRADITIONAL */ + NULL, /* RETRO_LANGUAGE_CHINESE_SIMPLIFIED */ + NULL, /* RETRO_LANGUAGE_ESPERANTO */ + NULL, /* RETRO_LANGUAGE_POLISH */ + NULL, /* RETRO_LANGUAGE_VIETNAMESE */ + NULL, /* RETRO_LANGUAGE_ARABIC */ + NULL, /* RETRO_LANGUAGE_GREEK */ + NULL, /* RETRO_LANGUAGE_TURKISH */ + NULL, /* RETRO_LANGUAGE_SLOVAK */ + NULL, /* RETRO_LANGUAGE_PERSIAN */ + NULL, /* RETRO_LANGUAGE_HEBREW */ + NULL, /* RETRO_LANGUAGE_ASTURIAN */ + NULL, /* RETRO_LANGUAGE_FINNISH */ + NULL, /* RETRO_LANGUAGE_INDONESIAN */ + NULL, /* RETRO_LANGUAGE_SWEDISH */ + NULL, /* RETRO_LANGUAGE_UKRAINIAN */ + NULL, /* RETRO_LANGUAGE_CZECH */ + +}; +#endif + +/* + ******************************** + * Functions + ******************************** +*/ + +/* Handles configuration/setting of core options. + * Should be called as early as possible - ideally inside + * retro_set_environment(), and no later than retro_load_game() + * > We place the function body in the header to avoid the + * necessity of adding more .c files (i.e. want this to + * be as painless as possible for core devs) + */ + +static INLINE void libretro_set_core_options(retro_environment_t environ_cb) +{ + unsigned version = 0; + + if (!environ_cb) + return; + + if (environ_cb(RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION, &version) && (version >= 1)) + { +#ifndef HAVE_NO_LANGEXTRA + struct retro_core_options_intl core_options_intl; + unsigned language = 0; + + core_options_intl.us = option_defs_us; + core_options_intl.local = NULL; + + if (environ_cb(RETRO_ENVIRONMENT_GET_LANGUAGE, &language) && + (language < RETRO_LANGUAGE_LAST) && (language != RETRO_LANGUAGE_ENGLISH)) + core_options_intl.local = option_defs_intl[language]; + + environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL, &core_options_intl); +#else + environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS, &option_defs_us); +#endif + } + else + { + size_t i; + size_t num_options = 0; + struct retro_variable *variables = NULL; + char **values_buf = NULL; + + /* Determine number of options */ + for (;;) + { + if (!option_defs_us[num_options].key) + break; + num_options++; + } + + /* Allocate arrays */ + variables = (struct retro_variable *)calloc(num_options + 1, sizeof(struct retro_variable)); + values_buf = (char **)calloc(num_options, sizeof(char *)); + + if (!variables || !values_buf) + goto error; + + /* Copy parameters from option_defs_us array */ + for (i = 0; i < num_options; i++) + { + const char *key = option_defs_us[i].key; + const char *desc = option_defs_us[i].desc; + const char *default_value = option_defs_us[i].default_value; + struct retro_core_option_value *values = option_defs_us[i].values; + size_t buf_len = 3; + size_t default_index = 0; + + values_buf[i] = NULL; + + if (desc) + { + size_t num_values = 0; + + /* Determine number of values */ + for (;;) + { + if (!values[num_values].value) + break; + + /* Check if this is the default value */ + if (default_value) + if (strcmp(values[num_values].value, default_value) == 0) + default_index = num_values; + + buf_len += strlen(values[num_values].value); + num_values++; + } + + /* Build values string */ + if (num_values > 0) + { + size_t j; + + buf_len += num_values - 1; + buf_len += strlen(desc); + + values_buf[i] = (char *)calloc(buf_len, sizeof(char)); + if (!values_buf[i]) + goto error; + + strcpy(values_buf[i], desc); + strcat(values_buf[i], "; "); + + /* Default value goes first */ + strcat(values_buf[i], values[default_index].value); + + /* Add remaining values */ + for (j = 0; j < num_values; j++) + { + if (j != default_index) + { + strcat(values_buf[i], "|"); + strcat(values_buf[i], values[j].value); + } + } + } + } + + variables[i].key = key; + variables[i].value = values_buf[i]; + } + + /* Set variables */ + environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, variables); + +error: + + /* Clean up */ + if (values_buf) + { + for (i = 0; i < num_options; i++) + { + if (values_buf[i]) + { + free(values_buf[i]); + values_buf[i] = NULL; + } + } + + free(values_buf); + values_buf = NULL; + } + + if (variables) + { + free(variables); + variables = NULL; + } + } +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/libretro/libretro_core_options_intl.h b/platform/libretro/libretro_core_options_intl.h new file mode 100644 index 00000000..8f7e1a98 --- /dev/null +++ b/platform/libretro/libretro_core_options_intl.h @@ -0,0 +1,90 @@ +#ifndef LIBRETRO_CORE_OPTIONS_INTL_H__ +#define LIBRETRO_CORE_OPTIONS_INTL_H__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1500 && _MSC_VER < 1900) +/* https://support.microsoft.com/en-us/kb/980263 */ +#pragma execution_character_set("utf-8") +#pragma warning(disable:4566) +#endif + +#include + +/* + ******************************** + * VERSION: 1.3 + ******************************** + * + * - 1.3: Move translations to libretro_core_options_intl.h + * - libretro_core_options_intl.h includes BOM and utf-8 + * fix for MSVC 2010-2013 + * - Added HAVE_NO_LANGEXTRA flag to disable translations + * on platforms/compilers without BOM support + * - 1.2: Use core options v1 interface when + * RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION is >= 1 + * (previously required RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION == 1) + * - 1.1: Support generation of core options v0 retro_core_option_value + * arrays containing options with a single value + * - 1.0: First commit +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + ******************************** + * Core Option Definitions + ******************************** +*/ + +/* RETRO_LANGUAGE_JAPANESE */ + +/* RETRO_LANGUAGE_FRENCH */ + +/* RETRO_LANGUAGE_SPANISH */ + +/* RETRO_LANGUAGE_GERMAN */ + +/* RETRO_LANGUAGE_ITALIAN */ + +/* RETRO_LANGUAGE_DUTCH */ + +/* RETRO_LANGUAGE_PORTUGUESE_BRAZIL */ + +/* RETRO_LANGUAGE_PORTUGUESE_PORTUGAL */ + +/* RETRO_LANGUAGE_RUSSIAN */ + +/* RETRO_LANGUAGE_KOREAN */ + +/* RETRO_LANGUAGE_CHINESE_TRADITIONAL */ + +/* RETRO_LANGUAGE_CHINESE_SIMPLIFIED */ + +/* RETRO_LANGUAGE_ESPERANTO */ + +/* RETRO_LANGUAGE_POLISH */ + +/* RETRO_LANGUAGE_VIETNAMESE */ + +/* RETRO_LANGUAGE_ARABIC */ + +/* RETRO_LANGUAGE_GREEK */ + +/* RETRO_LANGUAGE_TURKISH */ + +/* RETRO_LANGUAGE_SLOVAK */ + +/* RETRO_LANGUAGE_PERSIAN */ + +/* RETRO_LANGUAGE_HEBREW */ + +/* RETRO_LANGUAGE_ASTURIAN */ + +/* RETRO_LANGUAGE_FINNISH */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/libretro/retro_inline.h b/platform/libretro/retro_inline.h new file mode 100644 index 00000000..a9e41bc8 --- /dev/null +++ b/platform/libretro/retro_inline.h @@ -0,0 +1,45 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (retro_inline.h). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __LIBRETRO_SDK_INLINE_H +#define __LIBRETRO_SDK_INLINE_H + +#ifndef INLINE + +/** + * Cross-platform inline specifier. + * + * Expands to something like \c __inline or \c inline, + * depending on the compiler. + */ +#if defined(_WIN32) || defined(__INTEL_COMPILER) +#define INLINE __inline +#elif defined(__STDC_VERSION__) && __STDC_VERSION__>=199901L +#define INLINE inline +#elif defined(__GNUC__) +#define INLINE __inline__ +#else +#define INLINE +#endif + +#endif +#endif