summaryrefslogtreecommitdiff
path: root/libretro
diff options
context:
space:
mode:
authorToad King2012-06-14 03:23:47 -0400
committerToad King2012-06-14 03:23:47 -0400
commite8bad8c3ca33d6dfa672f2e570eddc3ab4b3a7e6 (patch)
tree3b5ee797171edafe59bc22ec1523554d81bc448e /libretro
parent6fb0c7a7a53e1eba7a0f5dc5b1ade312a0d76119 (diff)
downloadsnes9x2002-e8bad8c3ca33d6dfa672f2e570eddc3ab4b3a7e6.tar.gz
snes9x2002-e8bad8c3ca33d6dfa672f2e570eddc3ab4b3a7e6.tar.bz2
snes9x2002-e8bad8c3ca33d6dfa672f2e570eddc3ab4b3a7e6.zip
initial libretro changes
Diffstat (limited to 'libretro')
-rw-r--r--libretro/libretro.cpp616
-rw-r--r--libretro/libretro.h277
-rw-r--r--libretro/link.T5
-rw-r--r--libretro/memstream.c115
-rw-r--r--libretro/memstream.h30
5 files changed, 1043 insertions, 0 deletions
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 <stdio.h>
+#include <stdint.h>
+#ifndef _MSC_VER
+#include <stdbool.h>
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#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 <stdint.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#else
+#if defined(_MSC_VER) && !defined(__cplusplus)
+#define bool unsigned char
+#define true 1
+#define false 0
+#else
+#include <stdbool.h>
+#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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#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 <stddef.h>
+#include <stdint.h>
+
+#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