From dee8895db2ee59da1abe491228a029f9f28916be Mon Sep 17 00:00:00 2001 From: OV2 Date: Mon, 2 Jan 2012 01:48:51 +0100 Subject: [PATCH] Allow membased rom/state load/save, add libsnes interface (Themaister) --- apu/SNES_SPC.h | 4 + conffile.cpp | 10 +- conffile.h | 3 +- gtk/Makefile.am | 2 +- libsnes/Makefile | 53 + libsnes/libsnes-win32.vcproj | 794 ++++++++++++++ libsnes/libsnes.cpp | 754 ++++++++++++++ libsnes/libsnes.def | 47 + libsnes/libsnes.hpp | 1264 +++++++++++++++++++++++ libsnes/link.T | 4 + loadzip.cpp | 11 +- macosx/snes9x.xcodeproj/project.pbxproj | 32 +- memmap.cpp | 191 ++-- memmap.h | 9 +- netplay.cpp | 3 +- port.h | 42 +- server.cpp | 3 +- snapshot.cpp | 23 + snapshot.h | 3 + snes9x.cpp | 8 +- snes9x.h | 53 +- reader.cpp => stream.cpp | 281 ++++- reader.h => stream.h | 97 +- unix/Makefile.in | 2 +- win32/snes9xw.vcproj | 72 +- 25 files changed, 3527 insertions(+), 238 deletions(-) create mode 100644 libsnes/Makefile create mode 100644 libsnes/libsnes-win32.vcproj create mode 100644 libsnes/libsnes.cpp create mode 100644 libsnes/libsnes.def create mode 100644 libsnes/libsnes.hpp create mode 100644 libsnes/link.T rename reader.cpp => stream.cpp (64%) rename reader.h => stream.h (73%) diff --git a/apu/SNES_SPC.h b/apu/SNES_SPC.h index baf90063..9655c929 100644 --- a/apu/SNES_SPC.h +++ b/apu/SNES_SPC.h @@ -120,6 +120,8 @@ public: uint8_t dsp_reg_value( int, int ); int dsp_envx_value( int ); + uint8_t *apuram(); + //// Snes9x Debugger #ifdef DEBUGGER @@ -313,4 +315,6 @@ inline bool SNES_SPC::check_kon() { return dsp.check_kon(); } inline void SNES_SPC::spc_allow_time_overflow( bool allow ) { allow_time_overflow = allow; } +inline SNES_SPC::uint8_t * SNES_SPC::apuram() { return m.ram.ram; } + #endif diff --git a/conffile.cpp b/conffile.cpp index b7f60298..0582a9da 100644 --- a/conffile.cpp +++ b/conffile.cpp @@ -209,16 +209,16 @@ void ConfigFile::Clear(void){ } bool ConfigFile::LoadFile(const char *filename){ - STREAM s; + FSTREAM s; bool ret=false; const char *n, *n2; - if((s=OPEN_STREAM(filename, "r"))){ + if((s=OPEN_FSTREAM(filename, "r"))){ n=filename; n2=strrchr(n, '/'); if(n2!=NULL) n=n2+1; n2=strrchr(n, '\\'); if(n2!=NULL) n=n2+1; - LoadFile(new fReader(s), n); - CLOSE_STREAM(s); + LoadFile(new fStream(s), n); + CLOSE_FSTREAM(s); ret = true; } else { fprintf(stderr, "Couldn't open conffile "); @@ -228,7 +228,7 @@ bool ConfigFile::LoadFile(const char *filename){ } -void ConfigFile::LoadFile(Reader *r, const char *name){ +void ConfigFile::LoadFile(Stream *r, const char *name){ curConfigFile = this; string l, key, val; string section; diff --git a/conffile.h b/conffile.h index 24c8c20e..54339fd0 100644 --- a/conffile.h +++ b/conffile.h @@ -188,7 +188,6 @@ #include "unzip/unzip.h" #endif #include "snes9x.h" -#include "reader.h" #ifndef MAX # define MAX(a,b) ((a) > (b)? (a) : (b)) @@ -203,7 +202,7 @@ class ConfigFile { // return false on failure bool LoadFile(const char *filename); - void LoadFile(Reader *r, const char *name=NULL); + void LoadFile(Stream *r, const char *name=NULL); // return false if key does not exist or is empty bool Exists(const char *key); diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 14d99899..f5f08f45 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -157,7 +157,7 @@ snes9x_gtk_SOURCES += \ ../dma.cpp \ ../snes9x.cpp \ ../globals.cpp \ - ../reader.cpp \ + ../stream.cpp \ ../conffile.cpp \ ../bsx.cpp \ ../logger.cpp \ diff --git a/libsnes/Makefile b/libsnes/Makefile new file mode 100644 index 00000000..b72f8819 --- /dev/null +++ b/libsnes/Makefile @@ -0,0 +1,53 @@ +ifeq ($(platform),) +platform = unix +ifeq ($(shell uname -a),) + platform = win +else ifneq ($(findstring MINGW,$(shell uname -a)),) + platform = win +else ifneq ($(findstring win,$(shell uname -a)),) + platform = win +else ifneq ($(findstring Darwin,$(shell uname -a)),) + platform = osx +endif +endif + +ifeq ($(platform), unix) + TARGET := libsnes.so + fpic := -fPIC + SHARED := -shared -Wl,--version-script=link.T +else ifeq ($(platform), osx) + TARGET := libsnes.dylib + fpic := -fPIC + SHARED := -dynamiclib +else + TARGET := snes.dll + CC = gcc + CXX = g++ + SHARED := -shared -static-libgcc -static-libstdc++ -s -Wl,--version-script=link.T + CXXFLAGS += -D__WIN32__ -D__WIN32_LIBSNES__ +endif + +OBJECTS = ../apu/apu.o ../apu/SNES_SPC.o ../apu/SNES_SPC_misc.o ../apu/SNES_SPC_state.o ../apu/SPC_DSP.o ../apu/SPC_Filter.o ../bsx.o ../c4.o ../c4emu.o ../cheats.o ../cheats2.o ../clip.o ../conffile.o ../controls.o ../cpu.o ../cpuexec.o ../cpuops.o ../crosshairs.o ../dma.o ../dsp.o ../dsp1.o ../dsp2.o ../dsp3.o ../dsp4.o ../fxinst.o ../fxemu.o ../gfx.o ../globals.o ../logger.o ../memmap.o ../movie.o ../obc1.o ../ppu.o ../stream.o ../sa1.o ../sa1cpu.o ../screenshot.o ../sdd1.o ../sdd1emu.o ../seta.o ../seta010.o ../seta011.o ../seta018.o ../snapshot.o ../snes9x.o ../spc7110.o ../srtc.o ../tile.o libsnes.o + +CXX = g++ +CC = gcc +INCLUDES = -I. -I.. -I../apu/ + +CXXFLAGS += -O3 -fomit-frame-pointer -fno-exceptions -fno-rtti -pedantic -Wall -W -Wno-unused-parameter $(fpic) +CXXFLAGS += -DHAVE_STRINGS_H -DHAVE_STDINT_H -DRIGHTSHIFT_IS_SAR +CFLAGS = $(CXXFLAGS) + +all: $(TARGET) + +$(TARGET): $(OBJECTS) + $(CXX) $(fpic) $(SHARED) $(INCLUDES) -o $@ $(OBJECTS) -lm + +%.o: %.cpp + $(CXX) $(INCLUDES) $(CXXFLAGS) -c -o $@ $< + +%.o: %.c + $(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $< + +clean: + rm -f $(OBJECTS) $(TARGET) + diff --git a/libsnes/libsnes-win32.vcproj b/libsnes/libsnes-win32.vcproj new file mode 100644 index 00000000..a278a6d6 --- /dev/null +++ b/libsnes/libsnes-win32.vcproj @@ -0,0 +1,794 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libsnes/libsnes.cpp b/libsnes/libsnes.cpp new file mode 100644 index 00000000..7c47ede1 --- /dev/null +++ b/libsnes/libsnes.cpp @@ -0,0 +1,754 @@ +#include "libsnes.hpp" + +#include "snes9x.h" +#include "memmap.h" +#include "srtc.h" +#include "apu/apu.h" +#include "gfx.h" +#include "snapshot.h" +#include "controls.h" +#include "cheats.h" +#include "movie.h" +#include "logger.h" +#include "display.h" +#include "conffile.h" +#include +#ifndef __WIN32__ +#include +#endif +#include +#include +#include + + +static snes_video_refresh_t s9x_video_cb = NULL; +static snes_audio_sample_t s9x_audio_cb = NULL; +static snes_input_poll_t s9x_poller_cb = NULL; +static snes_input_state_t s9x_input_state_cb = NULL; + +void snes_set_video_refresh(snes_video_refresh_t cb) +{ + s9x_video_cb = cb; +} + +void snes_set_audio_sample(snes_audio_sample_t cb) +{ + s9x_audio_cb = cb; +} + +void snes_set_input_poll(snes_input_poll_t cb) +{ + s9x_poller_cb = cb; +} + +void snes_set_input_state(snes_input_state_t cb) +{ + s9x_input_state_cb = cb; +} + +static snes_environment_t environ_cb; +static bool use_overscan; +void snes_set_environment(snes_environment_t cb) +{ + environ_cb = cb; +} + +static void S9xAudioCallback(void*) +{ + // Just pick a big buffer. We won't use it all. + static int16_t audio_buf[0x10000]; + + S9xFinalizeSamples(); + size_t avail = S9xGetSampleCount(); + S9xMixSamples((uint8*)audio_buf, avail); + for (size_t i = 0; i < avail; i+=2) + s9x_audio_cb((uint16_t)audio_buf[i], (uint16_t)audio_buf[i + 1]); +} + +const char *snes_library_id() +{ + return "SNES9x v" VERSION; +} + +unsigned snes_library_revision_major() +{ + return 1; +} + +unsigned snes_library_revision_minor() +{ + return 3; +} + +void snes_power() +{ + S9xReset(); +} + +void snes_reset() +{ + S9xMovieUpdateOnReset(); + if (S9xMoviePlaying()) + { + S9xMovieStop(true); + } + S9xSoftReset(); +} + +static unsigned snes_devices[2]; +void snes_set_controller_port_device(bool in_port, unsigned device) +{ + int port = in_port == SNES_PORT_1 ? 0 : 1; + switch (device) + { + case SNES_DEVICE_JOYPAD: + S9xSetController(port, CTL_JOYPAD, 0, 0, 0, 0); + snes_devices[port] = SNES_DEVICE_JOYPAD; + break; + case SNES_DEVICE_MULTITAP: + S9xSetController(port, CTL_MP5, 1, 2, 3, 4); + snes_devices[port] = SNES_DEVICE_MULTITAP; + break; + case SNES_DEVICE_MOUSE: + S9xSetController(port, CTL_MOUSE, 0, 0, 0, 0); + snes_devices[port] = SNES_DEVICE_MOUSE; + break; + case SNES_DEVICE_SUPER_SCOPE: + S9xSetController(port, CTL_SUPERSCOPE, 0, 0, 0, 0); + snes_devices[port] = SNES_DEVICE_SUPER_SCOPE; + break; + case SNES_DEVICE_JUSTIFIER: + S9xSetController(port, CTL_JUSTIFIER, 0, 0, 0, 0); + snes_devices[port] = SNES_DEVICE_JUSTIFIER; + break; + case SNES_DEVICE_JUSTIFIERS: + S9xSetController(port, CTL_JUSTIFIER, 1, 0, 0, 0); + snes_devices[port] = SNES_DEVICE_JUSTIFIERS; + break; + default: + fprintf(stderr, "[libsnes]: Invalid device!\n"); + } +} + +void snes_cheat_reset() +{} + +void snes_cheat_set(unsigned, bool, const char*) +{} + +bool snes_load_cartridge_bsx_slotted( + const char *, const uint8_t *, unsigned, + const char *, const uint8_t *, unsigned + ) +{ + return false; +} + +bool snes_load_cartridge_bsx( + const char *, const uint8_t *, unsigned, + const char *, const uint8_t *, unsigned + ) +{ + return false; +} + +bool snes_load_cartridge_sufami_turbo( + const char *, const uint8_t *, unsigned, + const char *, const uint8_t *, unsigned, + const char *, const uint8_t *, unsigned + ) +{ + return false; +} + +bool snes_load_cartridge_super_game_boy( + const char *, const uint8_t *, unsigned, + const char *, const uint8_t *, unsigned + ) +{ + return false; +} + +static void map_buttons(); + + +void snes_init() +{ + if (environ_cb) + { + if (!environ_cb(SNES_ENVIRONMENT_GET_OVERSCAN, &use_overscan)) + use_overscan = false; + + if (use_overscan) + { + snes_geometry geom = {256, 239, 512, 512}; + environ_cb(SNES_ENVIRONMENT_SET_GEOMETRY, &geom); + unsigned pitch = 1024; + environ_cb(SNES_ENVIRONMENT_SET_PITCH, &pitch); + } + } + + memset(&Settings, 0, sizeof(Settings)); + Settings.MouseMaster = TRUE; + Settings.SuperScopeMaster = TRUE; + Settings.JustifierMaster = TRUE; + Settings.MultiPlayer5Master = TRUE; + Settings.FrameTimePAL = 20000; + Settings.FrameTimeNTSC = 16667; + Settings.SixteenBitSound = TRUE; + Settings.Stereo = TRUE; + Settings.SoundPlaybackRate = 32000; + Settings.SoundInputRate = 32000; + Settings.SupportHiRes = TRUE; + Settings.Transparency = TRUE; + Settings.AutoDisplayMessages = TRUE; + Settings.InitialInfoStringTimeout = 120; + Settings.HDMATimingHack = 100; + Settings.BlockInvalidVRAMAccessMaster = TRUE; + Settings.StopEmulation = TRUE; + Settings.WrongMovieStateProtection = TRUE; + Settings.DumpStreamsMaxFrames = -1; + Settings.StretchScreenshots = 0; + Settings.SnapshotScreenshots = FALSE; + Settings.SkipFrames = AUTO_FRAMERATE; + Settings.TurboSkipFrames = 15; + Settings.CartAName[0] = 0; + Settings.CartBName[0] = 0; + Settings.AutoSaveDelay = 1; + + CPU.Flags = 0; + + if (!Memory.Init() || !S9xInitAPU()) + { + Memory.Deinit(); + S9xDeinitAPU(); + fprintf(stderr, "[libsnes]: Failed to init Memory or APU.\n"); + exit(1); + } + + S9xInitSound(16, 0); + S9xSetSoundMute(FALSE); + S9xSetSamplesAvailableCallback(S9xAudioCallback, NULL); + + S9xSetRenderPixelFormat(RGB555); + GFX.Pitch = use_overscan ? 1024 : 2048; + GFX.Screen = (uint16*) calloc(1, GFX.Pitch * 512 * sizeof(uint16)); + S9xGraphicsInit(); + + S9xInitInputDevices(); + for (int i = 0; i < 2; i++) + { + S9xSetController(i, CTL_JOYPAD, i, 0, 0, 0); + snes_devices[i] = SNES_DEVICE_JOYPAD; + } + + S9xUnmapAllControls(); + map_buttons(); + +} + +#define MAP_BUTTON(id, name) S9xMapButton((id), S9xGetCommandT((name)), false) +#define MAKE_BUTTON(pad, btn) (((pad)<<4)|(btn)) + +#define PAD_1 1 +#define PAD_2 2 +#define PAD_3 3 +#define PAD_4 4 +#define PAD_5 5 + +#define BTN_B SNES_DEVICE_ID_JOYPAD_B +#define BTN_Y SNES_DEVICE_ID_JOYPAD_Y +#define BTN_SELECT SNES_DEVICE_ID_JOYPAD_SELECT +#define BTN_START SNES_DEVICE_ID_JOYPAD_START +#define BTN_UP SNES_DEVICE_ID_JOYPAD_UP +#define BTN_DOWN SNES_DEVICE_ID_JOYPAD_DOWN +#define BTN_LEFT SNES_DEVICE_ID_JOYPAD_LEFT +#define BTN_RIGHT SNES_DEVICE_ID_JOYPAD_RIGHT +#define BTN_A SNES_DEVICE_ID_JOYPAD_A +#define BTN_X SNES_DEVICE_ID_JOYPAD_X +#define BTN_L SNES_DEVICE_ID_JOYPAD_L +#define BTN_R SNES_DEVICE_ID_JOYPAD_R +#define BTN_FIRST BTN_B +#define BTN_LAST BTN_R + +#define MOUSE_X SNES_DEVICE_ID_MOUSE_X +#define MOUSE_Y SNES_DEVICE_ID_MOUSE_Y +#define MOUSE_LEFT SNES_DEVICE_ID_MOUSE_LEFT +#define MOUSE_RIGHT SNES_DEVICE_ID_MOUSE_RIGHT +#define MOUSE_FIRST MOUSE_X +#define MOUSE_LAST MOUSE_RIGHT + +#define SCOPE_X SNES_DEVICE_ID_SUPER_SCOPE_X +#define SCOPE_Y SNES_DEVICE_ID_SUPER_SCOPE_Y +#define SCOPE_TRIGGER SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER +#define SCOPE_CURSOR SNES_DEVICE_ID_SUPER_SCOPE_CURSOR +#define SCOPE_TURBO SNES_DEVICE_ID_SUPER_SCOPE_TURBO +#define SCOPE_PAUSE SNES_DEVICE_ID_SUPER_SCOPE_PAUSE +#define SCOPE_FIRST SCOPE_X +#define SCOPE_LAST SCOPE_PAUSE + +#define JUSTIFIER_X SNES_DEVICE_ID_JUSTIFIER_X +#define JUSTIFIER_Y SNES_DEVICE_ID_JUSTIFIER_Y +#define JUSTIFIER_TRIGGER SNES_DEVICE_ID_JUSTIFIER_TRIGGER +#define JUSTIFIER_START SNES_DEVICE_ID_JUSTIFIER_START +#define JUSTIFIER_FIRST JUSTIFIER_X +#define JUSTIFIER_LAST JUSTIFIER_START + +#define BTN_POINTER (BTN_LAST + 1) +#define BTN_POINTER2 (BTN_POINTER + 1) + +static void map_buttons() +{ + MAP_BUTTON(MAKE_BUTTON(PAD_1, BTN_A), "Joypad1 A"); + MAP_BUTTON(MAKE_BUTTON(PAD_1, BTN_B), "Joypad1 B"); + MAP_BUTTON(MAKE_BUTTON(PAD_1, BTN_X), "Joypad1 X"); + MAP_BUTTON(MAKE_BUTTON(PAD_1, BTN_Y), "Joypad1 Y"); + MAP_BUTTON(MAKE_BUTTON(PAD_1, BTN_SELECT), "{Joypad1 Select,Mouse1 L}"); + MAP_BUTTON(MAKE_BUTTON(PAD_1, BTN_START), "{Joypad1 Start,Mouse1 R}"); + MAP_BUTTON(MAKE_BUTTON(PAD_1, BTN_L), "Joypad1 L"); + MAP_BUTTON(MAKE_BUTTON(PAD_1, BTN_R), "Joypad1 R"); + MAP_BUTTON(MAKE_BUTTON(PAD_1, BTN_LEFT), "Joypad1 Left"); + MAP_BUTTON(MAKE_BUTTON(PAD_1, BTN_RIGHT), "Joypad1 Right"); + MAP_BUTTON(MAKE_BUTTON(PAD_1, BTN_UP), "Joypad1 Up"); + MAP_BUTTON(MAKE_BUTTON(PAD_1, BTN_DOWN), "Joypad1 Down"); + S9xMapPointer((BTN_POINTER), S9xGetCommandT("Pointer Mouse1+Superscope+Justifier1"), false); + S9xMapPointer((BTN_POINTER2), S9xGetCommandT("Pointer Mouse2"), false); + + MAP_BUTTON(MAKE_BUTTON(PAD_2, BTN_A), "Joypad2 A"); + MAP_BUTTON(MAKE_BUTTON(PAD_2, BTN_B), "Joypad2 B"); + MAP_BUTTON(MAKE_BUTTON(PAD_2, BTN_X), "Joypad2 X"); + MAP_BUTTON(MAKE_BUTTON(PAD_2, BTN_Y), "Joypad2 Y"); + MAP_BUTTON(MAKE_BUTTON(PAD_2, BTN_SELECT), "{Joypad2 Select,Mouse2 L,Superscope Fire,Justifier1 Trigger}"); + MAP_BUTTON(MAKE_BUTTON(PAD_2, BTN_START), "{Joypad2 Start,Mouse2 R,Superscope Cursor,Justifier1 Start}"); + MAP_BUTTON(MAKE_BUTTON(PAD_2, BTN_L), "Joypad2 L"); + MAP_BUTTON(MAKE_BUTTON(PAD_2, BTN_R), "Joypad2 R"); + MAP_BUTTON(MAKE_BUTTON(PAD_2, BTN_LEFT), "Joypad2 Left"); + MAP_BUTTON(MAKE_BUTTON(PAD_2, BTN_RIGHT), "Joypad2 Right"); + MAP_BUTTON(MAKE_BUTTON(PAD_2, BTN_UP), "{Joypad2 Up,Superscope ToggleTurbo}"); + MAP_BUTTON(MAKE_BUTTON(PAD_2, BTN_DOWN), "{Joypad2 Down,Superscope Pause}"); + + MAP_BUTTON(MAKE_BUTTON(PAD_3, BTN_A), "Joypad3 A"); + MAP_BUTTON(MAKE_BUTTON(PAD_3, BTN_B), "Joypad3 B"); + MAP_BUTTON(MAKE_BUTTON(PAD_3, BTN_X), "Joypad3 X"); + MAP_BUTTON(MAKE_BUTTON(PAD_3, BTN_Y), "Joypad3 Y"); + MAP_BUTTON(MAKE_BUTTON(PAD_3, BTN_SELECT), "Joypad3 Select"); + MAP_BUTTON(MAKE_BUTTON(PAD_3, BTN_START), "Joypad3 Start"); + MAP_BUTTON(MAKE_BUTTON(PAD_3, BTN_L), "Joypad3 L"); + MAP_BUTTON(MAKE_BUTTON(PAD_3, BTN_R), "Joypad3 R"); + MAP_BUTTON(MAKE_BUTTON(PAD_3, BTN_LEFT), "Joypad3 Left"); + MAP_BUTTON(MAKE_BUTTON(PAD_3, BTN_RIGHT), "Joypad3 Right"); + MAP_BUTTON(MAKE_BUTTON(PAD_3, BTN_UP), "Joypad3 Up"); + MAP_BUTTON(MAKE_BUTTON(PAD_3, BTN_DOWN), "Joypad3 Down"); + + MAP_BUTTON(MAKE_BUTTON(PAD_4, BTN_A), "Joypad4 A"); + MAP_BUTTON(MAKE_BUTTON(PAD_4, BTN_B), "Joypad4 B"); + MAP_BUTTON(MAKE_BUTTON(PAD_4, BTN_X), "Joypad4 X"); + MAP_BUTTON(MAKE_BUTTON(PAD_4, BTN_Y), "Joypad4 Y"); + MAP_BUTTON(MAKE_BUTTON(PAD_4, BTN_SELECT), "Joypad4 Select"); + MAP_BUTTON(MAKE_BUTTON(PAD_4, BTN_START), "Joypad4 Start"); + MAP_BUTTON(MAKE_BUTTON(PAD_4, BTN_L), "Joypad4 L"); + MAP_BUTTON(MAKE_BUTTON(PAD_4, BTN_R), "Joypad4 R"); + MAP_BUTTON(MAKE_BUTTON(PAD_4, BTN_LEFT), "Joypad4 Left"); + MAP_BUTTON(MAKE_BUTTON(PAD_4, BTN_RIGHT), "Joypad4 Right"); + MAP_BUTTON(MAKE_BUTTON(PAD_4, BTN_UP), "Joypad4 Up"); + MAP_BUTTON(MAKE_BUTTON(PAD_4, BTN_DOWN), "Joypad4 Down"); + + MAP_BUTTON(MAKE_BUTTON(PAD_5, BTN_A), "Joypad5 A"); + MAP_BUTTON(MAKE_BUTTON(PAD_5, BTN_B), "Joypad5 B"); + MAP_BUTTON(MAKE_BUTTON(PAD_5, BTN_X), "Joypad5 X"); + MAP_BUTTON(MAKE_BUTTON(PAD_5, BTN_Y), "Joypad5 Y"); + MAP_BUTTON(MAKE_BUTTON(PAD_5, BTN_SELECT), "Joypad5 Select"); + MAP_BUTTON(MAKE_BUTTON(PAD_5, BTN_START), "Joypad5 Start"); + MAP_BUTTON(MAKE_BUTTON(PAD_5, BTN_L), "Joypad5 L"); + MAP_BUTTON(MAKE_BUTTON(PAD_5, BTN_R), "Joypad5 R"); + MAP_BUTTON(MAKE_BUTTON(PAD_5, BTN_LEFT), "Joypad5 Left"); + MAP_BUTTON(MAKE_BUTTON(PAD_5, BTN_RIGHT), "Joypad5 Right"); + MAP_BUTTON(MAKE_BUTTON(PAD_5, BTN_UP), "Joypad5 Up"); + MAP_BUTTON(MAKE_BUTTON(PAD_5, BTN_DOWN), "Joypad5 Down"); + +} + +// 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. :) +static int16_t snes_mouse_state[2][2] = {{0}, {0}}; +static int16_t snes_scope_state[2] = {0}; +static int16_t snes_justifier_state[2][2] = {{0}, {0}}; +static void report_buttons() +{ + int _x, _y; + for (int port = SNES_PORT_1; port <= SNES_PORT_2; port++) + { + switch (snes_devices[port]) + { + case SNES_DEVICE_JOYPAD: + for (int i = BTN_FIRST; i <= BTN_LAST; i++) + S9xReportButton(MAKE_BUTTON(port + 1, i), s9x_input_state_cb(port == SNES_PORT_2, SNES_DEVICE_JOYPAD, 0, i)); + break; + + case SNES_DEVICE_MULTITAP: + for (int j = 0; j < 4; j++) + for (int i = BTN_FIRST; i <= BTN_LAST; i++) + S9xReportButton(MAKE_BUTTON(j + 2, i), s9x_input_state_cb(port == SNES_PORT_2, SNES_DEVICE_MULTITAP, j, i)); + break; + + case SNES_DEVICE_MOUSE: + _x = s9x_input_state_cb(port == SNES_PORT_2, SNES_DEVICE_MOUSE, 0, SNES_DEVICE_ID_MOUSE_X); + _y = s9x_input_state_cb(port == SNES_PORT_2, SNES_DEVICE_MOUSE, 0, SNES_DEVICE_ID_MOUSE_Y); + snes_mouse_state[port][0] += _x; + snes_mouse_state[port][1] += _y; + S9xReportPointer(BTN_POINTER + port, snes_mouse_state[port][0], snes_mouse_state[port][1]); + for (int i = MOUSE_LEFT; i <= MOUSE_LAST; i++) + S9xReportButton(MAKE_BUTTON(port + 1, i), s9x_input_state_cb(port == SNES_PORT_2, SNES_DEVICE_MOUSE, 0, i)); + break; + + case SNES_DEVICE_SUPER_SCOPE: + snes_scope_state[0] += s9x_input_state_cb(port == SNES_PORT_2, SNES_DEVICE_SUPER_SCOPE, 0, SNES_DEVICE_ID_SUPER_SCOPE_X); + snes_scope_state[1] += s9x_input_state_cb(port == SNES_PORT_2, SNES_DEVICE_SUPER_SCOPE, 0, SNES_DEVICE_ID_SUPER_SCOPE_Y); + S9xReportPointer(BTN_POINTER, snes_scope_state[0], snes_scope_state[1]); + for (int i = SCOPE_TRIGGER; i <= SCOPE_LAST; i++) + S9xReportButton(MAKE_BUTTON(port + 1, i), s9x_input_state_cb(port == SNES_PORT_2, SNES_DEVICE_SUPER_SCOPE, 0, i)); + break; + + case SNES_DEVICE_JUSTIFIER: + case SNES_DEVICE_JUSTIFIERS: + snes_justifier_state[0][0] += s9x_input_state_cb(port == SNES_PORT_2, SNES_DEVICE_JUSTIFIER, 0, SNES_DEVICE_ID_JUSTIFIER_X); + snes_justifier_state[0][1] += s9x_input_state_cb(port == SNES_PORT_2, SNES_DEVICE_JUSTIFIER, 0, SNES_DEVICE_ID_JUSTIFIER_Y); + S9xReportPointer(BTN_POINTER, snes_justifier_state[0][0], snes_justifier_state[0][1]); + for (int i = JUSTIFIER_TRIGGER; i <= JUSTIFIER_LAST; i++) + S9xReportButton(MAKE_BUTTON(port + 1, i), s9x_input_state_cb(port == SNES_PORT_2, SNES_DEVICE_JUSTIFIER, 0, i)); + break; + + default: + fprintf(stderr, "[libsnes]: Unknown device...\n"); + + } + } +} + +bool snes_load_cartridge_normal(const char *, const uint8_t *rom_data, unsigned rom_size) +{ + int loaded = Memory.LoadROMMem(rom_data,rom_size); + if (!loaded) + { + fprintf(stderr, "[libsnes]: Rom loading failed...\n"); + return false; + } + + if (environ_cb) + { + snes_system_timing timing; + timing.sample_rate = 32040.5; + if (!Settings.PAL) + timing.fps = 21477272.0 / 357366.0; + else + timing.fps = 21281370.0 / 425568.0; + + environ_cb(SNES_ENVIRONMENT_SET_TIMING, &timing); + } + + return true; +} + +void snes_run() +{ + s9x_poller_cb(); + report_buttons(); + S9xMainLoop(); +} + +void snes_term() +{ + S9xDeinitAPU(); + Memory.Deinit(); + S9xGraphicsDeinit(); + S9xUnmapAllControls(); +} + + +bool snes_get_region() +{ + return Settings.PAL ? SNES_REGION_PAL : SNES_REGION_NTSC; +} + +uint8_t* snes_get_memory_data(unsigned type) +{ + uint8_t* data; + + switch(type) { + case SNES_MEMORY_CARTRIDGE_RAM: + data = Memory.SRAM; + break; + case SNES_MEMORY_CARTRIDGE_RTC: + data = RTCData.reg; + break; + case SNES_MEMORY_WRAM: + data = Memory.RAM; + break; + case SNES_MEMORY_APURAM: + data = spc_core->apuram(); + break; + case SNES_MEMORY_VRAM: + data = Memory.VRAM; + break; + case SNES_MEMORY_CGRAM: + data = (uint8_t*)PPU.CGDATA; + break; + case SNES_MEMORY_OAM: + data = PPU.OAMData; + break; + default: + data = NULL; + break; + } + + return data; +} + +void snes_unload_cartridge() +{ + +} + +unsigned snes_get_memory_size(unsigned type) +{ + unsigned size; + + switch(type) { + case SNES_MEMORY_CARTRIDGE_RAM: + size = (unsigned) (Memory.SRAMSize ? (1 << (Memory.SRAMSize + 3)) * 128 : 0); + if (size > 0x20000) + size = 0x20000; + break; + case SNES_MEMORY_CARTRIDGE_RTC: + size = (Settings.SRTC || Settings.SPC7110RTC)?20:0; + break; + case SNES_MEMORY_WRAM: + size = 128 * 1024; + break; + case SNES_MEMORY_VRAM: + case SNES_MEMORY_APURAM: + size = 64 * 1024; + break; + case SNES_MEMORY_CGRAM: + size = 512; + break; + case SNES_MEMORY_OAM: + size = 512 + 32; + break; + default: + size = 0; + break; + } + + return size; +} + +void snes_set_cartridge_basename(const char*) +{} + +unsigned snes_serialize_size() +{ + return S9xFreezeSize(); +} + +bool snes_serialize(uint8_t *data, unsigned size) +{ + if (S9xFreezeGameMem(data,size) == FALSE) + return false; + + return true; +} + +bool snes_unserialize(const uint8_t* data, unsigned size) +{ + if (S9xUnfreezeGameMem(data,size) != SUCCESS) + return false; + return true; +} + +// Pitch 2048 -> 1024, only done once per res-change. +static void pack_frame(uint16_t *frame, int width, int height) +{ + for (int y = 1; y < height; y++) + { + uint16_t *src = frame + y * 1024; + uint16_t *dst = frame + y * 512; + + memcpy(dst, src, width * sizeof(uint16_t)); + } +} + +// Pitch 1024 -> 2048, only done once per res-change. +static void stretch_frame(uint16_t *frame, int width, int height) +{ + for (int y = height - 1; y >= 0; y--) + { + uint16_t *src = frame + y * 512; + uint16_t *dst = frame + y * 1024; + + memcpy(dst, src, width * sizeof(uint16_t)); + } +} + +bool8 S9xDeinitUpdate(int width, int height) +{ + if (use_overscan) + { + if (height == 224) + { + memmove(GFX.Screen + (GFX.Pitch / 2) * 7, GFX.Screen, GFX.Pitch * height); + memset(GFX.Screen, 0x00, GFX.Pitch * 7); + memset(GFX.Screen + (GFX.Pitch / 2) * (7 + 224), 0, GFX.Pitch * 8); + height = 239; + } + else if (height == 448) + { + memmove(GFX.Screen + (GFX.Pitch / 2) * 15, GFX.Screen, GFX.Pitch * height); + memset(GFX.Screen, 0x00, GFX.Pitch * 15); + memset(GFX.Screen + (GFX.Pitch / 2) * (15 + 224), 0x00, GFX.Pitch * 17); + height = 478; + } + } + else // libsnes classic behavior + { + if (height == 448 || height == 478) + { + if (GFX.Pitch == 2048) + pack_frame(GFX.Screen, width, height); + GFX.Pitch = 1024; + } + else + { + if (GFX.Pitch == 1024) + stretch_frame(GFX.Screen, width, height); + GFX.Pitch = 2048; + } + } + + s9x_video_cb(GFX.Screen, width, height); + return TRUE; +} + +bool8 S9xContinueUpdate(int width, int height) +{ + return S9xDeinitUpdate(width, height); +} + + +// Dummy functions that should probably be implemented correctly later. +void S9xParsePortConfig(ConfigFile&, int) {} +void S9xSyncSpeed() {} +//void S9xPollPointer(int, short*, short*) {} +const char* S9xStringInput(const char* in) { return in; } +const char* S9xGetFilename(const char* in, s9x_getdirtype) { return in; } +const char* S9xGetDirectory(s9x_getdirtype) { return NULL; } +void S9xInitInputDevices() {} +const char* S9xChooseFilename(unsigned char) { return NULL; } +void S9xHandlePortCommand(s9xcommand_t, short, short) {} +bool S9xPollButton(unsigned int, bool*) { return false; } +void S9xToggleSoundChannel(int) {} +const char* S9xGetFilenameInc(const char* in, s9x_getdirtype) { return NULL; } +const char* S9xBasename(const char* in) { return in; } +bool8 S9xInitUpdate() { return TRUE; } +void S9xExtraUsage() {} +bool8 S9xOpenSoundDevice() { return TRUE; } +void S9xMessage(int, int, const char*) {} +bool S9xPollAxis(unsigned int, short*) { return FALSE; } +void S9xSetPalette() {} +void S9xParseArg(char**, int&, int) {} +void S9xExit() {} +bool S9xPollPointer(unsigned int, short*, short*) { return false; } +const char *S9xChooseMovieFilename(unsigned char) { return NULL; } + +bool8 S9xOpenSnapshotFile(const char* filepath, bool8 read_only, STREAM *file) +{ + if(read_only) + { + if((*file = OPEN_STREAM(filepath, "rb")) != 0) + { + return (TRUE); + } + } + else + { + if((*file = OPEN_STREAM(filepath, "wb")) != 0) + { + return (TRUE); + } + } + return (FALSE); +} + +void S9xCloseSnapshotFile(STREAM file) +{ + CLOSE_STREAM(file); +} + +void S9xAutoSaveSRAM() +{ + return; +} + +#ifndef __WIN32__ +// S9x weirdness. +void _splitpath (const char *path, char *drive, char *dir, char *fname, char *ext) +{ + *drive = 0; + + const char *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 *, 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 // __WIN32__ diff --git a/libsnes/libsnes.def b/libsnes/libsnes.def new file mode 100644 index 00000000..c1017351 --- /dev/null +++ b/libsnes/libsnes.def @@ -0,0 +1,47 @@ +LIBRARY snes + +EXPORTS + +snes_library_id + +snes_library_revision_major +snes_library_revision_minor + +snes_set_video_refresh +snes_set_audio_sample +snes_set_input_poll +snes_set_input_state + +snes_set_environment + +snes_set_controller_port_device +snes_set_cartridge_basename + +snes_init +snes_term +snes_power +snes_reset +snes_run + +snes_serialize_size +snes_serialize +snes_unserialize + +snes_cheat_reset +snes_cheat_set + +snes_load_cartridge_normal + +snes_load_cartridge_bsx_slotted + +snes_load_cartridge_bsx + +snes_load_cartridge_sufami_turbo + +snes_load_cartridge_super_game_boy + +snes_unload_cartridge + +snes_get_region +snes_get_memory_data +snes_get_memory_size \ No newline at end of file diff --git a/libsnes/libsnes.hpp b/libsnes/libsnes.hpp new file mode 100644 index 00000000..78a49816 --- /dev/null +++ b/libsnes/libsnes.hpp @@ -0,0 +1,1264 @@ +#ifndef LIBSNES_HPP +#define LIBSNES_HPP + +#include "port.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/////////////////////////////////////////////////////////////////////////////// +// LIBSNES Super Nintendo emulation API +// +// Things you should know: +// - Linking against libsnes requires a C++ compiler. It can be compiled with +// a C99 compiler if you #include and if your C99 compiler's +// bool type is compatible with the bool type used by the C++ compiler used +// to compile libsnes. +// - libsnes supports exactly one emulated SNES; if you want to run two SNESes +// in a single process, you'll need to link against or dlopen() two +// different copies of the library. +// +// Typical usage of the libsnes API looks like this: +// +// 1. Call snes_init() to initialize the library. +// 2. Tell libsnes which callback should be called for each event (see the +// documentation on the individual callback types below. +// 3. Call one of the snes_load_cartridge_* functions to load cartridge data +// into the emulated SNES. +// 4. If the physical cart had any non-volatile storage, there may be data from +// a previous emulation run that needs to be loaded. Find the storage buffer +// by calling the snes_get_memory_* functions and load any saved data into +// it. +// 5. Call snes_set_controller_port_device() to connect appropriate controllers +// to the emulated SNES. +// 6. Call snes_get_region() to determine the intended screen refresh rate for +// this cartridge.. +// 7. Call snes_run() to emulate a single frame. Before snes_run() returns, the +// installed callbacks will be called - possibly multiple times. +// 8. When you're done, call snes_term() to free all memory allocated +// associated with the emulated SNES. +// +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Constants {{{ + +// These constants represent the two controller ports on the front of the SNES, +// for use with the snes_set_controller_port_device() function and the +// snes_input_state_t callback. +#define SNES_PORT_1 0 +#define SNES_PORT_2 1 + +// These constants represent the different kinds of controllers that can be +// connected to a controller port, for use with the +// snes_set_controller_port_device() function and the snes_input_state_t +// callback. +#define SNES_DEVICE_NONE 0 +#define SNES_DEVICE_JOYPAD 1 +#define SNES_DEVICE_MULTITAP 2 +#define SNES_DEVICE_MOUSE 3 +#define SNES_DEVICE_SUPER_SCOPE 4 +#define SNES_DEVICE_JUSTIFIER 5 +#define SNES_DEVICE_JUSTIFIERS 6 + +// These constants represent the button and axis inputs on various controllers, +// for use with the snes_input_state_t callback. +#define SNES_DEVICE_ID_JOYPAD_B 0 +#define SNES_DEVICE_ID_JOYPAD_Y 1 +#define SNES_DEVICE_ID_JOYPAD_SELECT 2 +#define SNES_DEVICE_ID_JOYPAD_START 3 +#define SNES_DEVICE_ID_JOYPAD_UP 4 +#define SNES_DEVICE_ID_JOYPAD_DOWN 5 +#define SNES_DEVICE_ID_JOYPAD_LEFT 6 +#define SNES_DEVICE_ID_JOYPAD_RIGHT 7 +#define SNES_DEVICE_ID_JOYPAD_A 8 +#define SNES_DEVICE_ID_JOYPAD_X 9 +#define SNES_DEVICE_ID_JOYPAD_L 10 +#define SNES_DEVICE_ID_JOYPAD_R 11 + +#define SNES_DEVICE_ID_MOUSE_X 0 +#define SNES_DEVICE_ID_MOUSE_Y 1 +#define SNES_DEVICE_ID_MOUSE_LEFT 2 +#define SNES_DEVICE_ID_MOUSE_RIGHT 3 + +#define SNES_DEVICE_ID_SUPER_SCOPE_X 0 +#define SNES_DEVICE_ID_SUPER_SCOPE_Y 1 +#define SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER 2 +#define SNES_DEVICE_ID_SUPER_SCOPE_CURSOR 3 +#define SNES_DEVICE_ID_SUPER_SCOPE_TURBO 4 +#define SNES_DEVICE_ID_SUPER_SCOPE_PAUSE 5 + +#define SNES_DEVICE_ID_JUSTIFIER_X 0 +#define SNES_DEVICE_ID_JUSTIFIER_Y 1 +#define SNES_DEVICE_ID_JUSTIFIER_TRIGGER 2 +#define SNES_DEVICE_ID_JUSTIFIER_START 3 + +// These constants will be returned by snes_get_region(), representing the +// region of the last loaded cartridge. +#define SNES_REGION_NTSC 0 +#define SNES_REGION_PAL 1 + +// These constants represent the kinds of non-volatile memory a SNES cartridge +// might have, for use with the snes_get_memory_* functions. +#define SNES_MEMORY_CARTRIDGE_RAM 0 +#define SNES_MEMORY_CARTRIDGE_RTC 1 +#define SNES_MEMORY_BSX_RAM 2 +#define SNES_MEMORY_BSX_PRAM 3 +#define SNES_MEMORY_SUFAMI_TURBO_A_RAM 4 +#define SNES_MEMORY_SUFAMI_TURBO_B_RAM 5 +#define SNES_MEMORY_GAME_BOY_RAM 6 +#define SNES_MEMORY_GAME_BOY_RTC 7 + +// These constants represent the various kinds of volatile storage the SNES +// offers, to allow libsnes clients to implement things like cheat-searching +// and certain kinds of debugging. They are for use with the snes_get_memory_* +// functions. +#define SNES_MEMORY_WRAM 100 +#define SNES_MEMORY_APURAM 101 +#define SNES_MEMORY_VRAM 102 +#define SNES_MEMORY_OAM 103 +#define SNES_MEMORY_CGRAM 104 + +// SSNES extension. Not required to be implemented for a working implementation. +#define SNES_ENVIRONMENT_GET_FULLPATH 0 // const char ** -- Full path of game loaded. +#define SNES_ENVIRONMENT_SET_GEOMETRY 1 // const struct snes_geometry * -- Window geometry information for the system/game. +#define SNES_ENVIRONMENT_SET_PITCH 2 // const unsigned * -- Pitch of game image. +#define SNES_ENVIRONMENT_GET_OVERSCAN 3 // bool * -- Boolean value whether or not the implementation should use overscan. +#define SNES_ENVIRONMENT_SET_TIMING 4 // const struct snes_system_timing * -- Set exact timings of the system. + // Used primarily for video recording. + +struct snes_geometry +{ + unsigned base_width; // Nominal video width of system. + unsigned base_height; // Nominal video height of system. + unsigned max_width; // Maximum possible width of system. + unsigned max_height; // Maximum possible height of system. +}; + +struct snes_system_timing +{ + double fps; + double sample_rate; +}; + +typedef bool (*snes_environment_t)(unsigned cmd, void *data); + +// Must be called before calling snes_init(). +void snes_set_environment(snes_environment_t); +//// + + +////////////////////////////////////////////////////////////////////////////}}} + +/////////////////////////////////////////////////////////////////////////////// +// Callback types {{{ +// +// In order to deliver controller input to the emulated SNES, and retrieve +// video frames and audio samples, you will need to register callbacks. + +// snes_audio_sample_t: +// +// This callback delivers a stereo audio sample pair generated by the +// emulated SNES. +// +// This function is called once for every audio frame (one sample from left +// and right channels). The SNES generates audio samples at a rate of about +// 32040Hz (varies from unit to unit). +// +// Because the SNES generates video at exactly 59.94fps and most computer +// monitors only support a 60fps refresh rate, real-time emulation needs to +// run slightly fast so that each computer frame displays one emulated SNES +// frame. Because the emulation runs slightly fast, and because most +// consumer audio hardware does not play audio at precisely the requested +// sample rate, you'll likely need to let the end-user tweak the effective +// sample rate by 100Hz or so in either direction. +// +// Although the parameters are declared as unsigned for historical reasons, +// the data they contain is actually signed. To work with the audio (e.g. +// resample), you will need to reinterpret the sample value: +// +// int16_t real_left = *(int16_t*)(&left); +// +// Parameters: +// +// left: +// A signed 16-bit integer containing the next audio sample from the +// left audio channel. Yes, it's declared as unsigned for historical +// reasons. +// +// right: +// A signed 16-bit integer containing the next audio sample from the +// right audio channel. Yes, it's declared as unsigned for historical +// reasons. +// + +typedef void (*snes_audio_sample_t)(uint16_t left, uint16_t right); + + +// snes_video_refresh_t: +// +// This callback delivers a single SNES frame, generated by the emulated +// SNES. The same memory buffer may be re-used later, so take a copy of the +// data if you want to refer to it after your callback returns. +// +// The framebuffer is an array of unsigned 16-bit pixels, in a somewhat +// complicated format. A quick refresher on SNES video modes: +// - The basic SNES video-mode renders 256 pixels per scanline for a total +// of 224 scanlines. +// - When "overscan" mode is enabled, the SNES renders a few extra +// scanlines at the end of the frame, for a total of 239 scanlines. +// - When "hi-res" mode is enabled, the SNES speeds up its pixel rendering +// to fit 512 pixels per scanline. +// - Normally the SNES renders its pixels to one field of the interlaced +// NTSC signal, but if "interlaced" mode is enabled the SNES renders +// a second set of scanlines inbetween the regular set, for a total of +// 448 (normal) or 478 (overscan) scanlines. +// +// Thus, the framebuffer memory layout for a standard 256x240 frame looks +// something like this (note that 'height' has been reduced to 4 or 8 for +// these examples): +// +// 0 1024b +// ,---------------------------------------. +// |====== width ======|...................| -. +// |.......................................| | +// |===================|...................| | +// |.......................................| +- height = 4 +// |===================|...................| | +// |.......................................| | +// |===================|...................| | +// |.......................................| -' +// `---------------------------------------' +// +// A hi-res frame would look like this: +// +// 0 1024b +// ,---------------------------------------. +// |================ width ================| -. +// |.......................................| | +// |=======================================| | +// |.......................................| +- height = 4 +// |=======================================| | +// |.......................................| | +// |=======================================| | +// |.......................................| -' +// `---------------------------------------' +// +// An interlaced frame would look like this: +// +// 0 1024b +// ,---------------------------------------. +// |====== width ======|...................| -. +// |===================|...................| | +// |===================|...................| | +// |===================|...................| +- height = 8 +// |===================|...................| | +// |===================|...................| | +// |===================|...................| | +// |===================|...................| -' +// `---------------------------------------' +// +// And of course a hi-res, interlaced frame would look like this: +// +// 0 1024b +// ,---------------------------------------. +// |================ width ================| -. +// |=======================================| | +// |=======================================| | +// |=======================================| |+- height = 8 +// |=======================================| | +// |=======================================| | +// |=======================================| | +// |=======================================| -' +// `---------------------------------------' +// +// More succinctly: +// - the buffer begins at the top-left of the frame +// - the first "width" bytes contain the first scanline. +// - if the emulated SNES is in an interlaced video-mode (that is, if the +// "height" parameter" is 448 or 478) then the second scanline begins at +// an offset of 1024 bytes (512 pixels) after the first. +// - otherwise the second scanline begins at an offset of 2048 bytes (1024 +// pixels) after the first. +// - there are "height" scanlines in total. +// +// Each pixel contains a 15-bit RGB tuple: 0RRRRRGGGGGBBBBB (XRGB1555) +// +// Example code: +// +// void pack_frame (uint16_t * restrict out, const uint16_t * restrict in, +// unsigned width, unsigned height) +// { +// // Normally our pitch is 2048 bytes. +// int pitch_pixels = 1024; +// // If we have an interlaced mode, pitch is 1024 bytes. +// if ( height == 448 || height == 478 ) +// pitch_pixels = 512; +// +// for ( int y = 0; y < height; y++ ) +// { +// const uint16_t *src = in + y * pitch_pixels; +// uint16_t *dst = out + y * width; +// +// memcpy(dst, src, width * sizeof(uint16_t)); +// } +// } +// +// Parameters: +// +// data: +// a pointer to the beginning of the framebuffer described above. +// +// width: +// the width of the frame, in pixels. +// +// height: +// the number of scanlines in the frame. + +typedef void (*snes_video_refresh_t)(const uint16_t *data, unsigned width, + unsigned height); + +// snes_input_poll_t: +// +// This callback requests that you poll your input devices for events, if +// required. +// +// Generally called once per frame before the snes_input_state_t callback is +// called. +// + +typedef void (*snes_input_poll_t)(void); + +// snes_input_state_t: +// +// This callback asks for information about the state of a particular input. +// +// The callback may be called multiple times per frame with the same +// parameters. +// +// The callback might not be called at all, if the software running in the +// emulated SNES does not try to probe the controllers. +// +// The callback will not be called for a particular port if DEVICE_NONE is +// connected to it. +// +// If you wish to emulate any kind of turbo-fire, etc. then you will need to +// put that logic into this callback. +// +// Parameters: +// +// port: +// One of the constants SNES_PORT_1 or SNES_PORT_2, describing which +// controller port you should report. +// +// device: +// One of the SNES_DEVICE_* constants describing which type of device +// is currently connected to the given port. +// +// index: +// A number describing which of the devices connected to the port is +// being reported. It's only useful for SNES_DEVICE_MULTITAP and +// SNES_DEVICE_JUSTIFIERS - for other device types, this parameter is +// always 0. +// +// id: +// One of the SNES_DEVICE_ID_* constants for the given device, +// describing which button or axis is being reported (for +// SNES_DEVICE_MULTITAP, use the SNES_DEVICE_ID_JOYPAD_* IDs; for +// SNES_DEVICE_JUSTIFIERS use the SNES_DEVICE_ID_JUSTIFIER_* IDs.). +// +// Returns: +// +// An integer representing the state of the described button or axis. +// +// - If it represents a digital input such as SNES_DEVICE_ID_JOYPAD_B or +// SNES_DEVICE_ID_MOUSE_LEFT), return 1 if the button is pressed, and +// 0 otherwise. +// - If "id" is SNES_DEVICE_ID_MOUSE_X or SNES_DEVICE_ID_MOUSE_Y then +// return the relative movement of the mouse during the current frame; +// values outside the range -127 to +127 will be clamped. +// - If "id" is one of the light-gun axes (such as +// SNES_DEVICE_ID_JUSTIFIER_Y or SNES_DEVICE_ID_SUPER_SCOPE_X), you +// should return the relative movement of the pointing device during the +// current frame. + +typedef int16_t (*snes_input_state_t)(bool port, unsigned device, + unsigned index, unsigned id); + +////////////////////////////////////////////////////////////////////////////}}} + +/////////////////////////////////////////////////////////////////////////////// +// libsnes setup {{{ +// +// These functions are used to get information about and manipulate the libsnes +// library itself, not the emulated SNES it implements. + +// snes_library_id: +// +// Returns a human readable string describing this libsnes implementation. +// It is not supposed to be parsed or used in any other way than being +// printed to screen on request by user or otherwise. +// +// Returns: +// +// A human-readable string describing this implementation. + +const char* snes_library_id(void); + + +// snes_library_revision_major: +// +// Returns the major API version of this libsnes implementation. +// +// This number is increased every time there is a compatibility-breaking +// change to the libsnes API. At startup, your program should call this +// function and compare the return value to the major API version the +// program was designed to work with. If they are different, your program +// will (very likely) not work with this libsnes implementation. +// +// For example, if your program was designed to work with the libsnes API +// whose major.minor revision was 1.5, and this function returns a major +// version of 2, you have a problem. +// +// Returns: +// +// An integer, the major API version of this libsnes implementation. + +unsigned snes_library_revision_major(void); + +// snes_library_revision_minor: +// +// Returns the minor API version of this libsnes implementation. +// +// This number is increased every time there is a backwards-compatible +// change to the libsnes API. At startup, your program should call this +// function and compare the return value to the minor API version the +// program was designed to work with. If the return value is less than the +// expected minor version, your program will (very likely) not work with +// this libsnes implementation. +// +// For example, if your program was designed to work with the libsnes API +// whose major.minor revision was 1.5, and this libsnes implementation's +// major.minor version is 1.3, it's probably missing features you require. +// On the other hand, if this libsnes implementation's major.minor version +// is 1.9, it probably has extra fancy features you don't need to worry +// about. +// +// Returns: +// +// An integer, the minor API version of this libsnes implementation. + +unsigned snes_library_revision_minor(void); + +// snes_init: +// +// Initializes the libsnes implementation. +// +// This function must be called exactly once before any other library +// functions are called. + +void snes_init(void); + +// snes_term: +// +// Shuts down the libsnes implementation. +// +// This function must be called exactly once. Once called, you should not +// call any other libsnes functions besides (perhaps) snes_init(). + +void snes_term(void); + +////////////////////////////////////////////////////////////////////////////}}} + +/////////////////////////////////////////////////////////////////////////////// +// Callback registration {{{ +// +// Note that all callbacks should be set up before snes_run() is called for the +// first time. + +// snes_set_video_refresh: +// +// Sets the callback that will receive new video frames. +// +// See the documentation for snes_video_refresh_t for details. +// +// Parameters: +// +// A pointer to a function matching the snes_video_refresh_t call +// signature. + +void snes_set_video_refresh(snes_video_refresh_t); + +// snes_set_audio_sample +// +// Sets the callback that will receive new audio sample pairs. +// +// See the documentation for snes_audio_sample_t for details. +// +// Parameters: +// +// A pointer to a function matching the snes_audio_sample_t call +// signature. + +void snes_set_audio_sample(snes_audio_sample_t); + +// snes_set_input_poll: +// +// Sets the callback that will be notified to poll input devices. +// +// See the documentation for snes_input_poll_t for details. +// +// Parameters: +// +// A pointer to a function matching the snes_input_poll_t call signature. + +void snes_set_input_poll(snes_input_poll_t); + +// snes_set_input_state: +// +// Sets the callback that will be used to read input device state. +// +// See the documentation for snes_input_state_t for details. +// +// Parameters: +// +// A pointer to a function matching the snes_input_state_t call signature. + +void snes_set_input_state(snes_input_state_t); + +////////////////////////////////////////////////////////////////////////////}}} + +/////////////////////////////////////////////////////////////////////////////// +// SNES operation {{{ +// +// Functions for manipulating the emulated SNES. + +// snes_set_controller_port_device: +// +// Sets the input device connected to a given controller port. +// +// Connecting a device to a port implicitly removes any device previously +// connected to that port. To remove a device without connecting a new one, +// pass DEVICE_NONE as the device parameter. From this point onward, the +// callback passed to set_input_state_cb() will be called with the +// appropriate device, index and id parameters. +// +// If this function is never called, the default is to have a DEVICE_JOYPAD +// connected to both ports. +// +// Calling this callback from inside the set_input_state_cb() has undefined +// results, so don't do that. +// +// Parameters: +// +// port: +// One of the constants SNES_PORT_1 or SNES_PORT_2, describing which +// controller port is being configured. +// +// device: +// One of the SNES_DEVICE_* constants describing which type of device +// should be connected to the given port. Note that some devices can +// only be connected to SNES_PORT_2. Attempting to connect +// a port-2-only device to SNES_PORT_1 has undefined results. +// +// These devices work in either port: +// - SNES_DEVICE_NONE: No device is connected to this port. +// - SNES_DEVICE_JOYPAD: A standard SNES gamepad. +// - SNES_DEVICE_MULTITAP: A multitap controller, which acts like +// 4 SNES_DEVICE_JOYPADs. Your input state callback will be +// passed "id" parameters between 0 and 3, inclusive. +// - SNES_DEVICE_MOUSE: A SNES mouse controller, as shipped with +// Mario Paint. +// +// These devices only work properly when connected to port 2: +// - SNES_DEVICE_SUPER_SCOPE: A Nintendo Super Scope light-gun +// device. +// - SNES_DEVICE_JUSTIFIER: A Konami Justifier light-gun device. +// - SNES_DEVICE_JUSTIFIERS: Two Konami Justifier light-gun +// devices, daisy-chained together. Your input state callback +// will be passed "id" parameters 0 and 1. + +void snes_set_controller_port_device(bool port, unsigned device); + +// snes_power: +// +// Turns the emulated console off and back on. +// +// This functionality is sometimes called "hard reset" and guarantees that +// all hardware state is reset to a reasonable default. +// +// Before bsnes v070r07, this resets the controller ports to both contain +// SNES_DEVICE_JOYPADs. +// +// This requires that a cartridge is loaded. + +void snes_power(void); + +// snes_reset: +// +// Presses the "reset" button on the emulated SNES. +// +// This functionality is sometimes called "soft reset". Most hardware state +// is reset to a reasonable befault, but not all. +// +// As of bsnes v073r01, this function (as a side-effect) resets the +// controller ports to both contain SNES_DEVICE_JOYPADs. +// +// This requires that a cartridge is loaded. + +void snes_reset(void); + +// snes_run(): +// +// Runs the emulated SNES until the end of the next video frame. +// +// Usually causes each registered callback to be called before returning. +// +// This function will run as fast as possible. It is up to the caller to +// make sure that the game runs at the intended speed. +// +// For optimal A/V sync, make sure that the audio callback never blocks for +// longer than a frame (approx 16ms for NTSC, 20ms for PAL) +// +// Optimally, it should never block for more than a few ms at a time. + +void snes_run(void); + +// snes_get_region(): +// +// Determines the intended frame-rate of the loaded cartridge. +// +// The two main SNES hardware variants are the US/Japan variant, designed +// for NTSC output, and the European variant, designed for PAL output. +// However, the world is not quite so tidy as that, and there are countries +// like Brazil that use PAL output at NTSC frame-rates. +// +// For historical reasons this function is named snes_get_region(), but +// effectively the only information you can reliably infer is the +// frame-rate. +// +// Returns: +// +// One of the SNES_REGION_* constants. SNES_REGION_PAL means 50fps, +// SNES_REGION_NTSC means 60fps. + +bool snes_get_region(void); + +////////////////////////////////////////////////////////////////////////////}}} + +/////////////////////////////////////////////////////////////////////////////// +// Save state support {{{ +// +// libsnes has the ability to save the current emulation state and restore it +// at a later time. +// +// Note 1: It is impossible to reliably restore the *exact* state, although the +// difference is only a few cycles. If you demand the ability to reliably +// restore state, call snes_serialize() after each frame to ensure the emulated +// SNES is in a state that can be reliably restored. +// +// Note 2: The save state information is specific to a particular cartridge +// loaded into a particular version of a particular libsnes implementation. +// Unfortunately, there is not yet a way to determine whether a given save +// state is compatible with a given libsnes implementation, other than by +// loading it. However, if snes_serialize_size() does not match the size of an +// old save state, that's a strong hint that something has incompatibly +// changed. + +// snes_serialize_size: +// +// Determines the minimum size of a save state. +// +// This value can change depending on the features used by the loaded +// cartridge, and the version of the libsnes implementation used. +// +// Returns: +// +// An integer representing the number of bytes required to store the +// current emulation state. + +unsigned snes_serialize_size(void); + +// snes_serialize: +// +// Serialize the current emulation state to a buffer. +// +// If the allocated buffer is smaller than the size returned by +// snes_serialize_size(), serialization will fail. If the allocated buffer +// is larger, only the first snes_serialize_size() bytes will be written to. +// +// The resulting buffer may be stored, and later passed to +// snes_unserialize() to restore the saved emulation state. +// +// Parameters: +// +// data: +// A pointer to an allocated buffer of memory. +// +// size: +// The size of the buffer pointed to by "data". Should be greater than +// or equal to the value returned by snes_serialize_size(). +// +// Returns: +// +// A boolean; True means the emulation state was serialized successfully, +// False means a problem was encountered. + +bool snes_serialize(uint8_t *data, unsigned size); + +// snes_unserialize: +// +// Unserialize the emulation state from a buffer. +// +// If the serialization data in the buffer does not appear to be compatible +// with the current libsnes implementation, the function returns False and +// the current emulation state is not modified. +// +// Parameters: +// +// data: +// A pointer to an allocated buffer of memory. +// +// size: +// The size of the buffer pointed to by "data". Should be greater than +// or equal to the value returned by snes_serialize_size(). +// +// Returns: +// +// A boolean; True means the emulation state was loaded successfully, +// False means a problem was encountered. + +bool snes_unserialize(const uint8_t *data, unsigned size); + +////////////////////////////////////////////////////////////////////////////}}} + +/////////////////////////////////////////////////////////////////////////////// +// Cheat support {{{ +// +// libsnes does not include any kind of cheat management API; the intention is +// that any change to the set of applied cheats will cause the containing +// application to call snes_cheat_reset() then apply the new set of cheats with +// snes_cheat_set(). +// +// Any currently-applied cheats are discarded when a new cartridge is loaded. + +// snes_cheat_reset: +// +// Discards all cheat codes applied to the emulated SNES. + +void snes_cheat_reset(void); + +// snes_cheat_set: +// +// Apply a sequence of cheat codes to the emulated SNES. +// +// Since a "cheat code" is basically an instruction to override the value of +// a particular byte in the SNES' memory, more complex cheats may require +// several individual codes applied at once. There's no effective difference +// between applying these codes in a group with one call to +// snes_cheat_set(), or applying them one at a time with individual calls. +// However, most cheat databases will have a collection of available cheats +// for each game, where each item in the collection has a description and +// a sequence of codes to be applied as a unit. This API makes it easy to +// present the list of descriptions to the user, and apply each cheat the +// user selects. +// +// Parameters: +// +// index: +// The given cheat code will be stored at this index in the array of +// applied cheats. If a cheat already exists at this location, it will +// be replaced by the new cheat. If the index is larger than any +// previously specififed index, the array will be resized to +// accommodate. +// +// enabled: +// True means that the cheat will actually be applied, False means +// that the cheat will have no effect. There is no way to enable or +// disable a cheat after it has been added, other than to call +// snes_cheat_set() a second time with the same values for "index" and +// "code". +// +// code: +// A string containing a sequence of cheat codes separated by '+' +// characters. Any spaces in the string will be removed before +// parsing. +// +// Each code in the sequence must be in either GameGenie format +// ("1234-ABCD") or ProActionReplay format ("1234AB:CD" or +// "1234ABCD"). + +void snes_cheat_set(unsigned index, bool enabled, const char *code); + +////////////////////////////////////////////////////////////////////////////}}} + +/////////////////////////////////////////////////////////////////////////////// +// Cartridge loading and unloading {{{ +// +// Before calling snes_run(), a cartridge must be loaded into the emulated SNES +// so that it has code to run. +// +// Loading a cartridge of any kind calls snes_cheat_reset() as a side-effect. + +// snes_load_cartridge_normal: +// +// Load a normal ROM image into the emulated SNES. +// +// Parameters: +// +// rom_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes where the ROM image is mapped into the SNES address +// space, what special chips it uses (and where they're mapped), etc. +// +// If NULL, libsnes will guess a memory map. The guessed memory map +// should be correct for all licenced games in all regions. +// +// rom_data: +// A pointer to a byte array containing the uncompressed, +// de-interleaved, headerless ROM image. +// +// rom_size: +// The length of the rom_data array, in bytes. +// +// Returns: +// +// A boolean; True means the cartridge was loaded correctly, False means +// an error occurred. + +bool snes_load_cartridge_normal( + const char *rom_xml, const uint8_t *rom_data, unsigned rom_size +); + +// snes_load_cartridge_bsx: +// +// Load a BS-X base cart image, optionally with a memory pack. +// +// The Satellaview system, abbreviated "BS-X" for unclear reasons, was an +// addon for the Super Famicom that connected it to the St. GIGA satellite +// network. The network would broadcast games at a particular time, and +// users could download them to replaceable memory packs. +// +// For more information, see http://en.wikipedia.org/wiki/Satellaview +// +// Parameters: +// +// rom_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes where the BS-X base cartridge ROM image is mapped +// into the SNES address space. +// +// If NULL, libsnes will guess a memory map. The guessed memory map +// should be correct for all known BS-X base cartridge images. +// +// rom_data: +// A pointer to a byte array containing the uncompressed, +// de-interleaved, headerless ROM image of the BS-X base cartridge. +// +// The BS-X base cartridge is named "BS-X - Sore wa Namae o Nusumareta +// Machi no Monogatari" in some SNES game databases. +// +// rom_size: +// The length of the rom_data array, in bytes. +// +// bsx_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes the BS-X memory pack. +// +// This parameter is currently ignored and should be passed as NULL. +// +// bsx_data: +// A pointer to a byte array containing the uncompressed, +// de-interleaved, headerless image of the BS-X memory-pack. +// +// If NULL, libsnes will behave as though no memory-pack were inserted +// into the base cartridge. +// +// bsx_size: +// The length of the bsx_data array, in bytes. +// +// Returns: +// +// A boolean; True means the cartridge was loaded correctly, False means +// an error occurred. + +bool snes_load_cartridge_bsx( + const char *rom_xml, const uint8_t *rom_data, unsigned rom_size, + const char *bsx_xml, const uint8_t *bsx_data, unsigned bsx_size +); + +// snes_load_cartridge_bsx_slotted: +// +// Load a BS-X slotted cartridge, optionally with a memory pack. +// +// A BS-X slotted cartridge is an ordinary SNES cartridge, with a slot in +// the top that accepts the same memory packs used by the BS-X base +// cartridge. +// +// Parameters: +// +// rom_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes where the ROM image is mapped into the SNES address +// space, what special chips it uses (and where they're mapped), etc. +// +// If NULL, libsnes will guess a memory map. The guessed memory map +// should be correct for all licenced games in all regions. +// +// rom_data: +// A pointer to a byte array containing the uncompressed, +// de-interleaved, headerless ROM image. +// +// rom_size: +// The length of the rom_data array, in bytes. +// +// bsx_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes the BS-X memory pack. +// +// This parameter is currently ignored and should be passed as NULL. +// +// bsx_data: +// A pointer to a byte array containing the uncompressed, +// de-interleaved, headerless image of the BS-X memory-pack. +// +// If NULL, libsnes will behave as though no memory-pack were inserted +// into the base cartridge. +// +// bsx_size: +// The length of the bsx_data array, in bytes. +// +// Returns: +// +// A boolean; True means the cartridge was loaded correctly, False means +// an error occurred. + +bool snes_load_cartridge_bsx_slotted( + const char *rom_xml, const uint8_t *rom_data, unsigned rom_size, + const char *bsx_xml, const uint8_t *bsx_data, unsigned bsx_size +); + +// snes_load_cartridge_sufami_turbo: +// +// Load a SuFami Turbo base cart image, optionally with game packs. +// +// The SuFami Turbo was a cartridge available for the Super Famicom, created +// by Bandai, with two slots in the top designed to accept special +// mini-cartridges. The cartridge in Slot A was the cartridge that actually +// ran, while the cartridge in Slot B was made available to the Slot +// A cartridge, enabling sharing of save-game data or using characters from +// one game in another. +// +// For more information, see: http://en.wikipedia.org/wiki/Sufami_Turbo +// +// Parameters: +// +// rom_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes where the SuFami Turbo base cartridge ROM image is +// mapped into the SNES address space. +// +// If NULL, libsnes will guess a memory map. The guessed memory map +// should be correct for all known SuFami Turbo base cartridge images. +// +// rom_data: +// A pointer to a byte array containing the uncompressed, +// de-interleaved, headerless ROM image of the SuFami Turbo base +// cartridge. +// +// The SuFami Turbo base cartridge is named "Sufami Turbo" in some +// SNES game databases. +// +// rom_size: +// The length of the rom_data array, in bytes. +// +// sta_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes the Sufami Turbo cartridge in Slot A. +// +// This parameter is currently ignored and should be passed as NULL. +// +// sta_data: +// A pointer to a byte array containing the uncompressed, +// de-interleaved, headerless ROM image of the SuFami Turbo cartridge +// in Slot A. +// +// This is the cartridge that will be executed by the SNES. +// +// If NULL, libsnes will behave as though no cartridge were inserted +// into the Slot A. +// +// sta_size: +// The length of the sta_data array, in bytes. +// +// stb_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes the Sufami Turbo cartridge in Slot B. +// +// This parameter is currently ignored and should be passed as NULL. +// +// stb_data: +// A pointer to a byte array containing the uncompressed, +// de-interleaved, headerless ROM image of the SuFami Turbo cartridge +// in Slot B. +// +// The data in this cartridge will be made available to the cartridge +// in Slot A. +// +// If NULL, libsnes will behave as though no cartridge were inserted +// into Slot B. +// +// stb_size: +// The length of the stb_data array, in bytes. +// +// Returns: +// +// A boolean; True means the cartridge was loaded correctly, False means +// an error occurred. + +bool snes_load_cartridge_sufami_turbo( + const char *rom_xml, const uint8_t *rom_data, unsigned rom_size, + const char *sta_xml, const uint8_t *sta_data, unsigned sta_size, + const char *stb_xml, const uint8_t *stb_data, unsigned stb_size +); + +// snes_load_cartridge_super_game_boy: +// +// Load a Super Game Boy base cart, optionally with a Gameboy cartridge. +// +// The Super Game Boy was a cartridge available for the Super Famicom and +// Super Nintendo that accepted ordinary (original) Gameboy cartridges and +// allowed the user to play them with a Super Nintendo controller, on a TV. +// It extended the orginal Gameboy hardware in a few ways, including the +// ability to display games in various palettes (rather than strictly +// monochrome), to display a full-colour border image around the Gameboy +// video output, or even run native SNES code to enhance the game. +// +// For more information, see: http://en.wikipedia.org/wiki/Super_Game_Boy +// +// Up until bsnes v073, loading Super Game Boy cartridges only works if the +// libsupergameboy library from the bsnes release is installed. bsnes v074 +// includes a custom Gameboy emulation core, and external code is no longer +// required. +// +// Parameters: +// +// rom_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes where the Super Game Boy base cartridge ROM image is +// mapped into the SNES address space. +// +// If NULL, libsnes will guess a memory map. The guessed memory map +// should be correct for all known Super Game Boy base cartridge +// images. +// +// rom_data: +// A pointer to a byte array containing the uncompressed, +// de-interleaved, headerless ROM image of the Super Game Boy base +// cartridge. +// +// Appropriate base cartridge images are named "Super Game Boy" or +// "Super Game Boy 2" in some SNES game databases. +// +// rom_size: +// The length of the rom_data array, in bytes. +// +// dmg_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes the inserted Gameboy cartridge. +// +// If NULL, libsnes will guess a memory map. The guesed memory map +// should be correct for all licensed original Gameboy games in all +// regions. +// +// dmg_data: +// A pointer to a byte array containing the uncompressed, headerless +// ROM image of the inserted Gameboy cartridge. +// +// If NULL, libsnes will behave as though no cartridge were inserted. +// +// dmg_size: +// The length of the dmg_size array, in bytes. +// +// Returns: +// +// A boolean; True means the cartridge was loaded correctly, False means +// an error occurred. + +bool snes_load_cartridge_super_game_boy( + const char *rom_xml, const uint8_t *rom_data, unsigned rom_size, + const char *dmg_xml, const uint8_t *dmg_data, unsigned dmg_size +); + +// snes_set_cartridge_basename: +// +// Set the location and name of the loaded cartridge. +// +// libsnes uses this information to locate additional resources the +// cartridge might require. Currently, these resources include: +// +// - The MSU-1 data pack and associated audio tracks, if the cartridge makes +// use of bsnes' MSU-1 special-chip. +// - The serial-port data receiving library, if the cartridge makes uses of +// bsnes' serial-data-over-controller-port feature. +// +// Parameters: +// +// basename: +// The path and basename of the loaded cartridge. For example, if the +// full path to the loaded cartridge is "/path/to/filename.sfc", this +// parameter should be set to "/path/to/filename". + +void snes_set_cartridge_basename(const char *basename); + +// snes_unload_cartridge: +// +// Unloads the currently loaded cartridge from the emulated SNES. +// +// You will be unable to call snes_run() until another cartridge is loaded. + +void snes_unload_cartridge(void); + +////////////////////////////////////////////////////////////////////////////}}} + +/////////////////////////////////////////////////////////////////////////////// +// Volatile and non-volatile storage {{{ +// +// Certain SNES cartridges include non-volatile storage or other kinds of data +// that would persist after the SNES is turned off. libsnes exposes this +// information via the snes_get_memory_data() and snes_get_memory_size() +// functions. Since version 1.2 of the libsnes API, libsnes also exposes the +// contents of volatile storage such as WRAM and VRAM. +// +// After a cartridge is loaded, call snes_get_memory_size() and +// snes_get_memory_data() with the various SNES_MEMORY_* constants to determine +// which kinds of non-volatile storage the cartridge supports - unsupported +// storage types will have a size of 0 and the data pointer NULL. +// +// If you have non-volatile storage data from a previous run, you can memcpy() +// the data from your storage into the buffer described by the data and size +// values before calling snes_run(). Do not load non-volatile storage data if +// the size of the data you have is different from the size returned by +// snes_get_memory_size(). +// +// Before calling snes_unload_cartridge(), you should copy the contents of the +// relevant storage buffers into a file (or some non-volatile storage of your +// own) so that you can load it the next time you load the same cartridge into +// the emulated SNES. Do not call free() on the storage buffers; they will be +// handled by libsnes. Note: It is not necessary to store the contents of +// volatile storage; the emulated SNES expects information in volatile storage +// to be lost (hence the name 'volatile'). +// +// Because non-volatile storage is read and written by the software running on +// the emulated SNES, it should be compatible between different versions of +// different emulators running on different platforms, unlike save states. +// +// The various kinds of non-volatile storage and their uses are: +// +// SNES_MEMORY_CARTRIDGE_RAM: +// Standard battery-backed static RAM (SRAM). Traditionally, the SRAM for +// a ROM image named "foo.sfc" is stored in a file named "foo.srm" beside +// it. +// +// SNES_MEMORY_CARTRIDGE_RTC: +// Real-time clock data. Traditionally, the RTC data for a ROM image named +// "foo.sfc" is stored in a file named "foo.rtc" beside it. +// +// SNES_MEMORY_BSX_RAM: +// RAM data used with the BS-X base cartridge. +// +// SNES_MEMORY_BSX_PRAM: +// PRAM data used with the BS-X base cartridge. +// +// SNES_MEMORY_SUFAMI_TURBO_A_RAM: +// RAM data stored in the mini-cartridge inserted into Slot A of the +// SuFami Turbo base cartridge. +// +// SNES_MEMORY_SUFAMI_TURBO_B_RAM: +// RAM data stored in the mini-cartridge inserted into Slot B of the +// SuFami Turbo base cartridge. +// +// SNES_MEMORY_GAME_BOY_RAM: +// Standard battery-backed static RAM (SRAM) in the Gameboy cartridge +// inserted into the Super Game Boy base cartridge. Not all Gameboy games +// have SRAM. +// +// SNES_MEMORY_GAME_BOY_RTC: +// Real-time clock data in the Gameboy cartridge inserted into the Super +// Game Boy base cartridge. Not all Gameboy games have an RTC. +// +// The various kinds of volatile storage are: +// +// SNES_MEMORY_WRAM: +// Working RAM, accessible by the CPU. SNES software tends to keep runtime +// information in here; games' life-bars and inventory contents and so +// forth are in here somewhere. +// +// SNES_MEMORY_APURAM: +// RAM accessible by the Audio Processing Unit. Contains audio samples, +// music data and the code responsible for feeding the right notes to the +// DSP at the right times. +// +// SNES_MEMORY_VRAM: +// Video RAM. Stores almost everything related to video output, including +// the patterns used for each tile and sprite, tilemaps for each +// background. The exact format used depends on the current video mode of +// the emulated SNES. +// +// SNES_MEMORY_OAM: +// Object Attribute Memory. Stores the location, orientation and priority +// of all the sprites the SNES displays. +// +// SNES_MEMORY_CGRAM: +// Color Generator RAM. Contains the colour palettes used by tiles and +// sprites. Each palette entry is stored in a 16-bit int, in the standard +// XBGR1555 format. + +// snes_get_memory_data: +// +// Returns a pointer to the given non-volatile storage buffer. +// +// This requires that a cartridge is loaded. +// +// Parameters: +// +// id: +// One of the SNES_MEMORY_* constants. +// +// Returns: +// +// A pointer to the memory buffer used for storing the given type of data. +// The size of the buffer can be obtained from snes_get_memory_size(). +// +// If NULL, the loaded cartridge does not store the given type of data. + +uint8_t* snes_get_memory_data(unsigned id); + +// snes_get_memory_size: +// +// Returns the size of the given non-volatile storage buffer. +// +// This requires that a cartridge is loaded. +// +// Parameters: +// +// id: +// One of the SNES_MEMORY_* constants. +// +// Returns: +// +// The size of the memory buffer used for storing the given type of data. +// A pointer to the buffer can be obtained from snes_get_memory_data(). +// +// If 0, the loaded cartridge does not store the given type of data. + +unsigned snes_get_memory_size(unsigned id); + +////////////////////////////////////////////////////////////////////////////}}} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libsnes/link.T b/libsnes/link.T new file mode 100644 index 00000000..432292c1 --- /dev/null +++ b/libsnes/link.T @@ -0,0 +1,4 @@ +{ + global: snes_*; + local: *; +}; diff --git a/loadzip.cpp b/loadzip.cpp index 6ce8e7c8..1a92828a 100644 --- a/loadzip.cpp +++ b/loadzip.cpp @@ -185,10 +185,9 @@ #include "memmap.h" -bool8 LoadZip (const char *zipname, int32 *TotalFileSize, int32 *headers, uint8 *buffer) +bool8 LoadZip (const char *zipname, uint32 *TotalFileSize, uint8 *buffer) { *TotalFileSize = 0; - *headers = 0; unzFile file = unzOpen(zipname); if (file == NULL) @@ -196,7 +195,7 @@ bool8 LoadZip (const char *zipname, int32 *TotalFileSize, int32 *headers, uint8 // find largest file in zip file (under MAX_ROM_SIZE) or a file with extension .1 char filename[132]; - int filesize = 0; + uint32 filesize = 0; int port = unzGoToFirstFile(file); unz_file_info info; @@ -212,7 +211,7 @@ bool8 LoadZip (const char *zipname, int32 *TotalFileSize, int32 *headers, uint8 continue; } - if ((int) info.uncompressed_size > filesize) + if (info.uncompressed_size > filesize) { strcpy(filename, name); filesize = info.uncompressed_size; @@ -259,7 +258,7 @@ bool8 LoadZip (const char *zipname, int32 *TotalFileSize, int32 *headers, uint8 { assert(info.uncompressed_size <= CMemory::MAX_ROM_SIZE + 512); - int FileSize = info.uncompressed_size; + uint32 FileSize = info.uncompressed_size; int l = unzReadCurrentFile(file, ptr, FileSize); if (unzCloseCurrentFile(file) == UNZ_CRCERROR) @@ -274,7 +273,7 @@ bool8 LoadZip (const char *zipname, int32 *TotalFileSize, int32 *headers, uint8 return (FALSE); } - FileSize = (int) Memory.HeaderRemove((uint32) FileSize, *headers, ptr); + FileSize = Memory.HeaderRemove(FileSize, ptr); ptr += FileSize; *TotalFileSize += FileSize; diff --git a/macosx/snes9x.xcodeproj/project.pbxproj b/macosx/snes9x.xcodeproj/project.pbxproj index 176f07e6..37ab5032 100755 --- a/macosx/snes9x.xcodeproj/project.pbxproj +++ b/macosx/snes9x.xcodeproj/project.pbxproj @@ -36,7 +36,7 @@ CF047D54109D0E0600FD0754 /* pixform.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061C60526CCB900A80003 /* pixform.h */; }; CF047D55109D0E0600FD0754 /* port.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061C70526CCB900A80003 /* port.h */; }; CF047D56109D0E0600FD0754 /* ppu.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061C90526CCB900A80003 /* ppu.h */; }; - CF047D57109D0E0600FD0754 /* reader.h in Headers */ = {isa = PBXBuildFile; fileRef = EA809E9708F8D70D0072CDFB /* reader.h */; }; + CF047D57109D0E0600FD0754 /* stream.h in Headers */ = {isa = PBXBuildFile; fileRef = EA809E9708F8D70D0072CDFB /* stream.h */; }; CF047D58109D0E0600FD0754 /* sa1.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061CC0526CCB900A80003 /* sa1.h */; }; CF047D59109D0E0600FD0754 /* sar.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061CE0526CCB900A80003 /* sar.h */; }; CF047D5A109D0E0600FD0754 /* screenshot.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061D00526CCB900A80003 /* screenshot.h */; }; @@ -145,7 +145,7 @@ CF047DC9109D0E0600FD0754 /* movie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EA813E9A066F50A5004F99B5 /* movie.cpp */; }; CF047DCA109D0E0600FD0754 /* obc1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EAE061C30526CCB900A80003 /* obc1.cpp */; }; CF047DCB109D0E0600FD0754 /* ppu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EAE061C80526CCB900A80003 /* ppu.cpp */; }; - CF047DCC109D0E0600FD0754 /* reader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EA809E9F08F8D7530072CDFB /* reader.cpp */; }; + CF047DCC109D0E0600FD0754 /* stream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EA809E9F08F8D7530072CDFB /* stream.cpp */; }; CF047DCD109D0E0600FD0754 /* sa1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EAE061CB0526CCB900A80003 /* sa1.cpp */; }; CF047DCE109D0E0600FD0754 /* sa1cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EAE061CD0526CCB900A80003 /* sa1cpu.cpp */; }; CF047DCF109D0E0600FD0754 /* sdd1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EAE061D10526CCB900A80003 /* sdd1.cpp */; }; @@ -237,7 +237,7 @@ CF0566A90CF98E7E00C7877C /* pixform.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061C60526CCB900A80003 /* pixform.h */; }; CF0566AA0CF98E7E00C7877C /* port.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061C70526CCB900A80003 /* port.h */; }; CF0566AB0CF98E7E00C7877C /* ppu.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061C90526CCB900A80003 /* ppu.h */; }; - CF0566AC0CF98E7E00C7877C /* reader.h in Headers */ = {isa = PBXBuildFile; fileRef = EA809E9708F8D70D0072CDFB /* reader.h */; }; + CF0566AC0CF98E7E00C7877C /* stream.h in Headers */ = {isa = PBXBuildFile; fileRef = EA809E9708F8D70D0072CDFB /* stream.h */; }; CF0566AD0CF98E7E00C7877C /* sa1.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061CC0526CCB900A80003 /* sa1.h */; }; CF0566AE0CF98E7E00C7877C /* sar.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061CE0526CCB900A80003 /* sar.h */; }; CF0566AF0CF98E7E00C7877C /* screenshot.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061D00526CCB900A80003 /* screenshot.h */; }; @@ -333,7 +333,7 @@ CF0567180CF98E7E00C7877C /* movie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EA813E9A066F50A5004F99B5 /* movie.cpp */; }; CF0567190CF98E7E00C7877C /* obc1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EAE061C30526CCB900A80003 /* obc1.cpp */; }; CF05671A0CF98E7E00C7877C /* ppu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EAE061C80526CCB900A80003 /* ppu.cpp */; }; - CF05671B0CF98E7E00C7877C /* reader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EA809E9F08F8D7530072CDFB /* reader.cpp */; }; + CF05671B0CF98E7E00C7877C /* stream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EA809E9F08F8D7530072CDFB /* stream.cpp */; }; CF05671C0CF98E7E00C7877C /* sa1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EAE061CB0526CCB900A80003 /* sa1.cpp */; }; CF05671D0CF98E7E00C7877C /* sa1cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EAE061CD0526CCB900A80003 /* sa1cpu.cpp */; }; CF05671E0CF98E7E00C7877C /* sdd1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EAE061D10526CCB900A80003 /* sdd1.cpp */; }; @@ -424,7 +424,7 @@ CF2F462E1095EE72007D33FA /* pixform.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061C60526CCB900A80003 /* pixform.h */; }; CF2F462F1095EE72007D33FA /* port.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061C70526CCB900A80003 /* port.h */; }; CF2F46301095EE72007D33FA /* ppu.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061C90526CCB900A80003 /* ppu.h */; }; - CF2F46311095EE72007D33FA /* reader.h in Headers */ = {isa = PBXBuildFile; fileRef = EA809E9708F8D70D0072CDFB /* reader.h */; }; + CF2F46311095EE72007D33FA /* stream.h in Headers */ = {isa = PBXBuildFile; fileRef = EA809E9708F8D70D0072CDFB /* stream.h */; }; CF2F46321095EE72007D33FA /* sa1.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061CC0526CCB900A80003 /* sa1.h */; }; CF2F46331095EE72007D33FA /* sar.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061CE0526CCB900A80003 /* sar.h */; }; CF2F46341095EE72007D33FA /* screenshot.h in Headers */ = {isa = PBXBuildFile; fileRef = EAE061D00526CCB900A80003 /* screenshot.h */; }; @@ -533,7 +533,7 @@ CF2F46A31095EE72007D33FA /* movie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EA813E9A066F50A5004F99B5 /* movie.cpp */; }; CF2F46A41095EE72007D33FA /* obc1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EAE061C30526CCB900A80003 /* obc1.cpp */; }; CF2F46A51095EE72007D33FA /* ppu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EAE061C80526CCB900A80003 /* ppu.cpp */; }; - CF2F46A61095EE72007D33FA /* reader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EA809E9F08F8D7530072CDFB /* reader.cpp */; }; + CF2F46A61095EE72007D33FA /* stream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EA809E9F08F8D7530072CDFB /* stream.cpp */; }; CF2F46A71095EE72007D33FA /* sa1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EAE061CB0526CCB900A80003 /* sa1.cpp */; }; CF2F46A81095EE72007D33FA /* sa1cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EAE061CD0526CCB900A80003 /* sa1cpu.cpp */; }; CF2F46A91095EE72007D33FA /* sdd1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EAE061D10526CCB900A80003 /* sdd1.cpp */; }; @@ -817,11 +817,11 @@ EA6E6C0E08F9734500CB3555 /* debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = debug.h; sourceTree = ""; }; EA809E9308F8D6C40072CDFB /* controls.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = controls.h; sourceTree = ""; }; EA809E9508F8D6E00072CDFB /* language.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = language.h; sourceTree = ""; }; - EA809E9708F8D70D0072CDFB /* reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = reader.h; sourceTree = ""; }; + EA809E9708F8D70D0072CDFB /* stream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stream.h; sourceTree = ""; }; EA809E9908F8D7240072CDFB /* controls.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = controls.cpp; sourceTree = ""; }; EA809E9B08F8D72C0072CDFB /* crosshairs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = crosshairs.cpp; sourceTree = ""; }; EA809E9D08F8D73A0072CDFB /* crosshairs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = crosshairs.h; sourceTree = ""; }; - EA809E9F08F8D7530072CDFB /* reader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = reader.cpp; sourceTree = ""; }; + EA809E9F08F8D7530072CDFB /* stream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stream.cpp; sourceTree = ""; }; EA809F9D08F8F2190072CDFB /* mac-controls.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "mac-controls.h"; sourceTree = ""; }; EA809FA108F8F2420072CDFB /* mac-controls.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = "mac-controls.cpp"; sourceTree = ""; }; EA813E86066F5076004F99B5 /* movie.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = movie.h; sourceTree = ""; }; @@ -1179,7 +1179,7 @@ EAE061C60526CCB900A80003 /* pixform.h */, EAE061C70526CCB900A80003 /* port.h */, EAE061C90526CCB900A80003 /* ppu.h */, - EA809E9708F8D70D0072CDFB /* reader.h */, + EA809E9708F8D70D0072CDFB /* stream.h */, EAE061CC0526CCB900A80003 /* sa1.h */, EAE061CE0526CCB900A80003 /* sar.h */, EAE061D00526CCB900A80003 /* screenshot.h */, @@ -1222,7 +1222,7 @@ EA813E9A066F50A5004F99B5 /* movie.cpp */, EAE061C30526CCB900A80003 /* obc1.cpp */, EAE061C80526CCB900A80003 /* ppu.cpp */, - EA809E9F08F8D7530072CDFB /* reader.cpp */, + EA809E9F08F8D7530072CDFB /* stream.cpp */, EAE061CB0526CCB900A80003 /* sa1.cpp */, EAE061CD0526CCB900A80003 /* sa1cpu.cpp */, EAE061D10526CCB900A80003 /* sdd1.cpp */, @@ -1384,7 +1384,7 @@ CF047D54109D0E0600FD0754 /* pixform.h in Headers */, CF047D55109D0E0600FD0754 /* port.h in Headers */, CF047D56109D0E0600FD0754 /* ppu.h in Headers */, - CF047D57109D0E0600FD0754 /* reader.h in Headers */, + CF047D57109D0E0600FD0754 /* stream.h in Headers */, CF047D58109D0E0600FD0754 /* sa1.h in Headers */, CF047D59109D0E0600FD0754 /* sar.h in Headers */, CF047D5A109D0E0600FD0754 /* screenshot.h in Headers */, @@ -1503,7 +1503,7 @@ CF0566A90CF98E7E00C7877C /* pixform.h in Headers */, CF0566AA0CF98E7E00C7877C /* port.h in Headers */, CF0566AB0CF98E7E00C7877C /* ppu.h in Headers */, - CF0566AC0CF98E7E00C7877C /* reader.h in Headers */, + CF0566AC0CF98E7E00C7877C /* stream.h in Headers */, CF0566AD0CF98E7E00C7877C /* sa1.h in Headers */, CF0566AE0CF98E7E00C7877C /* sar.h in Headers */, CF0566AF0CF98E7E00C7877C /* screenshot.h in Headers */, @@ -1622,7 +1622,7 @@ CF2F462E1095EE72007D33FA /* pixform.h in Headers */, CF2F462F1095EE72007D33FA /* port.h in Headers */, CF2F46301095EE72007D33FA /* ppu.h in Headers */, - CF2F46311095EE72007D33FA /* reader.h in Headers */, + CF2F46311095EE72007D33FA /* stream.h in Headers */, CF2F46321095EE72007D33FA /* sa1.h in Headers */, CF2F46331095EE72007D33FA /* sar.h in Headers */, CF2F46341095EE72007D33FA /* screenshot.h in Headers */, @@ -1938,7 +1938,7 @@ CF047DC9109D0E0600FD0754 /* movie.cpp in Sources */, CF047DCA109D0E0600FD0754 /* obc1.cpp in Sources */, CF047DCB109D0E0600FD0754 /* ppu.cpp in Sources */, - CF047DCC109D0E0600FD0754 /* reader.cpp in Sources */, + CF047DCC109D0E0600FD0754 /* stream.cpp in Sources */, CF047DCD109D0E0600FD0754 /* sa1.cpp in Sources */, CF047DCE109D0E0600FD0754 /* sa1cpu.cpp in Sources */, CF047DCF109D0E0600FD0754 /* sdd1.cpp in Sources */, @@ -2033,7 +2033,7 @@ CF0567180CF98E7E00C7877C /* movie.cpp in Sources */, CF0567190CF98E7E00C7877C /* obc1.cpp in Sources */, CF05671A0CF98E7E00C7877C /* ppu.cpp in Sources */, - CF05671B0CF98E7E00C7877C /* reader.cpp in Sources */, + CF05671B0CF98E7E00C7877C /* stream.cpp in Sources */, CF05671C0CF98E7E00C7877C /* sa1.cpp in Sources */, CF05671D0CF98E7E00C7877C /* sa1cpu.cpp in Sources */, CF05671E0CF98E7E00C7877C /* sdd1.cpp in Sources */, @@ -2128,7 +2128,7 @@ CF2F46A31095EE72007D33FA /* movie.cpp in Sources */, CF2F46A41095EE72007D33FA /* obc1.cpp in Sources */, CF2F46A51095EE72007D33FA /* ppu.cpp in Sources */, - CF2F46A61095EE72007D33FA /* reader.cpp in Sources */, + CF2F46A61095EE72007D33FA /* stream.cpp in Sources */, CF2F46A71095EE72007D33FA /* sa1.cpp in Sources */, CF2F46A81095EE72007D33FA /* sa1cpu.cpp in Sources */, CF2F46A91095EE72007D33FA /* sdd1.cpp in Sources */, diff --git a/memmap.cpp b/memmap.cpp index 811518eb..83effdbf 100644 --- a/memmap.cpp +++ b/memmap.cpp @@ -197,7 +197,6 @@ #include "controls.h" #include "cheats.h" #include "movie.h" -#include "reader.h" #include "display.h" #ifndef SET_UI_COLOR @@ -945,9 +944,9 @@ static bool8 is_GNEXT_BIOS (uint8 *, uint32); static bool8 is_GNEXT_Add_On (uint8 *, uint32); static uint32 caCRC32 (uint8 *, uint32, uint32 crc32 = 0xffffffff); static uint32 ReadUPSPointer (const uint8 *, unsigned &, unsigned); -static bool8 ReadUPSPatch (Reader *, long, int32 &); -static long ReadInt (Reader *, unsigned); -static bool8 ReadIPSPatch (Reader *, long, int32 &); +static bool8 ReadUPSPatch (Stream *, long, int32 &); +static long ReadInt (Stream *, unsigned); +static bool8 ReadIPSPatch (Stream *, long, int32 &); #ifdef UNZIP_SUPPORT static int unzFindExtension (unzFile &, const char *, bool restart = TRUE, bool print = TRUE); #endif @@ -1352,7 +1351,7 @@ int CMemory::ScoreLoROM (bool8 skip_header, int32 romoff) return (score); } -uint32 CMemory::HeaderRemove (uint32 size, int32 &headerCount, uint8 *buf) +uint32 CMemory::HeaderRemove (uint32 size, uint8 *buf) { uint32 calc_size = (size / 0x2000) * 0x2000; @@ -1373,20 +1372,20 @@ uint32 CMemory::HeaderRemove (uint32 size, int32 &headerCount, uint8 *buf) } memmove(buf, buf + 512, calc_size); - headerCount++; + HeaderCount++; size -= 512; } return (size); } -uint32 CMemory::FileLoader (uint8 *buffer, const char *filename, int32 maxsize) +uint32 CMemory::FileLoader (uint8 *buffer, const char *filename, uint32 maxsize) { // <- ROM size without header // ** Memory.HeaderCount // ** Memory.ROMFilename - int32 totalSize = 0; + uint32 totalSize = 0; char fname[PATH_MAX + 1]; char drive[_MAX_DRIVE + 1], dir[_MAX_DIR + 1], name[_MAX_FNAME + 1], exts[_MAX_EXT + 1]; char *ext; @@ -1415,7 +1414,7 @@ uint32 CMemory::FileLoader (uint8 *buffer, const char *filename, int32 maxsize) case FILE_ZIP: { #ifdef UNZIP_SUPPORT - if (!LoadZip(fname, &totalSize, &HeaderCount, buffer)) + if (!LoadZip(fname, &totalSize, buffer)) { S9xMessage(S9X_ERROR, S9X_ROM_INFO, "Invalid Zip archive."); return (0); @@ -1439,7 +1438,7 @@ uint32 CMemory::FileLoader (uint8 *buffer, const char *filename, int32 maxsize) return (0); } - totalSize = HeaderRemove(size, HeaderCount, buffer); + totalSize = HeaderRemove(size, buffer); strcpy(ROMFilename, fname); #else @@ -1468,7 +1467,7 @@ uint32 CMemory::FileLoader (uint8 *buffer, const char *filename, int32 maxsize) size = READ_STREAM(ptr, maxsize + 0x200 - (ptr - buffer), fp); CLOSE_STREAM(fp); - size = HeaderRemove(size, HeaderCount, ptr); + size = HeaderRemove(size, ptr); totalSize += size; ptr += size; @@ -1511,31 +1510,55 @@ uint32 CMemory::FileLoader (uint8 *buffer, const char *filename, int32 maxsize) return ((uint32) totalSize); } +bool8 CMemory::LoadROMMem (const uint8 *source, uint32 sourceSize) +{ + if(!source || sourceSize > MAX_ROM_SIZE) + return FALSE; + + strcpy(ROMFilename,"MemoryROM"); + + do + { + memset(ROM,0, MAX_ROM_SIZE); + memset(&Multi, 0,sizeof(Multi)); + memcpy(ROM,source,sourceSize); + } + while(!LoadROMInt(sourceSize)); + + return TRUE; +} + bool8 CMemory::LoadROM (const char *filename) { - int retry_count = 0; + if(!filename || !*filename) + return FALSE; - if (!filename || !*filename) - return (FALSE); + int32 totalFileSize; - ZeroMemory(ROM, MAX_ROM_SIZE); - ZeroMemory(&Multi, sizeof(Multi)); - -again: + do + { + memset(ROM,0, MAX_ROM_SIZE); + memset(&Multi, 0,sizeof(Multi)); + totalFileSize = FileLoader(ROM, filename, MAX_ROM_SIZE); + + if (!totalFileSize) + return (FALSE); + } + while(!LoadROMInt(totalFileSize)); + + return TRUE; +} + +bool8 CMemory::LoadROMInt (int32 ROMfillSize) +{ Settings.DisplayColor = BUILD_PIXEL(31, 31, 31); SET_UI_COLOR(255, 255, 255); CalculatedSize = 0; ExtendedFormat = NOPE; - int32 totalFileSize; - - totalFileSize = FileLoader(ROM, filename, MAX_ROM_SIZE); - if (!totalFileSize) - return (FALSE); - if (!Settings.NoPatch) - CheckForAnyPatch(filename, HeaderCount != 0, totalFileSize); + CheckForAnyPatch(ROMFilename, HeaderCount != 0, ROMfillSize); int hi_score, lo_score; @@ -1546,15 +1569,15 @@ again: ((hi_score > lo_score && ScoreHiROM(TRUE) > hi_score) || (hi_score <= lo_score && ScoreLoROM(TRUE) > lo_score))) { - memmove(ROM, ROM + 512, totalFileSize - 512); - totalFileSize -= 512; + memmove(ROM, ROM + 512, ROMfillSize - 512); + ROMfillSize -= 512; S9xMessage(S9X_INFO, S9X_HEADER_WARNING, "Try 'force no-header' option if the game doesn't work"); // modifying ROM, so we need to rescore hi_score = ScoreHiROM(FALSE); lo_score = ScoreLoROM(FALSE); } - CalculatedSize = (totalFileSize / 0x2000) * 0x2000; + CalculatedSize = (ROMfillSize / 0x2000) * 0x2000; if (CalculatedSize > 0x400000 && (ROM[0x7fd5] + (ROM[0x7fd6] << 8)) != 0x3423 && // exclude SA-1 @@ -1571,7 +1594,7 @@ again: ((ROM[0xfffc] + (ROM[0xfffd] << 8)) < 0x8000)) { if (!Settings.ForceInterleaved && !Settings.ForceNotInterleaved) - S9xDeinterleaveType1(totalFileSize, ROM); + S9xDeinterleaveType1(ROMfillSize, ROM); } // CalculatedSize is now set, so rescore @@ -1699,14 +1722,10 @@ again: if ((HiROM && (lo_score >= hi_score || hi_score < 0)) || (LoROM && (hi_score > lo_score || lo_score < 0))) { - if (retry_count == 0) - { - S9xMessage(S9X_INFO, S9X_ROM_CONFUSING_FORMAT_INFO, "ROM lied about its type! Trying again."); - Settings.ForceNotInterleaved = TRUE; - Settings.ForceInterleaved = FALSE; - retry_count++; - goto again; - } + S9xMessage(S9X_INFO, S9X_ROM_CONFUSING_FORMAT_INFO, "ROM lied about its type! Trying again."); + Settings.ForceNotInterleaved = TRUE; + Settings.ForceInterleaved = FALSE; + return (FALSE); } } @@ -1726,9 +1745,9 @@ again: } } - if (strncmp(LastRomFilename, filename, PATH_MAX + 1)) + if (strncmp(LastRomFilename, ROMFilename, PATH_MAX + 1)) { - strncpy(LastRomFilename, filename, PATH_MAX + 1); + strncpy(LastRomFilename, ROMFilename, PATH_MAX + 1); LastRomFilename[PATH_MAX] = 0; } @@ -3775,7 +3794,7 @@ static uint32 XPSdecode (const uint8 *data, unsigned &addr, unsigned size) //no-header patching errors that result in IPS patches having a 50/50 chance of //being applied correctly. -static bool8 ReadUPSPatch (Reader *r, long, int32 &rom_size) +static bool8 ReadUPSPatch (Stream *r, long, int32 &rom_size) { //Reader lacks size() and rewind(), so we need to read in the file to get its size uint8 *data = new uint8[8 * 1024 * 1024]; //allocate a lot of memory, better safe than sorry ... @@ -3857,7 +3876,7 @@ static bool8 ReadUPSPatch (Reader *r, long, int32 &rom_size) // // logic taken from http://byuu.org/programming/bps and the accompanying source // -static bool8 ReadBPSPatch (Reader *r, long, int32 &rom_size) +static bool8 ReadBPSPatch (Stream *r, long, int32 &rom_size) { uint8 *data = new uint8[8 * 1024 * 1024]; //allocate a lot of memory, better safe than sorry ... uint32 size = 0; @@ -3948,7 +3967,7 @@ static bool8 ReadBPSPatch (Reader *r, long, int32 &rom_size) } } -static long ReadInt (Reader *r, unsigned nbytes) +static long ReadInt (Stream *r, unsigned nbytes) { long v = 0; @@ -3963,7 +3982,7 @@ static long ReadInt (Reader *r, unsigned nbytes) return (v); } -static bool8 ReadIPSPatch (Reader *r, long offset, int32 &rom_size) +static bool8 ReadIPSPatch (Stream *r, long offset, int32 &rom_size) { const int32 IPS_EOF = 0x00454F46l; int32 ofs; @@ -4082,7 +4101,7 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r if (Settings.NoPatch) return; - STREAM patch_file = NULL; + FSTREAM patch_file = NULL; uint32 i; long offset = header ? 512 : 0; int ret; @@ -4095,12 +4114,12 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r // BPS _makepath(fname, drive, dir, name, "bps"); - if ((patch_file = OPEN_STREAM(fname, "rb")) != NULL) + if ((patch_file = OPEN_FSTREAM(fname, "rb")) != NULL) { printf("Using BPS patch %s", fname); - ret = ReadBPSPatch(new fReader(patch_file), 0, rom_size); - CLOSE_STREAM(patch_file); + ret = ReadBPSPatch(new fStream(patch_file), 0, rom_size); + CLOSE_FSTREAM(patch_file); if (ret) { @@ -4122,7 +4141,7 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r { printf(" in %s", rom_filename); - ret = ReadBPSPatch(new unzReader(file), offset, rom_size); + ret = ReadBPSPatch(new unzStream(file), offset, rom_size); unzCloseCurrentFile(file); if (ret) @@ -4136,12 +4155,12 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r n = S9xGetFilename(".bps", IPS_DIR); - if ((patch_file = OPEN_STREAM(n, "rb")) != NULL) + if ((patch_file = OPEN_FSTREAM(n, "rb")) != NULL) { printf("Using BPS patch %s", n); - ret = ReadBPSPatch(new fReader(patch_file), 0, rom_size); - CLOSE_STREAM(patch_file); + ret = ReadBPSPatch(new fStream(patch_file), 0, rom_size); + CLOSE_FSTREAM(patch_file); if (ret) { @@ -4156,12 +4175,12 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r _makepath(fname, drive, dir, name, "ups"); - if ((patch_file = OPEN_STREAM(fname, "rb")) != NULL) + if ((patch_file = OPEN_FSTREAM(fname, "rb")) != NULL) { printf("Using UPS patch %s", fname); - ret = ReadUPSPatch(new fReader(patch_file), 0, rom_size); - CLOSE_STREAM(patch_file); + ret = ReadUPSPatch(new fStream(patch_file), 0, rom_size); + CLOSE_FSTREAM(patch_file); if (ret) { @@ -4183,7 +4202,7 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r { printf(" in %s", rom_filename); - ret = ReadUPSPatch(new unzReader(file), offset, rom_size); + ret = ReadUPSPatch(new unzStream(file), offset, rom_size); unzCloseCurrentFile(file); if (ret) @@ -4197,12 +4216,12 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r n = S9xGetFilename(".ups", IPS_DIR); - if ((patch_file = OPEN_STREAM(n, "rb")) != NULL) + if ((patch_file = OPEN_FSTREAM(n, "rb")) != NULL) { printf("Using UPS patch %s", n); - ret = ReadUPSPatch(new fReader(patch_file), 0, rom_size); - CLOSE_STREAM(patch_file); + ret = ReadUPSPatch(new fStream(patch_file), 0, rom_size); + CLOSE_FSTREAM(patch_file); if (ret) { @@ -4217,12 +4236,12 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r _makepath(fname, drive, dir, name, "ips"); - if ((patch_file = OPEN_STREAM(fname, "rb")) != NULL) + if ((patch_file = OPEN_FSTREAM(fname, "rb")) != NULL) { printf("Using IPS patch %s", fname); - ret = ReadIPSPatch(new fReader(patch_file), offset, rom_size); - CLOSE_STREAM(patch_file); + ret = ReadIPSPatch(new fStream(patch_file), offset, rom_size); + CLOSE_FSTREAM(patch_file); if (ret) { @@ -4243,13 +4262,13 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r snprintf(ips, 8, "%03d.ips", i); _makepath(fname, drive, dir, name, ips); - if (!(patch_file = OPEN_STREAM(fname, "rb"))) + if (!(patch_file = OPEN_FSTREAM(fname, "rb"))) break; printf("Using IPS patch %s", fname); - ret = ReadIPSPatch(new fReader(patch_file), offset, rom_size); - CLOSE_STREAM(patch_file); + ret = ReadIPSPatch(new fStream(patch_file), offset, rom_size); + CLOSE_FSTREAM(patch_file); if (ret) { @@ -4279,13 +4298,13 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r break; _makepath(fname, drive, dir, name, ips); - if (!(patch_file = OPEN_STREAM(fname, "rb"))) + if (!(patch_file = OPEN_FSTREAM(fname, "rb"))) break; printf("Using IPS patch %s", fname); - ret = ReadIPSPatch(new fReader(patch_file), offset, rom_size); - CLOSE_STREAM(patch_file); + ret = ReadIPSPatch(new fStream(patch_file), offset, rom_size); + CLOSE_FSTREAM(patch_file); if (ret) { @@ -4313,13 +4332,13 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r snprintf(ips, 4, "ip%d", i); _makepath(fname, drive, dir, name, ips); - if (!(patch_file = OPEN_STREAM(fname, "rb"))) + if (!(patch_file = OPEN_FSTREAM(fname, "rb"))) break; printf("Using IPS patch %s", fname); - ret = ReadIPSPatch(new fReader(patch_file), offset, rom_size); - CLOSE_STREAM(patch_file); + ret = ReadIPSPatch(new fStream(patch_file), offset, rom_size); + CLOSE_FSTREAM(patch_file); if (ret) { @@ -4348,7 +4367,7 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r { printf(" in %s", rom_filename); - ret = ReadIPSPatch(new unzReader(file), offset, rom_size); + ret = ReadIPSPatch(new unzStream(file), offset, rom_size); unzCloseCurrentFile(file); if (ret) @@ -4375,7 +4394,7 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r printf(" in %s", rom_filename); - ret = ReadIPSPatch(new unzReader(file), offset, rom_size); + ret = ReadIPSPatch(new unzStream(file), offset, rom_size); unzCloseCurrentFile(file); if (ret) @@ -4409,7 +4428,7 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r printf(" in %s", rom_filename); - ret = ReadIPSPatch(new unzReader(file), offset, rom_size); + ret = ReadIPSPatch(new unzStream(file), offset, rom_size); unzCloseCurrentFile(file); if (ret) @@ -4441,7 +4460,7 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r printf(" in %s", rom_filename); - ret = ReadIPSPatch(new unzReader(file), offset, rom_size); + ret = ReadIPSPatch(new unzStream(file), offset, rom_size); unzCloseCurrentFile(file); if (ret) @@ -4470,12 +4489,12 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r n = S9xGetFilename(".ips", IPS_DIR); - if ((patch_file = OPEN_STREAM(n, "rb")) != NULL) + if ((patch_file = OPEN_FSTREAM(n, "rb")) != NULL) { printf("Using IPS patch %s", n); - ret = ReadIPSPatch(new fReader(patch_file), offset, rom_size); - CLOSE_STREAM(patch_file); + ret = ReadIPSPatch(new fStream(patch_file), offset, rom_size); + CLOSE_FSTREAM(patch_file); if (ret) { @@ -4496,13 +4515,13 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r snprintf(ips, 9, ".%03d.ips", i); n = S9xGetFilename(ips, IPS_DIR); - if (!(patch_file = OPEN_STREAM(n, "rb"))) + if (!(patch_file = OPEN_FSTREAM(n, "rb"))) break; printf("Using IPS patch %s", n); - ret = ReadIPSPatch(new fReader(patch_file), offset, rom_size); - CLOSE_STREAM(patch_file); + ret = ReadIPSPatch(new fStream(patch_file), offset, rom_size); + CLOSE_FSTREAM(patch_file); if (ret) { @@ -4532,13 +4551,13 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r break; n = S9xGetFilename(ips, IPS_DIR); - if (!(patch_file = OPEN_STREAM(n, "rb"))) + if (!(patch_file = OPEN_FSTREAM(n, "rb"))) break; printf("Using IPS patch %s", n); - ret = ReadIPSPatch(new fReader(patch_file), offset, rom_size); - CLOSE_STREAM(patch_file); + ret = ReadIPSPatch(new fStream(patch_file), offset, rom_size); + CLOSE_FSTREAM(patch_file); if (ret) { @@ -4566,13 +4585,13 @@ void CMemory::CheckForAnyPatch (const char *rom_filename, bool8 header, int32 &r snprintf(ips, 5, ".ip%d", i); n = S9xGetFilename(ips, IPS_DIR); - if (!(patch_file = OPEN_STREAM(n, "rb"))) + if (!(patch_file = OPEN_FSTREAM(n, "rb"))) break; printf("Using IPS patch %s", n); - ret = ReadIPSPatch(new fReader(patch_file), offset, rom_size); - CLOSE_STREAM(patch_file); + ret = ReadIPSPatch(new fStream(patch_file), offset, rom_size); + CLOSE_FSTREAM(patch_file); if (ret) { diff --git a/memmap.h b/memmap.h index 4991ae83..4bf43d36 100644 --- a/memmap.h +++ b/memmap.h @@ -271,9 +271,12 @@ struct CMemory int ScoreHiROM (bool8, int32 romoff = 0); int ScoreLoROM (bool8, int32 romoff = 0); - uint32 HeaderRemove (uint32, int32 &, uint8 *); - uint32 FileLoader (uint8 *, const char *, int32); + uint32 HeaderRemove (uint32, uint8 *); + uint32 FileLoader (uint8 *, const char *, uint32); + uint32 MemLoader (uint8 *, const char*, uint32); + bool8 LoadROMMem (const uint8 *, uint32); bool8 LoadROM (const char *); + bool8 LoadROMInt (int32); bool8 LoadMultiCart (const char *, const char *); bool8 LoadSufamiTurbo (const char *, const char *); bool8 LoadSameGame (const char *, const char *); @@ -361,7 +364,7 @@ extern CMemory Memory; extern SMulti Multi; void S9xAutoSaveSRAM (void); -bool8 LoadZip(const char *, int32 *, int32 *, uint8 *); +bool8 LoadZip(const char *, uint32 *, uint8 *); enum s9xwrap_t { diff --git a/netplay.cpp b/netplay.cpp index f0f15315..6e18291c 100644 --- a/netplay.cpp +++ b/netplay.cpp @@ -189,6 +189,8 @@ #include #include +#include "snes9x.h" + #ifdef __WIN32__ #include #include @@ -222,7 +224,6 @@ #include #endif -#include "snes9x.h" #include "memmap.h" #include "netplay.h" #include "snapshot.h" diff --git a/port.h b/port.h index 0e8192e5..a4d07330 100644 --- a/port.h +++ b/port.h @@ -202,7 +202,9 @@ #define RIGHTSHIFT_int8_IS_SAR #define RIGHTSHIFT_int16_IS_SAR #define RIGHTSHIFT_int32_IS_SAR +#ifndef __WIN32_LIBSNES__ #define SNES_JOY_READ_CALLBACKS +#endif //__WIN32_LIBSNES__ #endif #ifdef __MACOSX__ @@ -227,31 +229,22 @@ typedef uint64_t uint64; #else // HAVE_STDINT_H #ifdef __WIN32__ typedef intptr_t pint; -#else // __WIN32__ -#ifdef PTR_NOT_INT -typedef long pint; -#else -typedef int pint; -#endif -#endif // __WIN32__ -#ifdef __WIN32__ -#ifdef __BORLANDC__ -#include -#else typedef signed char int8; typedef unsigned char uint8; typedef signed short int16; typedef unsigned short uint16; -#ifndef WSAAP -// winsock2.h typedefs int32 as well -typedef signed int int32; -#endif +typedef signed int int32; typedef unsigned int uint32; -#endif -typedef unsigned char uint8_t; -typedef signed char int8_t; typedef signed __int64 int64; typedef unsigned __int64 uint64; +typedef int8 int8_t; +typedef uint8 uint8_t; +typedef int16 int16_t; +typedef uint16 uint16_t; +typedef int32 int32_t; +typedef uint32 uint32_t; +typedef int64 int64_t; +typedef uint64 uint64_t; typedef int socklen_t; #else // __WIN32__ typedef signed char int8; @@ -266,6 +259,11 @@ __extension__ #endif typedef long long int64; typedef unsigned long long uint64; +#ifdef PTR_NOT_INT +typedef long pint; +#else // __PTR_NOT_INT +typedef int pint; +#endif // __PTR_NOT_INT #endif // __WIN32__ #endif // HAVE_STDINT_H #endif // snes9x_types_defined @@ -300,15 +298,19 @@ typedef unsigned long long uint64; void _splitpath (const char *, char *, char *, char *, char *); void _makepath (char *, const char *, const char *, const char *, const char *); #define S9xDisplayString DisplayStringFromBottom -#else +#else // __WIN32__ #define snprintf _snprintf #define strcasecmp stricmp #define strncasecmp strnicmp +#ifndef __WIN32_LIBSNES__ void WinDisplayStringFromBottom(const char *string, int linesFromBottom, int pixelsFromLeft, bool allowWrap); #define S9xDisplayString WinDisplayStringFromBottom void SetInfoDlgColor(unsigned char, unsigned char, unsigned char); #define SET_UI_COLOR(r,g,b) SetInfoDlgColor(r,g,b) -#endif +#else // __WIN32_LIBSNES__ +#define S9xDisplayString DisplayStringFromBottom +#endif // __WIN32_LIBSNES__ +#endif // __WIN32__ #ifdef __DJGPP #define SLASH_STR "\\" diff --git a/server.cpp b/server.cpp index fc2f3fa4..6a376132 100644 --- a/server.cpp +++ b/server.cpp @@ -190,6 +190,8 @@ #include #endif +#include "snes9x.h" + #ifdef __WIN32__ #include @@ -219,7 +221,6 @@ #endif // !__WIN32__ -#include "snes9x.h" #include "memmap.h" #include "snapshot.h" #include "netplay.h" diff --git a/snapshot.cpp b/snapshot.cpp index 7b758b42..86dbdbc0 100644 --- a/snapshot.cpp +++ b/snapshot.cpp @@ -1169,6 +1169,21 @@ void S9xResetSaveTimer (bool8 dontsave) t = time(NULL); } +uint32 S9xFreezeSize() +{ + nulStream stream; + S9xFreezeToStream(&stream); + return stream.size(); +} + +bool8 S9xFreezeGameMem (uint8 *buf, uint32 bufSize) +{ + memStream mStream(buf, bufSize); + S9xFreezeToStream(&mStream); + + return (TRUE); +} + bool8 S9xFreezeGame (const char *filename) { STREAM stream = NULL; @@ -1194,6 +1209,14 @@ bool8 S9xFreezeGame (const char *filename) return (FALSE); } +int S9xUnfreezeGameMem (const uint8 *buf, uint32 bufSize) +{ + memStream stream(buf, bufSize); + int result = S9xUnfreezeFromStream(&stream); + + return result; +} + bool8 S9xUnfreezeGame (const char *filename) { STREAM stream = NULL; diff --git a/snapshot.h b/snapshot.h index d55b8b7e..d5ce8958 100644 --- a/snapshot.h +++ b/snapshot.h @@ -192,7 +192,10 @@ void S9xResetSaveTimer (bool8); bool8 S9xFreezeGame (const char *); +uint32 S9xFreezeSize (void); +bool8 S9xFreezeGameMem (uint8 *,uint32); bool8 S9xUnfreezeGame (const char *); +int S9xUnfreezeGameMem (const uint8 *,uint32); void S9xFreezeToStream (STREAM); int S9xUnfreezeFromStream (STREAM); bool8 S9xSPCDump (const char *); diff --git a/snes9x.cpp b/snes9x.cpp index 148073e8..4a84e70b 100644 --- a/snes9x.cpp +++ b/snes9x.cpp @@ -315,14 +315,14 @@ static void parse_crosshair_spec (enum crosscontrols ctl, const char *spec) static bool try_load_config_file (const char *fname, ConfigFile &conf) { - STREAM fp; + FSTREAM fp; - fp = OPEN_STREAM(fname, "r"); + fp = OPEN_FSTREAM(fname, "r"); if (fp) { fprintf(stdout, "Reading config file %s.\n", fname); - conf.LoadFile(new fReader(fp)); - CLOSE_STREAM(fp); + conf.LoadFile(new fStream(fp)); + CLOSE_FSTREAM(fp); return (true); } diff --git a/snes9x.h b/snes9x.h index 4e09c0dd..97689baf 100644 --- a/snes9x.h +++ b/snes9x.h @@ -189,29 +189,42 @@ #ifdef ZLIB #include -#define STREAM gzFile -#define READ_STREAM(p, l, s) gzread(s, p, l) -#define WRITE_STREAM(p, l, s) gzwrite(s, p, l) -#define GETS_STREAM(p, l, s) gzgets(s, p, l) -#define GETC_STREAM(s) gzgetc(s) -#define OPEN_STREAM(f, m) gzopen(f, m) -#define REOPEN_STREAM(f, m) gzdopen(f, m) -#define FIND_STREAM(f) gztell(f) -#define REVERT_STREAM(f, o, s) gzseek(f, o, s) -#define CLOSE_STREAM(s) gzclose(s) +#define FSTREAM gzFile +#define READ_FSTREAM(p, l, s) gzread(s, p, l) +#define WRITE_FSTREAM(p, l, s) gzwrite(s, p, l) +#define GETS_FSTREAM(p, l, s) gzgets(s, p, l) +#define GETC_FSTREAM(s) gzgetc(s) +#define OPEN_FSTREAM(f, m) gzopen(f, m) +#define REOPEN_FSTREAM(f, m) gzdopen(f, m) +#define FIND_FSTREAM(f) gztell(f) +#define REVERT_FSTREAM(s, o, p) gzseek(s, o, p) +#define CLOSE_FSTREAM(s) gzclose(s) #else -#define STREAM FILE * -#define READ_STREAM(p, l, s) fread(p, 1, l, s) -#define WRITE_STREAM(p, l, s) fwrite(p, 1, l, s) -#define GETS_STREAM(p, l, s) fgets(p, l, s) -#define GETC_STREAM(s) fgetc(s) -#define OPEN_STREAM(f, m) fopen(f, m) -#define REOPEN_STREAM(f, m) fdopen(f, m) -#define FIND_STREAM(f) ftell(f) -#define REVERT_STREAM(f, o, s) fseek(f, o, s) -#define CLOSE_STREAM(s) fclose(s) +#define FSTREAM FILE * +#define READ_FSTREAM(p, l, s) fread(p, 1, l, s) +#define WRITE_FSTREAM(p, l, s) fwrite(p, 1, l, s) +#define GETS_FSTREAM(p, l, s) fgets(p, l, s) +#define GETC_FSTREAM(s) fgetc(s) +#define OPEN_FSTREAM(f, m) fopen(f, m) +#define REOPEN_FSTREAM(f, m) fdopen(f, m) +#define FIND_FSTREAM(s) ftell(s) +#define REVERT_FSTREAM(s, o, p) fseek(s, o, p) +#define CLOSE_FSTREAM(s) fclose(s) #endif +#include "stream.h" + +#define STREAM Stream * +#define READ_STREAM(p, l, s) s->read(p,l) +#define WRITE_STREAM(p, l, s) s->write(p,l) +#define GETS_STREAM(p, l, s) s->gets(p,l) +#define GETC_STREAM(s) s->get_char() +#define OPEN_STREAM(f, m) openStreamFromFSTREAM(f, m) +#define REOPEN_STREAM(f, m) reopenStreamFromFd(f, m) +#define FIND_STREAM(s) s->pos() +#define REVERT_STREAM(s, o, p) s->revert(p, o) +#define CLOSE_STREAM(s) s->closeStream() + #define SNES_WIDTH 256 #define SNES_HEIGHT 224 #define SNES_HEIGHT_EXTENDED 239 diff --git a/reader.cpp b/stream.cpp similarity index 64% rename from reader.cpp rename to stream.cpp index 8ad1ed44..84f9424a 100644 --- a/reader.cpp +++ b/stream.cpp @@ -183,24 +183,24 @@ #include "unzip.h" #endif #include "snes9x.h" -#include "reader.h" +#include "stream.h" // Generic constructor/destructor -Reader::Reader (void) +Stream::Stream (void) { return; } -Reader::~Reader (void) +Stream::~Stream (void) { return; } // Generic getline function, based on gets. Reimlpement if you can do better. -char * Reader::getline (void) +char * Stream::getline (void) { bool eof; std::string ret; @@ -212,7 +212,7 @@ char * Reader::getline (void) return (strdup(ret.c_str())); } -std::string Reader::getline (bool &eof) +std::string Stream::getline (bool &eof) { char buf[1024]; std::string ret; @@ -235,50 +235,81 @@ std::string Reader::getline (bool &eof) return (ret); } -// snes9x.h STREAM reader +// snes9x.h FSTREAM Stream -fReader::fReader (STREAM f) +fStream::fStream (FSTREAM f) { fp = f; } -fReader::~fReader (void) +fStream::~fStream (void) { + CLOSE_FSTREAM(fp); return; } -int fReader::get_char (void) +int fStream::get_char (void) { - return (GETC_STREAM(fp)); + return (GETC_FSTREAM(fp)); } -char * fReader::gets (char *buf, size_t len) +char * fStream::gets (char *buf, size_t len) { - return (GETS_STREAM(buf, len, fp)); + return (GETS_FSTREAM(buf, len, fp)); } -size_t fReader::read (char *buf, size_t len) +size_t fStream::read (void *buf, size_t len) { - return (READ_STREAM(buf, len, fp)); + return (READ_FSTREAM(buf, len, fp)); } -// unzip reader +size_t fStream::write (void *buf, size_t len) +{ + return (WRITE_FSTREAM(buf, len, fp)); +} + +size_t fStream::pos (void) +{ + return (FIND_FSTREAM(fp)); +} + +size_t fStream::size (void) +{ + size_t sz; + REVERT_FSTREAM(fp,0L,SEEK_END); + sz = FIND_FSTREAM(fp); + REVERT_FSTREAM(fp,0L,SEEK_SET); + return sz; +} + +int fStream::revert (size_t from, size_t offset) +{ + return (REVERT_FSTREAM(fp, from, offset)); +} + +void fStream::closeStream() +{ + CLOSE_FSTREAM(fp); + delete this; +} + +// unzip Stream #ifdef UNZIP_SUPPORT -unzReader::unzReader (unzFile &v) +unzStream::unzStream (unzFile &v) { file = v; head = NULL; numbytes = 0; } -unzReader::~unzReader (void) +unzStream::~unzStream (void) { return; } -int unzReader::get_char (void) +int unzStream::get_char (void) { unsigned char c; @@ -297,7 +328,7 @@ int unzReader::get_char (void) return ((int) c); } -char * unzReader::gets (char *buf, size_t len) +char * unzStream::gets (char *buf, size_t len) { size_t i; int c; @@ -322,7 +353,7 @@ char * unzReader::gets (char *buf, size_t len) return (buf); } -size_t unzReader::read (char *buf, size_t len) +size_t unzStream::read (void *buf, size_t len) { if (len == 0) return (len); @@ -344,11 +375,219 @@ size_t unzReader::read (char *buf, size_t len) numbytes = 0; } - int l = unzReadCurrentFile(file, buf + numread, len - numread); + int l = unzReadCurrentFile(file, (uint8 *)buf + numread, len - numread); if (l > 0) numread += l; return (numread); } +// not supported +size_t unzStream::write (void *buf, size_t len) +{ + return (0); +} + +size_t unzStream::pos (void) +{ + return (unztell(file)); +} + +size_t unzStream::size (void) +{ + unz_file_info info; + unzGetCurrentFileInfo(file,&info,NULL,0,NULL,0,NULL,0); + return info.uncompressed_size; +} + +// not supported +int unzStream::revert (size_t from, size_t offset) +{ + return -1; +} + +void unzStream::closeStream() +{ + unzCloseCurrentFile(file); + delete this; +} + #endif + +// memory Stream + +memStream::memStream (uint8 *source, size_t sourceSize) +{ + mem = head = source; + msize = remaining = sourceSize; + readonly = false; +} + +memStream::memStream (const uint8 *source, size_t sourceSize) +{ + mem = head = const_cast(source); + msize = remaining = sourceSize; + readonly = true; +} + +memStream::~memStream (void) +{ + return; +} + +int memStream::get_char (void) +{ + if(!remaining) + return EOF; + + remaining--; + return *head++; +} + +char * memStream::gets (char *buf, size_t len) +{ + size_t i; + int c; + + for (i = 0; i < len - 1; i++) + { + c = get_char(); + if (c == EOF) + { + if (i == 0) + return (NULL); + break; + } + + buf[i] = (char) c; + if (buf[i] == '\n') + break; + } + + buf[i] = '\0'; + + return (buf); +} + +size_t memStream::read (void *buf, size_t len) +{ + size_t bytes = len < remaining ? len : remaining; + memcpy(buf,head,bytes); + head += bytes; + remaining -= bytes; + + return bytes; +} + +size_t memStream::write (void *buf, size_t len) +{ + if(readonly) + return 0; + + size_t bytes = len < remaining ? len : remaining; + memcpy(head,buf,bytes); + head += bytes; + remaining -= bytes; + + return bytes; +} + +size_t memStream::pos (void) +{ + return msize - remaining; +} + +size_t memStream::size (void) +{ + return msize; +} + +int memStream::revert (size_t from, size_t offset) +{ + size_t pos = from + offset; + + if(pos > msize) + return -1; + + head = mem + pos; + remaining = msize - pos; + + return 0; +} + +void memStream::closeStream() +{ + delete [] mem; + delete this; +} + +// dummy Stream + +nulStream::nulStream (void) +{ + bytes_written = 0; +} + +nulStream::~nulStream (void) +{ + return; +} + +int nulStream::get_char (void) +{ + return 0; +} + +char * nulStream::gets (char *buf, size_t len) +{ + *buf = '\0'; + return NULL; +} + +size_t nulStream::read (void *buf, size_t len) +{ + return 0; +} + +size_t nulStream::write (void *buf, size_t len) +{ + bytes_written += len; + return len; +} + +size_t nulStream::pos (void) +{ + return 0; +} + +size_t nulStream::size (void) +{ + return bytes_written; +} + +int nulStream::revert (size_t from, size_t offset) +{ + bytes_written = from + offset; + return 0; +} + +void nulStream::closeStream() +{ + delete this; +} + +Stream *openStreamFromFSTREAM(const char* filename, const char* mode) +{ + FSTREAM f = OPEN_FSTREAM(filename,mode); + if(!f) + return NULL; + return new fStream(f); +} + +Stream *reopenStreamFromFd(int fd, const char* mode) +{ + FSTREAM f = REOPEN_FSTREAM(fd,mode); + if(!f) + return NULL; + return new fStream(f); +} diff --git a/reader.h b/stream.h similarity index 73% rename from reader.h rename to stream.h index 80e92e4d..7a0e509a 100644 --- a/reader.h +++ b/stream.h @@ -176,46 +176,65 @@ ***********************************************************************************/ -#ifndef _READER_H_ -#define _READER_H_ +#ifndef _STREAM_H_ +#define _STREAM_H_ -class Reader +#include + +class Stream { public: - Reader (void); - virtual ~Reader (void); + Stream (void); + virtual ~Stream (void); virtual int get_char (void) = 0; virtual char * gets (char *, size_t) = 0; virtual char * getline (void); // free() when done virtual std::string getline (bool &); - virtual size_t read (char *, size_t) = 0; + virtual size_t read (void *, size_t) = 0; + virtual size_t write (void *, size_t) = 0; + virtual size_t pos (void) = 0; + virtual size_t size (void) = 0; + virtual int revert (size_t from, size_t offset) = 0; + virtual void closeStream() = 0; }; -class fReader : public Reader +class fStream : public Stream { public: - fReader (STREAM); - virtual ~fReader (void); + fStream (FSTREAM); + virtual ~fStream (void); virtual int get_char (void); virtual char * gets (char *, size_t); - virtual size_t read (char *, size_t); + virtual size_t read (void *, size_t); + virtual size_t write (void *, size_t); + virtual size_t pos (void); + virtual size_t size (void); + virtual int revert (size_t from, size_t offset); + virtual void closeStream(); private: - STREAM fp; + FSTREAM fp; }; #ifdef UNZIP_SUPPORT +#include "unzip.h" + #define unz_BUFFSIZ 1024 -class unzReader : public Reader +class unzStream : public Stream { public: - unzReader (unzFile &); - virtual ~unzReader (void); + unzStream (unzFile &); + virtual ~unzStream (void); virtual int get_char (void); virtual char * gets (char *, size_t); - virtual size_t read (char *, size_t); + virtual size_t read (void *, size_t); + virtual size_t write (void *, size_t); + virtual size_t pos (void); + virtual size_t size (void); + virtual int revert (size_t from, size_t offset); + virtual void closeStream(); private: unzFile file; @@ -226,4 +245,52 @@ class unzReader : public Reader #endif +class memStream : public Stream +{ + public: + memStream (uint8 *,size_t); + memStream (const uint8 *,size_t); + virtual ~memStream (void); + virtual int get_char (void); + virtual char * gets (char *, size_t); + virtual size_t read (void *, size_t); + virtual size_t write (void *, size_t); + virtual size_t pos (void); + virtual size_t size (void); + virtual int revert (size_t from, size_t offset); + virtual void closeStream(); + + private: + uint8 *mem; + size_t msize; + size_t remaining; + uint8 *head; + bool readonly; +}; + +/* dummy stream that always reads 0 and writes nowhere + but counts bytes written +*/ +class nulStream : public Stream +{ + public: + nulStream (void); + virtual ~nulStream (void); + virtual int get_char (void); + virtual char * gets (char *, size_t); + virtual size_t read (void *, size_t); + virtual size_t write (void *, size_t); + virtual size_t pos (void); + virtual size_t size (void); + virtual int revert (size_t from, size_t offset); + virtual void closeStream(); + + private: + size_t bytes_written; +}; + +Stream *openStreamFromFSTREAM(const char* filename, const char* mode); +Stream *reopenStreamFromFd(int fd, const char* mode); + + #endif diff --git a/unix/Makefile.in b/unix/Makefile.in index 36cf6050..ed12ed4b 100644 --- a/unix/Makefile.in +++ b/unix/Makefile.in @@ -7,7 +7,7 @@ OS = `uname -s -r -m|sed \"s/ /-/g\"|tr \"[A-Z]\" \"[a-z]\"|tr \"/()\" \"___\"` BUILDDIR = . -OBJECTS = ../apu/apu.o ../apu/SNES_SPC.o ../apu/SNES_SPC_misc.o ../apu/SNES_SPC_state.o ../apu/SPC_DSP.o ../apu/SPC_Filter.o ../bsx.o ../c4.o ../c4emu.o ../cheats.o ../cheats2.o ../clip.o ../conffile.o ../controls.o ../cpu.o ../cpuexec.o ../cpuops.o ../crosshairs.o ../dma.o ../dsp.o ../dsp1.o ../dsp2.o ../dsp3.o ../dsp4.o ../fxinst.o ../fxemu.o ../gfx.o ../globals.o ../logger.o ../memmap.o ../movie.o ../obc1.o ../ppu.o ../reader.o ../sa1.o ../sa1cpu.o ../screenshot.o ../sdd1.o ../sdd1emu.o ../seta.o ../seta010.o ../seta011.o ../seta018.o ../snapshot.o ../snes9x.o ../spc7110.o ../srtc.o ../tile.o ../filter/2xsai.o ../filter/blit.o ../filter/epx.o ../filter/hq2x.o ../filter/snes_ntsc.o unix.o x11.o +OBJECTS = ../apu/apu.o ../apu/SNES_SPC.o ../apu/SNES_SPC_misc.o ../apu/SNES_SPC_state.o ../apu/SPC_DSP.o ../apu/SPC_Filter.o ../bsx.o ../c4.o ../c4emu.o ../cheats.o ../cheats2.o ../clip.o ../conffile.o ../controls.o ../cpu.o ../cpuexec.o ../cpuops.o ../crosshairs.o ../dma.o ../dsp.o ../dsp1.o ../dsp2.o ../dsp3.o ../dsp4.o ../fxinst.o ../fxemu.o ../gfx.o ../globals.o ../logger.o ../memmap.o ../movie.o ../obc1.o ../ppu.o ../stream.o ../sa1.o ../sa1cpu.o ../screenshot.o ../sdd1.o ../sdd1emu.o ../seta.o ../seta010.o ../seta011.o ../seta018.o ../snapshot.o ../snes9x.o ../spc7110.o ../srtc.o ../tile.o ../filter/2xsai.o ../filter/blit.o ../filter/epx.o ../filter/hq2x.o ../filter/snes_ntsc.o unix.o x11.o DEFS = -DMITSHM ifdef S9XDEBUGGER diff --git a/win32/snes9xw.vcproj b/win32/snes9xw.vcproj index 454e4439..558c9b09 100644 --- a/win32/snes9xw.vcproj +++ b/win32/snes9xw.vcproj @@ -2082,42 +2082,6 @@ /> - - - - - - - - - - - - - - - - @@ -2902,6 +2866,42 @@ /> + + + + + + + + + + + + + + + +