2024-10-12 15:23:35 +02:00
|
|
|
#include <libgen.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include "SDL_libretro.h"
|
|
|
|
|
|
|
|
static void fallback_log(enum retro_log_level level, const char* fmt, ...);
|
|
|
|
static retro_log_printf_t log_cb = fallback_log;
|
|
|
|
static retro_environment_t environ_cb;
|
|
|
|
static retro_video_refresh_t video_cb;
|
|
|
|
static retro_input_poll_t input_poll_cb;
|
|
|
|
static retro_input_state_t input_state_cb;
|
|
|
|
static retro_audio_sample_batch_t audio_batch_cb;
|
|
|
|
|
|
|
|
static SDL_Thread* game_thread;
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
fallback_log(enum retro_log_level level, const char* fmt, ...)
|
|
|
|
{
|
|
|
|
(void)level;
|
|
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
|
|
|
vfprintf(stderr, fmt, va);
|
|
|
|
va_end(va);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned
|
|
|
|
retro_api_version(void)
|
|
|
|
{
|
|
|
|
return RETRO_API_VERSION;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
retro_set_environment(retro_environment_t cb)
|
|
|
|
{
|
|
|
|
static struct retro_log_callback log;
|
2024-10-12 16:50:37 +02:00
|
|
|
bool no_game = true;
|
2024-10-12 15:23:35 +02:00
|
|
|
|
|
|
|
environ_cb = cb;
|
|
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
|
|
|
|
log_cb = log.log;
|
2024-10-12 16:50:37 +02:00
|
|
|
|
|
|
|
environ_cb(RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME, &no_game);
|
2024-10-12 15:23:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
retro_set_video_refresh(retro_video_refresh_t cb)
|
|
|
|
{
|
|
|
|
video_cb = cb;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
retro_set_audio_sample(retro_audio_sample_t cb)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)
|
|
|
|
{
|
|
|
|
audio_batch_cb = 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_get_system_info(struct retro_system_info* info)
|
|
|
|
{
|
|
|
|
info->need_fullpath = true;
|
|
|
|
info->valid_extensions = "zip";
|
|
|
|
info->library_version = "0.1";
|
|
|
|
info->library_name = "vvvvvv";
|
2024-10-12 16:04:25 +02:00
|
|
|
info->block_extract = true;
|
2024-10-12 15:23:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
retro_get_system_av_info(struct retro_system_av_info* info)
|
|
|
|
{
|
|
|
|
int width = 320;
|
|
|
|
int height = 240;
|
|
|
|
info->geometry.base_width = width;
|
|
|
|
info->geometry.base_height = height;
|
|
|
|
info->geometry.max_width = width;
|
|
|
|
info->geometry.max_height = height;
|
|
|
|
info->geometry.aspect_ratio = 0.0;
|
|
|
|
info->timing.fps = 60.0;
|
|
|
|
info->timing.sample_rate = 44100.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
retro_init(void)
|
|
|
|
{
|
|
|
|
enum retro_pixel_format pixfmt = RETRO_PIXEL_FORMAT_XRGB8888;
|
|
|
|
environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &pixfmt);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int main(int argc, char *argv[]);
|
|
|
|
|
|
|
|
static int
|
|
|
|
game_main(void* data)
|
|
|
|
{
|
|
|
|
char buf[1024];
|
|
|
|
char *pwd = SDL_strdup(getcwd(buf, 1024));
|
|
|
|
char *argv[] = {
|
|
|
|
SDL_strdup(""),
|
|
|
|
SDL_strdup("-basedir"),
|
|
|
|
SDL_strdup(pwd),
|
|
|
|
SDL_strdup("-langdir"),
|
|
|
|
SDL_strdup((SDL_snprintf(buf, 1024, "%s/lang", pwd), buf)),
|
|
|
|
SDL_strdup("-fontsdir"),
|
|
|
|
SDL_strdup((SDL_snprintf(buf, 1024, "%s/fonts", pwd), buf)),
|
|
|
|
SDL_strdup("-assets"),
|
|
|
|
SDL_strdup((SDL_snprintf(buf, 1024, "%s/data.zip", pwd), buf)),
|
|
|
|
};
|
|
|
|
int argc = sizeof(argv) / sizeof(argv[0]);
|
|
|
|
return main(argc, argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
retro_load_game(const struct retro_game_info* game)
|
|
|
|
{
|
|
|
|
char *systemdir;
|
|
|
|
environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &systemdir);
|
|
|
|
if (game == NULL) {
|
|
|
|
chdir(systemdir);
|
|
|
|
chdir("vvvvvv");
|
|
|
|
} else {
|
2024-10-12 16:04:25 +02:00
|
|
|
chdir(dirname(SDL_strdup(game->path)));
|
2024-10-12 15:23:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
game_thread = SDL_CreateThread(game_main, "VVVVVV", NULL);
|
|
|
|
|
|
|
|
if (game_thread == NULL)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
struct retro_keyboard_callback keyboard = {
|
|
|
|
.callback = SDL_libretro_KeyboardCallback,
|
|
|
|
};
|
|
|
|
environ_cb(RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK, &keyboard);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
retro_set_controller_port_device(unsigned port, unsigned device)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
retro_deinit(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
retro_reset(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
PumpJoypadEvents(void)
|
|
|
|
{
|
|
|
|
static int16_t buttons[16] = { 0 };
|
|
|
|
static const int bkeys[16] = {
|
|
|
|
[RETRO_DEVICE_ID_JOYPAD_B] = SDLK_v,
|
|
|
|
[RETRO_DEVICE_ID_JOYPAD_Y] = SDLK_v,
|
2024-10-12 16:50:09 +02:00
|
|
|
[RETRO_DEVICE_ID_JOYPAD_SELECT] = SDLK_ESCAPE,
|
2024-10-12 15:23:35 +02:00
|
|
|
[RETRO_DEVICE_ID_JOYPAD_START] = SDLK_RETURN,
|
|
|
|
[RETRO_DEVICE_ID_JOYPAD_UP] = SDLK_UP,
|
|
|
|
[RETRO_DEVICE_ID_JOYPAD_DOWN] = SDLK_DOWN,
|
|
|
|
[RETRO_DEVICE_ID_JOYPAD_LEFT] = SDLK_LEFT,
|
|
|
|
[RETRO_DEVICE_ID_JOYPAD_RIGHT] = SDLK_RIGHT,
|
|
|
|
[RETRO_DEVICE_ID_JOYPAD_A] = SDLK_v,
|
|
|
|
[RETRO_DEVICE_ID_JOYPAD_X] = SDLK_v,
|
|
|
|
[RETRO_DEVICE_ID_JOYPAD_L] = SDLK_BACKSPACE,
|
|
|
|
[RETRO_DEVICE_ID_JOYPAD_R] = SDLK_RETURN,
|
|
|
|
[RETRO_DEVICE_ID_JOYPAD_L2] = SDLK_v,
|
|
|
|
[RETRO_DEVICE_ID_JOYPAD_R2] = SDLK_v,
|
|
|
|
[RETRO_DEVICE_ID_JOYPAD_L3] = SDLK_v,
|
|
|
|
[RETRO_DEVICE_ID_JOYPAD_R3] = SDLK_v,
|
|
|
|
};
|
|
|
|
for (int i = 0; i < 16; ++i) {
|
|
|
|
int16_t state = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, i);
|
|
|
|
int k = bkeys[i];
|
|
|
|
if (buttons[i] != state) {
|
|
|
|
buttons[i] = state;
|
|
|
|
SDL_SendKeyboardKey(state ? SDL_PRESSED : SDL_RELEASED,
|
|
|
|
SDL_GetScancodeFromKey(k));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
retro_run(void)
|
|
|
|
{
|
|
|
|
input_poll_cb();
|
|
|
|
PumpJoypadEvents();
|
|
|
|
SDL_libretro_RefreshVideo(video_cb);
|
|
|
|
SDL_libretro_ProduceAudio(audio_batch_cb);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
retro_serialize_size(void)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
retro_serialize(void* data, size_t size)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
retro_unserialize(const void* data, size_t size)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
retro_cheat_reset(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
|
|
retro_cheat_set(unsigned index, bool enabled, const char* code)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
retro_load_game_special(unsigned game_type,
|
|
|
|
const struct retro_game_info* info,
|
|
|
|
size_t num_info)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
retro_unload_game(void)
|
|
|
|
{
|
|
|
|
SDL_Event event = { SDL_QUIT };
|
|
|
|
SDL_PushEvent(&event);
|
|
|
|
SDL_WaitThread(game_thread, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned
|
|
|
|
retro_get_region(void)
|
|
|
|
{
|
|
|
|
return RETRO_REGION_NTSC;
|
|
|
|
}
|
|
|
|
|
|
|
|
void*
|
|
|
|
retro_get_memory_data(unsigned id)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
retro_get_memory_size(unsigned id)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|