From e8bad8c3ca33d6dfa672f2e570eddc3ab4b3a7e6 Mon Sep 17 00:00:00 2001 From: Toad King Date: Thu, 14 Jun 2012 03:23:47 -0400 Subject: initial libretro changes --- libretro/libretro.cpp | 616 ++++++++++++++++++++++++++++++++++++++++++++++++++ libretro/libretro.h | 277 +++++++++++++++++++++++ libretro/link.T | 5 + libretro/memstream.c | 115 ++++++++++ libretro/memstream.h | 30 +++ 5 files changed, 1043 insertions(+) create mode 100644 libretro/libretro.cpp create mode 100644 libretro/libretro.h create mode 100644 libretro/link.T create mode 100644 libretro/memstream.c create mode 100644 libretro/memstream.h (limited to 'libretro') diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp new file mode 100644 index 0000000..ec9e4d6 --- /dev/null +++ b/libretro/libretro.cpp @@ -0,0 +1,616 @@ +#include +#include +#ifndef _MSC_VER +#include +#include +#endif +#include +#include +#include +#include +#include + +#include "libretro.h" +#include "memstream.h" + +#include "../src/snes9x.h" +#include "../src/memmap.h" +#include "../src/cpuexec.h" +#include "../src/srtc.h" +#include "../src/apu.h" +#include "../src/ppu.h" +#include "../src/snapshot.h" +#include "../src/soundux.h" +#include "../src/cheats.h" +#include "../src/display.h" + +#define MAP_BUTTON(id, name) S9xMapButton((id), S9xGetCommandT((name)), false) +#define MAKE_BUTTON(pad, btn) (((pad)<<4)|(btn)) + +#define BTN_POINTER (RETRO_DEVICE_ID_JOYPAD_R + 1) +#define BTN_POINTER2 (BTN_POINTER + 1) + +static retro_video_refresh_t video_cb = NULL; +static retro_input_poll_t poll_cb = NULL; +static retro_input_state_t input_cb = NULL; +static retro_audio_sample_batch_t audio_batch_cb = NULL; +static retro_environment_t environ_cb = NULL; + +static uint32 joys[5]; + +bool8 ROMAPUEnabled = 0; +char currentWorkingDir[MAX_PATH+1] = {0}; + +memstream_t *s_stream; + +int s_open(const char *fname, const char *mode) +{ + s_stream = memstream_open(); + return TRUE; +} + +int s_read(void *p, int l) +{ + return memstream_read(s_stream, p, l); +} + +int s_write(void *p, int l) +{ + return memstream_write(s_stream, p, l); +} + +void s_close() +{ + memstream_close(s_stream); +} + +int (*statef_open)(const char *fname, const char *mode) = s_open; +int (*statef_read)(void *p, int l) = s_read; +int (*statef_write)(void *p, int l) = s_write; +void (*statef_close)() = s_close; + + + +void *retro_get_memory_data(unsigned type) +{ + uint8_t* data; + + switch(type) + { + case RETRO_MEMORY_SAVE_RAM: + data = Memory.SRAM; + break; + case RETRO_MEMORY_SYSTEM_RAM: + data = Memory.RAM; + break; + case RETRO_MEMORY_VIDEO_RAM: + data = Memory.VRAM; + break; + default: + data = NULL; + break; + } + + return data; +} + +size_t retro_get_memory_size(unsigned type) +{ + unsigned size; + + switch(type) + { + case RETRO_MEMORY_SAVE_RAM: + size = (unsigned) (Memory.SRAMSize ? (1 << (Memory.SRAMSize + 3)) * 128 : 0); + if (size > 0x20000) + size = 0x20000; + break; + /*case RETRO_MEMORY_RTC: + size = (Settings.SRTC || Settings.SPC7110RTC)?20:0; + break;*/ + case RETRO_MEMORY_SYSTEM_RAM: + size = 128 * 1024; + break; + case RETRO_MEMORY_VIDEO_RAM: + size = 64 * 1024; + break; + default: + size = 0; + break; + } + + return size; +} + +unsigned retro_api_version(void) +{ + return RETRO_API_VERSION; +} + +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) +{ + poll_cb = cb; +} + +void retro_set_input_state(retro_input_state_t cb) +{ + input_cb = cb; +} + +static bool use_overscan; + +void retro_set_environment(retro_environment_t cb) +{ + environ_cb = cb; +} + +void retro_get_system_info(struct retro_system_info *info) +{ + info->need_fullpath = false; + info->valid_extensions = "smc|fig|sfc|gd3|gd7|dx2|bsx|swc|zip|SMC|FIG|SFC|BSX|GD3|GD7|DX2|SWC|ZIP"; + info->library_version = "7.2.0"; + info->library_name = "PocketSNES"; + info->block_extract = false; +} + +static int16 audio_buf[0x10000]; +int avail = 534; + +void S9xGenerateSound() +{ +} + +uint32 S9xReadJoypad(int which1) +{ + if (which1 > 4) + return 0; + return joys[which1]; +} + +void retro_set_controller_port_device(unsigned in_port, unsigned device) +{ +} + +void retro_get_system_av_info(struct retro_system_av_info *info) +{ + info->geometry.base_width = 256; + info->geometry.base_height = 239; + info->geometry.max_width = 512; + info->geometry.max_height = 512; + if (!Settings.PAL) + info->timing.fps = 21477272.0 / 357366.0; + else + info->timing.fps = 21281370.0 / 425568.0; + info->timing.sample_rate = 32040.5; + info->geometry.aspect_ratio = 4.0f / 3.0f; +} + +static void snes_init (void) +{ + memset(&Settings, 0, sizeof(Settings)); + Settings.JoystickEnabled = FALSE; + Settings.SoundPlaybackRate = 32000; + Settings.Stereo = TRUE; + Settings.SoundBufferSize = 0; + Settings.CyclesPercentage = 100; + Settings.DisableSoundEcho = FALSE; + Settings.APUEnabled = FALSE; + Settings.H_Max = SNES_CYCLES_PER_SCANLINE; + Settings.SkipFrames = AUTO_FRAMERATE; + Settings.Shutdown = Settings.ShutdownMaster = TRUE; + Settings.FrameTimePAL = 20000; + Settings.FrameTimeNTSC = 16667; + Settings.FrameTime = Settings.FrameTimeNTSC; + Settings.DisableSampleCaching = FALSE; + Settings.DisableMasterVolume = FALSE; + Settings.Mouse = FALSE; + Settings.SuperScope = FALSE; + Settings.MultiPlayer5 = FALSE; + // Settings.ControllerOption = SNES_MULTIPLAYER5; + Settings.ControllerOption = 0; + + Settings.ForceTransparency = FALSE; + Settings.Transparency = TRUE; + Settings.SixteenBit = TRUE; + + Settings.SupportHiRes = FALSE; + Settings.NetPlay = FALSE; + Settings.ServerName [0] = 0; + Settings.AutoSaveDelay = 30; + Settings.ApplyCheats = FALSE; + Settings.TurboMode = FALSE; + Settings.TurboSkipFrames = 15; + Settings.ThreadSound = FALSE; + Settings.SoundSync = FALSE; + Settings.asmspc700 = TRUE; + Settings.SpeedHacks = TRUE; + + Settings.HBlankStart = (256 * Settings.H_Max) / SNES_HCOUNTER_MAX; + /* + Settings.SoundPlaybackRate = 5; + Settings.Stereo = TRUE; + Settings.SoundBufferSize = 0; + Settings.DisableSoundEcho = 0; + Settings.AltSampleDecode = 0; + Settings.SoundEnvelopeHeightReading = FALSE; + Settings.FixFrequency = 0; + Settings.CyclesPercentage = 100; + Settings.InterpolatedSound = TRUE; + Settings.APUEnabled = Settings.NextAPUEnabled = TRUE; + Settings.SoundMixInterval = 0; + Settings.H_Max = SNES_CYCLES_PER_SCANLINE; + Settings.SkipFrames = 10; + Settings.ShutdownMaster = TRUE; + Settings.FrameTimePAL = 20000; + Settings.FrameTimeNTSC = 16667; + Settings.DisableSampleCaching = FALSE; + Settings.DisableMasterVolume = FALSE; + Settings.Mouse = FALSE; + Settings.SuperScope = FALSE; + Settings.MultiPlayer5 = FALSE; + Settings.TurboMode = FALSE; + Settings.TurboSkipFrames = 40; + Settings.ControllerOption = SNES_MULTIPLAYER5; + Settings.Transparency = TRUE; + Settings.SixteenBit = TRUE; + Settings.SupportHiRes = TRUE; + Settings.NetPlay = FALSE; + Settings.ServerName [0] = 0; + Settings.ThreadSound = FALSE; + Settings.AutoSaveDelay = 30; + Settings.HBlankStart = (256 * Settings.H_Max) / SNES_HCOUNTER_MAX; + Settings.DisplayFrameRate = FALSE; + Settings.ReverseStereo = TRUE; + */ + + CPU.Flags = 0; + + if (!Memory.Init() || !S9xInitAPU()) + { + Memory.Deinit(); + S9xDeinitAPU(); + fprintf(stderr, "[libsnes]: Failed to init Memory or APU.\n"); + exit(1); + } + + if (!S9xInitSound() || !S9xGraphicsInit()) exit(1); + //S9xSetSamplesAvailableCallback(S9xAudioCallback); + + GFX.Pitch = use_overscan ? 1024 : 2048; + + // hack to make sure GFX.Delta is always (2048 * 512 * 2) >> 1, needed for tile16_t.h + GFX.Screen = (uint8 *) calloc(1, 2048 * 512 * 2 * 2); + GFX.SubScreen = GFX.Screen + 2048 * 512 * 2; + GFX.ZBuffer = (uint8 *) calloc(1, GFX.Pitch * 512 * sizeof(uint16)); + GFX.SubZBuffer = (uint8 *) calloc(1, GFX.Pitch * 512 * sizeof(uint16)); + GFX.Delta = 1048576; //(GFX.SubScreen - GFX.Screen) >> 1; + + if (GFX.Delta != ((GFX.SubScreen - GFX.Screen) >> 1)) + { + printf("BAD DELTA! (is %u, should be %u)\n", ((GFX.SubScreen - GFX.Screen) >> 1), GFX.Delta); + exit(1); + } + + /* controller port 1 */ + //S9xSetController(0, CTL_JOYPAD, 0, 0, 0, 0); + //retro_devices[0] = RETRO_DEVICE_JOYPAD; + + /* controller port 2 */ + //S9xSetController(1, CTL_JOYPAD, 1, 0, 0, 0); + //retro_devices[1] = RETRO_DEVICE_JOYPAD; + + //S9xUnmapAllControls(); + //map_buttons(); + + //S9xSetSoundMute(FALSE); +} + +void retro_init (void) +{ + if (!environ_cb(RETRO_ENVIRONMENT_GET_OVERSCAN, &use_overscan)) + use_overscan = FALSE; + + snes_init(); +} + +/* libsnes uses relative values for analogue devices. + S9x seems to use absolute values, but do convert these into relative values in the core. (Why?!) + Hack around it. :) */ + +void retro_deinit(void) +{ + S9xDeinitAPU(); + Memory.Deinit(); + S9xGraphicsDeinit(); + //S9xUnmapAllControls(); +} + +void retro_reset (void) +{ + S9xReset(); +} + +//static int16_t retro_mouse_state[2][2] = {{0}, {0}}; +//static int16_t retro_scope_state[2] = {0}; +//static int16_t retro_justifier_state[2][2] = {{0}, {0}}; +void S9xSetButton(int i, uint16 b, bool pressed); + +static void report_buttons (void) +{ + int i, j; + for ( i = 0; i < 5; i++) + { + for (j = 0; j <= RETRO_DEVICE_ID_JOYPAD_R; j++) + { + if (input_cb(i, RETRO_DEVICE_JOYPAD, 0, j)) + joys[i] |= (1 << (15 - j)); + else + joys[i] &= ~(1 << (15 - j)); + } + } +} + +void retro_run (void) +{ + S9xMainLoop(); + S9xMixSamples(audio_buf, avail * 2); + audio_batch_cb((int16_t *) audio_buf, avail); + + poll_cb(); + + report_buttons(); +} + +size_t retro_serialize_size (void) +{ + uint8_t *tmpbuf; + + tmpbuf = (uint8_t*)malloc(5000000); + memstream_set_buffer(tmpbuf, 5000000); + S9xFreezeGame(""); + free(tmpbuf); + return memstream_get_last_size(); +} + +bool retro_serialize(void *data, size_t size) +{ + memstream_set_buffer((uint8_t*)data, size); + if (S9xFreezeGame("") == FALSE) + return FALSE; + + return TRUE; +} + +bool retro_unserialize(const void * data, size_t size) +{ + memstream_set_buffer((uint8_t*)data, size); + if (S9xUnfreezeGame("") == FALSE) + return FALSE; + + return TRUE; +} + +void retro_cheat_reset(void) +{} + +void retro_cheat_set(unsigned unused, bool unused1, const char* unused2) +{} + +bool retro_load_game(const struct retro_game_info *game) +{ + bool8 loaded; + + /* Hack. S9x cannot do stuff from RAM. <_< */ + memstream_set_buffer((uint8_t*)game->data, game->size); + + loaded = Memory.LoadROM(""); + if (!loaded) + { + fprintf(stderr, "[libretro]: Rom loading failed...\n"); + return FALSE; + } + + //S9xGraphicsInit(); + S9xReset(); + CPU.APU_APUExecuting = Settings.APUEnabled = 3; + Settings.SixteenBitSound = true; + so.stereo = Settings.Stereo; + so.playback_rate = Settings.SoundPlaybackRate; + S9xSetPlaybackRate(so.playback_rate); + S9xSetSoundMute(FALSE); + + return TRUE; +} + +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) +{ } + +unsigned retro_get_region (void) +{ + return Settings.PAL ? RETRO_REGION_PAL : RETRO_REGION_NTSC; +} + +bool8 S9xDeinitUpdate(int width, int height, bool8 sixteen_bit) +{ + int y; + + if (height == 448 || height == 478) + { + /* Pitch 2048 -> 1024, only done once per res-change. */ + if (GFX.Pitch == 2048) + { + for ( y = 1; y < height; y++) + { + uint8_t *src = GFX.Screen + y * 1024; + uint8_t *dst = GFX.Screen + y * 512; + memcpy(dst, src, width * sizeof(uint8_t) * 2); + } + } + GFX.Pitch = 1024; + } + else + { + /* Pitch 1024 -> 2048, only done once per res-change. */ + if (GFX.Pitch == 1024) + { + for ( y = height - 1; y >= 0; y--) + { + uint8_t *src = GFX.Screen + y * 512; + uint8_t *dst = GFX.Screen + y * 1024; + memcpy(dst, src, width * sizeof(uint8_t) * 2); + } + } + GFX.Pitch = 2048; + } + + video_cb(GFX.Screen, width, height, GFX_PITCH); + + return TRUE; +} + + +/* Dummy functions that should probably be implemented correctly later. */ +const char* S9xGetFilename(const char* in) { return in; } +const char* S9xGetFilenameInc(const char* in) { return in; } +const char *S9xGetHomeDirectory() { return NULL; } +const char *S9xGetSnapshotDirectory() { return NULL; } +const char *S9xGetROMDirectory() { return NULL; } +const char* S9xChooseFilename(bool8 a) { return NULL; } +bool8 S9xInitUpdate() { return TRUE; } +bool8 S9xContinueUpdate(int width, int height) { return TRUE; } +void S9xSetPalette() {} +void S9xAutoSaveSRAM() {} +void S9xLoadSDD1Data() {} +bool8 S9xReadMousePosition (int which1_0_to_1, int &x, int &y, uint32 &buttons) { return FALSE; } +bool8 S9xReadSuperScopePosition (int &x, int &y, uint32 &buttons) { return FALSE; } +void JustifierButtons(uint32& x) {} +bool JustifierOffscreen() { return false; } + +START_EXTERN_C + +void S9xToggleSoundChannel (int channel) {} + +bool8 S9xMovieActive() { return FALSE; } +bool8 S9xMoviePlaying() { return FALSE; } +void S9xMovieFreeze() {} +void S9xMovieUnfreeze() {} +int S9xMovieCreate (const char* filename, uint8 controllers_mask, uint8 opts, const wchar_t* metadata, int metadata_length) { return FALSE; } +void S9xMovieStop (bool8 suppress_message) {} +const char *S9xChooseMovieFilename(bool8 read_only) { return NULL; } +void S9xMovieUpdate(bool addFrame) {} +void S9xMovieUpdateOnReset() {} +int S9xMovieOpen(const char* filename, bool8 read_only) { return FALSE; } +uint32 S9xMovieGetFrameCounter() { return 0; } +const char *S9xStringInput(const char *message) { return NULL; } + +END_EXTERN_C + +//void Write16(uint16 v, uint8*& ptr) {} +//uint16 Read16(const uint8*& ptr) { return 0; } + +//void S9xHandlePortCommand(s9xcommand_t cmd, int16 data1, int16 data2) {} +//bool S9xPollButton(uint32 id, bool *pressed) { return false; } +//bool S9xPollPointer(uint32 id, int16 *x, int16 *y) { return false; } +//bool S9xPollAxis(uint32 id, int16 *value) { return false; } + +void S9xExit() { exit(1); } +bool8 S9xOpenSoundDevice (int mode, bool8 stereo, int buffer_size) { + //so.sixteen_bit = 1; + so.stereo = TRUE; + //so.buffer_size = 534; + so.playback_rate = 32000; + return TRUE; +} + +const char *emptyString = ""; +const char *S9xBasename (const char *filename) { return emptyString; } +bool8 S9xOpenSnapshotFile (const char *base, bool8 read_only, STREAM *file) { *file = OPEN_STREAM(0, 0); return TRUE; } +void S9xCloseSnapshotFile (STREAM file) { CLOSE_STREAM(file); } + +void S9xMessage(int a, int b, const char* msg) +{ + fprintf(stderr, "%s\n", msg); +} + +/* S9x weirdness. */ +#ifndef _WIN32 +void _splitpath (const char * path, char * drive, char * dir, char * fname, char * ext) +{ + const char *slash, *dot; + + slash = strrchr(path, SLASH_CHAR); + dot = strrchr(path, '.'); + + if (dot && slash && dot < slash) + dot = NULL; + + if (!slash) + { + *dir = 0; + + strcpy(fname, path); + + if (dot) + { + fname[dot - path] = 0; + strcpy(ext, dot + 1); + } + else + *ext = 0; + } + else + { + strcpy(dir, path); + dir[slash - path] = 0; + + strcpy(fname, slash + 1); + + if (dot) + { + fname[dot - slash - 1] = 0; + strcpy(ext, dot + 1); + } + else + *ext = 0; + } +} + +void _makepath (char *path, const char * a, const char *dir, const char *fname, const char *ext) +{ + if (dir && *dir) + { + strcpy(path, dir); + strcat(path, SLASH_STR); + } + else + *path = 0; + + strcat(path, fname); + + if (ext && *ext) + { + strcat(path, "."); + strcat(path, ext); + } +} +#endif + diff --git a/libretro/libretro.h b/libretro/libretro.h new file mode 100644 index 0000000..4b5d71d --- /dev/null +++ b/libretro/libretro.h @@ -0,0 +1,277 @@ +#ifndef LIBRETRO_H__ +#define LIBRETRO_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#else +#if defined(_MSC_VER) && !defined(__cplusplus) +#define bool unsigned char +#define true 1 +#define false 0 +#else +#include +#endif +#endif + +#define RETRO_API_VERSION 1 + +#define RETRO_DEVICE_MASK 0xff +#define RETRO_DEVICE_NONE 0 +#define RETRO_DEVICE_JOYPAD 1 +#define RETRO_DEVICE_MOUSE 2 +#define RETRO_DEVICE_KEYBOARD 3 +#define RETRO_DEVICE_LIGHTGUN 4 + +#define RETRO_DEVICE_JOYPAD_MULTITAP ((1 << 8) | RETRO_DEVICE_JOYPAD) +#define RETRO_DEVICE_LIGHTGUN_SUPER_SCOPE ((1 << 8) | RETRO_DEVICE_LIGHTGUN) +#define RETRO_DEVICE_LIGHTGUN_JUSTIFIER ((2 << 8) | RETRO_DEVICE_LIGHTGUN) +#define RETRO_DEVICE_LIGHTGUN_JUSTIFIERS ((3 << 8) | RETRO_DEVICE_LIGHTGUN) + +#define RETRO_DEVICE_ID_JOYPAD_B 0 +#define RETRO_DEVICE_ID_JOYPAD_Y 1 +#define RETRO_DEVICE_ID_JOYPAD_SELECT 2 +#define RETRO_DEVICE_ID_JOYPAD_START 3 +#define RETRO_DEVICE_ID_JOYPAD_UP 4 +#define RETRO_DEVICE_ID_JOYPAD_DOWN 5 +#define RETRO_DEVICE_ID_JOYPAD_LEFT 6 +#define RETRO_DEVICE_ID_JOYPAD_RIGHT 7 +#define RETRO_DEVICE_ID_JOYPAD_A 8 +#define RETRO_DEVICE_ID_JOYPAD_X 9 +#define RETRO_DEVICE_ID_JOYPAD_L 10 +#define RETRO_DEVICE_ID_JOYPAD_R 11 + +#define RETRO_DEVICE_ID_MOUSE_X 0 +#define RETRO_DEVICE_ID_MOUSE_Y 1 +#define RETRO_DEVICE_ID_MOUSE_LEFT 2 +#define RETRO_DEVICE_ID_MOUSE_RIGHT 3 + +#define RETRO_DEVICE_ID_LIGHTGUN_X 0 +#define RETRO_DEVICE_ID_LIGHTGUN_Y 1 +#define RETRO_DEVICE_ID_LIGHTGUN_TRIGGER 2 +#define RETRO_DEVICE_ID_LIGHTGUN_CURSOR 3 +#define RETRO_DEVICE_ID_LIGHTGUN_TURBO 4 +#define RETRO_DEVICE_ID_LIGHTGUN_PAUSE 5 +#define RETRO_DEVICE_ID_LIGHTGUN_START 6 + +#define RETRO_REGION_NTSC 0 +#define RETRO_REGION_PAL 1 + +#define RETRO_MEMORY_MASK 0xff +#define RETRO_MEMORY_SAVE_RAM 0 +#define RETRO_MEMORY_RTC 1 +#define RETRO_MEMORY_SYSTEM_RAM 2 +#define RETRO_MEMORY_VIDEO_RAM 3 + +#define RETRO_MEMORY_SNES_BSX_RAM ((1 << 8) | RETRO_MEMORY_SAVE_RAM) +#define RETRO_MEMORY_SNES_BSX_PRAM ((2 << 8) | RETRO_MEMORY_SAVE_RAM) +#define RETRO_MEMORY_SNES_SUFAMI_TURBO_A_RAM ((3 << 8) | RETRO_MEMORY_SAVE_RAM) +#define RETRO_MEMORY_SNES_SUFAMI_TURBO_B_RAM ((4 << 8) | RETRO_MEMORY_SAVE_RAM) +#define RETRO_MEMORY_SNES_GAME_BOY_RAM ((5 << 8) | RETRO_MEMORY_SAVE_RAM) +#define RETRO_MEMORY_SNES_GAME_BOY_RTC ((6 << 8) | RETRO_MEMORY_RTC) + +#define RETRO_GAME_TYPE_BSX 0x101 +#define RETRO_GAME_TYPE_BSX_SLOTTED 0x102 +#define RETRO_GAME_TYPE_SUFAMI_TURBO 0x103 +#define RETRO_GAME_TYPE_SUPER_GAME_BOY 0x104 + + +// Environment commands. +#define RETRO_ENVIRONMENT_SET_ROTATION 1 // const unsigned * -- + // Sets screen rotation of graphics. + // Is only implemented if rotation can be accelerated by hardware. + // Valid values are 0, 1, 2, 3, which rotates screen by 0, 90, 180, 270 degrees + // counter-clockwise respectively. + // +#define RETRO_ENVIRONMENT_GET_OVERSCAN 2 // bool * -- + // Boolean value whether or not the implementation should use overscan, or crop away overscan. + // +#define RETRO_ENVIRONMENT_GET_CAN_DUPE 3 // bool * -- + // Boolean value whether or not RetroArch supports frame duping, + // passing NULL to video frame callback. + // +#define RETRO_ENVIRONMENT_GET_VARIABLE 4 // struct retro_variable * -- + // Interface to aquire user-defined information from environment + // that cannot feasibly be supported in a multi-system way. + // Mostly used for obscure, + // specific features that the user can tap into when neseccary. + // +#define RETRO_ENVIRONMENT_SET_VARIABLES 5 // const struct retro_variable * -- + // Allows an implementation to signal the environment + // which variables it might want to check for later using GET_VARIABLE. + // 'data' points to an array of retro_variable structs terminated by a { NULL, NULL } element. + // retro_variable::value should contain a human readable description of the key. + // +#define RETRO_ENVIRONMENT_SET_MESSAGE 6 // const struct retro_message * -- + // Sets a message to be displayed in implementation-specific manner for a certain amount of 'frames'. + // Should not be used for trivial messages, which should simply be logged to stderr. + +struct retro_message +{ + const char *msg; // Message to be displayed. + unsigned frames; // Duration in frames of message. +}; + +struct retro_system_info +{ + const char *library_name; // Descriptive name of library. Should not contain any version numbers, etc. + const char *library_version; // Descriptive version of core. + + const char *valid_extensions; // A string listing probably rom extensions the core will be able to load, separated with pipe. + // I.e. "bin|rom|iso". + // Typically used for a GUI to filter out extensions. + + bool need_fullpath; // If true, retro_load_game() is guaranteed to provide a valid pathname in retro_game_info::path. + // ::data and ::size are both invalid. + // If false, ::data and ::size are guaranteed to be valid, but ::path might not be valid. + // This is typically set to true for libretro implementations that must load from file. + // Implementations should strive for setting this to false, as it allows the frontend to perform patching, etc. + + bool block_extract; // If true, the frontend is not allowed to extract any archives before loading the real ROM. + // Necessary for certain libretro implementations that load games from zipped archives. +}; + +struct retro_game_geometry +{ + unsigned base_width; // Nominal video width of game. + unsigned base_height; // Nominal video height of game. + unsigned max_width; // Maximum possible width of game. + unsigned max_height; // Maximum possible height of game. + + float aspect_ratio; // Nominal aspect ratio of game. If aspect_ratio is <= 0.0, + // an aspect ratio of base_width / base_height is assumed. + // A frontend could override this setting if desired. +}; + +struct retro_system_timing +{ + double fps; // FPS of video content. + double sample_rate; // Sampling rate of audio. +}; + +struct retro_system_av_info +{ + struct retro_game_geometry geometry; + struct retro_system_timing timing; +}; + +struct retro_variable +{ + const char *key; // Variable to query in RETRO_ENVIRONMENT_GET_VARIABLE. + // If NULL, obtains the complete environment string if more complex parsing is necessary. + // The environment string is formatted as key-value pairs delimited by semicolons as so: + // "key1=value1;key2=value2;..." + const char *value; // Value to be obtained. If key does not exist, it is set to NULL. +}; + +struct retro_game_info +{ + const char *path; // Path to game, UTF-8 encoded. Usually used as a reference. + // May be NULL if rom was loaded from stdin or similar. + // retro_system_info::need_fullpath guaranteed that this path is valid. + const void *data; // Memory buffer of loaded game. Will be NULL if need_fullpath was set. + size_t size; // Size of memory buffer. + const char *meta; // String of implementation specific meta-data. +}; + +// Callbacks +// +// Environment callback. Gives implementations a way of performing uncommon tasks. Extensible. +typedef bool (*retro_environment_t)(unsigned cmd, void *data); + +// Render a frame. Pixel format is 15-bit XRGB1555 native endian. +// Width and height specify dimensions of buffer. +// Pitch specifices length in bytes between two lines in buffer. +typedef void (*retro_video_refresh_t)(const void *data, unsigned width, unsigned height, size_t pitch); + +// Renders a single audio frame. Should only be used if implementation generates a single sample at a time. +// Format is signed 16-bit native endian. +typedef void (*retro_audio_sample_t)(int16_t left, int16_t right); +// Renders multiple audio frames in one go. One frame is defined as a sample of left and right channels, interleaved. +// I.e. int16_t buf[4] = { l, r, l, r }; would be 2 frames. +// Only one of the audio callbacks must ever be used. +typedef size_t (*retro_audio_sample_batch_t)(const int16_t *data, size_t frames); + +// Polls input. +typedef void (*retro_input_poll_t)(void); +// Queries for input for player 'port'. device will be masked with RETRO_DEVICE_MASK. +// Specialization of devices such as RETRO_DEVICE_JOYPAD_MULTITAP that have been set with retro_set_controller_port_device() +// will still use the higher level RETRO_DEVICE_JOYPAD to request input. +typedef int16_t (*retro_input_state_t)(unsigned port, unsigned device, unsigned index, unsigned id); + +// Sets callbacks. retro_set_environment() is guaranteed to be called before retro_init(). +// The rest of the set_* functions are guaranteed to have been called before the first call to retro_run() is made. +void retro_set_environment(retro_environment_t); +void retro_set_video_refresh(retro_video_refresh_t); +void retro_set_audio_sample(retro_audio_sample_t); +void retro_set_audio_sample_batch(retro_audio_sample_batch_t); +void retro_set_input_poll(retro_input_poll_t); +void retro_set_input_state(retro_input_state_t); + +// Library global initialization/deinitialization. +void retro_init(void); +void retro_deinit(void); + +// Must return RETRO_API_VERSION. Used to validate ABI compatibility when the API is revised. +unsigned retro_api_version(void); + +// Gets statically known system info. Pointers provided in *info must be statically allocated. +// Can be called at any time, even before retro_init(). +void retro_get_system_info(struct retro_system_info *info); + +// Gets information about system audio/video timings and geometry. +// Can be called only after retro_load_game() has successfully completed. +void retro_get_system_av_info(struct retro_system_av_info *info); + +// Sets device to be used for player 'port'. +void retro_set_controller_port_device(unsigned port, unsigned device); + +// Resets the current game. +void retro_reset(void); + +// Runs the game for one video frame. +// During retro_run(), input_poll callback must be called at least once. +// +// If a frame is not rendered for reasons where a game "dropped" a frame, +// this still counts as a frame, and retro_run() should explicitly dupe a frame if GET_CAN_DUPE returns true. +// In this case, the video callback can take a NULL argument for data. +void retro_run(void); + +// Returns the amount of data the implementation requires to serialize internal state (save states). +// Beetween calls to retro_load_game() and retro_unload_game(), the returned size is never allowed to be larger than a previous returned value, to +// ensure that the frontend can allocate a save state buffer once. +size_t retro_serialize_size(void); + +// Serializes internal state. If failed, or size is lower than retro_serialize_size(), it should return false, true otherwise. +bool retro_serialize(void *data, size_t size); +bool retro_unserialize(const void *data, size_t size); + +void retro_cheat_reset(void); +void retro_cheat_set(unsigned index, bool enabled, const char *code); + +// Loads a game. +bool retro_load_game(const struct retro_game_info *game); + +// Loads a "special" kind of game. Should not be used except in extreme cases. +bool retro_load_game_special( + unsigned game_type, + const struct retro_game_info *info, size_t num_info +); + +// Unloads a currently loaded game. +void retro_unload_game(void); + +// Gets region of game. +unsigned retro_get_region(void); + +// Gets region of memory. +void *retro_get_memory_data(unsigned id); +size_t retro_get_memory_size(unsigned id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libretro/link.T b/libretro/link.T new file mode 100644 index 0000000..b0c262d --- /dev/null +++ b/libretro/link.T @@ -0,0 +1,5 @@ +{ + global: retro_*; + local: *; +}; + diff --git a/libretro/memstream.c b/libretro/memstream.c new file mode 100644 index 0000000..4e1ac21 --- /dev/null +++ b/libretro/memstream.c @@ -0,0 +1,115 @@ +#include +#include +#include +#include "memstream.h" + +static uint8_t* g_buffer = NULL; +static size_t g_size = 0; + +static size_t last_file_size = 0; + +struct memstream +{ + uint8_t *m_buf; + size_t m_size; + size_t m_ptr; +}; + +void memstream_set_buffer(uint8_t *buffer, size_t size) +{ + g_buffer = buffer; + g_size = size; +} + +size_t memstream_get_last_size() +{ + return last_file_size; +} + +static void memstream_init(memstream_t *stream, uint8_t *buffer, size_t max_size) +{ + stream->m_buf = buffer; + stream->m_size = max_size; + stream->m_ptr = 0; +} + +memstream_t *memstream_open() +{ + memstream_t *stream; + if (!g_buffer || !g_size) + return NULL; + + stream = (memstream_t*)calloc(1, sizeof(*stream)); + memstream_init(stream, g_buffer, g_size); + + g_buffer = NULL; + g_size = 0; + return stream; +} + +void memstream_close(memstream_t *stream) +{ + last_file_size = stream->m_ptr; + free(stream); +} + +size_t memstream_read(memstream_t *stream, void *data, size_t bytes) +{ + size_t avail = stream->m_size - stream->m_ptr; + if (bytes > avail) + bytes = avail; + + memcpy(data, stream->m_buf + stream->m_ptr, bytes); + stream->m_ptr += bytes; + return bytes; +} + +size_t memstream_write(memstream_t *stream, const void *data, size_t bytes) +{ + size_t avail = stream->m_size - stream->m_ptr; + if (bytes > avail) + bytes = avail; + + memcpy(stream->m_buf + stream->m_ptr, data, bytes); + stream->m_ptr += bytes; + return bytes; +} + +int memstream_seek(memstream_t *stream, int offset, int whence) +{ + size_t ptr; + if (whence == SEEK_SET) + ptr = offset; + else if (whence == SEEK_CUR) + ptr = stream->m_ptr + offset; + else if (whence == SEEK_END) + ptr = stream->m_size + offset; + else + return -1; + + if (ptr <= stream->m_size) + { + stream->m_ptr = ptr; + return 0; + } + else + return -1; +} + +size_t memstream_pos(memstream_t *stream) +{ + return stream->m_ptr; +} + +char *memstream_gets(memstream_t *stream, char *buffer, size_t len) +{ + return NULL; +} + +int memstream_getc(memstream_t *stream) +{ + if (stream->m_ptr >= stream->m_size) + return EOF; + else + return stream->m_buf[stream->m_ptr++]; +} diff --git a/libretro/memstream.h b/libretro/memstream.h new file mode 100644 index 0000000..812086a --- /dev/null +++ b/libretro/memstream.h @@ -0,0 +1,30 @@ +#ifndef __MEMSTREAM_H +#define __MEMSTREAM_H + +#include +#include + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +typedef struct memstream memstream_t; + +memstream_t *memstream_open(); +void memstream_close(memstream_t * stream); + +size_t memstream_read(memstream_t * stream, void *data, size_t bytes); +size_t memstream_write(memstream_t * stream, const void *data, size_t bytes); +int memstream_getc(memstream_t * stream); +char *memstream_gets(memstream_t * stream, char *buffer, size_t len); +size_t memstream_pos(memstream_t * stream); +int memstream_seek(memstream_t * stream, int offset, int whence); + +void memstream_set_buffer(uint8_t *buffer, size_t size); +size_t memstream_get_last_size(); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif -- cgit v1.2.3