Compare commits

..
116 Commits
Author SHA1 Message Date
Themaister ec7dab05e8 Small bloop fix :D 2011-09-02 20:50:51 +03:00
Themaister 9588f6061f Merge branch 'master' of github.com:Themaister/SSNES 2011-09-02 16:12:53 +02:00
Themaister 57c25dcb0b Should detect MacPorts. 2011-09-02 16:12:34 +02:00
Themaister 38763c4554 Merge branch 'master' of github.com:Themaister/SSNES 2011-09-01 10:07:24 +02:00
Themaister fac2ea00dd Update docs. 2011-09-01 10:07:07 +02:00
Themaister 74782a1bea Clarify. 2011-09-01 10:06:18 +02:00
Themaister c1265e7227 Update build script a bit. 2011-08-29 18:47:52 +02:00
Themaister f0841256e8 Fixup. 2011-08-29 17:23:49 +02:00
Themaister 600efab1bd Consistently use BGRA for possible performance gains. 2011-08-29 17:19:38 +02:00
Themaister dc9dd2e6bf Fix bug where shaders could crash using LUT textures. 2011-08-29 17:05:11 +02:00
Themaister de8fe0e88c Reword a bit. 2011-08-29 16:20:55 +02:00
Themaister 338432c35f Add build automated build script for MinGW. 2011-08-29 16:15:55 +02:00
Themaister 11aeb05a6e Do not drop netplay in SLIM build. 2011-08-28 22:43:50 +02:00
Themaister e28a32d922 Add in SDL.dll. 2011-08-28 22:31:11 +02:00
Themaister edac39cb2b Link explicitly to iconv. 2011-08-28 22:20:15 +02:00
Themaister c1954aa4a6 Fixups in Makefile. 2011-08-28 22:19:02 +02:00
Themaister ef048fcfd5 Remove cruft. 2011-08-28 21:57:24 +02:00
Themaister 48a1531d7c Start generalize the windows makefiles a bit. 2011-08-28 21:56:56 +02:00
Themaister 4b4686ae04 Merge ../SSNES-w64 2011-08-28 21:48:06 +02:00
Themaister cb4a54fefe Add SLIM targets. 2011-08-28 21:47:58 +02:00
Themaister 131b2386cd Merge ../SSNES-w64 2011-08-28 21:35:20 +02:00
Themaister 4d6d01228c Use static zlib. 2011-08-28 21:34:53 +02:00
Themaister a050955113 Merge ../SSNES-w64 2011-08-28 15:06:37 +02:00
Themaister b02789d280 Update build some more ... 2011-08-28 15:06:22 +02:00
Themaister 6f92d0588d Merge branch 'master' of github.com:Themaister/SSNES 2011-08-28 12:13:02 +02:00
Themaister 45df48ab67 Add to dist. 2011-08-28 12:12:15 +02:00
Themaister aba5c7722e Build with python and sdl_image as well. 2011-08-28 12:11:30 +02:00
Themaister 07a1a1ee2f Add python to build. 2011-08-28 11:16:38 +02:00
Themaister 3b952973de Start updating to 0.8. 2011-08-27 17:56:24 +02:00
Themaister ac4adebdf6 Small optimization. 2011-08-26 18:28:05 +02:00
Themaister 1bfd9716c5 Small refactor. 2011-08-26 17:32:04 +02:00
Themaister 702cf9c804 Optimize some more... 2011-08-25 16:29:17 +02:00
Themaister 5993fffb64 Add config_get_array() for simpler and faster config access. 2011-08-25 16:15:34 +02:00
Themaister 58758d2818 Check Cg with CXX. 2011-08-25 09:49:32 +02:00
Themaister 21f88fcf39 Obey library_dirs in makefile. 2011-08-24 16:24:58 +02:00
Themaister e3f029a446 Add /usr/lib64 to LIBRARY_DIRS. 2011-08-24 16:12:38 +02:00
Themaister 0694d8884f Allow configurable save files/states. 2011-08-24 15:47:39 +02:00
Themaister 0730a34a5b Reverting config path to system default if it doesn't find it. 2011-08-24 08:14:03 +02:00
Themaister f827bb4cbd Allow directory path for config files. 2011-08-24 07:54:52 +02:00
Themaister eafd8d91d7 Avoid spamming down stderr with autosave messages. 2011-08-23 21:04:00 +02:00
Themaister 56a4d1ddac Attempt to check for mysterious crashes :D 2011-08-23 17:10:02 +02:00
Themaister 8d3f15602c More robust recovery. 2011-08-22 18:45:38 +02:00
Themaister 7877c7939d Add backquote as alias to tilde ... 2011-08-22 18:26:39 +02:00
Themaister c55263b956 Clean up some file handling stuff ... 2011-08-22 18:13:30 +02:00
Themaister 96ce288120 Update docs to reflect new changes. 2011-08-22 17:44:37 +02:00
Themaister e5326467a1 Attempt to recover SRAM writes! 2011-08-22 17:39:29 +02:00
Themaister 596d8d9985 Attempt to recover SRAM write failures gracefully ... 2011-08-22 17:35:31 +02:00
Themaister b4b0be9118 Allow directories for -s/-S. Paths will be inferred. 2011-08-22 17:05:27 +02:00
Themaister 00e5f0fe97 Cleanup in cheat module. Do not use assert().
More rigid error checking for memory.
2011-08-21 20:48:27 +02:00
Themaister cb140c3691 Update XAudio driver to use floating point. Nits here and there. 2011-08-21 19:27:11 +02:00
Themaister ad85ccb42f Minor nit. 2011-08-20 03:28:24 +02:00
Themaister 97e50f15de Small cleanups. 2011-08-20 03:25:44 +02:00
Themaister 071c03e194 Do not link XAudio2 dynamically. Build with MinGW. 2011-08-20 03:22:19 +02:00
Themaister 6f47c21ce9 Fix capitalization. 2011-08-20 02:05:26 +02:00
Themaister 68bfda24ff Update dist. 2011-08-20 01:27:43 +02:00
Themaister 4f2fbcb9bc Update dist. 2011-08-20 00:30:43 +02:00
Themaister 90ab5dfc2f Add Cg as well. 2011-08-19 23:46:52 +02:00
Themaister 362cbe5deb Add RSound. 2011-08-19 19:22:52 +02:00
Themaister e133662346 FFmpeg as well. Small fixup. 2011-08-19 19:16:53 +02:00
Themaister 1fe83ab25a Use more proper .dll.a. 2011-08-19 18:50:05 +02:00
Themaister 1d95b5ee22 Update Makefile for 64-bit. 2011-08-19 18:39:05 +02:00
Themaister 9dc603d405 Update Makefile for freetype. 2011-08-19 14:35:09 +02:00
Themaister 5cb2863e6e Fix up Win64 warnings/errors. 2011-08-19 13:33:10 +02:00
Themaister 98cf4223ab Do not try to remove dead hqflt :D 2011-08-19 12:58:47 +02:00
Themaister ff7843cbe9 Update API usage of FFmpeg. Use 2 threads for encoding :) 2011-08-19 02:06:13 +02:00
Themaister c4d6b0b7c1 Update makefiles for BPS. 2011-08-18 00:45:10 +02:00
Themaister fb739621ac BPS seems to work ... :) 2011-08-18 00:37:43 +02:00
Themaister 24e8ca6856 Some more needed stuff ... 2011-08-18 00:24:57 +02:00
Themaister 1ff9b6a375 Start adding support for BPS patches. 2011-08-18 00:05:56 +02:00
Themaister cb6086635c Nit. 2011-08-16 12:11:44 +02:00
Themaister bce679df78 Only use hack on X when in windowed mode. 2011-08-16 10:26:36 +02:00
Themaister 22e8c3d895 Should fix breakage of OGL on OSX. 2011-08-15 22:21:50 +02:00
Themaister d2b06a92c5 0.7 baby! 2011-08-14 23:17:02 +02:00
Themaister 3901934d97 Add an ugly hack to try and remedy things ... 2011-08-14 23:03:48 +02:00
Themaister a76b25aff9 Fix ugly Jack crash with race condition. 2011-08-14 16:29:31 +02:00
Themaister 343852af26 Allocate before activation. 2011-08-14 16:24:42 +02:00
Themaister 4395f124ca Slight fixups. 2011-08-14 04:50:16 +02:00
Themaister b16ff3024c Add new DSP plugin API, events. 2011-08-13 04:09:08 +02:00
Themaister bc23b421e1 New resize checking code. 2011-08-12 19:02:12 +02:00
Themaister d9cb85a8d5 Add new possible workaround for tiling WMs ... 2011-08-11 15:19:30 +02:00
Themaister 7d1cc23d16 Fix build. 2011-08-11 06:02:05 +02:00
Themaister 4496ed3c13 Should make sizes right after startup ... 2011-08-11 05:59:32 +02:00
Themaister 3f5b6a450e Should fix behavior for tiled WMs. 2011-08-11 05:32:03 +02:00
Themaister 9e24474047 Can record hi-res and filtered output. 2011-08-11 05:25:31 +02:00
Themaister 7c17ede1ef Updates for 0.7beta1. 2011-08-09 23:04:13 +02:00
Themaister 5d9805c3e5 Small fix. 2011-08-09 22:07:11 +02:00
Themaister b17617bed5 Updated copyrights. 2011-08-09 12:58:13 +02:00
Themaister a405708f1c Huzzah, ordering matters. 2011-08-09 00:11:09 +02:00
Themaister 594e66704d Allow disabling composition in SDL as well. 2011-08-08 23:38:11 +02:00
Themaister 50579aaeea Floating point. 2011-08-08 17:45:49 +02:00
Themaister 38eb855b48 Works! :D 2011-08-08 17:41:55 +02:00
Themaister 12cc85c8a5 CoreAudio builds. 2011-08-08 17:31:03 +02:00
Themaister 5b12595c85 Merge branch 'master' of git://github.com/Themaister/SSNES 2011-08-08 17:29:06 +02:00
Themaister 3753f0bfca Merge branch 'master' of github.com:Themaister/SSNES 2011-08-08 17:28:32 +02:00
Themaister 14ae5e397a Add CoreAudio driver. Untested. 2011-08-08 17:27:52 +02:00
Themaister 657b4ce8c7 Merge branch 'master' of git://github.com/Themaister/SSNES 2011-08-08 14:47:47 +02:00
Themaister 777e6a5334 Better shader path semantics. 2011-08-07 23:35:54 +02:00
Themaister a2ec78320b Improve behavior a bit. 2011-08-07 22:31:59 +02:00
Themaister 1fb53627ac Make sure to free lib. 2011-08-07 21:18:51 +02:00
Themaister fc2a39cd58 More appropriate renaming. 2011-08-07 21:15:50 +02:00
Themaister 7425e1f879 Allow disabling composition in Win Vista/7Allow disabling composition in Win Vista/7. 2011-08-07 15:00:34 +02:00
Themaister 6a260a57dd Reset frame counter properly. 2011-08-06 03:28:07 +02:00
Themaister dadd794ece Remove useless define. 2011-08-05 19:18:43 +02:00
Themaister 706a3738b5 Abandon POT buffer sizes only for dsound.
Allows more fine tuned latencies.
2011-08-05 19:17:33 +02:00
Themaister 9aef33aabd More fixups in DSound. 2011-08-05 14:01:58 +02:00
Themaister ac4219bcde Small clarification. 2011-08-05 13:39:27 +02:00
Themaister b337856f80 More extensive error checking in dsound. 2011-08-05 13:35:02 +02:00
Themaister 079a0100c0 Remove unneeded call. 2011-08-05 03:06:48 +02:00
Themaister 014c3c90e0 Fix possible race condition. 2011-08-05 02:57:30 +02:00
Themaister 4759e3411f Fix DSound up some more in Windows ... :D 2011-08-05 02:05:32 +02:00
Themaister 4a05e50636 Minor progress. But no audio, hrm. 2011-08-04 19:11:14 +02:00
Themaister 72c070f338 Alter default ordering of audio drivers. 2011-08-04 18:52:00 +02:00
Themaister 776291a137 Start working on DirectSound driver. Untested so far. 2011-08-04 18:45:40 +02:00
Themaister f3cdb95b04 Fix endian issues in OSS driver. 2011-07-26 00:53:24 +02:00
Themaister 033860ceb9 Add -f/--fullscreen command line option. 2011-07-09 08:37:08 +02:00
Themaister 2eeb1fa6d1 Fix up VSync calls for OSX. 2011-06-29 03:45:17 +02:00
51 changed files with 2611 additions and 795 deletions

No files matched your search

+3
View File
@@ -3,3 +3,6 @@ Hans-Kristian Arntzen - <[email protected]>
Devin J. Pohly - <[email protected]>
- Joypad axis support
Chris Moeller - <[email protected]>
- CoreAudio audio driver
+9 -4
View File
@@ -2,8 +2,8 @@ include config.mk
TARGET = ssnes tools/ssnes-joyconfig
OBJ = ssnes.o file.o driver.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o strl.o screenshot.o
JOYCONFIG_OBJ = tools/ssnes-joyconfig.o conf/config_file.o
OBJ = ssnes.o file.o driver.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o bps.o strl.o screenshot.o
JOYCONFIG_OBJ = tools/ssnes-joyconfig.o conf/config_file.o strl.o
HEADERS = $(wildcard */*.h) $(wildcard *.h)
LIBS = -lm
@@ -83,6 +83,11 @@ ifeq ($(HAVE_PULSE), 1)
DEFINES += $(PULSE_CFLAGS)
endif
ifeq ($(HAVE_COREAUDIO), 1)
OBJ += audio/coreaudio.o
LIBS += -framework CoreServices -framework CoreAudio -framework AudioUnit
endif
ifeq ($(HAVE_SDL), 1)
OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o fifo_buffer.o
DEFINES += $(SDL_CFLAGS) $(BSD_LOCAL_INC)
@@ -167,11 +172,11 @@ config.mk: configure qb/*
@exit 1
ssnes: $(OBJ)
$(Q)$(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS)
$(Q)$(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS) $(LIBRARY_DIRS)
@$(if $(Q), $(shell echo echo LD $@),)
tools/ssnes-joyconfig: $(JOYCONFIG_OBJ)
$(Q)$(CC) -o $@ $(JOYCONFIG_OBJ) $(SDL_LIBS) $(LDFLAGS)
$(Q)$(CC) -o $@ $(JOYCONFIG_OBJ) $(SDL_LIBS) $(LDFLAGS) $(LIBRARY_DIRS)
@$(if $(Q), $(shell echo echo LD $@),)
%.o: %.c config.h config.mk $(HEADERS)
+55 -43
View File
@@ -1,46 +1,40 @@
TARGET = ssnes.exe
JTARGET = ssnes-joyconfig.exe
OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o strl.o screenshot.o
JOBJ = conf/config_file.o tools/main-stub.o tools/ssnes-joyconfig.o
OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o bps.o ups.o strl.o screenshot.o audio/hermite.o
JOBJ = conf/config_file.o tools/main-stub.o tools/ssnes-joyconfig.o strl.o
CC = gcc
CXX = g++
HAVE_DINPUT = 1
HAVE_SRC = 0
HAVE_SDL = 1
HAVE_SDL_IMAGE = 1
HAVE_XML = 1
HAVE_FREETYPE = 1
HAVE_XAUDIO = 1
HAVE_RSOUND = 1
HAVE_DSOUND = 1
HAVE_SDL = 1
HAVE_DYLIB = 1
HAVE_NETPLAY = 1
HAVE_FBO = 1
HAVE_CG = 1
HAVE_PYTHON = 1
HAVE_FFMPEG = 0
ifeq ($(SLIM),)
HAVE_SDL_IMAGE = 1
HAVE_XML = 1
HAVE_FREETYPE = 1
HAVE_RSOUND = 1
HAVE_FBO = 1
HAVE_CG = 1
HAVE_PYTHON = 1
HAVE_FFMPEG = 1
endif
libsnes ?= -lsnes
LIBS = -lm
LIBS = -lm -lshlwapi
DEFINES = -I. -DHAVE_CONFIGFILE
LDFLAGS = -L. -static-libgcc -s
SRC_LIBS = -lsamplerate-0
SDL_LIBS = -lSDL
SDL_CFLAGS = -ISDL
ifeq ($(HAVE_SRC), 1)
LIBS += $(SRC_LIBS)
DEFINES += $(SRC_CFLAGS) -DHAVE_SRC
else
OBJ += audio/hermite.o
endif
LDCXXFLAGS = -static-libstdc++
ifeq ($(HAVE_SDL), 1)
OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o fifo_buffer.o
LIBS += $(SDL_LIBS) -lopengl32
DEFINES += $(SDL_CFLAGS) -DHAVE_SDL
LIBS += -lSDL -lopengl32
DEFINES += -ISDL -DHAVE_SDL
endif
ifeq ($(HAVE_SDL_IMAGE), 1)
@@ -55,8 +49,15 @@ ifeq ($(HAVE_CG), 1)
endif
ifeq ($(HAVE_XAUDIO), 1)
OBJ += audio/xaudio.o
OBJ += audio/xaudio.o audio/xaudio-c/xaudio-c.o
DEFINES += -DHAVE_XAUDIO
LIBS += -lole32
endif
ifeq ($(HAVE_DSOUND), 1)
OBJ += audio/dsound.o
DEFINES += -DHAVE_DSOUND
LIBS += -ldxguid -ldsound
endif
ifeq ($(HAVE_RSOUND), 1)
@@ -67,8 +68,8 @@ endif
ifeq ($(HAVE_XML), 1)
OBJ += gfx/shader_glsl.o gfx/image.o gfx/snes_state.o sha256.o cheats.o
DEFINES += $(XML_CFLAGS) -DHAVE_XML
LIBS += -lxml2
DEFINES += -Ilibxml2 -DHAVE_XML
LIBS += -lxml2 -liconv
endif
ifeq ($(HAVE_DYLIB), 1)
@@ -84,7 +85,7 @@ endif
ifeq ($(HAVE_FREETYPE), 1)
OBJ += gfx/fonts.o
DEFINES += -DHAVE_FREETYPE -Ifreetype2
LIBS += -lz -lfreetype6
LIBS += -lfreetype -lz
endif
ifeq ($(DYNAMIC), 1)
@@ -109,13 +110,13 @@ endif
ifeq ($(HAVE_DINPUT), 1)
LIBS += -ldinput8 -ldxguid -lole32
DEFINES += -DHAVE_DINPUT -Iffmpeg
DEFINES += -DHAVE_DINPUT
OBJ += input/dinput.o
endif
ifeq ($(HAVE_FFMPEG), 1)
LIBS += -lavformat -lavcodec -lavutil -lswscale -lws2_32 -lzlib1
DEFINES += -DHAVE_FFMPEG
LIBS += -lavformat -lavcodec -lavutil -lswscale -lws2_32 -lz
DEFINES += -DHAVE_FFMPEG -Iffmpeg
OBJ += record/ffemu.o
endif
@@ -123,42 +124,53 @@ ifneq ($(V),1)
Q := @
endif
CFLAGS = -Wall -O3 -std=gnu99 -I.
CFLAGS += -Wall -O3 -std=gnu99 -I.
CXXFLAGS += -Wall -O3 -pedantic -I.
all: $(TARGET) $(JTARGET)
$(TARGET): $(OBJ)
$(Q)$(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS)
$(Q)$(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS) $(LDCXXFLAGS)
@$(if $(Q), $(shell echo echo LD $@),)
%.o: %.c
$(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
@$(if $(Q), $(shell echo echo CC $<),)
%.o: %.cpp
$(Q)$(CXX) $(CXXFLAGS) $(DEFINES) -c -o $@ $<
@$(if $(Q), $(shell echo echo CXX $<),)
$(JTARGET): $(JOBJ)
$(Q)$(CC) -o $@ $(JOBJ) $(SDL_LIBS) $(LDFLAGS)
$(Q)$(CC) -o $@ $(JOBJ) -lSDL $(LDFLAGS)
@$(if $(Q), $(shell echo echo LD $@),)
clean:
rm -f *.o
rm -f audio/*.o
rm -f audio/xaudio-c/*.o
rm -f conf/*.o
rm -f gfx/*.o
rm -f gfx/py_state/*.o
rm -f record/*.o
rm -f hqflt/*.o
rm -f input/*.o
rm -f hqflt/snes_ntsc/*.o
rm -f $(TARGET)
rm -f ssnes-joyconfig.exe
rm -f tools/*.o
dist: all
zip -r ssnes-win32-0.6.2.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll freetype6.dll xaudio-c.dll rsound.dll pthreadGC2.dll cg.dll cgGL.dll libjpeg-8.dll libpng15-15.dll python32.dll SDL_image.dll $(JTARGET)
dist_x86: all
zip -r ssnes-win32-0.8.zip $(TARGET) $(JTARGET) ssnes.cfg snes.dll SDL.dll
libs:
dist_x86_64: all
zip -r ssnes-win64-0.8.zip $(TARGET) $(JTARGET) ssnes.cfg snes.dll SDL.dll
libs_x86:
wget https://github.com/downloads/Themaister/SSNES/SSNES-win32-libs.zip --no-check-certificate
unzip SSNES-win32-libs.zip
.PHONY: all install uninstall clean dist libs
libs_x86_64:
wget https://github.com/downloads/Themaister/SSNES/SSNES-win64-libs.zip --no-check-certificate
unzip SSNES-win64-libs.zip
.PHONY: all install uninstall clean dist_x86 dist_x86_64 libs_x86 libs_x86_64
-151
View File
@@ -1,151 +0,0 @@
TARGET = ssnes.exe
JTARGET = ssnes-joyconfig.exe
OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o strl.o screenshot.o
JOBJ = conf/config_file.o tools/main-stub.o tools/ssnes-joyconfig.o
CC = gcc
CXX = g++
HAVE_DINPUT = 1
HAVE_SRC = 0
HAVE_SDL = 1
HAVE_XML = 1
HAVE_FREETYPE = 1
HAVE_XAUDIO = 1
HAVE_RSOUND = 0
HAVE_DYLIB = 1
HAVE_NETPLAY = 1
HAVE_FBO = 1
HAVE_CG = 0
HAVE_PYTHON = 0
libsnes ?= -lsnes
LIBS = -lm
DEFINES = -I. -DHAVE_CONFIGFILE
LDFLAGS = -L. -static-libgcc -s
SRC_LIBS = -lsamplerate-0
SDL_LIBS = -lmingw32 -lSDL -lSDLmain -lwinmm -mwindows -lm -luser32 -lgdi32 -lwinmm -ldxguid
SDL_CFLAGS = -ISDL
ifeq ($(HAVE_SRC), 1)
LIBS += $(SRC_LIBS)
DEFINES += $(SRC_CFLAGS) -DHAVE_SRC
else
OBJ += audio/hermite.o
endif
ifeq ($(HAVE_SDL), 1)
OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o fifo_buffer.o
LIBS += $(SDL_LIBS) -lopengl32
DEFINES += $(SDL_CFLAGS) -DHAVE_SDL
endif
ifeq ($(HAVE_CG), 1)
OBJ += gfx/shader_cg.o
LIBS += -lcg -lcgGL
DEFINES += -DHAVE_CG
endif
ifeq ($(HAVE_XAUDIO), 1)
OBJ += audio/xaudio.o
DEFINES += -DHAVE_XAUDIO
endif
ifeq ($(HAVE_RSOUND), 1)
OBJ += audio/rsound.o
DEFINES += -DHAVE_RSOUND
LIBS += -lrsound
endif
ifeq ($(HAVE_XML), 1)
OBJ += gfx/shader_glsl.o gfx/image.o gfx/snes_state.o sha256.o cheats.o
DEFINES += $(XML_CFLAGS) -DHAVE_XML -DLIBXML_STATIC
LIBS += -lxml2 -lz -lws2_32
endif
ifeq ($(HAVE_DYLIB), 1)
DEFINES += -DHAVE_DYLIB
endif
ifeq ($(HAVE_NETPLAY), 1)
DEFINES += -DHAVE_NETPLAY
OBJ += netplay.o
LIBS += -lws2_32
endif
ifeq ($(HAVE_FREETYPE), 1)
OBJ += gfx/fonts.o
DEFINES += -DHAVE_FREETYPE -Ifreetype2
LIBS += -lz -lfreetype6
endif
ifeq ($(DYNAMIC), 1)
DEFINES += -DHAVE_DYNAMIC
else
LIBS += $(libsnes)
endif
ifeq ($(HAVE_FBO), 1)
DEFINES += -DHAVE_FBO
endif
ifeq ($(HAVE_DYLIB), 1)
OBJ += gfx/ext.o audio/ext.o
endif
ifeq ($(HAVE_PYTHON), 1)
LIBS += -lpython32
DEFINES += -DHAVE_PYTHON -Ipython
OBJ += gfx/py_state/py_state.o
endif
ifeq ($(HAVE_DINPUT), 1)
LIBS += -ldinput8 -ldxguid -lole32
DEFINES += -DHAVE_DINPUT
OBJ += input/dinput.o
endif
ifneq ($(V),1)
Q := @
endif
CFLAGS = -Wall -O3 -std=gnu99 -I.
all: $(TARGET) $(JTARGET)
$(TARGET): $(OBJ)
$(Q)$(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS)
@$(if $(Q), $(shell echo echo LD $@),)
%.o: %.c
$(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
@$(if $(Q), $(shell echo echo CC $<),)
$(JTARGET): $(JOBJ)
$(Q)$(CC) -o $@ $(JOBJ) $(SDL_LIBS) $(LDFLAGS)
@$(if $(Q), $(shell echo echo LD $@),)
clean:
rm -f *.o
rm -f audio/*.o
rm -f conf/*.o
rm -f gfx/*.o
rm -f record/*.o
rm -f hqflt/*.o
rm -f input/*.o
rm -f hqflt/snes_ntsc/*.o
rm -f $(TARGET)
rm -f ssnes-joyconfig.exe
rm -f tools/*.o
dist: all
zip -r ssnes-win64-0.6.2.zip $(TARGET) ssnes.cfg snes.dll xaudio-c.dll README.win32.txt $(JTARGET)
libs:
wget https://github.com/downloads/Themaister/SSNES/ssnes-win64-libs.zip --no-check-certificate
unzip ssnes-win64-libs.zip
.PHONY: all install uninstall clean dist libs
+281
View File
@@ -0,0 +1,281 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
* Copyright (C) 2011 - Chris Moeller
*
* Some code herein may be based on code found in BSNES.
*
* SSNES is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "driver.h"
#include "general.h"
#include "fifo_buffer.h"
#include <stdlib.h>
#include <stdbool.h>
#include <pthread.h>
#include <CoreAudio/CoreAudio.h>
#include <CoreServices/CoreServices.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioUnit/AUComponent.h>
typedef struct coreaudio
{
pthread_mutex_t lock;
pthread_cond_t cond;
ComponentInstance dev;
bool dev_alive;
fifo_buffer_t *buffer;
bool nonblock;
} coreaudio_t;
static void coreaudio_free(void *data)
{
coreaudio_t *dev = data;
if (!dev)
return;
if (dev->dev_alive)
{
AudioOutputUnitStop(dev->dev);
CloseComponent(dev->dev);
}
if (dev->buffer)
fifo_free(dev->buffer);
pthread_mutex_destroy(&dev->lock);
pthread_cond_destroy(&dev->cond);
free(dev);
}
static OSStatus audio_cb(void *userdata, AudioUnitRenderActionFlags *action_flags,
const AudioTimeStamp *time_stamp, UInt32 bus_number,
UInt32 number_frames, AudioBufferList *io_data)
{
coreaudio_t *dev = userdata;
(void)time_stamp;
(void)bus_number;
(void)number_frames;
if (!io_data)
return noErr;
if (io_data->mNumberBuffers != 1)
return noErr;
unsigned write_avail = io_data->mBuffers[0].mDataByteSize;
void *outbuf = io_data->mBuffers[0].mData;
pthread_mutex_lock(&dev->lock);
if (fifo_read_avail(dev->buffer) < write_avail)
{
*action_flags = kAudioUnitRenderAction_OutputIsSilence;
memset(outbuf, 0, write_avail); // Seems to be needed.
pthread_mutex_unlock(&dev->lock);
pthread_cond_signal(&dev->cond); // Technically possible to deadlock without.
return noErr;
}
fifo_read(dev->buffer, outbuf, write_avail);
pthread_mutex_unlock(&dev->lock);
pthread_cond_signal(&dev->cond);
return noErr;
}
static void* coreaudio_init(const char* device, unsigned rate, unsigned latency)
{
(void)device;
coreaudio_t *dev = calloc(1, sizeof(*dev));
if (!dev)
return NULL;
CFRunLoopRef run_loop = NULL;
AudioObjectPropertyAddress addr = {
kAudioHardwarePropertyRunLoop,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
pthread_mutex_init(&dev->lock, NULL);
pthread_cond_init(&dev->cond, NULL);
AudioObjectSetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL,
sizeof(CFRunLoopRef), &run_loop);
ComponentDescription desc = {
.componentType = kAudioUnitType_Output,
.componentSubType = kAudioUnitSubType_HALOutput,
.componentManufacturer = kAudioUnitManufacturer_Apple,
};
Component comp = FindNextComponent(NULL, &desc);
if (comp == NULL)
goto error;
OSStatus res = OpenAComponent(comp, &dev->dev);
if (res != noErr)
goto error;
dev->dev_alive = true;
AudioStreamBasicDescription stream_desc = {
.mSampleRate = rate,
.mBitsPerChannel = sizeof(float) * CHAR_BIT,
.mChannelsPerFrame = 2,
.mBytesPerPacket = 2 * sizeof(float),
.mBytesPerFrame = 2 * sizeof(float),
.mFramesPerPacket = 1,
.mFormatID = kAudioFormatLinearPCM,
.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | (is_little_endian() ? 0 : kAudioFormatFlagIsBigEndian),
};
res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0, &stream_desc, sizeof(stream_desc));
if (res != noErr)
goto error;
AudioStreamBasicDescription real_desc;
UInt32 i_size = sizeof(real_desc);
res = AudioUnitGetProperty(dev->dev, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &real_desc, &i_size);
if (res != noErr)
goto error;
SSNES_LOG("[CoreAudio]: Using output sample rate of %.1f Hz\n", (float)real_desc.mSampleRate);
g_settings.audio.out_rate = real_desc.mSampleRate;
if (real_desc.mChannelsPerFrame != stream_desc.mChannelsPerFrame)
goto error;
if (real_desc.mBitsPerChannel != stream_desc.mBitsPerChannel)
goto error;
if (real_desc.mFormatFlags != stream_desc.mFormatFlags)
goto error;
if (real_desc.mFormatID != stream_desc.mFormatID)
goto error;
AudioChannelLayout layout = {
.mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelBitmap,
.mChannelBitmap = (1 << 2) - 1,
};
res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_AudioChannelLayout,
kAudioUnitScope_Input, 0, &layout, sizeof(layout));
if (res != noErr)
goto error;
AURenderCallbackStruct cb = {
.inputProc = audio_cb,
.inputProcRefCon = dev,
};
res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, 0, &cb, sizeof(cb));
if (res != noErr)
goto error;
res = AudioUnitInitialize(dev->dev);
if (res != noErr)
goto error;
size_t fifo_size = (latency * g_settings.audio.out_rate) / 1000;
fifo_size *= 2 * sizeof(float);
dev->buffer = fifo_new(fifo_size);
if (!dev->buffer)
goto error;
SSNES_LOG("[CoreAudio]: Using buffer size of %u bytes: (latency = %u ms)\n", (unsigned)fifo_size, latency);
res = AudioOutputUnitStart(dev->dev);
if (res != noErr)
goto error;
return dev;
error:
SSNES_ERR("[CoreAudio]: Failed to initialize driver ...\n");
coreaudio_free(dev);
return NULL;
}
static ssize_t coreaudio_write(void* data, const void* buf_, size_t size)
{
coreaudio_t *dev = data;
const uint8_t *buf = buf_;
size_t written = 0;
while (size > 0)
{
pthread_mutex_lock(&dev->lock);
size_t write_avail = fifo_write_avail(dev->buffer);
if (write_avail > size)
write_avail = size;
fifo_write(dev->buffer, buf, write_avail);
buf += write_avail;
written += write_avail;
size -= write_avail;
if (dev->nonblock)
{
pthread_mutex_unlock(&dev->lock);
break;
}
if (write_avail == 0)
pthread_cond_wait(&dev->cond, &dev->lock);
pthread_mutex_unlock(&dev->lock);
}
return written;
}
static bool coreaudio_stop(void *data)
{
coreaudio_t *dev = data;
return AudioOutputUnitStop(dev->dev) == noErr;
}
static void coreaudio_set_nonblock_state(void *data, bool state)
{
coreaudio_t *dev = data;
dev->nonblock = state;
}
static bool coreaudio_start(void *data)
{
coreaudio_t *dev = data;
return AudioOutputUnitStart(dev->dev) == noErr;
}
static bool coreaudio_use_float(void *data)
{
(void)data;
return true;
}
const audio_driver_t audio_coreaudio = {
.init = coreaudio_init,
.write = coreaudio_write,
.stop = coreaudio_stop,
.start = coreaudio_start,
.set_nonblock_state = coreaudio_set_nonblock_state,
.free = coreaudio_free,
.use_float = coreaudio_use_float,
.ident = "coreaudio"
};
+387
View File
@@ -0,0 +1,387 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
*
* Some code herein may be based on code found in BSNES.
*
* SSNES is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "driver.h"
#include <stdlib.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <dsound.h>
#include "fifo_buffer.h"
#include "general.h"
typedef struct dsound
{
LPDIRECTSOUND ds;
LPDIRECTSOUNDBUFFER dsb;
HANDLE event;
bool nonblock;
fifo_buffer_t *buffer;
CRITICAL_SECTION crit;
volatile bool thread_alive;
HANDLE thread;
unsigned buffer_size;
} dsound_t;
static inline unsigned write_avail(unsigned read_ptr, unsigned write_ptr, unsigned buffer_size)
{
return (read_ptr + buffer_size - write_ptr) % buffer_size;
}
static inline void get_positions(dsound_t *ds, DWORD *read_ptr, DWORD *write_ptr)
{
IDirectSoundBuffer_GetCurrentPosition(ds->dsb, read_ptr, write_ptr);
}
#define CHUNK_SIZE 256
struct audio_lock
{
void *chunk1;
DWORD size1;
void *chunk2;
DWORD size2;
};
static inline bool grab_region(dsound_t *ds, DWORD write_ptr, struct audio_lock *region)
{
HRESULT res = IDirectSoundBuffer_Lock(ds->dsb, write_ptr, CHUNK_SIZE, &region->chunk1, &region->size1, &region->chunk2, &region->size2, 0);
if (res == DSERR_BUFFERLOST)
{
res = IDirectSoundBuffer_Restore(ds->dsb);
if (res != DS_OK)
return false;
res = IDirectSoundBuffer_Lock(ds->dsb, write_ptr, CHUNK_SIZE, &region->chunk1, &region->size1, &region->chunk2, &region->size2, 0);
if (res != DS_OK)
return false;
}
const char *err;
switch (res)
{
case DSERR_BUFFERLOST:
err = "DSERR_BUFFERLOST";
break;
case DSERR_INVALIDCALL:
err = "DSERR_INVALIDCALL";
break;
case DSERR_INVALIDPARAM:
err = "DSERR_INVALIDPARAM";
break;
case DSERR_PRIOLEVELNEEDED:
err = "DSERR_PRIOLEVELNEEDED";
break;
default:
err = NULL;
}
if (err)
{
SSNES_WARN("[DirectSound error]: %s\n", err);
return false;
}
return true;
}
static inline void release_region(dsound_t *ds, const struct audio_lock *region)
{
IDirectSoundBuffer_Unlock(ds->dsb, region->chunk1, region->size1, region->chunk2, region->size2);
}
static DWORD CALLBACK dsound_thread(PVOID data)
{
dsound_t *ds = data;
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
DWORD write_ptr;
get_positions(ds, NULL, &write_ptr);
write_ptr = (write_ptr + ds->buffer_size / 2) % ds->buffer_size;
while (ds->thread_alive)
{
DWORD read_ptr;
get_positions(ds, &read_ptr, NULL);
DWORD avail = write_avail(read_ptr, write_ptr, ds->buffer_size);
EnterCriticalSection(&ds->crit);
DWORD fifo_avail = fifo_read_avail(ds->buffer);
LeaveCriticalSection(&ds->crit);
// No space to write, or we don't have data in our fifo, but we can wait some time before it underruns ...
if (avail < CHUNK_SIZE || ((fifo_avail < CHUNK_SIZE) && (avail < ds->buffer_size / 2)))
{
Sleep(1);
// We could opt for using the notification interface,
// but it is not guaranteed to work, so use high priority sleeping patterns. :(
}
else if (fifo_avail < CHUNK_SIZE) // Got space to write, but nothing in FIFO (underrun), fill block with silence.
{
struct audio_lock region;
if (!grab_region(ds, write_ptr, &region))
{
ds->thread_alive = false;
SetEvent(ds->event);
break;
}
memset(region.chunk1, 0, region.size1);
memset(region.chunk2, 0, region.size2);
release_region(ds, &region);
write_ptr = (write_ptr + region.size1 + region.size2) % ds->buffer_size;
}
else // All is good. Pull from it and notify FIFO :D
{
struct audio_lock region;
if (!grab_region(ds, write_ptr, &region))
{
ds->thread_alive = false;
SetEvent(ds->event);
break;
}
EnterCriticalSection(&ds->crit);
if (region.chunk1)
fifo_read(ds->buffer, region.chunk1, region.size1);
if (region.chunk2)
fifo_read(ds->buffer, region.chunk2, region.size2);
LeaveCriticalSection(&ds->crit);
release_region(ds, &region);
write_ptr = (write_ptr + region.size1 + region.size2) % ds->buffer_size;
SetEvent(ds->event);
}
}
ExitThread(0);
}
static void dsound_stop_thread(dsound_t *ds)
{
if (ds->thread)
{
ds->thread_alive = false;
WaitForSingleObject(ds->thread, INFINITE);
CloseHandle(ds->thread);
ds->thread = NULL;
}
}
static bool dsound_start_thread(dsound_t *ds)
{
if (!ds->thread)
{
ds->thread_alive = true;
ds->thread = CreateThread(NULL, 0, dsound_thread, ds, 0, NULL);
if (ds->thread == NULL)
return false;
}
return true;
}
static void dsound_clear_buffer(dsound_t *ds)
{
IDirectSoundBuffer_SetCurrentPosition(ds->dsb, 0);
void *ptr;
DWORD size;
if (IDirectSoundBuffer_Lock(ds->dsb, 0, 0, &ptr, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER) == DS_OK)
{
memset(ptr, 0, size);
IDirectSoundBuffer_Unlock(ds->dsb, ptr, size, NULL, 0);
}
}
static void dsound_free(void *data)
{
dsound_t *ds = data;
if (ds)
{
if (ds->thread)
{
ds->thread_alive = false;
WaitForSingleObject(ds->thread, INFINITE);
CloseHandle(ds->thread);
}
DeleteCriticalSection(&ds->crit);
if (ds->dsb)
{
IDirectSoundBuffer_Stop(ds->dsb);
IDirectSoundBuffer_Release(ds->dsb);
}
if (ds)
IDirectSound_Release(ds->ds);
if (ds->event)
CloseHandle(ds->event);
if (ds->buffer)
fifo_free(ds->buffer);
free(ds);
}
}
static void* dsound_init(const char *device, unsigned rate, unsigned latency)
{
dsound_t *ds = calloc(1, sizeof(*ds));
if (!ds)
goto error;
InitializeCriticalSection(&ds->crit);
if (DirectSoundCreate(NULL, &ds->ds, NULL) != DS_OK)
goto error;
if (IDirectSound_SetCooperativeLevel(ds->ds, GetDesktopWindow(), DSSCL_PRIORITY) != DS_OK)
goto error;
WAVEFORMATEX wfx = {
.wFormatTag = WAVE_FORMAT_PCM,
.nChannels = 2,
.nSamplesPerSec = rate,
.wBitsPerSample = 16,
.nBlockAlign = 2 * sizeof(int16_t),
.nAvgBytesPerSec = rate * 2 * sizeof(int16_t),
};
ds->buffer_size = (latency * wfx.nAvgBytesPerSec) / 1000;
ds->buffer_size /= CHUNK_SIZE;
ds->buffer_size *= CHUNK_SIZE;
if (ds->buffer_size < 4 * CHUNK_SIZE)
ds->buffer_size = 4 * CHUNK_SIZE;
SSNES_LOG("[DirectSound]: Setting buffer size of %u bytes\n", ds->buffer_size);
SSNES_LOG("[DirectSound]: Latency = %u ms\n", (unsigned)((1000 * ds->buffer_size) / wfx.nAvgBytesPerSec));
DSBUFFERDESC bufdesc = {
.dwSize = sizeof(DSBUFFERDESC),
.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS,
.dwBufferBytes = ds->buffer_size,
.lpwfxFormat = &wfx,
};
ds->event = CreateEvent(NULL, false, false, NULL);
if (!ds->event)
goto error;
ds->buffer = fifo_new(4 * 1024);
if (!ds->buffer)
goto error;
if (IDirectSound_CreateSoundBuffer(ds->ds, &bufdesc, &ds->dsb, 0) != DS_OK)
goto error;
IDirectSoundBuffer_SetCurrentPosition(ds->dsb, 0);
dsound_clear_buffer(ds);
if (IDirectSoundBuffer_Play(ds->dsb, 0, 0, DSBPLAY_LOOPING) != DS_OK)
goto error;
if (!dsound_start_thread(ds))
goto error;
return ds;
error:
SSNES_ERR("[DirectSound] Error occured in init!\n");
dsound_free(ds);
return NULL;
}
static bool dsound_stop(void *data)
{
dsound_t *ds = data;
dsound_stop_thread(ds);
return IDirectSoundBuffer_Stop(ds->dsb) == DS_OK;
}
static bool dsound_start(void *data)
{
dsound_t *ds = data;
dsound_clear_buffer(ds);
if (!dsound_start_thread(ds))
return false;
return IDirectSoundBuffer_Play(ds->dsb, 0, 0, DSBPLAY_LOOPING) == DS_OK;
}
static void dsound_set_nonblock_state(void *data, bool state)
{
dsound_t *ds = data;
ds->nonblock = state;
}
static ssize_t dsound_write(void *data, const void *buf_, size_t size)
{
dsound_t *ds = data;
const uint8_t *buf = buf_;
if (!ds->thread_alive)
return -1;
size_t written = 0;
while (size > 0)
{
EnterCriticalSection(&ds->crit);
size_t avail = fifo_write_avail(ds->buffer);
if (avail > size)
avail = size;
fifo_write(ds->buffer, buf, avail);
LeaveCriticalSection(&ds->crit);
buf += avail;
size -= avail;
written += avail;
if (ds->nonblock || !ds->thread_alive)
break;
if (avail == 0)
WaitForSingleObject(ds->event, INFINITE);
}
return written;
}
const audio_driver_t audio_dsound = {
.init = dsound_init,
.write = dsound_write,
.stop = dsound_stop,
.start = dsound_start,
.set_nonblock_state = dsound_set_nonblock_state,
.free = dsound_free,
.ident = "dsound"
};
+7 -2
View File
@@ -30,7 +30,7 @@ extern "C" {
#define SSNES_TRUE 1
#endif
#define SSNES_DSP_API_VERSION 2
#define SSNES_DSP_API_VERSION 3
typedef struct ssnes_dsp_info
{
@@ -94,7 +94,7 @@ typedef struct ssnes_dsp_plugin
void (*process)(void *data, ssnes_dsp_output_t *output,
const ssnes_dsp_input_t *input);
// Frees the handle.
// Frees the handle.
void (*free)(void *data);
// API version used to compile the plugin.
@@ -110,6 +110,11 @@ typedef struct ssnes_dsp_plugin
// Human readable identification string.
const char *ident;
// Called every frame, allows creating a GUI main loop in the main thread.
// GUI events can be processed here in a non-blocking fashion.
// Can be set to NULL to ignore it.
void (*events)(void *data);
} ssnes_dsp_plugin_t;
// Called by SSNES at startup to get the callback struct.
+17 -18
View File
@@ -127,9 +127,12 @@ static size_t find_buffersize(jack_t *jd, int latency)
static void* __jack_init(const char* device, unsigned rate, unsigned latency)
{
jack_t *jd = calloc(1, sizeof(jack_t));
if ( jd == NULL )
if (!jd)
return NULL;
pthread_cond_init(&jd->cond, NULL);
pthread_mutex_init(&jd->cond_lock, NULL);
const char **jports = NULL;
jd->client = jack_client_open("SSNES", JackNullOption, NULL);
@@ -148,7 +151,6 @@ static void* __jack_init(const char* device, unsigned rate, unsigned latency)
SSNES_ERR("Failed to register ports.\n");
goto error;
}
char *dest_ports[2];
jports = jack_get_ports(jd->client, NULL, NULL, JackPortIsPhysical | JackPortIsInput);
@@ -158,6 +160,18 @@ static void* __jack_init(const char* device, unsigned rate, unsigned latency)
goto error;
}
size_t bufsize = find_buffersize(jd, latency);
SSNES_LOG("JACK: Internal buffer size: %d frames.\n", (int)(bufsize / sizeof(jack_default_audio_sample_t)));
for (int i = 0; i < 2; i++)
{
jd->buffer[i] = jack_ringbuffer_create(bufsize);
if (jd->buffer[i] == NULL)
{
SSNES_ERR("Failed to create buffers.\n");
goto error;
}
}
int parsed = parse_ports(dest_ports, jports);
if (jack_activate(jd->client) < 0)
@@ -178,22 +192,7 @@ static void* __jack_init(const char* device, unsigned rate, unsigned latency)
for (int i = 0; i < parsed; i++)
free(dest_ports[i]);
size_t bufsize = find_buffersize(jd, latency);
SSNES_LOG("JACK: Internal buffer size: %d frames.\n", (int)(bufsize / sizeof(jack_default_audio_sample_t)));
for (int i = 0; i < 2; i++)
{
jd->buffer[i] = jack_ringbuffer_create(bufsize);
if (jd->buffer[i] == NULL)
{
SSNES_ERR("Failed to create buffers.\n");
goto error;
}
}
pthread_cond_init(&jd->cond, NULL);
pthread_mutex_init(&jd->cond_lock, NULL);
jack_free(jports);
return jd;
+3 -1
View File
@@ -20,6 +20,7 @@
#endif
#include "driver.h"
#include "general.h"
#include <stdlib.h>
#ifdef HAVE_OSS_BSD
@@ -68,7 +69,8 @@ static void* __oss_init(const char* device, unsigned rate, unsigned latency)
}
int channels = 2;
int format = AFMT_S16_LE;
int format = is_little_endian() ?
AFMT_S16_LE : AFMT_S16_BE;
if (ioctl(*fd, SNDCTL_DSP_CHANNELS, &channels) < 0)
{
-12
View File
@@ -59,18 +59,6 @@ static void __pulse_free(void *data)
}
}
static inline uint8_t is_little_endian(void)
{
union
{
uint16_t x;
uint8_t y[2];
} u;
u.x = 1;
return u.y[0];
}
static void context_state_cb(pa_context *c, void *data)
{
pa_t *pa = data;
-5
View File
@@ -196,9 +196,4 @@ const audio_driver_t audio_sdl = {
.free = sdl_audio_free,
.ident = "sdl"
};
-160
View File
@@ -1,160 +0,0 @@
/*
Simple C interface for XAudio2
Author: Hans-Kristian Arntzen
License: Public Domain
*/
// This will currently only compile with MSVC2k10.
// Should be compilable with some MinGW trickery as well.
#include "xaudio-c.h"
struct xaudio2 : public IXAudio2VoiceCallback
{
xaudio2(unsigned samplerate, unsigned channels, unsigned bits, unsigned size) :
buf(nullptr), pXAudio2(nullptr), pMasterVoice(nullptr), pSourceVoice(nullptr),
buffers(0), bufptr(0)
{
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
HRESULT hr;
if (FAILED(hr = XAudio2Create(&pXAudio2, 0)))
throw -1;
if (FAILED(hr = pXAudio2->CreateMasteringVoice(
&pMasterVoice, channels, samplerate, 0, 0, nullptr)))
throw -1;
WAVEFORMATEX wfx;
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = channels;
wfx.nSamplesPerSec = samplerate;
wfx.nBlockAlign = channels * bits / 8;
wfx.wBitsPerSample = bits;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
wfx.cbSize = 0;
if (FAILED(hr = pXAudio2->CreateSourceVoice(
&pSourceVoice, &wfx, XAUDIO2_VOICE_NOSRC, XAUDIO2_DEFAULT_FREQ_RATIO, this, nullptr, nullptr)))
throw -1;
pSourceVoice->Start();
hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (!hEvent)
throw -1;
max_buffers = 8;
bufsize = size / max_buffers;
buf = new uint8_t[bufsize * max_buffers];
write_buffer = 0;
}
~xaudio2()
{
if (pSourceVoice)
{
pSourceVoice->Stop();
pSourceVoice->DestroyVoice();
}
if (pMasterVoice)
pMasterVoice->DestroyVoice();
if (pXAudio2)
pXAudio2->Release();
if (hEvent)
CloseHandle(hEvent);
if (buf)
delete[] buf;
}
size_t write_avail() const
{
return bufsize * (max_buffers - buffers - 1);
}
size_t write(const void *buffer, size_t bytes)
{
size_t written = 0;
while (written < bytes)
{
size_t need = min(bytes - written, bufsize - bufptr);
memcpy((uint8_t*)buf + write_buffer * bufsize + bufptr, (const uint8_t*)buffer + written, need);
written += need;
bufptr += need;
if (bufptr == bufsize)
{
while (buffers == max_buffers - 1)
WaitForSingleObject(hEvent, INFINITE);
XAUDIO2_BUFFER xa2buffer = {0};
xa2buffer.AudioBytes = bufsize;
xa2buffer.pAudioData = buf + write_buffer * bufsize;
xa2buffer.pContext = nullptr;
InterlockedIncrement(&buffers);
pSourceVoice->SubmitSourceBuffer(&xa2buffer);
bufptr = 0;
write_buffer = (write_buffer + 1) % max_buffers;
}
}
return bytes;
}
STDMETHOD_(void, OnBufferStart) (void *) {}
STDMETHOD_(void, OnBufferEnd) (void *)
{
InterlockedDecrement(&buffers);
SetEvent(hEvent);
}
STDMETHOD_(void, OnLoopEnd) (void *) {}
STDMETHOD_(void, OnStreamEnd) () {}
STDMETHOD_(void, OnVoiceError) (void *, HRESULT) {}
STDMETHOD_(void, OnVoiceProcessingPassEnd) ()
{
SetEvent(hEvent);
}
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) {}
uint8_t *buf;
IXAudio2 *pXAudio2;
IXAudio2MasteringVoice *pMasterVoice;
IXAudio2SourceVoice *pSourceVoice;
HANDLE hEvent;
volatile long buffers;
unsigned max_buffers;
unsigned bufsize;
unsigned bufptr;
unsigned write_buffer;
};
XAUDIOC_API xaudio2_t *xaudio2_new(unsigned samplerate, unsigned channels, unsigned bits, size_t size)
{
xaudio2 *handle = nullptr;
try {
handle = new xaudio2(samplerate, channels, bits, size);
} catch(...) { delete handle; return nullptr; }
return handle;
}
XAUDIOC_API size_t xaudio2_write_avail(xaudio2_t *handle)
{
try {
return handle->write_avail();
} catch(...) { return 0; }
}
XAUDIOC_API size_t xaudio2_write(xaudio2_t *handle, const void *buf, size_t bytes)
{
try {
return handle->write(buf, bytes);
} catch(...) { return 0; }
}
XAUDIOC_API void xaudio2_free(xaudio2_t *handle)
{
delete handle;
}
-38
View File
@@ -1,38 +0,0 @@
/*
Simple C interface for XAudio2
Author: Hans-Kristian Arntzen
License: Public Domain
*/
#ifndef XAUDIO_C_H
#define XAUDIO_C_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef XAUDIOC_EXPORTS
#define XAUDIOC_API __declspec(dllexport)
#else
#define XAUDIOC_API __declspec(dllimport)
#endif
typedef struct xaudio2 xaudio2_t;
XAUDIOC_API xaudio2_t* xaudio2_new(unsigned samplerate, unsigned channels, unsigned bits, size_t bufsize);
XAUDIOC_API size_t xaudio2_write_avail(xaudio2_t *handle);
XAUDIOC_API size_t xaudio2_write(xaudio2_t *handle, const void *data, size_t bytes);
XAUDIOC_API void xaudio2_free(xaudio2_t *handle);
typedef xaudio2_t* (*xaudio2_new_t)(unsigned, unsigned, unsigned, size_t);
typedef size_t (*xaudio2_write_avail_t)(xaudio2_t*);
typedef size_t (*xaudio2_write_t)(xaudio2_t*, const void*, size_t);
typedef void (*xaudio2_free_t)(xaudio2_t*);
#ifdef __cplusplus
}
#endif
#endif
+164
View File
@@ -0,0 +1,164 @@
/*
Simple C interface for XAudio2
Author: Hans-Kristian Arntzen
License: Public Domain
*/
#include "xaudio-c.h"
#include "xaudio2.hpp"
#include <stdint.h>
#include <algorithm>
struct xaudio2 : public IXAudio2VoiceCallback
{
xaudio2(unsigned samplerate, unsigned channels, unsigned size) :
buf(0), pXAudio2(0), pMasterVoice(0), pSourceVoice(0),
buffers(0), bufptr(0)
{
CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(XAudio2Create(&pXAudio2, 0)))
throw -1;
if (FAILED(pXAudio2->CreateMasteringVoice(
&pMasterVoice, channels, samplerate, 0, 0, 0)))
throw -1;
WAVEFORMATEX wfx;
wfx.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
wfx.nChannels = channels;
wfx.nSamplesPerSec = samplerate;
wfx.nBlockAlign = channels * sizeof(float);
wfx.wBitsPerSample = sizeof(float) * 8;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
wfx.cbSize = 0;
if (FAILED(pXAudio2->CreateSourceVoice(
&pSourceVoice, &wfx, XAUDIO2_VOICE_NOSRC, XAUDIO2_DEFAULT_FREQ_RATIO, this, 0, 0)))
throw -1;
hEvent = CreateEvent(0, FALSE, FALSE, 0);
if (!hEvent)
throw -1;
pSourceVoice->Start(0);
bufsize = size / max_buffers;
buf = new uint8_t[bufsize * max_buffers];
write_buffer = 0;
}
virtual ~xaudio2()
{
if (pSourceVoice)
{
pSourceVoice->Stop(0);
pSourceVoice->DestroyVoice();
}
if (pMasterVoice)
pMasterVoice->DestroyVoice();
if (pXAudio2)
pXAudio2->Release();
if (hEvent)
CloseHandle(hEvent);
if (buf)
delete[] buf;
}
size_t write_avail() const
{
return bufsize * (max_buffers - buffers - 1);
}
size_t write(const void *buffer_, size_t bytes_)
{
unsigned bytes = bytes_;
const uint8_t *buffer = reinterpret_cast<const uint8_t*>(buffer_);
while (bytes > 0)
{
unsigned need = std::min(bytes, bufsize - bufptr);
memcpy(buf + write_buffer * bufsize + bufptr,
buffer, need);
bufptr += need;
buffer += need;
bytes -= need;
if (bufptr == bufsize)
{
WaitForSingleObject(hEvent, 0); // Clear out ready state.
while (buffers == max_buffers - 1)
WaitForSingleObject(hEvent, INFINITE);
XAUDIO2_BUFFER xa2buffer = {0};
xa2buffer.AudioBytes = bufsize;
xa2buffer.pAudioData = buf + write_buffer * bufsize;
xa2buffer.pContext = 0;
if (FAILED(pSourceVoice->SubmitSourceBuffer(&xa2buffer)))
return 0;
InterlockedIncrement(&buffers);
bufptr = 0;
write_buffer = (write_buffer + 1) & (max_buffers_mask);
}
}
return bytes_;
}
enum { max_buffers = 16, max_buffers_mask = max_buffers - 1 };
STDMETHOD_(void, OnBufferStart) (void *) {}
STDMETHOD_(void, OnBufferEnd) (void *)
{
InterlockedDecrement(&buffers);
SetEvent(hEvent);
}
STDMETHOD_(void, OnLoopEnd) (void *) {}
STDMETHOD_(void, OnStreamEnd) () {}
STDMETHOD_(void, OnVoiceError) (void *, HRESULT) {}
STDMETHOD_(void, OnVoiceProcessingPassEnd) () {}
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) {}
uint8_t *buf;
IXAudio2 *pXAudio2;
IXAudio2MasteringVoice *pMasterVoice;
IXAudio2SourceVoice *pSourceVoice;
HANDLE hEvent;
volatile long buffers;
unsigned bufsize;
unsigned bufptr;
unsigned write_buffer;
};
xaudio2_t *xaudio2_new(unsigned samplerate, unsigned channels, size_t size)
{
xaudio2 *handle = 0;
try {
handle = new xaudio2(samplerate, channels, size);
} catch(...) { delete handle; return 0; }
return handle;
}
size_t xaudio2_write_avail(xaudio2_t *handle)
{
try {
return handle->write_avail();
} catch(...) { return 0; }
}
size_t xaudio2_write(xaudio2_t *handle, const void *buf, size_t bytes)
{
try {
return handle->write(buf, bytes);
} catch(...) { return 0; }
}
void xaudio2_free(xaudio2_t *handle)
{
delete handle;
}
+27
View File
@@ -0,0 +1,27 @@
/*
Simple C interface for XAudio2
Author: Hans-Kristian Arntzen
License: Public Domain
*/
#ifndef XAUDIO_C_H
#define XAUDIO_C_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct xaudio2 xaudio2_t;
xaudio2_t* xaudio2_new(unsigned samplerate, unsigned channels, size_t bufsize);
size_t xaudio2_write_avail(xaudio2_t *handle);
size_t xaudio2_write(xaudio2_t *handle, const void *data, size_t bytes);
void xaudio2_free(xaudio2_t *handle);
#ifdef __cplusplus
}
#endif
#endif
+329
View File
@@ -0,0 +1,329 @@
/*
xaudio2.hpp (2010-08-14)
author: OV2
*/
#ifndef XAUDIO2_MINGW_H
#define XAUDIO2_MINGW_H
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef min
#undef max
#include <basetyps.h>
#include <objbase.h>
#include <mmreg.h>
// 64-bit GCC fix
#define GUID_EXT EXTERN_C
#define GUID_SECT
#define DEFINE_GUID_X(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) GUID_EXT const GUID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
#define DEFINE_CLSID_X(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
DEFINE_GUID_X(CLSID_##className, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
#define DEFINE_IID_X(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
DEFINE_GUID_X(IID_##interfaceName, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
#define X2DEFAULT(x) =x
DEFINE_CLSID_X(XAudio2, e21a7345, eb21, 468e, be, 50, 80, 4d, b9, 7c, f7, 08);
DEFINE_CLSID_X(XAudio2_Debug, f7a76c21, 53d4, 46bb, ac, 53, 8b, 45, 9c, ae, 46, bd);
DEFINE_IID_X(IXAudio2, 8bcf1f58, 9fe7, 4583, 8a, c6, e2, ad, c4, 65, c8, bb);
DECLARE_INTERFACE(IXAudio2Voice);
#define XAUDIO2_COMMIT_NOW 0
#define XAUDIO2_DEFAULT_CHANNELS 0
#define XAUDIO2_DEFAULT_SAMPLERATE 0
#define XAUDIO2_DEFAULT_FREQ_RATIO 4.0f
#define XAUDIO2_DEBUG_ENGINE 0x0001
#define XAUDIO2_VOICE_NOSRC 0x0004
typedef enum XAUDIO2_DEVICE_ROLE
{
NotDefaultDevice = 0x0,
DefaultConsoleDevice = 0x1,
DefaultMultimediaDevice = 0x2,
DefaultCommunicationsDevice = 0x4,
DefaultGameDevice = 0x8,
GlobalDefaultDevice = 0xf,
InvalidDeviceRole = ~GlobalDefaultDevice
} XAUDIO2_DEVICE_ROLE;
typedef struct XAUDIO2_DEVICE_DETAILS
{
WCHAR DeviceID[256];
WCHAR DisplayName[256];
XAUDIO2_DEVICE_ROLE Role;
WAVEFORMATEXTENSIBLE OutputFormat;
} XAUDIO2_DEVICE_DETAILS;
typedef struct XAUDIO2_VOICE_DETAILS
{
UINT32 CreationFlags;
UINT32 InputChannels;
UINT32 InputSampleRate;
} XAUDIO2_VOICE_DETAILS;
typedef enum XAUDIO2_WINDOWS_PROCESSOR_SPECIFIER
{
Processor1 = 0x00000001,
Processor2 = 0x00000002,
Processor3 = 0x00000004,
Processor4 = 0x00000008,
Processor5 = 0x00000010,
Processor6 = 0x00000020,
Processor7 = 0x00000040,
Processor8 = 0x00000080,
Processor9 = 0x00000100,
Processor10 = 0x00000200,
Processor11 = 0x00000400,
Processor12 = 0x00000800,
Processor13 = 0x00001000,
Processor14 = 0x00002000,
Processor15 = 0x00004000,
Processor16 = 0x00008000,
Processor17 = 0x00010000,
Processor18 = 0x00020000,
Processor19 = 0x00040000,
Processor20 = 0x00080000,
Processor21 = 0x00100000,
Processor22 = 0x00200000,
Processor23 = 0x00400000,
Processor24 = 0x00800000,
Processor25 = 0x01000000,
Processor26 = 0x02000000,
Processor27 = 0x04000000,
Processor28 = 0x08000000,
Processor29 = 0x10000000,
Processor30 = 0x20000000,
Processor31 = 0x40000000,
Processor32 = 0x80000000,
XAUDIO2_ANY_PROCESSOR = 0xffffffff,
XAUDIO2_DEFAULT_PROCESSOR = XAUDIO2_ANY_PROCESSOR
} XAUDIO2_WINDOWS_PROCESSOR_SPECIFIER, XAUDIO2_PROCESSOR;
typedef struct XAUDIO2_VOICE_SENDS
{
UINT32 OutputCount;
IXAudio2Voice** pOutputVoices;
} XAUDIO2_VOICE_SENDS;
typedef struct XAUDIO2_EFFECT_DESCRIPTOR
{
IUnknown* pEffect;
BOOL InitialState;
UINT32 OutputChannels;
} XAUDIO2_EFFECT_DESCRIPTOR;
typedef struct XAUDIO2_EFFECT_CHAIN
{
UINT32 EffectCount;
const XAUDIO2_EFFECT_DESCRIPTOR* pEffectDescriptors;
} XAUDIO2_EFFECT_CHAIN;
typedef enum XAUDIO2_FILTER_TYPE
{
LowPassFilter,
BandPassFilter,
HighPassFilter
} XAUDIO2_FILTER_TYPE;
typedef struct XAUDIO2_FILTER_PARAMETERS
{
XAUDIO2_FILTER_TYPE Type;
float Frequency;
float OneOverQ;
} XAUDIO2_FILTER_PARAMETERS;
typedef struct XAUDIO2_BUFFER
{
UINT32 Flags;
UINT32 AudioBytes;
const BYTE* pAudioData;
UINT32 PlayBegin;
UINT32 PlayLength;
UINT32 LoopBegin;
UINT32 LoopLength;
UINT32 LoopCount;
void* pContext;
} XAUDIO2_BUFFER;
typedef struct XAUDIO2_BUFFER_WMA
{
const UINT32* pDecodedPacketCumulativeBytes;
UINT32 PacketCount;
} XAUDIO2_BUFFER_WMA;
typedef struct XAUDIO2_VOICE_STATE
{
void* pCurrentBufferContext;
UINT32 BuffersQueued;
UINT64 SamplesPlayed;
} XAUDIO2_VOICE_STATE;
typedef struct XAUDIO2_PERFORMANCE_DATA
{
UINT64 AudioCyclesSinceLastQuery;
UINT64 TotalCyclesSinceLastQuery;
UINT32 MinimumCyclesPerQuantum;
UINT32 MaximumCyclesPerQuantum;
UINT32 MemoryUsageInBytes;
UINT32 CurrentLatencyInSamples;
UINT32 GlitchesSinceEngineStarted;
UINT32 ActiveSourceVoiceCount;
UINT32 TotalSourceVoiceCount;
UINT32 ActiveSubmixVoiceCount;
UINT32 TotalSubmixVoiceCount;
UINT32 ActiveXmaSourceVoices;
UINT32 ActiveXmaStreams;
} XAUDIO2_PERFORMANCE_DATA;
typedef struct XAUDIO2_DEBUG_CONFIGURATION
{
UINT32 TraceMask;
UINT32 BreakMask;
BOOL LogThreadID;
BOOL LogFileline;
BOOL LogFunctionName;
BOOL LogTiming;
} XAUDIO2_DEBUG_CONFIGURATION;
DECLARE_INTERFACE(IXAudio2EngineCallback)
{
STDMETHOD_(void, OnProcessingPassStart) (THIS) PURE;
STDMETHOD_(void, OnProcessingPassEnd) (THIS) PURE;
STDMETHOD_(void, OnCriticalError) (THIS_ HRESULT Error) PURE;
};
DECLARE_INTERFACE(IXAudio2VoiceCallback)
{
STDMETHOD_(void, OnVoiceProcessingPassStart) (THIS_ UINT32 BytesRequired) PURE;
STDMETHOD_(void, OnVoiceProcessingPassEnd) (THIS) PURE;
STDMETHOD_(void, OnStreamEnd) (THIS) PURE;
STDMETHOD_(void, OnBufferStart) (THIS_ void* pBufferContext) PURE;
STDMETHOD_(void, OnBufferEnd) (THIS_ void* pBufferContext) PURE;
STDMETHOD_(void, OnLoopEnd) (THIS_ void* pBufferContext) PURE;
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) PURE;
};
DECLARE_INTERFACE(IXAudio2Voice)
{
#define Declare_IXAudio2Voice_Methods() \
STDMETHOD_(void, GetVoiceDetails) (THIS_ XAUDIO2_VOICE_DETAILS* pVoiceDetails) PURE; \
STDMETHOD(SetOutputVoices) (THIS_ const XAUDIO2_VOICE_SENDS* pSendList) PURE; \
STDMETHOD(SetEffectChain) (THIS_ const XAUDIO2_EFFECT_CHAIN* pEffectChain) PURE; \
STDMETHOD(EnableEffect) (THIS_ UINT32 EffectIndex, \
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
STDMETHOD(DisableEffect) (THIS_ UINT32 EffectIndex, \
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
STDMETHOD_(void, GetEffectState) (THIS_ UINT32 EffectIndex, BOOL* pEnabled) PURE; \
STDMETHOD(SetEffectParameters) (THIS_ UINT32 EffectIndex, \
const void* pParameters, \
UINT32 ParametersByteSize, \
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
STDMETHOD(GetEffectParameters) (THIS_ UINT32 EffectIndex, void* pParameters, \
UINT32 ParametersByteSize) PURE; \
STDMETHOD(SetFilterParameters) (THIS_ const XAUDIO2_FILTER_PARAMETERS* pParameters, \
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
STDMETHOD_(void, GetFilterParameters) (THIS_ XAUDIO2_FILTER_PARAMETERS* pParameters) PURE; \
STDMETHOD(SetVolume) (THIS_ float Volume, \
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
STDMETHOD_(void, GetVolume) (THIS_ float* pVolume) PURE; \
STDMETHOD(SetChannelVolumes) (THIS_ UINT32 Channels, const float* pVolumes, \
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
STDMETHOD_(void, GetChannelVolumes) (THIS_ UINT32 Channels, float* pVolumes) PURE; \
STDMETHOD(SetOutputMatrix) (THIS_ IXAudio2Voice* pDestinationVoice, \
UINT32 SourceChannels, UINT32 DestinationChannels, \
const float* pLevelMatrix, \
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
STDMETHOD_(void, GetOutputMatrix) (THIS_ IXAudio2Voice* pDestinationVoice, \
UINT32 SourceChannels, UINT32 DestinationChannels, \
float* pLevelMatrix) PURE; \
STDMETHOD_(void, DestroyVoice) (THIS) PURE
Declare_IXAudio2Voice_Methods();
};
DECLARE_INTERFACE_(IXAudio2MasteringVoice, IXAudio2Voice)
{
Declare_IXAudio2Voice_Methods();
};
DECLARE_INTERFACE_(IXAudio2SubmixVoice, IXAudio2Voice)
{
Declare_IXAudio2Voice_Methods();
};
DECLARE_INTERFACE_(IXAudio2SourceVoice, IXAudio2Voice)
{
Declare_IXAudio2Voice_Methods();
STDMETHOD(Start) (THIS_ UINT32 Flags, UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
STDMETHOD(Stop) (THIS_ UINT32 Flags, UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
STDMETHOD(SubmitSourceBuffer) (THIS_ const XAUDIO2_BUFFER* pBuffer, const XAUDIO2_BUFFER_WMA* pBufferWMA X2DEFAULT(NULL)) PURE;
STDMETHOD(FlushSourceBuffers) (THIS) PURE;
STDMETHOD(Discontinuity) (THIS) PURE;
STDMETHOD(ExitLoop) (THIS_ UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
STDMETHOD_(void, GetState) (THIS_ XAUDIO2_VOICE_STATE* pVoiceState) PURE;
STDMETHOD(SetFrequencyRatio) (THIS_ float Ratio,
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
STDMETHOD_(void, GetFrequencyRatio) (THIS_ float* pRatio) PURE;
};
DECLARE_INTERFACE_(IXAudio2, IUnknown)
{
STDMETHOD(QueryInterface) (THIS_ REFIID riid, void** ppvInterface) PURE;
STDMETHOD_(ULONG, AddRef) (THIS) PURE;
STDMETHOD_(ULONG, Release) (THIS) PURE;
STDMETHOD(GetDeviceCount) (THIS_ UINT32* pCount) PURE;
STDMETHOD(GetDeviceDetails) (THIS_ UINT32 Index, XAUDIO2_DEVICE_DETAILS* pDeviceDetails) PURE;
STDMETHOD(Initialize) (THIS_ UINT32 Flags X2DEFAULT(0),
XAUDIO2_PROCESSOR XAudio2Processor X2DEFAULT(XAUDIO2_DEFAULT_PROCESSOR)) PURE;
STDMETHOD(RegisterForCallbacks) (IXAudio2EngineCallback* pCallback) PURE;
STDMETHOD_(void, UnregisterForCallbacks) (IXAudio2EngineCallback* pCallback) PURE;
STDMETHOD(CreateSourceVoice) (THIS_ IXAudio2SourceVoice** ppSourceVoice,
const WAVEFORMATEX* pSourceFormat,
UINT32 Flags X2DEFAULT(0),
float MaxFrequencyRatio X2DEFAULT(XAUDIO2_DEFAULT_FREQ_RATIO),
IXAudio2VoiceCallback* pCallback X2DEFAULT(NULL),
const XAUDIO2_VOICE_SENDS* pSendList X2DEFAULT(NULL),
const XAUDIO2_EFFECT_CHAIN* pEffectChain X2DEFAULT(NULL)) PURE;
STDMETHOD(CreateSubmixVoice) (THIS_ IXAudio2SubmixVoice** ppSubmixVoice,
UINT32 InputChannels, UINT32 InputSampleRate,
UINT32 Flags X2DEFAULT(0), UINT32 ProcessingStage X2DEFAULT(0),
const XAUDIO2_VOICE_SENDS* pSendList X2DEFAULT(NULL),
const XAUDIO2_EFFECT_CHAIN* pEffectChain X2DEFAULT(NULL)) PURE;
STDMETHOD(CreateMasteringVoice) (THIS_ IXAudio2MasteringVoice** ppMasteringVoice,
UINT32 InputChannels X2DEFAULT(XAUDIO2_DEFAULT_CHANNELS),
UINT32 InputSampleRate X2DEFAULT(XAUDIO2_DEFAULT_SAMPLERATE),
UINT32 Flags X2DEFAULT(0), UINT32 DeviceIndex X2DEFAULT(0),
const XAUDIO2_EFFECT_CHAIN* pEffectChain X2DEFAULT(NULL)) PURE;
STDMETHOD(StartEngine) (THIS) PURE;
STDMETHOD_(void, StopEngine) (THIS) PURE;
STDMETHOD(CommitChanges) (THIS_ UINT32 OperationSet) PURE;
STDMETHOD_(void, GetPerformanceData) (THIS_ XAUDIO2_PERFORMANCE_DATA* pPerfData) PURE;
STDMETHOD_(void, SetDebugConfiguration) (THIS_ const XAUDIO2_DEBUG_CONFIGURATION* pDebugConfiguration,
void* pReserved X2DEFAULT(NULL)) PURE;
};
static inline HRESULT XAudio2Create(IXAudio2** ppXAudio2, UINT32 Flags X2DEFAULT(0),
XAUDIO2_PROCESSOR XAudio2Processor X2DEFAULT(XAUDIO2_DEFAULT_PROCESSOR))
{
IXAudio2* pXAudio2;
HRESULT hr = CoCreateInstance((Flags & XAUDIO2_DEBUG_ENGINE) ? CLSID_XAudio2_Debug : CLSID_XAudio2,
NULL, CLSCTX_INPROC_SERVER, IID_IXAudio2, reinterpret_cast<void**>(&pXAudio2));
if (SUCCEEDED(hr))
{
hr = pXAudio2->Initialize(Flags, XAudio2Processor);
if (SUCCEEDED(hr))
*ppXAudio2 = pXAudio2;
else
pXAudio2->Release();
}
return hr;
}
#endif
+19 -58
View File
@@ -15,63 +15,19 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "driver.h"
#include <stdlib.h>
#include "xaudio-c.h"
#include "xaudio-c/xaudio-c.h"
#include "general.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef struct
{
xaudio2_t *xa;
bool nonblock;
} xa_t;
static xaudio2_new_t pxanew = NULL;
static xaudio2_write_t pxawrite = NULL;
static xaudio2_write_avail_t pxawrite_avail = NULL;
static xaudio2_free_t pxafree = NULL;
static HMODULE lib = NULL;
#define LIB_NAME "xaudio-c.dll"
#define SYM(X) ((void*)GetProcAddress(lib, "xaudio2_" #X))
static void deinit_lib(void)
{
FreeModule(lib);
lib = NULL;
}
static bool init_lib(void)
{
if (lib)
return true;
lib = LoadLibrary(LIB_NAME);
if (!lib)
return false;
pxanew = SYM(new);
pxawrite = SYM(write);
pxawrite_avail = SYM(write_avail);
pxafree = SYM(free);
if (!pxanew || !pxawrite || !pxawrite_avail || !pxafree)
{
deinit_lib();
return false;
}
return true;
}
static void* __xa_init(const char* device, unsigned rate, unsigned latency)
{
if (!init_lib())
return NULL;
if (latency < 8)
latency = 8; // Do not allow shenanigans.
@@ -80,11 +36,10 @@ static void* __xa_init(const char* device, unsigned rate, unsigned latency)
return NULL;
size_t bufsize = latency * rate / 1000;
bufsize = next_pow2(bufsize);
SSNES_LOG("XAudio2: Requesting %d ms latency, using %d ms latency.\n", latency, (int)bufsize * 1000 / rate);
xa->xa = pxanew(rate, 2, 16, bufsize << 2);
xa->xa = xaudio2_new(rate, 2, bufsize * 2 * sizeof(float));
if (!xa->xa)
{
SSNES_ERR("Failed to init XAudio2.\n");
@@ -99,11 +54,16 @@ static ssize_t __xa_write(void* data, const void* buf, size_t size)
xa_t *xa = data;
if (xa->nonblock)
{
size_t avail = pxawrite_avail(xa->xa);
size_t avail = xaudio2_write_avail(xa->xa);
if (avail == 0)
return 0;
if (avail < size)
size = avail;
}
return pxawrite(xa->xa, buf, size);
size_t ret = xaudio2_write(xa->xa, buf, size);
if (ret == 0)
return -1;
return ret;
}
static bool __xa_stop(void *data)
@@ -124,16 +84,21 @@ static bool __xa_start(void *data)
return true;
}
static bool __xa_use_float(void *data)
{
(void)data;
return true;
}
static void __xa_free(void *data)
{
xa_t *xa = data;
if (xa && xa->xa)
if (xa)
{
if (pxafree)
pxafree(xa->xa);
if (xa->xa)
xaudio2_free(xa->xa);
free(xa);
}
deinit_lib();
}
const audio_driver_t audio_xa = {
@@ -142,12 +107,8 @@ const audio_driver_t audio_xa = {
.stop = __xa_stop,
.start = __xa_start,
.set_nonblock_state = __xa_set_nonblock_state,
.use_float = __xa_use_float,
.free = __xa_free,
.ident = "xaudio"
};
+10 -1
View File
@@ -42,6 +42,9 @@ struct autosave
static int autosave_thread(void *data)
{
autosave_t *save = data;
bool first_log = true;
while (!save->quit)
{
autosave_lock(save);
@@ -52,7 +55,13 @@ static int autosave_thread(void *data)
FILE *file = fopen(save->path, "wb");
if (file)
{
SSNES_LOG("Autosaving SRAM to \"%s\"\n", save->path);
// Avoid spamming down stderr ... :)
if (first_log)
{
SSNES_LOG("Autosaving SRAM to \"%s\", will continue to autosave every %u seconds ...\n", save->path, save->interval);
first_log = false;
}
bool failed = false;
failed |= fwrite(save->buffer, 1, save->bufsize, file) != save->bufsize;
failed |= fflush(file) != 0;
+175
View File
@@ -0,0 +1,175 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
*
* Some code herein may be based on code found in BSNES.
*
* SSNES is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "bps.h"
#include "movie.h"
#include <stdint.h>
#include <stdbool.h>
enum bps_mode
{
SOURCE_READ = 0,
TARGET_READ,
SOURCE_COPY,
TARGET_COPY
};
struct bps_data
{
const uint8_t *modify_data, *source_data;
uint8_t *target_data;
size_t modify_length, source_length, target_length;
size_t modify_offset, source_offset, target_offset;
uint32_t modify_checksum, source_checksum, target_checksum;
size_t source_relative_offset, target_relative_offset, output_offset;
};
static uint8_t bps_read(struct bps_data *bps)
{
uint8_t data = bps->modify_data[bps->modify_offset++];
bps->modify_checksum = crc32_adjust(bps->modify_checksum, data);
return data;
}
static uint64_t bps_decode(struct bps_data *bps)
{
uint64_t data = 0, shift = 1;
for (;;)
{
uint8_t x = bps_read(bps);
data += (x & 0x7f) * shift;
if (x & 0x80)
break;
shift <<= 7;
data += shift;
}
return data;
}
static void bps_write(struct bps_data *bps, uint8_t data)
{
bps->target_data[bps->output_offset++] = data;
bps->target_checksum = crc32_adjust(bps->target_checksum, data);
}
bps_error_t bps_apply_patch(
const uint8_t *modify_data, size_t modify_length,
const uint8_t *source_data, size_t source_length,
uint8_t *target_data, size_t *target_length)
{
if (modify_length < 19)
return BPS_PATCH_TOO_SMALL;
struct bps_data bps = {
.modify_data = modify_data,
.modify_length = modify_length,
.target_data = target_data,
.target_length = *target_length,
.source_data = source_data,
.source_length = source_length,
.modify_checksum = ~0,
.target_checksum = ~0,
};
if ((bps_read(&bps) != 'B') || (bps_read(&bps) != 'P') || (bps_read(&bps) != 'S') || (bps_read(&bps) != '1'))
return BPS_PATCH_INVALID_HEADER;
size_t modify_source_size = bps_decode(&bps);
size_t modify_target_size = bps_decode(&bps);
size_t modify_markup_size = bps_decode(&bps);
for (size_t i = 0; i < modify_markup_size; i++)
bps_read(&bps);
if (modify_source_size > bps.source_length)
return BPS_SOURCE_TOO_SMALL;
if (modify_target_size > bps.target_length)
return BPS_TARGET_TOO_SMALL;
while (bps.modify_offset < bps.modify_length - 12)
{
size_t length = bps_decode(&bps);
unsigned mode = length & 3;
length = (length >> 2) + 1;
switch (mode)
{
case SOURCE_READ:
while (length--)
bps_write(&bps, bps.source_data[bps.output_offset]);
break;
case TARGET_READ:
while (length--)
bps_write(&bps, bps_read(&bps));
break;
case SOURCE_COPY:
case TARGET_COPY:
{
int offset = bps_decode(&bps);
bool negative = offset & 1;
offset >>= 1;
if (negative)
offset = -offset;
if (mode == SOURCE_COPY)
{
bps.source_offset += offset;
while (length--)
bps_write(&bps, bps.source_data[bps.source_offset++]);
}
else
{
bps.target_offset += offset;
while (length--)
bps_write(&bps, bps.target_data[bps.target_offset++]);
break;
}
break;
}
}
}
uint32_t modify_source_checksum = 0, modify_target_checksum = 0, modify_modify_checksum = 0;
for (unsigned i = 0; i < 32; i += 8)
modify_source_checksum |= bps_read(&bps) << i;
for (unsigned i = 0; i < 32; i += 8)
modify_target_checksum |= bps_read(&bps) << i;
uint32_t checksum = ~bps.modify_checksum;
for (unsigned i = 0; i < 32; i += 8)
modify_modify_checksum |= bps_read(&bps) << i;
bps.source_checksum = crc32_calculate(bps.source_data, bps.source_length);
bps.target_checksum = ~bps.target_checksum;
if (bps.source_checksum != modify_source_checksum)
return BPS_SOURCE_CHECKSUM_INVALID;
if (bps.target_checksum != modify_target_checksum)
return BPS_TARGET_CHECKSUM_INVALID;
if (checksum != modify_modify_checksum)
return BPS_PATCH_CHECKSUM_INVALID;
*target_length = modify_target_size;
return BPS_SUCCESS;
}
+45
View File
@@ -0,0 +1,45 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
*
* Some code herein may be based on code found in BSNES.
*
* SSNES is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __BPS_H
#define __BPS_H
#include <stdint.h>
#include <stddef.h>
// BPS implementation from bSNES (nall::).
typedef enum bps_error
{
BPS_UNKNOWN,
BPS_SUCCESS,
BPS_PATCH_TOO_SMALL,
BPS_PATCH_INVALID_HEADER,
BPS_SOURCE_TOO_SMALL,
BPS_TARGET_TOO_SMALL,
BPS_SOURCE_CHECKSUM_INVALID,
BPS_TARGET_CHECKSUM_INVALID,
BPS_PATCH_CHECKSUM_INVALID
} bps_error_t;
bps_error_t bps_apply_patch(
const uint8_t *patch_data, size_t patch_length,
const uint8_t *source_data, size_t source_length,
uint8_t *target_data, size_t *target_length);
#endif
+46 -20
View File
@@ -24,7 +24,6 @@
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <assert.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
@@ -51,7 +50,8 @@ static char* strcat_alloc(char *dest, const char *input)
size_t required_len = dest_len + input_len + 1;
char *output = realloc(dest, required_len);
assert(output);
if (!output)
return NULL;
if (dest)
strlcat(output, input, required_len);
@@ -61,58 +61,74 @@ static char* strcat_alloc(char *dest, const char *input)
return output;
}
static void xml_grab_cheat(struct cheat *cht, xmlNodePtr ptr)
static bool xml_grab_cheat(struct cheat *cht, xmlNodePtr ptr)
{
if (!ptr)
return false;
memset(cht, 0, sizeof(struct cheat));
bool first = true;
for (xmlNodePtr node = ptr; node; node = node->next)
for (; ptr; ptr = ptr->next)
{
if (strcmp((const char*)node->name, "description") == 0)
if (strcmp((const char*)ptr->name, "description") == 0)
{
cht->desc = (char*)xmlNodeGetContent(node);
cht->desc = (char*)xmlNodeGetContent(ptr);
}
else if (strcmp((const char*)node->name, "code") == 0)
else if (strcmp((const char*)ptr->name, "code") == 0)
{
if (!first)
{
cht->code = strcat_alloc(cht->code, "+");
if (!cht->code)
return false;
}
xmlChar *code = xmlNodeGetContent(node);
assert(code);
xmlChar *code = xmlNodeGetContent(ptr);
if (!code)
return false;
cht->code = strcat_alloc(cht->code, (const char*)code);
xmlFree(code);
if (!cht->code)
return false;
first = false;
}
}
return true;
}
static void xml_grab_cheats(cheat_manager_t *handle, xmlNodePtr ptr)
static bool xml_grab_cheats(cheat_manager_t *handle, xmlNodePtr ptr)
{
xmlNodePtr node = NULL;
for (node = ptr; node; node = node->next)
for (; ptr; ptr = ptr->next)
{
if (strcmp((const char*)node->name, "name") == 0)
if (strcmp((const char*)ptr->name, "name") == 0)
{
xmlChar *name = xmlNodeGetContent(node);
xmlChar *name = xmlNodeGetContent(ptr);
if (name)
{
SSNES_LOG("Found cheat for game: \"%s\"\n", name);
xmlFree(name);
}
}
else if (strcmp((const char*)node->name, "cheat") == 0)
else if (strcmp((const char*)ptr->name, "cheat") == 0)
{
if (handle->size == handle->buf_size)
{
handle->buf_size *= 2;
handle->cheats = realloc(handle->cheats, handle->buf_size * sizeof(struct cheat));
assert(handle->cheats);
if (!handle->cheats)
return false;
}
xml_grab_cheat(&handle->cheats[handle->size++], node->children);
if (xml_grab_cheat(&handle->cheats[handle->size], ptr->children))
handle->size++;
}
}
return true;
}
cheat_manager_t* cheat_manager_new(const char *path)
@@ -125,8 +141,8 @@ cheat_manager_t* cheat_manager_new(const char *path)
if (!handle)
return NULL;
handle->buf_size = 32;
handle->cheats = malloc(handle->buf_size * sizeof(struct cheat));
handle->buf_size = 1;
handle->cheats = calloc(handle->buf_size, sizeof(struct cheat));
if (!handle->cheats)
{
handle->buf_size = 0;
@@ -185,7 +201,17 @@ cheat_manager_t* cheat_manager_new(const char *path)
if (!cur)
goto error;
xml_grab_cheats(handle, cur->children);
if (!xml_grab_cheats(handle, cur->children))
{
SSNES_ERR("Failed to grab cheats. This should not happen.\n");
goto error;
}
if (handle->size == 0)
{
SSNES_ERR("Did not find any cheats in XML file: %s\n", path);
goto error;
}
xmlFreeDoc(doc);
xmlFreeParserCtxt(ctx);
+18 -1
View File
@@ -21,6 +21,7 @@
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include "strl.h"
struct entry_list
{
@@ -247,7 +248,7 @@ bool config_get_char(config_file_t *conf, const char *key, char *in)
{
if (strcmp(key, list->key) == 0)
{
if (strlen(list->value) > 1)
if (list->value[0] && list->value[1])
return false;
*in = *list->value;
return true;
@@ -273,6 +274,22 @@ bool config_get_string(config_file_t *conf, const char *key, char **str)
return false;
}
bool config_get_array(config_file_t *conf, const char *key, char *buf, size_t size)
{
struct entry_list *list = conf->entries;
while (list != NULL)
{
if (strcmp(key, list->key) == 0)
{
strlcpy(buf, list->value, size);
return true;
}
list = list->next;
}
return false;
}
bool config_get_bool(config_file_t *conf, const char *key, bool *in)
{
struct entry_list *list = conf->entries;
+3
View File
@@ -22,6 +22,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stddef.h>
typedef struct config_file config_file_t;
@@ -48,6 +49,8 @@ bool config_get_hex(config_file_t *conf, const char *entry, unsigned *in);
bool config_get_char(config_file_t *conf, const char *entry, char *in);
// Extracts an allocated string in *in. This must be free()-d if this function succeeds.
bool config_get_string(config_file_t *conf, const char *entry, char **in);
// Extracts a string to a preallocated buffer. Avoid memory allocation.
bool config_get_array(config_file_t *conf, const char *entry, char *in, size_t size);
// Extracts a boolean from config. Valid boolean true are "true" and "1". Valid false are "false" and "0". Other values will be treated as an error.
bool config_get_bool(config_file_t *conf, const char *entry, bool *in);
+15
View File
@@ -57,6 +57,8 @@
#define AUDIO_XAUDIO 9
#define AUDIO_PULSE 10
#define AUDIO_EXT 15
#define AUDIO_DSOUND 16
#define AUDIO_COREAUDIO 17
////////////////////////
#define INPUT_SDL 7
#define INPUT_X 12
@@ -82,8 +84,12 @@
#define AUDIO_DEFAULT_DRIVER AUDIO_OSS
#elif defined(HAVE_JACK)
#define AUDIO_DEFAULT_DRIVER AUDIO_JACK
#elif defined(HAVE_COREAUDIO)
#define AUDIO_DEFAULT_DRIVER AUDIO_COREAUDIO
#elif defined(HAVE_AL)
#define AUDIO_DEFAULT_DRIVER AUDIO_AL
#elif defined(HAVE_DSOUND)
#define AUDIO_DEFAULT_DRIVER AUDIO_DSOUND
#elif defined(HAVE_SDL)
#define AUDIO_DEFAULT_DRIVER AUDIO_SDL
#elif defined(HAVE_XAUDIO)
@@ -123,6 +129,9 @@ static const unsigned fullscreen_y = 0;
// Force 16-bit colors.
static const bool force_16bit = false;
// Forcibly disable composition. Only valid on Windows Vista/7 for now.
static const bool disable_composition = false;
// Video VSYNC (recommended)
static const bool vsync = true;
@@ -148,6 +157,12 @@ static const float fbo_scale_x = 2.0;
static const float fbo_scale_y = 2.0;
static const bool second_pass_smooth = true;
// Record video assuming game runs hi-res.
static const bool hires_record = false;
// Record post-filtered (CPU filter) video rather than raw SNES output.
static const bool post_filter_record = false;
#define SNES_ASPECT_RATIO (4.0/3)
////////////////
+19 -2
View File
@@ -32,19 +32,30 @@ Prints help text.
Overrides the path used for save ram (*.srm).
Without this flag, the save ram path will be inferred from the rom path name, and put in the same directory as the rom file with the extention replaced with '.srm'.
When rom is loaded from \fBstdin\fR, this flag is mandatory to define as no path can be inferred.
If PATH is a directory, SSNES will treat this as the save file directory, where the save file name will be inferred from the rom name.
When loading a rom from stdin, the path must be a full path, however.
.TP
\fB--savestate PATH, -S path\fR
\fB--savestate PATH, -S PATH\fR
Overrides the path used for save states.
Without this flag, the save state path will be inferred from the rom path name, and put in the same directory as the rom file with the extention replace with '.state'.
When rom is loaded from \fBstdin\fR, this flag is mandatory to define as no path can be inferred.
If PATH is a directory, SSNES will treat this as the save state directory, where the state file name will be inferred from the rom name.
When loading a rom from stdin, the path must be a full path, however.
Do note that save states are bound to the libsnes implementation being used. Using a different libsnes could invalidate the save state file.
.TP
\fB--fullscreen, -f\fR
Always starts SSNES in fullscreen. Disregards settings in configuration file.
.TP
\fB--config PATH, -c PATH\fR
Sets the configuration file path. \fBssnes\fR will use this path to load the configuration file.
Should this not be defined, \fBssnes\fR will look in platform specific paths to attempt finding the config file.
/etc/ssnes.cfg (when installed), or ssnes.cfg in the source tarball serves as a skeleton configuration file.
If PATH is a directory, SSNES will treat this as the config file directory, where the state file name will be inferred from the rom name (*.cfg).
When loading a rom from stdin, the path must be a full path, however.
If a config cannot be found when using directory path, the default config path will be used instead.
.IP
Unix-like systems will look in $XDG_CONFIG_HOME/ssnes/ssnes.cfg first. Then it will try $HOME/.ssnes.cfg. Last, it will try /etc/ssnes.cfg. If no configuration is found, default settings will be assumed. A configuration file does not need to define every possible option, only those that should be overridden.
@@ -101,7 +112,8 @@ Connects a four-way multitap into port 2 of the SNES. This allows for up to 5 pl
.TP
\fB--record PATH, -r PATH\fR
Activates video recording of gameplay into PATH. Video recording options can be configured in config file.
Activates video recording of gameplay into PATH. Using .mkv extension is recommended.
Codecs used are FFV1/FLAC, suitable for processing the material further.
.TP
\fB--bsvplay PATH, -P PATH\fR
@@ -134,6 +146,11 @@ Network port used for netplay. This defaults to 55435. This option affects both
Attempts to apply an UPS patch to the current ROM image. No files are altered.
If this flag is not specified, SSNES will look for a .ups file with same basename as ROM specified.
.TP
\fB--bps PATCH\fR
Attempts to apply a BPS patch to the current ROM image. No files are altered.
If this flag is not specified, SSNES will look for a .bps file with same basename as ROM specified.
.TP
\fB--xml MAP, -X MAP\fR
Specifies path to XML memory map for the given ROM.
+11 -5
View File
@@ -38,6 +38,9 @@ static const audio_driver_t *audio_drivers[] = {
#ifdef HAVE_RSOUND
&audio_rsound,
#endif
#ifdef HAVE_COREAUDIO
&audio_coreaudio,
#endif
#ifdef HAVE_AL
&audio_openal,
#endif
@@ -53,6 +56,9 @@ static const audio_driver_t *audio_drivers[] = {
#ifdef HAVE_XAUDIO
&audio_xa,
#endif
#ifdef HAVE_DSOUND
&audio_dsound,
#endif
#ifdef HAVE_PULSE
&audio_pulse,
#endif
@@ -162,7 +168,7 @@ static void init_dsp_plugin(void)
}
const ssnes_dsp_plugin_t* (*SSNES_API_CALLTYPE plugin_init)(void) =
(const ssnes_dsp_plugin_t *(*)(void))dylib_proc(g_extern.audio_data.dsp_lib, "ssnes_dsp_plugin_init");
(const ssnes_dsp_plugin_t *(SSNES_API_CALLTYPE*)(void))dylib_proc(g_extern.audio_data.dsp_lib, "ssnes_dsp_plugin_init");
if (!plugin_init)
{
SSNES_ERR("Failed to find symbol \"ssnes_dsp_plugin_init\" in DSP plugin.\n");
@@ -184,7 +190,7 @@ static void init_dsp_plugin(void)
SSNES_LOG("Loaded DSP plugin: \"%s\"\n", g_extern.audio_data.dsp_plugin->ident ? g_extern.audio_data.dsp_plugin->ident : "Unknown");
ssnes_dsp_info_t info = {
const ssnes_dsp_info_t info = {
.input_rate = g_settings.audio.in_rate,
.output_rate = g_settings.audio.out_rate
};
@@ -479,12 +485,12 @@ void init_video_input(void)
void uninit_video_input(void)
{
if ( driver.video_data && driver.video )
driver.video->free(driver.video_data);
if ( driver.input_data != driver.video_data && driver.input )
driver.input->free(driver.input_data);
if ( driver.video_data && driver.video )
driver.video->free(driver.video_data);
deinit_filter();
#ifdef HAVE_XML
+2
View File
@@ -158,6 +158,8 @@ extern const audio_driver_t audio_sdl;
extern const audio_driver_t audio_xa;
extern const audio_driver_t audio_pulse;
extern const audio_driver_t audio_ext;
extern const audio_driver_t audio_dsound;
extern const audio_driver_t audio_coreaudio;
extern const video_driver_t video_gl;
extern const video_driver_t video_xvideo;
extern const video_driver_t video_sdl;
+20
View File
@@ -184,6 +184,26 @@ void init_dlsym(void)
assert(sizeof(void*) == sizeof(void (*)(void)));
#ifdef HAVE_DYNAMIC
#ifndef _WIN32 // Try to verify that -lsnes was not linked in from other modules
// since loading it dynamically and with -l will fail hard.
void *lib = dlopen(NULL, RTLD_LAZY);
if (lib)
{
void *sym = dlsym(lib, "snes_init");
if (sym)
{
SSNES_ERR("Serious problem! SSNES wants to load libsnes dyamically, but it is already linked!\n");
SSNES_ERR("This could happen if other modules SSNES depends on link against libsnes directly.\n");
SSNES_ERR("Proceeding could cause a crash! Aborting ...\n");
dlclose(lib);
exit(1);
}
dlclose(lib);
}
#endif
if (strlen(g_settings.libsnes) > 0)
load_dynamic();
else
+267 -59
View File
@@ -21,9 +21,12 @@
#include <stdbool.h>
#include <libsnes.hpp>
#include <string.h>
#include <assert.h>
#include <time.h>
#include "dynamic.h"
#include "movie.h"
#include "ups.h"
#include "bps.h"
#include "strl.h"
#ifdef HAVE_XML
@@ -34,8 +37,10 @@
#include <io.h>
#include <fcntl.h>
#include <windows.h>
#include <shlwapi.h>
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#endif
@@ -116,6 +121,114 @@ error:
return false;
}
enum patch_type
{
PATCH_NONE,
PATCH_UPS,
PATCH_BPS
};
static void patch_rom(uint8_t **buf, ssize_t *size)
{
uint8_t *ret_buf = *buf;
ssize_t ret_size = *size;
ssize_t patch_size = 0;
void *patch_data = NULL;
enum patch_type type = PATCH_NONE;
if (g_extern.ups_pref && g_extern.bps_pref)
{
SSNES_WARN("Both UPS and BPS patch explicitly defined, ignoring both ...\n");
return;
}
if (!g_extern.bps_pref && *g_extern.ups_name && (patch_size = read_file(g_extern.ups_name, &patch_data)) >= 0)
type = PATCH_UPS;
else if (!g_extern.ups_pref && *g_extern.bps_name && (patch_size = read_file(g_extern.bps_name, &patch_data)) >= 0)
type = PATCH_BPS;
if (type == PATCH_NONE)
{
SSNES_LOG("Did not find a valid ROM patch.\n");
return;
}
switch (type)
{
case PATCH_UPS:
SSNES_LOG("Found UPS file in \"%s\", attempting to patch ...\n", g_extern.ups_name);
break;
case PATCH_BPS:
SSNES_LOG("Found BPS file in \"%s\", attempting to patch ...\n", g_extern.bps_name);
break;
default:
return; // Should not happen, but.
}
size_t target_size = ret_size * 4; // Just to be sure ...
uint8_t *patched_rom = malloc(target_size);
if (!patched_rom)
{
SSNES_ERR("Failed to allocate memory for patched ROM ...\n");
goto error;
}
bool success = false;
switch (type)
{
case PATCH_UPS:
{
ups_error_t err = ups_apply_patch(patch_data, patch_size, ret_buf, ret_size, patched_rom, &target_size);
if (err == UPS_SUCCESS)
{
SSNES_LOG("ROM patched successfully (UPS)!\n");
success = true;
}
else
SSNES_ERR("Failed to patch UPS: Error #%u\n", (unsigned)err);
break;
}
case PATCH_BPS:
{
bps_error_t err = bps_apply_patch(patch_data, patch_size, ret_buf, ret_size, patched_rom, &target_size);
if (err == BPS_SUCCESS)
{
SSNES_LOG("ROM patched successfully (BPS)!\n");
success = true;
}
else
SSNES_ERR("Failed to patch BPS: Error #%u\n", (unsigned)err);
break;
}
default:
return;
}
if (success)
{
free(ret_buf);
*buf = patched_rom;
*size = target_size;
}
if (patch_data)
free(patch_data);
return;
error:
*buf = ret_buf;
*size = ret_size;
if (patch_data)
free(patch_data);
}
// Load SNES rom only. Applies a hack for headered ROMs.
static ssize_t read_rom_file(FILE* file, void** buf)
{
@@ -183,37 +296,6 @@ static ssize_t read_rom_file(FILE* file, void** buf)
ret_buf = rom_buf;
}
// Patch with UPS.
ssize_t ups_patch_size;
void *ups_patch = NULL;
if (*g_extern.ups_name && (ups_patch_size = read_file(g_extern.ups_name, &ups_patch)) >= 0)
{
SSNES_LOG("Found UPS file in \"%s\", attempting to patch ...\n", g_extern.ups_name);
size_t target_size = ret * 4; // Just to be sure ...
uint8_t *patched_rom = malloc(target_size);
if (patched_rom)
{
ups_error_t err = ups_apply_patch(ups_patch, ups_patch_size, ret_buf, ret, patched_rom, &target_size);
if (err == UPS_SUCCESS)
{
free(ret_buf);
ret_buf = patched_rom;
ret = target_size;
SSNES_LOG("ROM patched successfully (UPS)!\n");
}
else
{
free(patched_rom);
SSNES_LOG("ROM failed to patch (UPS).\n");
}
free(ups_patch);
}
}
else if (*g_extern.ups_name)
SSNES_LOG("Could not find UPS patch in: \"%s\".\n", g_extern.ups_name);
// Remove copier header if present (512 first bytes).
if ((ret & 0x7fff) == 512)
{
@@ -221,6 +303,9 @@ static ssize_t read_rom_file(FILE* file, void** buf)
ret -= 512;
}
// Attempt to apply a patch :)
patch_rom(&ret_buf, &ret);
g_extern.cart_crc = crc32_calculate(ret_buf, ret);
#ifdef HAVE_XML
sha256_hash(g_extern.sha256, ret_buf, ret);
@@ -239,13 +324,72 @@ static bool dump_to_file(const char *path, const void *data, size_t size)
return false;
else
{
fwrite(data, 1, size, file);
bool ret = fwrite(data, 1, size, file) == size;
fclose(file);
return true;
return ret;
}
}
bool save_state(const char* path)
static const char *ramtype2str(int type)
{
switch (type)
{
case SNES_MEMORY_CARTRIDGE_RAM:
case SNES_MEMORY_GAME_BOY_RAM:
case SNES_MEMORY_BSX_RAM:
return ".srm";
case SNES_MEMORY_CARTRIDGE_RTC:
case SNES_MEMORY_GAME_BOY_RTC:
return ".rtc";
case SNES_MEMORY_BSX_PRAM:
return ".pram";
case SNES_MEMORY_SUFAMI_TURBO_A_RAM:
return ".aram";
case SNES_MEMORY_SUFAMI_TURBO_B_RAM:
return ".bram";
default:
return "";
}
}
// Attempt to save valuable RAM data somewhere ...
static void dump_to_file_desperate(const void *data, size_t size, int type)
{
#ifdef _WIN32
const char *base = getenv("APPDATA");
#else
const char *base = getenv("HOME");
#endif
if (!base)
goto error;
char path[MAXPATHLEN];
snprintf(path, sizeof(path), "%s/SSNES-recovery-", base);
char timebuf[MAXPATHLEN];
time_t time_;
time(&time_);
strftime(timebuf, sizeof(timebuf), "%Y-%m-%d-%H-%M-%S", localtime(&time_));
strlcat(path, timebuf, sizeof(path));
strlcat(path, ramtype2str(type), sizeof(path));
if (dump_to_file(path, data, size))
SSNES_WARN("Succeeded in saving RAM data to \"%s\". Phew! :D\n", path);
else
goto error;
return;
error:
SSNES_WARN("Failed ... Tough luck ... :(\n");
}
bool save_state(const char *path)
{
SSNES_LOG("Saving state: \"%s\".\n", path);
size_t size = psnes_serialize_size();
@@ -268,7 +412,7 @@ bool save_state(const char* path)
return ret;
}
bool load_state(const char* path)
bool load_state(const char *path)
{
SSNES_LOG("Loading state: \"%s\".\n", path);
void *buf = NULL;
@@ -288,7 +432,7 @@ bool load_state(const char* path)
return true;
}
void load_ram_file(const char* path, int type)
void load_ram_file(const char *path, int type)
{
size_t size = psnes_get_memory_size(type);
uint8_t *data = psnes_get_memory_data(type);
@@ -304,13 +448,20 @@ void load_ram_file(const char* path, int type)
free(buf);
}
void save_ram_file(const char* path, int type)
void save_ram_file(const char *path, int type)
{
size_t size = psnes_get_memory_size(type);
uint8_t *data = psnes_get_memory_data(type);
if (data && size > 0)
dump_to_file(path, data, size);
{
if (!dump_to_file(path, data, size))
{
SSNES_ERR("Failed to save SNES RAM!\n");
SSNES_WARN("Attempting to recover ...\n");
dump_to_file_desperate(data, size, type);
}
}
}
static char* load_xml_map(const char *path)
@@ -585,8 +736,6 @@ bool init_rom_file(enum ssnes_game_type type)
// Yep, this is C alright ;)
char** dir_list_new(const char *dir, const char *ext)
{
size_t path_len = strlen(dir);
size_t cur_ptr = 0;
size_t cur_size = 32;
char **dir_list = NULL;
@@ -595,18 +744,18 @@ char** dir_list_new(const char *dir, const char *ext)
WIN32_FIND_DATAW ffd;
HANDLE hFind = INVALID_HANDLE_VALUE;
wchar_t wchar_buf[MAXPATHLEN];
char utf8_buf[MAXPATHLEN];
size_t final_off = MAX_PATH + path_len + 2;
if (strlcpy(utf8_buf, dir, sizeof(utf8_buf)) >= sizeof(utf8_buf))
goto error;
if (strlcat(utf8_buf, "/*", sizeof(utf8_buf)) >= sizeof(utf8_buf))
goto error;
if (strlcat(utf8_buf, ext, sizeof(utf8_buf)) >= sizeof(utf8_buf))
goto error;
wchar_t wchar_buf[MAX_PATH + 1];
char utf8_buf[MAX_PATH + 3];
strlcpy(utf8_buf, dir, sizeof(utf8_buf));
strlcat(utf8_buf, "/*", sizeof(utf8_buf));
utf8_buf[MAX_PATH + 2] = '\0';
int ret = MultiByteToWideChar(CP_UTF8, 0, utf8_buf, strlen(utf8_buf), wchar_buf, MAX_PATH);
wchar_buf[ret] = 0;
if (MultiByteToWideChar(CP_UTF8, 0, utf8_buf, -1, wchar_buf, MAXPATHLEN) == 0)
goto error;
hFind = FindFirstFileW(wchar_buf, &ffd);
if (hFind == INVALID_HANDLE_VALUE)
@@ -614,7 +763,6 @@ char** dir_list_new(const char *dir, const char *ext)
#else
DIR *directory = NULL;
const struct dirent *entry = NULL;
size_t final_off = sizeof(entry->d_name) + path_len + 2;
directory = opendir(dir);
if (!directory)
@@ -635,8 +783,8 @@ char** dir_list_new(const char *dir, const char *ext)
#ifdef _WIN32
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
continue;
int ret = WideCharToMultiByte(CP_UTF8, 0, ffd.cFileName, wcslen(ffd.cFileName), utf8_buf, MAX_PATH, NULL, NULL);
utf8_buf[ret] = '\0';
if (WideCharToMultiByte(CP_UTF8, 0, ffd.cFileName, -1, utf8_buf, MAX_PATH, NULL, NULL) == 0)
continue;
if (ext && !strstr(utf8_buf, ext))
continue;
#else
@@ -644,16 +792,16 @@ char** dir_list_new(const char *dir, const char *ext)
continue;
#endif
dir_list[cur_ptr] = malloc(final_off);
dir_list[cur_ptr] = malloc(MAXPATHLEN);
if (!dir_list[cur_ptr])
goto error;
strlcpy(dir_list[cur_ptr], dir, final_off);
strlcat(dir_list[cur_ptr], "/", final_off);
strlcpy(dir_list[cur_ptr], dir, MAXPATHLEN);
strlcat(dir_list[cur_ptr], "/", MAXPATHLEN);
#ifdef _WIN32
strlcat(dir_list[cur_ptr], utf8_buf, final_off);
strlcat(dir_list[cur_ptr], utf8_buf, MAXPATHLEN);
#else
strlcat(dir_list[cur_ptr], entry->d_name, final_off);
strlcat(dir_list[cur_ptr], entry->d_name, MAXPATHLEN);
#endif
cur_ptr++;
@@ -670,7 +818,6 @@ char** dir_list_new(const char *dir, const char *ext)
}
#ifdef _WIN32
while (FindNextFileW(hFind, &ffd) != 0);
#endif
#ifdef _WIN32
@@ -703,3 +850,64 @@ void dir_list_free(char **dir_list)
free(*dir_list++);
free(orig);
}
bool path_is_directory(const char *path)
{
#ifdef _WIN32
wchar_t buf[MAXPATHLEN];
if (MultiByteToWideChar(CP_UTF8, 0, path, -1, buf, MAXPATHLEN) == 0)
return false;
return PathIsDirectoryW(buf) == FILE_ATTRIBUTE_DIRECTORY;
#else
struct stat buf;
if (stat(path, &buf) < 0)
return false;
return S_ISDIR(buf.st_mode);
#endif
}
bool path_file_exists(const char *path)
{
FILE *dummy = fopen(path, "rb");
if (dummy)
{
fclose(dummy);
return true;
}
return false;
}
void fill_pathname(char *out_path, const char *in_path, const char *replace, size_t size)
{
char tmp_path[strlen(in_path) + 1];
strlcpy(tmp_path, in_path, sizeof(tmp_path));
char *tok = strrchr(tmp_path, '.');
if (tok != NULL)
*tok = '\0';
assert(strlcpy(out_path, tmp_path, size) < size);
assert(strlcat(out_path, replace, size) < size);
}
void fill_pathname_noext(char *out_path, const char *in_path, const char *replace, size_t size)
{
assert(strlcpy(out_path, in_path, size) < size);
assert(strlcat(out_path, replace, size) < size);
}
void fill_pathname_dir(char *in_dir, const char *in_basename, const char *replace, size_t size)
{
assert(strlcat(in_dir, "/", size) < size);
const char *base = strrchr(in_basename, '/');
if (!base)
base = strrchr(in_basename, '\\');
if (base)
base++;
else
base = in_basename;
assert(strlcat(in_dir, base, size) < size);
assert(strlcat(in_dir, replace, size) < size);
}
+16 -4
View File
@@ -28,11 +28,11 @@
ssize_t read_file(const char *path, void **buf);
bool load_state(const char* path);
bool save_state(const char* path);
bool load_state(const char *path);
bool save_state(const char *path);
void load_ram_file(const char* path, int type);
void save_ram_file(const char* path, int type);
void load_ram_file(const char *path, int type);
void save_ram_file(const char *path, int type);
bool init_rom_file(enum ssnes_game_type type);
@@ -40,4 +40,16 @@ bool init_rom_file(enum ssnes_game_type type);
char** dir_list_new(const char *dir, const char *ext);
void dir_list_free(char **dir_list);
bool path_is_directory(const char *path);
bool path_file_exists(const char *path);
// Path-name operations.
// Replaces filename extension with replace.
void fill_pathname(char *out_path, const char *in_path, const char *replace, size_t size);
// Sets filename extension. Assumes in_path has no extension.
void fill_pathname_noext(char *out_path, const char *in_path, const char *replace, size_t size);
// Concatenates in_basename and replace to in_dir.
void fill_pathname_dir(char *in_dir, const char *in_basename, const char *replace, size_t size);
#endif
+61 -29
View File
@@ -42,6 +42,17 @@
#include "audio/hermite.h"
#endif
#ifndef _WIN32
#include <sys/param.h> // MAXPATHLEN
#endif
#ifndef MAXPATHLEN
#ifdef PATH_MAX
#define MAXPATHLEN PATH_MAX
#else
#define MAXPATHLEN 512
#endif
#endif
#define MAX_PLAYERS 5
#define MAX_BINDS 34 // Needs to be increased every time there are new binds added.
@@ -71,26 +82,30 @@ struct settings
bool force_aspect;
bool crop_overscan;
float aspect_ratio;
char cg_shader_path[256];
char bsnes_shader_path[256];
char filter_path[256];
char cg_shader_path[MAXPATHLEN];
char bsnes_shader_path[MAXPATHLEN];
char filter_path[MAXPATHLEN];
enum ssnes_shader_type shader_type;
bool render_to_texture;
double fbo_scale_x;
double fbo_scale_y;
char second_pass_shader[256];
char second_pass_shader[MAXPATHLEN];
bool second_pass_smooth;
char shader_dir[256];
char shader_dir[MAXPATHLEN];
char font_path[256];
char font_path[MAXPATHLEN];
unsigned font_size;
float msg_pos_x;
float msg_pos_y;
bool force_16bit;
bool disable_composition;
char external_driver[256];
bool hires_record;
bool post_filter_record;
char external_driver[MAXPATHLEN];
} video;
struct
@@ -100,13 +115,13 @@ struct settings
unsigned out_rate;
float in_rate;
float rate_step;
char device[256];
char device[MAXPATHLEN];
unsigned latency;
bool sync;
int src_quality;
char dsp_plugin[256];
char external_driver[256];
char dsp_plugin[MAXPATHLEN];
char external_driver[MAXPATHLEN];
} audio;
struct
@@ -118,10 +133,10 @@ struct settings
bool netplay_client_swap_input;
} input;
char libsnes[256];
char cheat_database[256];
char libsnes[MAXPATHLEN];
char cheat_database[MAXPATHLEN];
char screenshot_directory[256];
char screenshot_directory[MAXPATHLEN];
bool rewind_enable;
unsigned rewind_buffer_size;
@@ -147,6 +162,7 @@ struct global
bool verbose;
bool audio_active;
bool video_active;
bool force_fullscreen;
bool has_mouse[2];
bool has_scope[2];
@@ -159,25 +175,29 @@ struct global
enum ssnes_game_type game_type;
uint32_t cart_crc;
char gb_rom_path[256];
char bsx_rom_path[256];
char sufami_rom_path[2][256];
char gb_rom_path[MAXPATHLEN];
char bsx_rom_path[MAXPATHLEN];
char sufami_rom_path[2][MAXPATHLEN];
bool has_set_save_path;
bool has_set_state_path;
#ifdef HAVE_CONFIGFILE
char config_path[256];
char config_path[MAXPATHLEN];
#endif
char basename[256];
char savefile_name_srm[256];
char savefile_name_rtc[512]; // Make sure that fill_pathname has space.
char savefile_name_psrm[512];
char savefile_name_asrm[512];
char savefile_name_bsrm[512];
char savestate_name[256];
char ups_name[512];
char xml_name[512];
char basename[MAXPATHLEN];
char savefile_name_srm[MAXPATHLEN];
char savefile_name_rtc[MAXPATHLEN]; // Make sure that fill_pathname has space.
char savefile_name_psrm[MAXPATHLEN];
char savefile_name_asrm[MAXPATHLEN];
char savefile_name_bsrm[MAXPATHLEN];
char savestate_name[MAXPATHLEN];
char xml_name[MAXPATHLEN];
bool ups_pref;
bool bps_pref;
char ups_name[MAXPATHLEN];
char bps_name[MAXPATHLEN];
unsigned state_slot;
@@ -228,7 +248,7 @@ struct global
// Movie record support
bsv_movie_t *bsv_movie;
char bsv_movie_path[256];
char bsv_movie_path[MAXPATHLEN];
bool bsv_movie_end;
bool bsv_movie_playback;
@@ -240,7 +260,7 @@ struct global
// Netplay.
netplay_t *netplay;
char netplay_server[256];
char netplay_server[MAXPATHLEN];
bool netplay_enable;
bool netplay_is_client;
unsigned netplay_sync_frames;
@@ -249,7 +269,7 @@ struct global
// FFmpeg record.
#ifdef HAVE_FFMPEG
ffemu_t *rec;
char record_path[256];
char record_path[MAXPATHLEN];
bool recording;
#endif
@@ -304,6 +324,18 @@ static inline uint32_t next_pow2(uint32_t v)
return v;
}
static inline uint8_t is_little_endian(void)
{
union
{
uint16_t x;
uint8_t y[2];
} u;
u.x = 1;
return u.y[0];
}
#endif
+11 -2
View File
@@ -224,6 +224,15 @@ static bool setup_video(ext_t *ext, const video_info_t *video, const input_drive
return false;
}
const char *cg_shader = NULL;
const char *xml_shader = NULL;
enum ssnes_shader_type type = g_settings.video.shader_type;
if ((type == SSNES_SHADER_CG || type == SSNES_SHADER_AUTO) && *g_settings.video.cg_shader_path)
cg_shader = g_settings.video.cg_shader_path;
else if ((type == SSNES_SHADER_BSNES || type == SSNES_SHADER_AUTO) && *g_settings.video.bsnes_shader_path)
xml_shader = g_settings.video.bsnes_shader_path;
ssnes_video_info_t info = {
.width = video->width,
.height = video->height,
@@ -234,8 +243,8 @@ static bool setup_video(ext_t *ext, const video_info_t *video, const input_drive
.smooth = video->smooth,
.input_scale = video->input_scale,
.color_format = video->rgb32 ? SSNES_COLOR_FORMAT_ARGB8888 : SSNES_COLOR_FORMAT_XRGB1555,
.xml_shader = g_settings.video.bsnes_shader_path,
.cg_shader = g_settings.video.cg_shader_path,
.xml_shader = xml_shader,
.cg_shader = cg_shader,
.ttf_font = *g_settings.video.font_path ? g_settings.video.font_path : NULL,
.ttf_font_size = g_settings.video.font_size
};
+82 -6
View File
@@ -25,21 +25,26 @@ static float tv_to_fps(const struct timeval *tv, const struct timeval *new_tv, i
return frames/time;
}
static unsigned gl_frames = 0;
void gfx_window_title_reset(void)
{
gl_frames = 0;
}
bool gfx_window_title(char *buf, size_t size)
{
static int frames = 0;
static struct timeval tv;
struct timeval new_tv;
bool ret = false;
if (frames == 0)
if (gl_frames == 0)
{
gettimeofday(&tv, NULL);
snprintf(buf, size, "%s", g_extern.title_buf);
ret = true;
}
if ((frames % 180) == 0 && frames > 0)
else if ((gl_frames % 180) == 0)
{
gettimeofday(&new_tv, NULL);
struct timeval tmp_tv = tv;
@@ -47,12 +52,83 @@ bool gfx_window_title(char *buf, size_t size)
float fps = tv_to_fps(&tmp_tv, &new_tv, 180);
snprintf(buf, size, "%s || FPS: %6.1f || Frames: %d", g_extern.title_buf, fps, frames);
snprintf(buf, size, "%s || FPS: %6.1f || Frames: %d", g_extern.title_buf, fps, gl_frames);
ret = true;
}
frames++;
gl_frames++;
return ret;
}
#ifdef _WIN32
#include <windows.h>
#include "dynamic.h"
// We only load this library once, so we let it be unloaded at application shutdown,
// since unloading it early seems to cause issues on some systems.
static dylib_t dwmlib = NULL;
static void gfx_dwm_shutdown(void)
{
if (dwmlib)
dylib_close(dwmlib);
}
void gfx_set_dwm(void)
{
static bool inited = false;
if (inited)
return;
inited = true;
dwmlib = dylib_load("dwmapi.dll");
if (!dwmlib)
{
SSNES_LOG("Did not find dwmapi.dll");
return;
}
atexit(gfx_dwm_shutdown);
HRESULT (WINAPI *mmcss)(BOOL) = (HRESULT (WINAPI*)(BOOL))dylib_proc(dwmlib, "DwmEnableMMCSS");
if (mmcss)
{
SSNES_LOG("Setting multimedia scheduling for DWM.\n");
mmcss(TRUE);
}
if (!g_settings.video.disable_composition)
return;
HRESULT (WINAPI *composition_enable)(UINT) = (HRESULT (WINAPI*)(UINT))dylib_proc(dwmlib, "DwmEnableComposition");
if (!composition_enable)
{
SSNES_ERR("Did not find DwmEnableComposition ...\n");
return;
}
HRESULT ret = composition_enable(0);
if (FAILED(ret))
SSNES_ERR("Failed to set composition state ...\n");
}
#endif
#include "SDL_syswm.h"
#include "SDL.h"
#if !defined(__APPLE__) && !defined(_WIN32)
void gfx_get_window_size(unsigned *width, unsigned *height)
{
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
SDL_GetWMInfo(&info);
XWindowAttributes target;
info.info.x11.lock_func();
XGetWindowAttributes(info.info.x11.display, info.info.x11.window,
&target);
info.info.x11.unlock_func();
*width = target.width;
*height = target.height;
}
#endif
+9
View File
@@ -22,5 +22,14 @@
#include <stdbool.h>
bool gfx_window_title(char *buf, size_t size);
void gfx_window_title_reset(void);
#ifdef _WIN32
void gfx_set_dwm(void);
#endif
#if !defined(__APPLE__) && !defined(_WIN32)
void gfx_get_window_size(unsigned *width, unsigned *height);
#endif
#endif
+31 -7
View File
@@ -147,6 +147,7 @@ typedef struct gl
bool should_resize;
bool quitting;
bool fullscreen;
bool keep_aspect;
unsigned full_x, full_y;
@@ -498,7 +499,7 @@ static void gl_init_fbo(gl_t *gl, unsigned width, unsigned height)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_type);
glTexImage2D(GL_TEXTURE_2D,
0, GL_RGBA, gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0, GL_RGBA,
0, GL_RGBA, gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0, GL_BGRA,
GL_UNSIGNED_INT_8_8_8_8, NULL);
}
@@ -705,6 +706,22 @@ static void check_window(gl_t *gl)
break;
}
}
#if !defined(__APPLE__) && !defined(_WIN32)
// Hack to workaround limitations in tiling WMs ...
if (!gl->should_resize && !gl->fullscreen)
{
unsigned new_width, new_height;
gfx_get_window_size(&new_width, &new_height);
if ((new_width != gl->win_width || new_height != gl->win_height) || (gl->frame_count == 10)) // Ugly hack :D
{
gl->should_resize = true;
gl->win_width = new_width;
gl->win_height = new_height;
SSNES_LOG("GL: Verified window size: %u x %u\n", gl->win_width, gl->win_height);
}
}
#endif
}
static bool gl_frame(void *data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
@@ -807,7 +824,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
pglBindFramebuffer(GL_FRAMEBUFFER, gl->fbo[i]);
glBindTexture(GL_TEXTURE_2D, gl->fbo_texture[i]);
glTexImage2D(GL_TEXTURE_2D,
0, GL_RGBA, gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0, GL_RGBA,
0, GL_RGBA, gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0, GL_BGRA,
GL_UNSIGNED_INT_8_8_8_8, NULL);
pglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl->fbo_texture[i], 0);
@@ -1036,6 +1053,10 @@ static void gl_set_nonblock_state(void *data, bool state)
static void* gl_init(const video_info_t *video, const input_driver_t **input, void **input_data)
{
#ifdef _WIN32
gfx_set_dwm();
#endif
if (SDL_Init(SDL_INIT_VIDEO) < 0)
return NULL;
@@ -1051,6 +1072,11 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
if (!SDL_SetVideoMode(video->width, video->height, g_settings.video.force_16bit ? 16 : 0, SDL_OPENGL | SDL_RESIZABLE | (video->fullscreen ? SDL_FULLSCREEN : 0)))
return NULL;
gfx_window_title_reset();
char buf[128];
if (gfx_window_title(buf, sizeof(buf)))
SDL_WM_SetCaption(buf, NULL);
// Remove that ugly mouse :D
SDL_ShowCursor(SDL_DISABLE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -1073,6 +1099,7 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
}
gl->vsync = video->vsync;
gl->fullscreen = video->fullscreen;
int attr = 0;
SDL_GL_GetAttribute(SDL_GL_SWAP_CONTROL, &attr);
if (attr <= 0 && video->vsync)
@@ -1138,10 +1165,6 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
glColor4f(1, 1, 1, 1);
glClearColor(0, 0, 0, 1);
char buf[128];
if (gfx_window_title(buf, sizeof(buf)))
SDL_WM_SetCaption(buf, NULL);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
@@ -1171,11 +1194,12 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
gl->tex_h = 256 * video->input_scale;
// Empty buffer that we use to clear out the texture with on res change.
gl->empty_buf = calloc(gl->base_size, gl->tex_w * gl->tex_h);
gl->empty_buf = calloc(gl->tex_w * gl->tex_h, gl->base_size);
for (unsigned i = 0; i < 2; i++)
{
glBindTexture(GL_TEXTURE_2D, gl->texture[i]);
glPixelStorei(GL_UNPACK_ROW_LENGTH, gl->tex_w);
glTexImage2D(GL_TEXTURE_2D,
0, GL_RGBA, gl->tex_w, gl->tex_h, 0, gl->texture_type,
gl->texture_fmt, gl->empty_buf ? gl->empty_buf : NULL);
+8 -8
View File
@@ -63,7 +63,7 @@ bool texture_image_load(const char *path, struct texture_image *out_img)
uint32_t g = (src[x] & fmt->Gmask) >> fmt->Gshift;
uint32_t b = (src[x] & fmt->Bmask) >> fmt->Bshift;
uint32_t a = (src[x] & fmt->Amask) >> fmt->Ashift;
dst[x] = (r << 24) | (g << 16) | (b << 8) | a;
dst[x] = (b << 24) | (g << 16) | (r << 8) | a;
}
}
}
@@ -84,7 +84,7 @@ bool texture_image_load(const char *path, struct texture_image *out_img)
uint32_t r = (color & fmt->Rmask) >> fmt->Rshift;
uint32_t g = (color & fmt->Gmask) >> fmt->Gshift;
uint32_t b = (color & fmt->Bmask) >> fmt->Bshift;
dst[x] = (r << 24) | (g << 16) | (b << 8) | 0xff;
dst[x] = (b << 24) | (g << 16) | (r << 8) | 0xff;
}
}
}
@@ -148,24 +148,24 @@ bool texture_image_load(const char *path, struct texture_image *out_img)
{
for (unsigned i = 0; i < width * height; i++)
{
uint32_t r = tmp[i * 4 + 2];
uint32_t g = tmp[i * 4 + 1];
uint32_t b = tmp[i * 4 + 0];
uint32_t g = tmp[i * 4 + 1];
uint32_t r = tmp[i * 4 + 2];
uint32_t a = tmp[i * 4 + 3];
out_img->pixels[i] = (r << 24) | (g << 16) | (b << 8) | a;
out_img->pixels[i] = (b << 24) | (g << 16) | (r << 8) | a;
}
}
else if (bits == 24)
{
for (unsigned i = 0; i < width * height; i++)
{
uint32_t r = tmp[i * 3 + 2];
uint32_t g = tmp[i * 3 + 1];
uint32_t b = tmp[i * 3 + 0];
uint32_t g = tmp[i * 3 + 1];
uint32_t r = tmp[i * 3 + 2];
uint32_t a = 0xff;
out_img->pixels[i] = (r << 24) | (g << 16) | (b << 8) | a;
out_img->pixels[i] = (b << 24) | (g << 16) | (r << 8) | a;
}
}
else
+4
View File
@@ -213,6 +213,10 @@ static void sdl_render_msg_32(sdl_video_t *vid, SDL_Surface *buffer, const char
static void* sdl_gfx_init(const video_info_t *video, const input_driver_t **input, void **input_data)
{
#ifdef _WIN32
gfx_set_dwm();
#endif
SDL_InitSubSystem(SDL_INIT_VIDEO);
sdl_video_t *vid = calloc(1, sizeof(*vid));
+1 -1
View File
@@ -398,7 +398,7 @@ static bool load_textures(const char *dir_path, config_file_t *conf)
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelStorei(GL_UNPACK_ROW_LENGTH, img.width);
glTexImage2D(GL_TEXTURE_2D,
0, GL_RGBA, img.width, img.height, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, img.pixels);
0, GL_RGBA, img.width, img.height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, img.pixels);
lut_textures_num++;
+1 -1
View File
@@ -356,7 +356,7 @@ static bool get_texture_image(const char *shader_path, xmlNodePtr ptr)
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelStorei(GL_UNPACK_ROW_LENGTH, img.width);
glTexImage2D(GL_TEXTURE_2D,
0, GL_RGBA, img.width, img.height, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, img.pixels);
0, GL_RGBA, img.width, img.height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, img.pixels);
pglActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
+2 -2
View File
@@ -15,10 +15,10 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "ssnes_dinput.h"
#include "SDL.h"
#include "SDL_syswm.h"
#include <stdbool.h>
#include "ssnes_dinput.h"
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
@@ -45,7 +45,7 @@ void sdl_dinput_free(sdl_dinput_t *di)
static BOOL CALLBACK enum_axes_cb(const DIDEVICEOBJECTINSTANCE *inst, void *p)
{
IDirectInputDevice8 *joypad = p;
LPDIRECTINPUTDEVICE8 joypad = p;
DIPROPRANGE range;
memset(&range, 0, sizeof(range));
+2 -2
View File
@@ -30,8 +30,8 @@
typedef struct sdl_dinput
{
HWND hWnd;
IDirectInput8 *ctx;
IDirectInputDevice8 *joypad[MAX_PLAYERS];
LPDIRECTINPUT8 ctx;
LPDIRECTINPUTDEVICE8 joypad[MAX_PLAYERS];
unsigned joypad_cnt;
DIJOYSTATE2 joy_state[MAX_PLAYERS];
} sdl_dinput_t;
-12
View File
@@ -107,18 +107,6 @@ struct bsv_movie
#define CRC_INDEX 2
#define STATE_SIZE_INDEX 3
static inline uint8_t is_little_endian(void)
{
union
{
uint16_t u16;
uint8_t u8[2];
} u;
u.u16 = 1;
return u.u8[0];
}
// Convert to big-endian if needed
static inline uint32_t swap_if_big32(uint32_t val)
{
+3 -1
View File
@@ -25,10 +25,12 @@
#include <assert.h>
#ifdef _WIN32
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
// Woohoo, Winsock has headers from the STONE AGE! :D
#define close(x) closesocket(x)
+15 -2
View File
@@ -3,6 +3,15 @@
check_switch_c C99 -std=gnu99
check_critical C99 "Cannot find C99 compatible compiler."
# There are still broken 64-bit Linux distros out there. :)
if [ -d /usr/lib64 ]; then
add_library_dirs /usr/lib64
fi
if [ -d /opt/local/lib ]; then
add_library_dirs /opt/local/lib
fi
if [ "$OS" = BSD ]; then
DYLIB=-lc
else
@@ -46,6 +55,8 @@ check_pkgconf ROAR libroar
check_pkgconf JACK jack 0.120.1
check_pkgconf PULSE libpulse
check_lib COREAUDIO "-framework AudioUnit" AudioUnitInitialize
check_pkgconf SDL sdl 1.2.10
check_critical SDL "Cannot find SDL 1.2 library."
check_pkgconf SDL_NEW sdl 1.3
@@ -54,7 +65,9 @@ if [ $HAVE_SDL_NEW = yes ]; then
exit 1
fi
check_lib CG -lCg cgCreateContext
# On some distros, -lCg doesn't link against -lstdc++ it seems ...
check_lib_cxx CG -lCg cgCreateContext
check_pkgconf XML libxml-2.0
check_pkgconf SDL_IMAGE SDL_image
@@ -81,7 +94,7 @@ check_pkgconf PYTHON python3
add_define_make OS $OS
# Creates config.mk and config.h.
VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK PULSE SDL DYLIB CG XML SDL_IMAGE DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE SRC CONFIGFILE FREETYPE XVIDEO NETPLAY FBO STRL PYTHON"
VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL DYLIB CG XML SDL_IMAGE DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE SRC CONFIGFILE FREETYPE XVIDEO NETPLAY FBO STRL PYTHON"
create_config_make config.mk $VARS
create_config_header config.h $VARS
+2 -1
View File
@@ -1,7 +1,7 @@
. qb/qb.params.sh
PACKAGE_NAME=ssnes
PACKAGE_VERSION=0.6.2
PACKAGE_VERSION=0.8
# Adds a command line opt to ./configure --help
# $1: Variable (HAVE_ALSA, HAVE_OSS, etc)
@@ -23,6 +23,7 @@ add_command_line_enable RSOUND "Enable RSound support" auto
add_command_line_enable ROAR "Enable RoarAudio support" auto
add_command_line_enable AL "Enable OpenAL support" auto
add_command_line_enable JACK "Enable JACK support" auto
add_command_line_enable COREAUDIO "Enable CoreAudio support" auto
add_command_line_enable PULSE "Enable PulseAudio support" auto
add_command_line_enable FREETYPE "Enable FreeType support" auto
add_command_line_enable XVIDEO "Enable XVideo support" auto
+36 -18
View File
@@ -5,6 +5,7 @@
#include <libavutil/opt.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/avconfig.h>
#include <stdint.h>
#include <stdio.h>
#include <assert.h>
@@ -25,6 +26,9 @@ struct video_info
uint8_t *outbuf;
size_t outbuf_size;
int fmt;
int pix_size;
AVFormatContext *format;
struct SwsContext *sws_ctx;
@@ -41,8 +45,6 @@ struct audio_info
void *outbuf;
size_t outbuf_size;
int pix_fmt;
} audio;
struct muxer_info
@@ -77,21 +79,20 @@ static bool init_audio(struct audio_info *audio, struct ffemu_params *param)
if (!codec)
return false;
audio->codec = avcodec_alloc_context();
avcodec_get_context_defaults(audio->codec);
audio->codec = avcodec_alloc_context3(codec);
audio->codec->sample_rate = param->samplerate;
audio->codec->time_base = (AVRational) { 1, param->samplerate };
audio->codec->channels = param->channels;
audio->codec->sample_fmt = AV_SAMPLE_FMT_S16;
if (avcodec_open(audio->codec, codec) != 0)
if (avcodec_open2(audio->codec, codec, NULL) != 0)
return false;
audio->buffer = av_malloc(audio->codec->frame_size * param->channels * sizeof(int16_t));
if (!audio->buffer)
return false;
audio->outbuf_size = 200000;
audio->outbuf_size = 2000000;
audio->outbuf = av_malloc(audio->outbuf_size);
if (!audio->outbuf)
return false;
@@ -105,18 +106,31 @@ static bool init_video(struct video_info *video, struct ffemu_params *param)
if (!codec)
return false;
video->codec = avcodec_alloc_context();
#if AV_HAVE_BIGENDIAN
video->fmt = PIX_FMT_RGB555BE;
#else
video->fmt = PIX_FMT_RGB555LE;
#endif
video->pix_size = sizeof(uint16_t);
if (param->rgb32)
{
video->fmt = PIX_FMT_RGB32;
video->pix_size = sizeof(uint32_t);
}
video->codec = avcodec_alloc_context3(codec);
video->codec->width = param->out_width;
video->codec->height = param->out_height;
video->codec->time_base = (AVRational) {param->fps.den, param->fps.num};
video->codec->pix_fmt = PIX_FMT_RGB32;
video->codec->sample_aspect_ratio = av_d2q(param->aspect_ratio * param->out_height / param->out_width, 255);
video->codec->thread_count = 2;
if (avcodec_open(video->codec, codec) != 0)
if (avcodec_open2(video->codec, codec, NULL) != 0)
return false;
// Allocate a big buffer :p ffmpeg API doesn't seem to give us some clues how big this buffer should be.
video->outbuf_size = 2000000;
video->outbuf_size = 1 << 23;
video->outbuf = av_malloc(video->outbuf_size);
// Just to make sure we can handle the biggest frames. Seemed to crash with just 256 * 224.
@@ -159,7 +173,7 @@ static bool init_muxer(ffemu_t *handle)
handle->audio.codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
handle->muxer.astream = stream;
if (av_write_header(ctx) < 0)
if (avformat_write_header(ctx, NULL) < 0)
return false;
handle->muxer.ctx = ctx;
@@ -177,7 +191,8 @@ static bool init_thread(ffemu_t *handle)
assert(handle->cond = SDL_CreateCond());
assert(handle->audio_fifo = fifo_new(32000 * sizeof(int16_t) * handle->params.channels * MAX_FRAMES / 60));
assert(handle->attr_fifo = fifo_new(sizeof(struct ffemu_video_data) * MAX_FRAMES));
assert(handle->video_fifo = fifo_new(handle->params.fb_width * handle->params.fb_height * sizeof(int16_t) * MAX_FRAMES));
assert(handle->video_fifo = fifo_new(handle->params.fb_width * handle->params.fb_height *
handle->video.pix_size * MAX_FRAMES));
handle->alive = true;
handle->can_sleep = true;
@@ -229,7 +244,6 @@ static void deinit_thread_buf(ffemu_t *handle)
ffemu_t *ffemu_new(const struct ffemu_params *params)
{
avcodec_init();
av_register_all();
ffemu_t *handle = calloc(1, sizeof(*handle));
@@ -365,18 +379,20 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
static int ffemu_push_video_thread(ffemu_t *handle, const struct ffemu_video_data *data)
{
handle->video.sws_ctx = sws_getCachedContext(handle->video.sws_ctx, data->width, data->height, PIX_FMT_RGB555LE,
handle->video.sws_ctx = sws_getCachedContext(handle->video.sws_ctx, data->width, data->height, handle->video.fmt,
handle->params.out_width, handle->params.out_height, PIX_FMT_RGB32, SWS_POINT,
NULL, NULL, NULL);
int linesize = data->pitch;
sws_scale(handle->video.sws_ctx, (const uint8_t* const*)&data->data, &linesize, 0, handle->params.out_width, handle->video.conv_frame->data, handle->video.conv_frame->linesize);
sws_scale(handle->video.sws_ctx, (const uint8_t* const*)&data->data, &linesize, 0,
handle->video.codec->height, handle->video.conv_frame->data, handle->video.conv_frame->linesize);
handle->video.conv_frame->pts = handle->video.frame_cnt;
handle->video.conv_frame->display_picture_number = handle->video.frame_cnt;
int outsize = avcodec_encode_video(handle->video.codec, handle->video.outbuf, handle->video.outbuf_size, handle->video.conv_frame);
int outsize = avcodec_encode_video(handle->video.codec, handle->video.outbuf,
handle->video.outbuf_size, handle->video.conv_frame);
if (outsize < 0)
return -1;
@@ -387,7 +403,8 @@ static int ffemu_push_video_thread(ffemu_t *handle, const struct ffemu_video_dat
pkt.data = handle->video.outbuf;
pkt.size = outsize;
pkt.pts = av_rescale_q(handle->video.codec->coded_frame->pts, handle->video.codec->time_base, handle->muxer.vstream->time_base);
pkt.pts = av_rescale_q(handle->video.codec->coded_frame->pts, handle->video.codec->time_base,
handle->muxer.vstream->time_base);
if (handle->video.codec->coded_frame->key_frame)
pkt.flags |= AV_PKT_FLAG_KEY;
@@ -454,7 +471,7 @@ int ffemu_finalize(ffemu_t *handle)
deinit_thread(handle);
// Push out frames still stuck in queue.
uint16_t *video_buf = av_malloc(handle->params.fb_width * handle->params.fb_height * sizeof(uint16_t));
void *video_buf = av_malloc(2 * handle->params.fb_width * handle->params.fb_height * handle->video.pix_size);
if (video_buf)
{
struct ffemu_video_data attr_buf;
@@ -524,7 +541,8 @@ static int SDLCALL ffemu_thread(void *data)
{
ffemu_t *ff = data;
uint16_t *video_buf = av_malloc(ff->params.fb_width * ff->params.fb_height * sizeof(uint16_t));
// For some reason, FFmpeg has a tendency to crash if we don't overallocate a bit. :s
void *video_buf = av_malloc(2 * ff->params.fb_width * ff->params.fb_height * ff->video.pix_size);
assert(video_buf);
size_t audio_buf_size = 128 * ff->params.channels * sizeof(int16_t);
+3 -27
View File
@@ -8,29 +8,6 @@
extern "C" {
#endif
// Available video codecs
typedef enum ffemu_video_codec
{
FFEMU_VIDEO_FFV1,
} ffemu_video_codec;
// Available audio codecs
typedef enum ffemu_audio_codec
{
FFEMU_AUDIO_VORBIS,
} ffemu_audio_codec;
// Available pixel formats
typedef enum ffemu_pixel_format
{
FFEMU_FMT_XBGR1555,
} ffemu_pixel_format;
typedef enum ffemu_rescaler
{
FFEMU_RESCALER_POINT
} ffemu_rescaler;
struct ffemu_rational
{
unsigned num;
@@ -40,9 +17,6 @@ struct ffemu_rational
// Parameters passed to ffemu_new()
struct ffemu_params
{
// Video codec to use. If not recording video, select FFEMU_VIDEO_NONE.
ffemu_video_codec vcodec;
// Desired output resolution.
unsigned out_width;
unsigned out_height;
@@ -68,6 +42,9 @@ struct ffemu_params
// Audio channels.
unsigned channels;
// If input is ARGB or XRGB1555.
bool rgb32;
// Audio bits. Sample format is always signed PCM in native byte order.
//unsigned bits;
@@ -107,7 +84,6 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data);
int ffemu_finalize(ffemu_t *handle);
#ifdef __cplusplus
}
#endif
+59 -18
View File
@@ -20,6 +20,7 @@
#include <assert.h>
#include "strl.h"
#include "config.def.h"
#include "file.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -28,6 +29,7 @@
#include <ctype.h>
struct settings g_settings;
struct global g_extern;
#ifdef HAVE_CONFIGFILE
static void read_keybinds(config_file_t *conf);
@@ -71,12 +73,18 @@ static void set_defaults(void)
case AUDIO_ROAR:
def_audio = "roar";
break;
case AUDIO_COREAUDIO:
def_audio = "coreaudio";
break;
case AUDIO_AL:
def_audio = "openal";
break;
case AUDIO_SDL:
def_audio = "sdl";
break;
case AUDIO_DSOUND:
def_audio = "dsound";
break;
case AUDIO_XAUDIO:
def_audio = "xaudio";
break;
@@ -111,10 +119,11 @@ static void set_defaults(void)
g_settings.video.xscale = xscale;
g_settings.video.yscale = yscale;
g_settings.video.fullscreen = fullscreen;
g_settings.video.fullscreen = g_extern.force_fullscreen ? true : fullscreen;
g_settings.video.fullscreen_x = fullscreen_x;
g_settings.video.fullscreen_y = fullscreen_y;
g_settings.video.force_16bit = force_16bit;
g_settings.video.disable_composition = disable_composition;
g_settings.video.vsync = vsync;
g_settings.video.smooth = video_smooth;
g_settings.video.force_aspect = force_aspect;
@@ -135,6 +144,9 @@ static void set_defaults(void)
g_settings.video.second_pass_smooth = second_pass_smooth;
#endif
g_settings.video.hires_record = hires_record;
g_settings.video.post_filter_record = post_filter_record;
g_settings.audio.enable = audio_enable;
g_settings.audio.out_rate = out_rate;
g_settings.audio.in_rate = in_rate;
@@ -173,7 +185,6 @@ static void parse_config_file(void);
void parse_config(void)
{
memset(&g_settings, 0, sizeof(struct settings));
set_defaults();
#ifdef HAVE_CONFIGFILE
@@ -193,7 +204,7 @@ static config_file_t *open_default_config_file(void)
const char *appdata = getenv("APPDATA");
if (appdata)
{
char conf_path[strlen(appdata) + strlen("/ssnes.cfg ")];
char conf_path[MAXPATHLEN];
strlcpy(conf_path, appdata, sizeof(conf_path));
strlcat(conf_path, "/ssnes.cfg", sizeof(conf_path));
conf = config_file_new(conf_path);
@@ -203,7 +214,7 @@ static config_file_t *open_default_config_file(void)
const char *home = getenv("HOME");
if (home)
{
char conf_path[strlen(home) + strlen("/.ssnes.cfg ")];
char conf_path[MAXPATHLEN];
strlcpy(conf_path, home, sizeof(conf_path));
strlcat(conf_path, "/.ssnes.cfg", sizeof(conf_path));
conf = config_file_new(conf_path);
@@ -218,14 +229,14 @@ static config_file_t *open_default_config_file(void)
const char *home = getenv("HOME");
if (xdg)
{
char conf_path[strlen(xdg) + strlen("/ssnes/ssnes.cfg ")];
char conf_path[MAXPATHLEN];
strlcpy(conf_path, xdg, sizeof(conf_path));
strlcat(conf_path, "/ssnes/ssnes.cfg", sizeof(conf_path));
conf = config_file_new(conf_path);
}
else if (home)
{
char conf_path[strlen(home) + strlen("/.ssnes.cfg ")];
char conf_path[MAXPATHLEN];
strlcpy(conf_path, home, sizeof(conf_path));
strlcat(conf_path, "/.ssnes.cfg", sizeof(conf_path));
conf = config_file_new(conf_path);
@@ -249,13 +260,8 @@ static config_file_t *open_default_config_file(void)
#define CONFIG_GET_DOUBLE(var, key) if (config_get_double(conf, key, &tmp_double)) \
g_settings.var = tmp_double
#define CONFIG_GET_STRING(var, key) do { \
if (config_get_string(conf, key, &tmp_str)) \
{ \
strlcpy(g_settings.var, tmp_str, sizeof(g_settings.var)); \
free(tmp_str); \
} \
} while(0)
#define CONFIG_GET_STRING(var, key) \
config_get_array(conf, key, g_settings.var, sizeof(g_settings.var))
#ifdef HAVE_CONFIGFILE
static void parse_config_file(void)
@@ -283,14 +289,20 @@ static void parse_config_file(void)
int tmp_int;
double tmp_double;
bool tmp_bool;
char *tmp_str;
char tmp_str[MAXPATHLEN];
CONFIG_GET_DOUBLE(video.xscale, "video_xscale");
CONFIG_GET_DOUBLE(video.yscale, "video_yscale");
CONFIG_GET_INT(video.fullscreen_x, "video_fullscreen_x");
CONFIG_GET_INT(video.fullscreen_y, "video_fullscreen_y");
CONFIG_GET_BOOL(video.fullscreen, "video_fullscreen");
if (!g_extern.force_fullscreen)
{
CONFIG_GET_BOOL(video.fullscreen, "video_fullscreen");
}
CONFIG_GET_BOOL(video.force_16bit, "video_force_16bit");
CONFIG_GET_BOOL(video.disable_composition, "video_disable_composition");
CONFIG_GET_BOOL(video.vsync, "video_vsync");
CONFIG_GET_BOOL(video.smooth, "video_smooth");
CONFIG_GET_BOOL(video.force_aspect, "video_force_aspect");
@@ -312,6 +324,9 @@ static void parse_config_file(void)
CONFIG_GET_DOUBLE(video.msg_pos_y, "video_message_pos_y");
#endif
CONFIG_GET_BOOL(video.hires_record, "video_hires_record");
CONFIG_GET_BOOL(video.post_filter_record, "video_post_filter_record");
#ifdef HAVE_DYLIB
CONFIG_GET_STRING(video.filter_path, "video_filter");
CONFIG_GET_STRING(video.external_driver, "video_external_driver");
@@ -319,7 +334,7 @@ static void parse_config_file(void)
#endif
#if defined(HAVE_CG) || defined(HAVE_XML)
if (config_get_string(conf, "video_shader_type", &tmp_str))
if (config_get_array(conf, "video_shader_type", tmp_str, sizeof(tmp_str)))
{
if (strcmp("cg", tmp_str) == 0)
g_settings.video.shader_type = SSNES_SHADER_CG;
@@ -329,8 +344,6 @@ static void parse_config_file(void)
g_settings.video.shader_type = SSNES_SHADER_AUTO;
else if (strcmp("none", tmp_str) == 0)
g_settings.video.shader_type = SSNES_SHADER_NONE;
free(tmp_str);
}
#endif
@@ -371,7 +384,13 @@ static void parse_config_file(void)
CONFIG_GET_STRING(audio.dsp_plugin, "audio_dsp_plugin");
CONFIG_GET_STRING(input.driver, "input_driver");
CONFIG_GET_STRING(libsnes, "libsnes_path");
CONFIG_GET_STRING(screenshot_directory, "screenshot_directory");
if (*g_settings.screenshot_directory && !path_is_directory(g_settings.screenshot_directory))
{
SSNES_WARN("screenshot_directory is not an existing directory, ignoring ...\n");
*g_settings.screenshot_directory = '\0';
}
CONFIG_GET_BOOL(rewind_enable, "rewind_enable");
@@ -385,6 +404,27 @@ static void parse_config_file(void)
CONFIG_GET_STRING(cheat_database, "cheat_database_path");
if (!g_extern.has_set_save_path && config_get_array(conf, "savefile_directory", tmp_str, sizeof(tmp_str)))
{
if (path_is_directory(tmp_str))
{
strlcpy(g_extern.savefile_name_srm, tmp_str, sizeof(g_extern.savefile_name_srm));
fill_pathname_dir(g_extern.savefile_name_srm, g_extern.basename, ".srm", sizeof(g_extern.savefile_name_srm));
}
else
SSNES_WARN("savefile_directory is not a directory, ignoring ...!\n");
}
if (!g_extern.has_set_state_path && config_get_array(conf, "savestate_directory", tmp_str, sizeof(tmp_str)))
{
if (path_is_directory(tmp_str))
{
strlcpy(g_extern.savestate_name, tmp_str, sizeof(g_extern.savestate_name));
fill_pathname_dir(g_extern.savestate_name, g_extern.basename, ".state", sizeof(g_extern.savestate_name));
}
else
SSNES_WARN("savestate_directory is not a directory, ignoring ...\n");
}
read_keybinds(conf);
config_file_free(conf);
@@ -650,6 +690,7 @@ static const struct key_map sdlk_map[] = {
{ "print_screen", SDLK_PRINT },
{ "scroll_lock", SDLK_SCROLLOCK },
{ "tilde", SDLK_BACKQUOTE },
{ "backquote", SDLK_BACKQUOTE },
{ "nul", SDLK_UNKNOWN },
};
+146
View File
@@ -0,0 +1,146 @@
#!/bin/sh
###############
# Build script which builds and packages SSNES for MinGW 32/64-bit.
# Preferably build on Linux with a cross chain ... :D
##########
####
## Tweak these to suit your environment.
## Not defining the variable will avoid building that target.
## Set MINGW32_BASE and/or MINGW64_BASE to set toolchain prefix:
## E.g.: i486-mingw32-gcc would get prefix "i486-mingw32".
BUILD_32BIT=yes
BUILD_64BIT=yes
BUILD_PHOENIX_GUI=yes
if [ ! -z "$NOBUILD_32BIT" ]; then
BUILD_32BIT=no
fi
if [ ! -z "$NOBUILD_64BIT" ]; then
BUILD_64BIT=no
fi
if [ ! -z "$NOBUILD_PHOENIX_GUI" ]; then
BUILD_PHOENIX_GUI=no
fi
########
die()
{
echo "$@"
exit 1
}
message()
{
echo ""
echo "================================"
echo "$@"
echo "================================"
echo ""
}
if [ ! -f "`which zip`" ]; then
echo "Cannot find 'zip'. Cannot package, quitting ..."
exit 1
fi
if [ ! -f "`which unzip`" ]; then
echo "Cannot find 'unzip'. Cannot unpack, quitting ..."
exit 1
fi
if [ ! -f "`which wget`" ]; then
echo "Cannot find 'wget'. Cannot unpack, quitting ..."
exit 1
fi
do_phoenix_build()
{
message "Build Phoenix GUI"
### Build Phoenix GUI
if [ ! -d "Phoenix" ]; then
git clone git://github.com/Themaister/SSNES-Phoenix.git Phoenix
cd Phoenix
else
cd Phoenix
git pull origin master
fi
make -f Makefile.win32 clean || die "Failed to clean ..."
make -f Makefile.win32 CC="$C_COMPILER" CXX="$CXX_COMPILER" WINDRES="$WINDRES" -j4 all || die "Failed to build ..."
touch ssnes-phoenix.cfg
cp ssnes-phoenix.cfg ssnes-phoenix.exe ../ || die "Failed to copy ..."
cd ..
}
do_build()
{
SSNES_DIR="$1"
LIBZIPNAME="$2"
BUILDTYPE="$3"
if [ ! -d "$SSNES_DIR" ]; then
git clone git://github.com/Themaister/SSNES.git "$SSNES_DIR"
cd "$SSNES_DIR"
else
cd "$SSNES_DIR"
git pull origin master
fi
if [ ! -f "$LIBZIPNAME" ]; then
make -f Makefile.win libs_${BUILDTYPE} || die "Failed to extract"
fi
make -f Makefile.win clean || die "Failed to clean ..."
make -f Makefile.win CC="$C_COMPILER" CXX="$CXX_COMPILER" -j4 all SLIM=1 || die "Failed to build ..."
make -f Makefile.win CC="$C_COMPILER" CXX="$CXX_COMPILER" dist_${BUILDTYPE} SLIM=1 || die "Failed to dist ..."
if [ -z "`find . | grep "ssnes-win"`" ]; then
die "Did not find build ..."
fi
if [ "$BUILD_PHOENIX_GUI" = "yes" ]; then
do_phoenix_build
fi
ZIP_BASE="`find . | grep "ssnes-win" | head -n1`"
ZIP_SLIM="`echo $ZIP_BASE | sed -e 's|\.zip|-slim.zip|'`"
ZIP_FULL="`echo $ZIP_BASE | sed -e 's|\.zip|-full.zip|'`"
if [ "$BUILD_PHOENIX_GUI" = "yes" ]; then
zip "$ZIP_BASE" ssnes-phoenix.exe ssnes-phoenix.cfg
fi
mv -v "$ZIP_BASE" "../$ZIP_SLIM" || die "Failed to move final build ..."
make -f Makefile.win clean || die "Failed to clean ..."
make -f Makefile.win CC="$C_COMPILER" CXX="$CXX_COMPILER" -j4 all || die "Failed to build ..."
make -f Makefile.win CC="$C_COMPILER" CXX="$CXX_COMPILER" dist_${BUILDTYPE} || die "Failed to dist ..."
if [ "$BUILD_PHOENIX_GUI" = "yes" ]; then
zip "$ZIP_BASE" ssnes-phoenix.exe ssnes-phoenix.cfg
fi
mv -v "$ZIP_BASE" "../$ZIP_FULL" || die "Failed to move final build ..."
cd ..
}
if [ "$BUILD_32BIT" = yes ]; then
message "Building for 32-bit!"
C_COMPILER=${MINGW32_BASE}-gcc
CXX_COMPILER=${MINGW32_BASE}-g++
WINDRES=${MINGW32_BASE}-windres
do_build "SSNES-w32" "SSNES-win32-libs.zip" "x86"
fi
if [ "$BUILD_64BIT" = yes ]; then
message "Building for 64-bit!"
C_COMPILER=${MINGW64_BASE}-gcc
CXX_COMPILER=${MINGW64_BASE}-g++
WINDRES=${MINGW64_BASE}-windres
do_build "SSNES-w64" "SSNES-win64-libs.zip" "x86_64"
fi
message "Built successfully! :)"
+170 -74
View File
@@ -49,15 +49,9 @@
#include "SDL.h"
// OSX seems to really need -lSDLmain,
// so we include SDL.h here so it can hack our main.
// I had issues including this in Win32 for some reason. :)
// We want to use -mconsole in Win32, so we need main().
#endif
struct global g_extern = {
.video_active = true,
.audio_active = true,
.game_type = SSNES_CART_NORMAL,
};
// To avoid continous switching if we hold the button down, we require that the button must go from pressed, unpressed back to pressed to be able to toggle between then.
static void set_fast_forward_button(bool new_button_state, bool new_hold_button_state)
{
@@ -96,7 +90,7 @@ static void set_fast_forward_button(bool new_button_state, bool new_hold_button_
static void take_screenshot(const uint16_t *frame, unsigned width, unsigned height)
{
if (!*(g_settings.screenshot_directory))
if (!(*g_settings.screenshot_directory))
return;
bool ret = screenshot_dump(g_settings.screenshot_directory, frame,
@@ -142,14 +136,16 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height)
if (g_extern.do_screenshot)
take_screenshot(data, width, height);
// Slightly messy code,
// but we really need to do processing before blocking on VSync for best possible scheduling.
#ifdef HAVE_FFMPEG
if (g_extern.recording)
if (g_extern.recording && (!g_extern.filter.active || !g_settings.video.post_filter_record))
{
struct ffemu_video_data ffemu_data = {
.data = data,
.pitch = height == 448 || height == 478 ? 1024 : 2048,
.width = width,
.height = height
.height = height,
};
ffemu_push_video(g_extern.rec, &ffemu_data);
}
@@ -165,6 +161,20 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height)
g_extern.filter.psize(&owidth, &oheight);
g_extern.filter.prender(g_extern.filter.colormap, g_extern.filter.buffer,
g_extern.filter.pitch, data, (height == 448 || height == 478) ? 1024 : 2048, width, height);
#ifdef HAVE_FFMPEG
if (g_extern.recording && g_settings.video.post_filter_record)
{
struct ffemu_video_data ffemu_data = {
.data = g_extern.filter.buffer,
.pitch = g_extern.filter.pitch,
.width = owidth,
.height = oheight,
};
ffemu_push_video(g_extern.rec, &ffemu_data);
}
#endif
if (!driver.video->frame(driver.video_data, g_extern.filter.buffer, owidth, oheight, g_extern.filter.pitch, msg))
g_extern.video_active = false;
}
@@ -313,26 +323,8 @@ static int16_t input_state(bool port, unsigned device, unsigned index, unsigned
return res;
}
static void fill_pathname(char *out_path, const char *in_path, const char *replace, size_t size)
{
char tmp_path[strlen(in_path) + 1];
strlcpy(tmp_path, in_path, sizeof(tmp_path));
char *tok = NULL;
tok = strrchr(tmp_path, '.');
if (tok != NULL)
*tok = '\0';
assert(strlcpy(out_path, tmp_path, size) < size);
assert(strlcat(out_path, replace, size) < size);
}
static void fill_pathname_noext(char *out_path, const char *in_path, const char *replace, size_t size)
{
assert(strlcpy(out_path, in_path, size) < size);
assert(strlcat(out_path, replace, size) < size);
}
#ifdef _WIN32
#define SSNES_DEFAULT_CONF_PATH_STR "\n\tDefaults to ssnes.cfg in same directory as ssnes.exe."
#define SSNES_DEFAULT_CONF_PATH_STR "\n\t\tDefaults to ssnes.cfg in same directory as ssnes.exe."
#elif defined(__APPLE__)
#define SSNES_DEFAULT_CONF_PATH_STR " Defaults to $HOME/.ssnes.cfg."
#else
@@ -340,7 +332,7 @@ static void fill_pathname_noext(char *out_path, const char *in_path, const char
#endif
#ifdef _WIN32
#define PACKAGE_VERSION "0.6.2"
#define PACKAGE_VERSION "0.8"
#endif
#include "config.features.h"
@@ -383,6 +375,7 @@ static void print_help(void)
puts("Usage: ssnes [rom file] [options...]");
puts("\t-h/--help: Show this help message.");
puts("\t-s/--save: Path for save file (*.srm). Required when rom is input from stdin.");
puts("\t-f/--fullscreen: Start SSNES in fullscreen regardless of config settings.");
puts("\t-S/--savestate: Path to use for save states. If not selected, *.state will be assumed.");
#ifdef HAVE_CONFIGFILE
puts("\t-c/--config: Path for config file." SSNES_DEFAULT_CONF_PATH_STR);
@@ -408,10 +401,11 @@ static void print_help(void)
#endif
#ifdef HAVE_FFMPEG
puts("\t-r/--record: Path to record video file.\n\t\tSettings for video/audio codecs are found in config file.");
puts("\t-r/--record: Path to record video file.\n\t\tUsing .mkv extension is recommended, and codecs used are FFV1/FLAC.");
#endif
puts("\t-v/--verbose: Verbose logging.");
puts("\t-U/--ups: Specifies path for UPS patch that will be applied to ROM.");
puts("\t--bps: Specifies path for BPS patch that will be applied to ROM.");
puts("\t-X/--xml: Specifies path to XML memory map.");
puts("\t-D/--detach: Detach SSNES from the running console. Not relevant for all platforms.\n");
@@ -420,12 +414,84 @@ static void print_help(void)
static void set_basename(const char *path)
{
char tmp[strlen(path) + 1];
strlcpy(tmp, path, sizeof(tmp));
char *dst = strrchr(tmp, '.');
strlcpy(g_extern.basename, path, sizeof(g_extern.basename));
char *dst = strrchr(g_extern.basename, '.');
if (dst)
*dst = '\0';
strlcpy(g_extern.basename, tmp, sizeof(g_extern.basename));
}
static void set_paths(const char *path)
{
set_basename(path);
SSNES_LOG("Opening file: \"%s\"\n", path);
g_extern.rom_file = fopen(path, "rb");
if (g_extern.rom_file == NULL)
{
SSNES_ERR("Could not open file: \"%s\"\n", path);
exit(1);
}
if (!g_extern.has_set_save_path)
fill_pathname_noext(g_extern.savefile_name_srm, g_extern.basename, ".srm", sizeof(g_extern.savefile_name_srm));
if (!g_extern.has_set_state_path)
fill_pathname_noext(g_extern.savestate_name, g_extern.basename, ".state", sizeof(g_extern.savestate_name));
if (path_is_directory(g_extern.savefile_name_srm))
{
fill_pathname_dir(g_extern.savefile_name_srm, g_extern.basename, ".srm", sizeof(g_extern.savefile_name_srm));
SSNES_LOG("Redirecting save file to \"%s\".\n", g_extern.savefile_name_srm);
}
if (path_is_directory(g_extern.savestate_name))
{
fill_pathname_dir(g_extern.savestate_name, g_extern.basename, ".state", sizeof(g_extern.savestate_name));
SSNES_LOG("Redirecting save state to \"%s\".\n", g_extern.savestate_name);
}
if (*g_extern.config_path && path_is_directory(g_extern.config_path))
{
fill_pathname_dir(g_extern.config_path, g_extern.basename, ".cfg", sizeof(g_extern.config_path));
SSNES_LOG("Redirecting config file to \"%s\".\n", g_extern.config_path);
if (!path_file_exists(g_extern.config_path))
{
*g_extern.config_path = '\0';
SSNES_LOG("Did not find config file. Using system default.\n");
}
}
}
static void verify_stdin_paths(void)
{
if (strlen(g_extern.savefile_name_srm) == 0)
{
SSNES_ERR("Need savefile path argument (--save) when reading rom from stdin.\n");
print_help();
exit(1);
}
else if (strlen(g_extern.savestate_name) == 0)
{
SSNES_ERR("Need savestate path argument (--savestate) when reading rom from stdin.\n");
print_help();
exit(1);
}
if (path_is_directory(g_extern.savefile_name_srm))
{
SSNES_ERR("Cannot specify directory for path argument (--save) when reading from stdin.\n");
print_help();
exit(1);
}
else if (path_is_directory(g_extern.savestate_name))
{
SSNES_ERR("Cannot specify directory for path argument (--savestate) when reading from stdin.\n");
print_help();
exit(1);
}
else if (path_is_directory(g_extern.config_path))
{
SSNES_ERR("Cannot specify directory for config file (--config) when reading from stdin.\n");
print_help();
exit(1);
}
}
static void parse_input(int argc, char *argv[])
@@ -441,6 +507,7 @@ static void parse_input(int argc, char *argv[])
struct option opts[] = {
{ "help", 0, NULL, 'h' },
{ "save", 1, NULL, 's' },
{ "fullscreen", 0, NULL, 'f' },
#ifdef HAVE_FFMPEG
{ "record", 1, NULL, 'r' },
#endif
@@ -466,6 +533,7 @@ static void parse_input(int argc, char *argv[])
{ "frames", 1, NULL, 'F' },
{ "port", 1, &val, 'p' },
{ "ups", 1, NULL, 'U' },
{ "bps", 1, &val, 'B' },
{ "xml", 1, NULL, 'X' },
{ "detach", 0, NULL, 'D' },
{ NULL, 0, NULL, 0 }
@@ -485,7 +553,7 @@ static void parse_input(int argc, char *argv[])
#define CONFIG_FILE_ARG
#endif
char optstring[] = "hs:vS:m:p4jJg:b:B:Y:Z:P:HC:F:U:DN:X:" FFMPEG_RECORD_ARG CONFIG_FILE_ARG;
char optstring[] = "hs:fvS:m:p4jJg:b:B:Y:Z:P:HC:F:U:DN:X:" FFMPEG_RECORD_ARG CONFIG_FILE_ARG;
for(;;)
{
val = 0;
@@ -518,6 +586,10 @@ static void parse_input(int argc, char *argv[])
g_extern.has_set_save_path = true;
break;
case 'f':
g_extern.force_fullscreen = true;
break;
case 'g':
strlcpy(g_extern.gb_rom_path, optarg, sizeof(g_extern.gb_rom_path));
g_extern.game_type = SSNES_CART_SGB;
@@ -613,6 +685,7 @@ static void parse_input(int argc, char *argv[])
case 'U':
strlcpy(g_extern.ups_name, optarg, sizeof(g_extern.ups_name));
g_extern.ups_pref = true;
break;
case 'X':
@@ -629,7 +702,11 @@ static void parse_input(int argc, char *argv[])
switch (val)
{
case 'p':
g_extern.netplay_port = strtol(optarg, NULL, 0);
g_extern.netplay_port = strtoul(optarg, NULL, 0);
break;
case 'B':
strlcpy(g_extern.bps_name, optarg, sizeof(g_extern.bps_name));
g_extern.bps_pref = true;
break;
default:
break;
@@ -647,34 +724,9 @@ static void parse_input(int argc, char *argv[])
}
if (optind < argc)
{
set_basename(argv[optind]);
SSNES_LOG("Opening file: \"%s\"\n", argv[optind]);
g_extern.rom_file = fopen(argv[optind], "rb");
if (g_extern.rom_file == NULL)
{
SSNES_ERR("Could not open file: \"%s\"\n", argv[optind]);
exit(1);
}
// strl* would be nice :D
if (!g_extern.has_set_save_path)
fill_pathname_noext(g_extern.savefile_name_srm, g_extern.basename, ".srm", sizeof(g_extern.savefile_name_srm));
if (!g_extern.has_set_state_path)
fill_pathname_noext(g_extern.savestate_name, g_extern.basename, ".state", sizeof(g_extern.savestate_name));
}
else if (strlen(g_extern.savefile_name_srm) == 0)
{
SSNES_ERR("Need savefile path argument (--save) when reading rom from stdin.\n");
print_help();
exit(1);
}
else if (strlen(g_extern.savestate_name) == 0)
{
SSNES_ERR("Need savestate path argument (--savefile) when reading rom from stdin.\n");
print_help();
exit(1);
}
set_paths(argv[optind]);
else
verify_stdin_paths();
}
// TODO: Add rest of the controllers.
@@ -786,9 +838,11 @@ static inline void save_files(void)
#ifdef HAVE_FFMPEG
static void init_recording(void)
{
// Hardcode these options at the moment. Should be specificed in the config file later on.
if (g_extern.recording)
{
// Not perfectly SNES accurate, but this can be adjusted later during processing
// and playback losslessly, and we please the containers more by
// using "sane" values.
struct ffemu_rational ntsc_fps = {60000, 1000};
struct ffemu_rational pal_fps = {50000, 1000};
struct ffemu_params params = {
@@ -800,9 +854,29 @@ static void init_recording(void)
.samplerate = 32000,
.filename = g_extern.record_path,
.fps = psnes_get_region() == SNES_REGION_NTSC ? ntsc_fps : pal_fps,
.aspect_ratio = 4.0/3
.aspect_ratio = 4.0 / 3,
.rgb32 = false,
};
SSNES_LOG("Recording with FFmpeg to %s.\n", g_extern.record_path);
if (g_settings.video.hires_record)
{
params.out_width = 512;
params.out_height = 448;
}
if (g_settings.video.post_filter_record && g_extern.filter.active)
{
g_extern.filter.psize(&params.out_width, &params.out_height);
params.rgb32 = true;
unsigned max_width = 512;
unsigned max_height = 512;
g_extern.filter.psize(&max_width, &max_height);
params.fb_width = g_extern.filter.pitch >> 2;
params.fb_height = next_pow2(max_height);
}
SSNES_LOG("Recording with FFmpeg to %s @ %ux%u. (FB size: %ux%u 32-bit: %s)\n", g_extern.record_path, params.out_width, params.out_height, params.fb_width, params.fb_height, params.rgb32 ? "yes" : "no");
g_extern.rec = ffemu_new(&params);
if (!g_extern.rec)
{
@@ -1033,11 +1107,17 @@ static void fill_pathnames(void)
if (!g_extern.bsv_movie_playback)
fill_pathname(g_extern.bsv_movie_path, g_extern.savefile_name_srm, "", sizeof(g_extern.bsv_movie_path));
if (!(*g_extern.ups_name) && *g_extern.basename)
fill_pathname_noext(g_extern.ups_name, g_extern.basename, ".ups", sizeof(g_extern.ups_name));
if (*g_extern.basename)
{
if (!(*g_extern.ups_name))
fill_pathname_noext(g_extern.ups_name, g_extern.basename, ".ups", sizeof(g_extern.ups_name));
if (!(*g_extern.xml_name) && *g_extern.basename)
fill_pathname_noext(g_extern.xml_name, g_extern.basename, ".xml", sizeof(g_extern.xml_name));
if (!(*g_extern.bps_name))
fill_pathname_noext(g_extern.bps_name, g_extern.basename, ".bps", sizeof(g_extern.bps_name));
if (!(*g_extern.xml_name))
fill_pathname_noext(g_extern.xml_name, g_extern.basename, ".xml", sizeof(g_extern.xml_name));
}
}
// Save or load state here.
@@ -1047,7 +1127,7 @@ static void check_savestates(void)
bool should_savestate = driver.input->key_pressed(driver.input_data, SSNES_SAVE_STATE_KEY);
if (should_savestate && !old_should_savestate)
{
char save_path[strlen(g_extern.savestate_name) * 2];
char save_path[MAXPATHLEN];
if (g_extern.state_slot > 0)
snprintf(save_path, sizeof(save_path), "%s%u", g_extern.savestate_name, g_extern.state_slot);
@@ -1073,7 +1153,7 @@ static void check_savestates(void)
bool should_loadstate = driver.input->key_pressed(driver.input_data, SSNES_LOAD_STATE_KEY);
if (!should_savestate && should_loadstate && !old_should_loadstate)
{
char load_path[strlen(g_extern.savestate_name) * 2];
char load_path[MAXPATHLEN];
if (g_extern.state_slot > 0)
snprintf(load_path, sizeof(load_path), "%s%u", g_extern.savestate_name, g_extern.state_slot);
@@ -1469,9 +1549,20 @@ static void fill_title_buf(void)
snprintf(g_extern.title_buf, sizeof(g_extern.title_buf), "SSNES");
}
static void init_state(void)
{
// Make absolutely sure our big global structs are in-fact zeroed out.
memset(&g_extern, 0, sizeof(g_extern));
memset(&g_settings, 0, sizeof(g_settings));
g_extern.video_active = true;
g_extern.audio_active = true;
g_extern.game_type = SSNES_CART_NORMAL;
}
int main(int argc, char *argv[])
{
init_state();
parse_input(argc, argv);
parse_config();
init_dlsym();
@@ -1529,6 +1620,10 @@ int main(int argc, char *argv[])
// Main loop
for(;;)
{
// DSP plugin GUI events.
if (g_extern.audio_data.dsp_handle && g_extern.audio_data.dsp_plugin->events)
g_extern.audio_data.dsp_plugin->events(g_extern.audio_data.dsp_handle);
// Time to drop?
if (driver.input->key_pressed(driver.input_data, SSNES_QUIT_KEY) ||
!driver.video->alive(driver.video_data) || g_extern.bsv_movie_end)
@@ -1615,3 +1710,4 @@ error:
return 1;
}
+17
View File
@@ -1,5 +1,13 @@
##### Config file for SSNES
# Save all save files (*.srm) to this directory. This includes related files like .bsv, .rtc, .psrm, etc ...
# This will be overridden by explicit command line options.
# savefile_directory =
# Save all save states (*.state) to this directory.
# This will be overridden by explicit command line options.
# savestate_directory =
## If enabled, load libsnes from a dynamic location.
# libsnes_path = "/path/to/libsnes.so"
@@ -25,6 +33,9 @@
# Force 16-bit colors. Apparently some video cards in use today have troubles with 32-bit ...
# video_force_16bit = false
# Forcibly disable composition. Only works in Windows Vista/7 for now.
# video_disable_composition = false
# Video vsync.
# video_vsync = true
@@ -290,3 +301,9 @@
# Directory to dump screenshots to.
# screenshot_directory =
# Records video assuming video is hi-res.
# video_hires_record = false
# Records video after CPU video filter.
# video_post_filter_record = false