mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 08:10:30 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
331f46fb24 | ||
|
|
e88662cab0 | ||
|
|
395f6d1d3c | ||
|
|
94bd78b3e1 | ||
|
|
dc373fb7d0 | ||
|
|
140812d64a | ||
|
|
a91c4e556f | ||
|
|
372c622166 | ||
|
|
d961d68e9c | ||
|
|
33a325a730 | ||
|
|
909b25c7ac | ||
|
|
77f110c23e | ||
|
|
9dc9416ef9 | ||
|
|
0b20f11617 | ||
|
|
c9594776f8 | ||
|
|
8f55b9904d | ||
|
|
093ba1c33d | ||
|
|
67a434d563 | ||
|
|
ff0c3f047a | ||
|
|
2934ba301b | ||
|
|
f3627aaf59 | ||
|
|
dc8a2dea72 | ||
|
|
329969eb05 | ||
|
|
71923af675 | ||
|
|
18e58ca5f4 | ||
|
|
8c3aebbbc8 | ||
|
|
7f40c11eb7 | ||
|
|
0ed668d1b5 | ||
|
|
1c8632cac9 | ||
|
|
e7324989fc | ||
|
|
a7fc3807b9 | ||
|
|
99ad7bcc51 | ||
|
|
5b88e338df | ||
|
|
425af71450 | ||
|
|
19c5eab07a | ||
|
|
e5286cbe39 | ||
|
|
02d40aa212 | ||
|
|
8a5c537e15 | ||
|
|
c6c4f2f4d1 | ||
|
|
5bc7ea4045 | ||
|
|
077acf7894 | ||
|
|
c5f86f12c0 | ||
|
|
243e941d81 | ||
|
|
d67e6b7c15 | ||
|
|
f76bf59fed | ||
|
|
1c4d88eb04 | ||
|
|
ad04db8365 | ||
|
|
a63ccbbdab | ||
|
|
278f314c03 | ||
|
|
a3abda7e7b | ||
|
|
c0a9db111d | ||
|
|
676ce10119 | ||
|
|
c18dce4783 | ||
|
|
088b01b52e | ||
|
|
c0f436b8ab | ||
|
|
3377020242 | ||
|
|
6db58fe1dd | ||
|
|
67c7cd8de9 | ||
|
|
457a3d458a | ||
|
|
6538312d86 | ||
|
|
7f1a5075b1 | ||
|
|
e412ecdefe | ||
|
|
67b89d6f42 | ||
|
|
c8d3278dbc | ||
|
|
3556445660 | ||
|
|
f28fc0cabb | ||
|
|
e0bc548400 | ||
|
|
011a4c8a89 | ||
|
|
87bc5220eb | ||
|
|
673d0ed1a2 | ||
|
|
a5079bdda1 | ||
|
|
fc43e7155e | ||
|
|
4935333b63 | ||
|
|
5a3232adff | ||
|
|
d03f351f33 | ||
|
|
8d5bf997a9 | ||
|
|
0738480439 | ||
|
|
9a5e1a31ad | ||
|
|
5ab7664f49 | ||
|
|
1cb2c60ccc | ||
|
|
1356d0ab4c | ||
|
|
b4c4a65f2d | ||
|
|
ab3cf8daca | ||
|
|
2b6c252747 | ||
|
|
ec9a6ac90a | ||
|
|
a207549f18 | ||
|
|
efbf9354f9 | ||
|
|
216ddc9fd6 | ||
|
|
3b668cccd3 | ||
|
|
30a86a2845 | ||
|
|
a7870aa888 | ||
|
|
271b99981c | ||
|
|
08ef01bb13 | ||
|
|
e778af462a | ||
|
|
9d124b2380 | ||
|
|
f24e7cd665 | ||
|
|
e7f1526942 | ||
|
|
647b1ba53c | ||
|
|
eac8d9f191 | ||
|
|
50babe8432 | ||
|
|
8171977ed7 | ||
|
|
f8aaf1df1e | ||
|
|
04d0d84d01 | ||
|
|
f4aa9fa865 | ||
|
|
40ef7d6f00 | ||
|
|
66b7b1691c | ||
|
|
43972d4c35 | ||
|
|
632545c409 | ||
|
|
3a47e2cd63 | ||
|
|
a221e3e4cd | ||
|
|
b87f50dc9d | ||
|
|
a5b3f421d1 | ||
|
|
182525afa9 | ||
|
|
419afbcb83 | ||
|
|
12ca70a308 | ||
|
|
1fa7663d3b | ||
|
|
2e34c6fc5c | ||
|
|
cb7002559d | ||
|
|
36ec92793f | ||
|
|
4187d70b30 | ||
|
|
5969655c3c | ||
|
|
09d2ec7d3a | ||
|
|
27b975d0fc | ||
|
|
6da64013fa | ||
|
|
d8eb5de380 | ||
|
|
23aa8bbe7a | ||
|
|
3b5472dbac | ||
|
|
3b4e60aada | ||
|
|
8d8fb44c18 | ||
|
|
b46fb60a6e | ||
|
|
f7399bbc8b | ||
|
|
7f7d47b120 | ||
|
|
e7aa3ed0cc | ||
|
|
0ece8b1319 | ||
|
|
557e05dd12 | ||
|
|
08a1496501 | ||
|
|
5a24037c61 | ||
|
|
5dbd1ea35c | ||
|
|
2476e3dee3 | ||
|
|
c05a5c9fdd | ||
|
|
503c51d633 | ||
|
|
46054de4f8 | ||
|
|
604eb9b973 | ||
|
|
0e72120bdc | ||
|
|
b8064f5d42 | ||
|
|
379b14a0f3 | ||
|
|
2a598c0b97 | ||
|
|
aa124b4c40 | ||
|
|
bb5e31df0d | ||
|
|
e49d216884 | ||
|
|
598d06fcb5 | ||
|
|
7f13ec88e6 | ||
|
|
c5c60c6f53 | ||
|
|
81b0e6d0a3 | ||
|
|
da97b29d0b | ||
|
|
69b7f92285 | ||
|
|
3a9dbbc8b7 | ||
|
|
bf5db10459 | ||
|
|
4fa27a5f0f | ||
|
|
3e7c412738 | ||
|
|
88c870dcfa | ||
|
|
66e1179503 | ||
|
|
4ecea5d0a4 | ||
|
|
57816edfd3 | ||
|
|
17cf0985c5 | ||
|
|
776b0a1e2d | ||
|
|
4a6392aefa | ||
|
|
29dd4727b8 | ||
|
|
face42a888 | ||
|
|
45fa6795c3 | ||
|
|
a9d339efb4 | ||
|
|
60841b01a5 | ||
|
|
ad67511483 | ||
|
|
104fb4fb16 | ||
|
|
816ef095ed | ||
|
|
48e8472f9c | ||
|
|
1755500b19 | ||
|
|
4bfc827eb2 | ||
|
|
ddfeacbd83 | ||
|
|
79353f861c | ||
|
|
f3f4efe304 | ||
|
|
51721b4cb6 | ||
|
|
477091ce8f | ||
|
|
2707b87767 | ||
|
|
b3d422b5cd | ||
|
|
d1dc65f202 | ||
|
|
7815940a97 | ||
|
|
4f841c208d | ||
|
|
b4f0d9b8f8 | ||
|
|
b96ab2982c | ||
|
|
825c1de7d1 | ||
|
|
6ada4d87d3 | ||
|
|
53abc92322 | ||
|
|
a816082ac3 | ||
|
|
47e63f1c6b | ||
|
|
cdf3d05c8e | ||
|
|
389e9ba0dc | ||
|
|
a6f7a2311f | ||
|
|
f19d99d523 | ||
|
|
e7082fd6ea | ||
|
|
4806d2fe88 | ||
|
|
f85ff7124b | ||
|
|
0f010cf3d2 | ||
|
|
95636d4f04 | ||
|
|
43905e410e | ||
|
|
d23a7e3ddf | ||
|
|
652f4c5856 | ||
|
|
90febc4ecf | ||
|
|
34b5994842 | ||
|
|
0c3f6ed66b | ||
|
|
f3e487557e | ||
|
|
ee12266a2b | ||
|
|
1981b14de9 | ||
|
|
65665f36cc | ||
|
|
a48dc55c41 | ||
|
|
202a458524 | ||
|
|
b4de704a09 | ||
|
|
fef5edfac8 | ||
|
|
a6e74775b1 | ||
|
|
ee66a28d05 | ||
|
|
5e28d82af2 | ||
|
|
c2a06bfa09 | ||
|
|
66510e21aa | ||
|
|
ec5e3355b9 | ||
|
|
0522a2d6a0 | ||
|
|
fad89f9d2d | ||
|
|
97f937ec6d | ||
|
|
9647006a2b | ||
|
|
418ba455f8 | ||
|
|
197d62860b | ||
|
|
402451626f | ||
|
|
3739cbfc70 | ||
|
|
235355ffc7 | ||
|
|
4856d0cac3 | ||
|
|
77a2723bb0 | ||
|
|
41cd6e21c3 | ||
|
|
1fc99c7123 | ||
|
|
fddcc3447c | ||
|
|
70e9f084b7 | ||
|
|
824790a80d | ||
|
|
23d23983bc | ||
|
|
c42655ed91 | ||
|
|
f46ee78c76 | ||
|
|
5d1d188231 | ||
|
|
c55ce543c2 | ||
|
|
de8bc62474 | ||
|
|
397da887a7 | ||
|
|
f44733d7f6 | ||
|
|
5faa28a1a2 | ||
|
|
efaa85ff9b | ||
|
|
8f5580be10 | ||
|
|
c7acd99548 | ||
|
|
64ee13e6f4 | ||
|
|
e134f8afc7 | ||
|
|
dd89e6fbe0 | ||
|
|
a79a1b46f7 | ||
|
|
6467f51ea8 | ||
|
|
141eefb03b | ||
|
|
068c224d83 | ||
|
|
5bc8090ea7 | ||
|
|
466ab1ef8d | ||
|
|
a1c19ed1f5 | ||
|
|
cce4b7d20c | ||
|
|
80925ffc11 | ||
|
|
9eb14e328d | ||
|
|
5ae0cb1821 | ||
|
|
fc594f7f0f | ||
|
|
0c4e406ee7 | ||
|
|
a082ec5251 | ||
|
|
953b5daf0a | ||
|
|
b4ff622f32 | ||
|
|
f26b54c998 | ||
|
|
d7b3f39136 | ||
|
|
5bcf601a40 | ||
|
|
e91ebdac97 | ||
|
|
b2dd12d186 | ||
|
|
9c79bf0ba2 | ||
|
|
f48247a18b | ||
|
|
48e015558e | ||
|
|
0ff3cbc93b | ||
|
|
25f0e162a4 | ||
|
|
0810812cd9 | ||
|
|
15e0b721e6 | ||
|
|
9d12c3ac87 | ||
|
|
a7c3d8d17a | ||
|
|
8fbfdee011 | ||
|
|
bc999e72fa | ||
|
|
c7f8110670 | ||
|
|
b0dbc67281 | ||
|
|
4916f7b08b | ||
|
|
1058d94ec1 | ||
|
|
340dca3c6d | ||
|
|
4ae0f3da22 | ||
|
|
7d2d1ce49e | ||
|
|
fa26f6a10c | ||
|
|
db71a5cdba | ||
|
|
5058fdcd1a | ||
|
|
ef04f199d5 | ||
|
|
d26dd9d9e7 | ||
|
|
d2bc4096de | ||
|
|
08ca1a54d3 | ||
|
|
869eddfc1a | ||
|
|
fbced43f11 | ||
|
|
c996f816b8 | ||
|
|
33ecedb462 | ||
|
|
a58676c9e1 | ||
|
|
1ee9722e5f | ||
|
|
81250657f0 | ||
|
|
7d33cccc8e | ||
|
|
3db79ab2f0 | ||
|
|
ea0f6813d2 | ||
|
|
f6d4d32c03 | ||
|
|
892e82141f | ||
|
|
65ec7fae9c | ||
|
|
a0acb75a19 | ||
|
|
bcae973d82 | ||
|
|
d01a4fe61b | ||
|
|
8d4620b680 | ||
|
|
c2cedb1b6f | ||
|
|
157dcad87b | ||
|
|
8ea5ba171a | ||
|
|
fa876b10a3 | ||
|
|
3c58d10eae | ||
|
|
a3ee8c8849 | ||
|
|
5e14b302bc | ||
|
|
efe3f943d5 | ||
|
|
38e7fc6659 | ||
|
|
a1ceb534de | ||
|
|
0d897dd65d | ||
|
|
721d73f451 | ||
|
|
e8bd64e898 | ||
|
|
7da7ab2ce7 | ||
|
|
3f6183ff15 | ||
|
|
642316a1e2 | ||
|
|
21c6b75839 | ||
|
|
87d34deb0c | ||
|
|
1b3408919c | ||
|
|
23516572b7 | ||
|
|
924848fdad | ||
|
|
4c40e56060 | ||
|
|
cf34444505 | ||
|
|
9ca5e4e470 | ||
|
|
32f2689b6a | ||
|
|
13ae61031f | ||
|
|
1ced96c5e9 | ||
|
|
0d905eb3f6 | ||
|
|
32a5460ea7 | ||
|
|
be5acff69c | ||
|
|
1b0ef5793e | ||
|
|
2bd0caae2e | ||
|
|
3045f2c478 | ||
|
|
1282a142d6 | ||
|
|
ac1a539d22 | ||
|
|
6f47775239 | ||
|
|
0823d72dee | ||
|
|
407ad94f58 | ||
|
|
b2b103362d | ||
|
|
231680944f |
No files matched your search
@@ -6,3 +6,7 @@ Devin J. Pohly - <[email protected]>
|
||||
|
||||
Chris Moeller - <[email protected]>
|
||||
- CoreAudio audio driver
|
||||
|
||||
Daniel De Matteis - <[email protected]>
|
||||
- PlayStation 3 port
|
||||
|
||||
@@ -2,7 +2,7 @@ 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 bps.o strl.o screenshot.o
|
||||
OBJ = ssnes.o file.o driver.o settings.o dynamic.o message.o rewind.o movie.o gfx/gfx_common.o ups.o bps.o strl.o getopt.o screenshot.o audio/hermite.o audio/utils.o
|
||||
JOYCONFIG_OBJ = tools/ssnes-joyconfig.o conf/config_file.o strl.o
|
||||
HEADERS = $(wildcard */*.h) $(wildcard *.h)
|
||||
|
||||
@@ -23,11 +23,13 @@ ifneq ($(findstring BSD,$(OS)),)
|
||||
DYLIB_LIB = -lc
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SRC), 1)
|
||||
LIBS += $(SRC_LIBS)
|
||||
DEFINES += $(SRC_CFLAGS)
|
||||
else
|
||||
OBJ += audio/hermite.o
|
||||
ifneq ($(findstring Linux,$(OS)),)
|
||||
LIBS += -lrt
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_THREADS), 1)
|
||||
OBJ += autosave.o thread.o
|
||||
LIBS += -lpthread
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_CONFIGFILE), 1)
|
||||
@@ -89,20 +91,24 @@ ifeq ($(HAVE_COREAUDIO), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SDL), 1)
|
||||
OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o fifo_buffer.o
|
||||
OBJ += gfx/sdl.o gfx/sdlwrap.o input/sdl.o audio/sdl.o fifo_buffer.o
|
||||
DEFINES += $(SDL_CFLAGS) $(BSD_LOCAL_INC)
|
||||
LIBS += $(SDL_LIBS)
|
||||
|
||||
ifeq ($(HAVE_OPENGL), 1)
|
||||
OBJ += gfx/gl.o
|
||||
ifeq ($(OSX),1)
|
||||
LIBS += -framework OpenGL
|
||||
LIBS += -framework OpenGL
|
||||
else
|
||||
LIBS += -lGL
|
||||
LIBS += -lGL
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_XVIDEO), 1)
|
||||
OBJ += gfx/xvideo.o input/x11_input.o
|
||||
LIBS += $(XVIDEO_LIBS)
|
||||
DEFINES += $(XVIDEO_CFLAGS)
|
||||
LIBS += $(XVIDEO_LIBS) $(X11_LIBS) $(XEXT_LIBS)
|
||||
DEFINES += $(XVIDEO_CFLAGS) $(X11_CFLAGS) $(XEXT_CFLAGS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_CG), 1)
|
||||
@@ -111,9 +117,19 @@ ifeq ($(HAVE_CG), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_XML), 1)
|
||||
OBJ += gfx/shader_glsl.o gfx/image.o gfx/snes_state.o sha256.o cheats.o
|
||||
OBJ += sha256.o cheats.o
|
||||
LIBS += $(XML_LIBS)
|
||||
DEFINES += $(XML_CFLAGS)
|
||||
|
||||
ifeq ($(HAVE_OPENGL), 1)
|
||||
OBJ += gfx/shader_glsl.o
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_XML), 1)
|
||||
OBJ += gfx/snes_state.o gfx/image.o
|
||||
else ifeq ($(HAVE_CG), 1)
|
||||
OBJ += gfx/snes_state.o gfx/image.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DYLIB), 1)
|
||||
@@ -144,10 +160,6 @@ else
|
||||
LIBS += $(libsnes)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_STRL), 1)
|
||||
DEFINES += -DHAVE_STRL
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_PYTHON), 1)
|
||||
DEFINES += $(PYTHON_CFLAGS) -Wno-unused-parameter
|
||||
LIBS += $(PYTHON_LIBS)
|
||||
@@ -158,9 +170,14 @@ ifneq ($(V),1)
|
||||
Q := @
|
||||
endif
|
||||
|
||||
CFLAGS += -Wall -O3 -g -I. -pedantic
|
||||
OPTIMIZE_FLAG = -O3
|
||||
ifeq ($(DEBUG), 1)
|
||||
OPTIMIZE_FLAG = -O0
|
||||
endif
|
||||
|
||||
CFLAGS += -Wall $(OPTIMIZE_FLAG) -g -I. -pedantic
|
||||
ifneq ($(findstring icc,$(CC)),)
|
||||
CFLAGS += -std=c99
|
||||
CFLAGS += -std=c99 -D_GNU_SOURCE
|
||||
else
|
||||
CFLAGS += -std=gnu99
|
||||
endif
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
###
|
||||
##
|
||||
# Makefile for SSNES PS3. Somewhat generic and avoids using the odd SDK examples.
|
||||
##
|
||||
###
|
||||
##
|
||||
DEBUG = 0
|
||||
CELL_BUILD_TOOLS = GCC
|
||||
|
||||
## Platform detection from nall/Makefile. Only two applicable hosts for PS3 SDK.
|
||||
ifeq ($(platform),)
|
||||
uname := $(shell uname -a)
|
||||
ifeq ($(uname),)
|
||||
platform := win
|
||||
else
|
||||
platform := linux
|
||||
endif
|
||||
endif
|
||||
|
||||
CELL_SDK := /usr/local/cell
|
||||
ifeq ($(platform),win)
|
||||
HOST_DIR := host-win32
|
||||
else
|
||||
HOST_DIR := host-linux
|
||||
endif
|
||||
|
||||
ifeq ($(CELL_BUILD_TOOLS),SNC)
|
||||
CC = $(CELL_SDK)/sn/ps3ppusnc
|
||||
CXX = $(CC)
|
||||
LD = $(CELL_SDK)/sn/ps3ppuld
|
||||
else
|
||||
CC = $(CELL_SDK)/$(HOST_DIR)/ppu/bin/ppu-lv2-gcc
|
||||
CXX = $(CELL_SDK)/$(HOST_DIR)/ppu/bin/ppu-lv2-g++
|
||||
LD = $(CELL_SDK)/$(HOST_DIR)/ppu/bin/ppu-lv2-g++
|
||||
endif
|
||||
STRIP = $(CELL_SDK)/$(HOST_DIR)/ppu/bin/ppu-lv2-strip
|
||||
|
||||
PPU_TARGET := ssnes.elf
|
||||
|
||||
LDDIRS = -L. -L$(CELL_SDK)/target/ppu/lib/PSGL/RSX/ultra-opt
|
||||
INCDIRS = -I. -Icommon
|
||||
|
||||
MAKE_FSELF_NPDRM = $(CELL_SDK)/$(HOST_DIR)/bin/make_fself_npdrm
|
||||
MAKE_PACKAGE_NPDRM = $(CELL_SDK)/$(HOST_DIR)/bin/make_package_npdrm
|
||||
|
||||
OBJ = fifo_buffer.o ps3/ps3_audio.o ps3/ps3_input.o ps3/pad_input.o getopt.o ssnes.o driver.o file.o settings.o message.o rewind.o movie.o gfx/gfx_common.o ps3/ps3_video_psgl.o gfx/shader_cg.o gfx/snes_state.o ups.o bps.o strl.o screenshot.o audio/hermite.o dynamic.o ps3/main.o audio/utils.o conf/config_file.o gfx/image.o
|
||||
|
||||
LIBS = -ldbgfont -lPSGL -lPSGLcgc -lcgc -lgcm_cmd -lgcm_sys_stub -lsnes -lresc_stub -lm -lio_stub -lfs_stub -lsysutil_stub -lsysmodule_stub -laudio_stub -lnet_stub -lpthread
|
||||
|
||||
DEFINES = -DHAVE_OPENGL=1 -DHAVE_CG=1 -DHAVE_FBO=1 -D__CELLOS_LV2__ -DHAVE_CONFIGFILE=1 -DPACKAGE_VERSION=\"0.9.3\" -Dmain=ssnes_main
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
PPU_OPTIMIZE_LV := -O0 -g
|
||||
else
|
||||
PPU_OPTIMIZE_LV := -O3
|
||||
endif
|
||||
|
||||
## Use this for new SDK!
|
||||
CFLAGS = $(PPU_OPTIMIZE_LV) $(DEFINES)
|
||||
CXXFLAGS = $(PPU_OPTIMIZE_LV) $(DEFINES)
|
||||
|
||||
all: pkg
|
||||
|
||||
$(PPU_TARGET): $(OBJ)
|
||||
$(LD) -o $@ $(OBJ) $(LDFLAGS) $(LDDIRS) $(LIBS)
|
||||
$(STRIP) $(PPU_TARGET)
|
||||
|
||||
%.o: %.c config.h
|
||||
$(CC) $(INCDIRS) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
%.o: %.cpp config.h
|
||||
$(CXX) $(INCDIRS) $(CXXFLAGS) -c -o $@ $<
|
||||
|
||||
pkg: $(PPU_TARGET)
|
||||
$(MAKE_FSELF_NPDRM) $(PPU_TARGET) ps3/pkg/USRDIR/EBOOT.BIN
|
||||
$(MAKE_PACKAGE_NPDRM) ps3/pkg/package.conf ps3/pkg
|
||||
|
||||
clean:
|
||||
rm -f $(PPU_TARGET)
|
||||
rm -f $(OBJ)
|
||||
rm -f *.pkg
|
||||
|
||||
.PHONY: all clean pkg
|
||||
|
||||
+31
-8
@@ -1,6 +1,6 @@
|
||||
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 bps.o ups.o strl.o screenshot.o audio/hermite.o
|
||||
OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o message.o rewind.o movie.o gfx/gfx_common.o bps.o ups.o strl.o screenshot.o audio/hermite.o getopt.o audio/utils.o
|
||||
JOBJ = conf/config_file.o tools/main-stub.o tools/ssnes-joyconfig.o strl.o
|
||||
|
||||
CC = gcc
|
||||
@@ -10,8 +10,11 @@ HAVE_DINPUT = 1
|
||||
HAVE_XAUDIO = 1
|
||||
HAVE_DSOUND = 1
|
||||
HAVE_SDL = 1
|
||||
HAVE_OPENGL = 1
|
||||
HAVE_DYLIB = 1
|
||||
HAVE_NETPLAY = 1
|
||||
HAVE_THREADS = 1
|
||||
DYNAMIC = 1
|
||||
|
||||
ifeq ($(SLIM),)
|
||||
HAVE_SDL_IMAGE = 1
|
||||
@@ -27,16 +30,26 @@ endif
|
||||
libsnes ?= -lsnes
|
||||
|
||||
LIBS = -lm -lshlwapi
|
||||
DEFINES = -I. -DHAVE_CONFIGFILE
|
||||
LDFLAGS = -L. -static-libgcc -s
|
||||
LDCXXFLAGS = -static-libstdc++
|
||||
DEFINES = -I. -DHAVE_CONFIGFILE -DPACKAGE_VERSION=\"0.9.3\"
|
||||
LDFLAGS = -L. -static-libgcc
|
||||
LDCXXFLAGS = -static-libstdc++ -s
|
||||
|
||||
ifeq ($(HAVE_SDL), 1)
|
||||
OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o fifo_buffer.o
|
||||
LIBS += -lSDL -lopengl32
|
||||
OBJ += gfx/sdl.o gfx/gl.o gfx/sdlwrap.o input/sdl.o audio/sdl.o fifo_buffer.o
|
||||
LIBS += -lSDL
|
||||
DEFINES += -ISDL -DHAVE_SDL
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_THREADS), 1)
|
||||
OBJ += autosave.o thread.o
|
||||
DEFINES += -DHAVE_THREADS
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_OPENGL), 1)
|
||||
DEFINES += -DHAVE_OPENGL
|
||||
LIBS += -lopengl32
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SDL_IMAGE), 1)
|
||||
LIBS += -lSDL_image
|
||||
DEFINES += -DHAVE_SDL_IMAGE
|
||||
@@ -117,6 +130,12 @@ endif
|
||||
ifeq ($(HAVE_FFMPEG), 1)
|
||||
LIBS += -lavformat -lavcodec -lavutil -lswscale -lws2_32 -lz
|
||||
DEFINES += -DHAVE_FFMPEG -Iffmpeg
|
||||
DEFINES += -DHAVE_FFMPEG_ALLOC_CONTEXT3
|
||||
DEFINES += -DHAVE_FFMPEG_AVCODEC_OPEN2
|
||||
DEFINES += -DHAVE_FFMPEG_AVIO_OPEN
|
||||
DEFINES += -DHAVE_FFMPEG_AVFORMAT_WRITE_HEADER
|
||||
DEFINES += -DHAVE_FFMPEG_AVFORMAT_NEW_STREAM
|
||||
DEFINES += -DHAVE_X264RGB
|
||||
OBJ += record/ffemu.o
|
||||
endif
|
||||
|
||||
@@ -160,17 +179,21 @@ clean:
|
||||
rm -f tools/*.o
|
||||
|
||||
dist_x86: all
|
||||
zip -r ssnes-win32-0.8.zip $(TARGET) $(JTARGET) ssnes.cfg snes.dll SDL.dll
|
||||
zip -r ssnes-win32-0.9.3.zip $(TARGET) $(JTARGET) ssnes.cfg snes.dll SDL.dll
|
||||
|
||||
dist_x86_64: all
|
||||
zip -r ssnes-win64-0.8.zip $(TARGET) $(JTARGET) ssnes.cfg snes.dll SDL.dll
|
||||
zip -r ssnes-win64-0.9.3.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
|
||||
wget https://github.com/downloads/Themaister/SSNES/SSNES-win32-headers.zip --no-check-certificate
|
||||
unzip SSNES-win32-headers.zip
|
||||
|
||||
libs_x86_64:
|
||||
wget https://github.com/downloads/Themaister/SSNES/SSNES-win64-libs.zip --no-check-certificate
|
||||
unzip SSNES-win64-libs.zip
|
||||
wget https://github.com/downloads/Themaister/SSNES/SSNES-win64-headers.zip --no-check-certificate
|
||||
unzip SSNES-win64-headers.zip
|
||||
|
||||
.PHONY: all install uninstall clean dist_x86 dist_x86_64 libs_x86 libs_x86_64
|
||||
@@ -0,0 +1,55 @@
|
||||
###
|
||||
##
|
||||
# Makefile for SSNES libxenon.
|
||||
##
|
||||
###
|
||||
##
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
CC = xenon-gcc
|
||||
CXX = xenon-g++
|
||||
OBJCOPY = xenon-objcopy
|
||||
STRIP = xenon-strip
|
||||
|
||||
PPU_TARGET := ssnes-libxenon.elf
|
||||
PPU_TARGET_ADJUSTED := ssnes-libxenon.elf32
|
||||
|
||||
LDDIRS = -L. -L$(DEVKITXENON)/usr/lib -L$(DEVKITXENON)/xenon/lib/32
|
||||
INCDIRS = -I. -I$(DEVKITXENON)/usr/include -I$(DEVKITXENON)/usr/include/SDL
|
||||
|
||||
OBJ = fifo_buffer.o ssnes.o driver.o file.o settings.o message.o rewind.o movie.o input/sdl.o audio/sdl.o gfx/sdl.o gfx/sdlwrap.o gfx/gfx_common.o ups.o bps.o strl.o screenshot.o audio/hermite.o dynamic.o audio/utils.o conf/config_file.o
|
||||
|
||||
LIBS = -lsnes -lSDL -lxenon -lm -lc
|
||||
DEFINES = -std=gnu99 -DHAVE_CONFIGFILE=1 -DHAVE_SDL=1 -DPACKAGE_VERSION=\"0.9.3\" -DHAVE_GETOPT_LONG=1
|
||||
DEFINES += -maltivec -mhard-float -m32 -mpowerpc64 -mcpu=cell -mtune=cell -fno-pic -g -Wall -DSDL -DXENON $(INCDIRS)
|
||||
DEFINES += -u read -u _start -u exc_base
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
OPTIMIZE_LV := -O0 -g
|
||||
else
|
||||
OPTIMIZE_LV := -O3
|
||||
endif
|
||||
|
||||
all: $(PPU_TARGET_ADJUSTED)
|
||||
|
||||
CFLAGS := $(OPTIMIZE_LV) $(DEFINES)
|
||||
|
||||
LDFLAGS := -m32 -n -T $(DEVKITXENON)/app.lds
|
||||
|
||||
$(PPU_TARGET_ADJUSTED): $(PPU_TARGET)
|
||||
$(OBJCOPY) -O elf32-powerpc --adjust-vma 0x80000000 $< $@
|
||||
$(STRIP) $@
|
||||
|
||||
$(PPU_TARGET): $(OBJ)
|
||||
$(CXX) -o $@ $(OBJ) $(LDFLAGS) $(LDDIRS) $(LIBS)
|
||||
|
||||
%.o: %.c config.h
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f $(PPU_TARGET)
|
||||
rm -f $(OBJ)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
@@ -7,6 +7,10 @@ SSNES is a simple frontend for the libsnes library.
|
||||
libsnes is the emulator core of [bSNES](http://www.byuu.org), the most accurate SNES emulator to date, in library form. The libsnes API has been implemented by SNES9x as well, allowing multiple different cores to be used for an emulator that supports libsnes.
|
||||
This enables the possibility of custom front-ends for the emulator.
|
||||
|
||||
# Binaries
|
||||
|
||||
Latest Windows binaries are currently hosted on my [homepage](http://themaister.net/ssnes.html).
|
||||
|
||||
# Philosophy
|
||||
|
||||
SSNES attempts to be very small and lean, while still having all the useful core features expected from an emulator.
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
===================================================
|
||||
SSNES - Simple Super Nintendo Emulator System
|
||||
===================================================
|
||||
|
||||
SSNES is a simplistic emulator frontend designed for use without any GUI.
|
||||
It is used from command line, but is designed to play well with eventual GUI frontends,
|
||||
as the configuration file format is very simple.
|
||||
|
||||
This allows for great portability and focus on what matters in a frontend, such as video and audio performance.
|
||||
Its core libraries for video and input are OpenGL and SDL, with many different audio library supported.
|
||||
|
||||
On first use you should get accustomed to how SSNES works.
|
||||
|
||||
- SSNES emphasizes simplicity. It has no graphical user interface, and is primarily used through the command line.
|
||||
It is recommended for experienced users.
|
||||
It is possible though to create a GUI which can use SSNES.
|
||||
|
||||
- SSNES isn't dependant on any emulator core, such as bSNES, SNES9x or ZSNES.
|
||||
SSNES only talks to the libsnes interface,
|
||||
which was created initially by bSNES but it has an implementation available for SNES9x as well.
|
||||
|
||||
- SSNES is configured through a config file. In the Win32 distribution, there should be a ssnes.cfg included.
|
||||
This config file serves as a base for your custom configuration file.
|
||||
It is recommended that you go through the config file and alter any configurations you might use.
|
||||
This is a good way to learn about the features SSNES have.
|
||||
It is possible to pick which configuration file you want with the '-c' flag.
|
||||
E.g.: ssnes.exe -c myconfig.cfg rom.sfc.
|
||||
|
||||
- When unsure which command line flags to use, use the command: ssnes.exe --help.
|
||||
|
||||
- When something is not working as expected, try using verbose logging first to see what is happening:
|
||||
E.g.: ssnes.exe -v romfile.sfc
|
||||
If something is not working, and you want to file a bug report,
|
||||
please include console output with the -v flag turned on.
|
||||
|
||||
- SSNES does not support zipped roms directly. It only supports roms with extentions .sfc and .smc.
|
||||
However, SSNES can read roms from stdin,
|
||||
which makes it possible to create wrapper programs that can support every zip format under the sun.
|
||||
This can also be performed by a GUI frontend.
|
||||
+23
-28
@@ -21,7 +21,7 @@
|
||||
#include <asoundlib.h>
|
||||
#include "general.h"
|
||||
|
||||
#define TRY_ALSA(x) if ( x < 0 ) { \
|
||||
#define TRY_ALSA(x) if (x < 0) { \
|
||||
goto error; \
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ typedef struct alsa
|
||||
bool has_float;
|
||||
} alsa_t;
|
||||
|
||||
static bool __alsa_use_float(void *data)
|
||||
static bool alsa_use_float(void *data)
|
||||
{
|
||||
alsa_t *alsa = data;
|
||||
return alsa->has_float;
|
||||
@@ -49,17 +49,17 @@ static bool find_float_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void* __alsa_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *alsa_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
alsa_t *alsa = calloc(1, sizeof(alsa_t));
|
||||
if ( alsa == NULL )
|
||||
if (!alsa)
|
||||
return NULL;
|
||||
|
||||
snd_pcm_hw_params_t *params = NULL;
|
||||
snd_pcm_sw_params_t *sw_params = NULL;
|
||||
|
||||
const char *alsa_dev = "default";
|
||||
if ( device != NULL )
|
||||
if (device)
|
||||
alsa_dev = device;
|
||||
|
||||
TRY_ALSA(snd_pcm_open(&alsa->pcm, alsa_dev, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK));
|
||||
@@ -117,7 +117,7 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t __alsa_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t alsa_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
alsa_t *alsa = data;
|
||||
|
||||
@@ -141,16 +141,16 @@ static ssize_t __alsa_write(void* data, const void* buf, size_t size)
|
||||
|
||||
frames = snd_pcm_writei(alsa->pcm, (const char*)buf + written * 2 * (alsa->has_float ? sizeof(float) : sizeof(int16_t)), size - written);
|
||||
|
||||
if ( frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE )
|
||||
if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE)
|
||||
{
|
||||
if ( snd_pcm_recover(alsa->pcm, frames, 1) < 0 )
|
||||
if (snd_pcm_recover(alsa->pcm, frames, 1) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
else if ( frames == -EAGAIN && alsa->nonblock )
|
||||
else if (frames == -EAGAIN && alsa->nonblock)
|
||||
return 0;
|
||||
else if ( frames < 0 )
|
||||
else if (frames < 0)
|
||||
return -1;
|
||||
|
||||
written += frames;
|
||||
@@ -159,28 +159,28 @@ static ssize_t __alsa_write(void* data, const void* buf, size_t size)
|
||||
return snd_pcm_frames_to_bytes(alsa->pcm, size);
|
||||
}
|
||||
|
||||
static bool __alsa_stop(void *data)
|
||||
static bool alsa_stop(void *data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __alsa_set_nonblock_state(void *data, bool state)
|
||||
static void alsa_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
alsa_t *alsa = data;
|
||||
alsa->nonblock = state;
|
||||
}
|
||||
|
||||
static bool __alsa_start(void *data)
|
||||
static bool alsa_start(void *data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __alsa_free(void *data)
|
||||
static void alsa_free(void *data)
|
||||
{
|
||||
alsa_t *alsa = data;
|
||||
if ( alsa )
|
||||
if (alsa)
|
||||
{
|
||||
if ( alsa->pcm )
|
||||
if (alsa->pcm)
|
||||
{
|
||||
snd_pcm_drop(alsa->pcm);
|
||||
snd_pcm_close(alsa->pcm);
|
||||
@@ -190,18 +190,13 @@ static void __alsa_free(void *data)
|
||||
}
|
||||
|
||||
const audio_driver_t audio_alsa = {
|
||||
.init = __alsa_init,
|
||||
.write = __alsa_write,
|
||||
.stop = __alsa_stop,
|
||||
.start = __alsa_start,
|
||||
.use_float = __alsa_use_float,
|
||||
.set_nonblock_state = __alsa_set_nonblock_state,
|
||||
.free = __alsa_free,
|
||||
.init = alsa_init,
|
||||
.write = alsa_write,
|
||||
.stop = alsa_stop,
|
||||
.start = alsa_start,
|
||||
.use_float = alsa_use_float,
|
||||
.set_nonblock_state = alsa_set_nonblock_state,
|
||||
.free = alsa_free,
|
||||
.ident = "alsa"
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -95,7 +95,7 @@ static OSStatus audio_cb(void *userdata, AudioUnitRenderActionFlags *action_flag
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static void* coreaudio_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *coreaudio_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
(void)device;
|
||||
|
||||
@@ -210,7 +210,7 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t coreaudio_write(void* data, const void* buf_, size_t size)
|
||||
static ssize_t coreaudio_write(void *data, const void *buf_, size_t size)
|
||||
{
|
||||
coreaudio_t *dev = data;
|
||||
|
||||
|
||||
+1
-1
@@ -249,7 +249,7 @@ static void dsound_free(void *data)
|
||||
}
|
||||
}
|
||||
|
||||
static void* dsound_init(const char *device, unsigned rate, unsigned latency)
|
||||
static void *dsound_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
dsound_t *ds = calloc(1, sizeof(*ds));
|
||||
if (!ds)
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ static void audio_ext_free(void *data)
|
||||
}
|
||||
}
|
||||
|
||||
static void* audio_ext_init(const char *device, unsigned rate, unsigned latency)
|
||||
static void *audio_ext_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
if (!(*g_settings.audio.external_driver))
|
||||
{
|
||||
|
||||
@@ -61,7 +61,7 @@ typedef struct ssnes_audio_driver_info
|
||||
typedef struct ssnes_audio_driver
|
||||
{
|
||||
// Initializes the device.
|
||||
void* (*init)(const ssnes_audio_driver_info_t *info);
|
||||
void *(*init)(const ssnes_audio_driver_info_t *info);
|
||||
|
||||
// Write data in buffer to audio driver.
|
||||
// A frame here is defined as one combined sample of left and right
|
||||
|
||||
@@ -87,7 +87,7 @@ typedef struct ssnes_dsp_input
|
||||
typedef struct ssnes_dsp_plugin
|
||||
{
|
||||
// Creates a handle of the plugin. Returns NULL if failed.
|
||||
void* (*init)(const ssnes_dsp_info_t *info);
|
||||
void *(*init)(const ssnes_dsp_info_t *info);
|
||||
|
||||
// Processes input data.
|
||||
// The plugin is allowed to return variable sizes for output data.
|
||||
|
||||
@@ -207,7 +207,7 @@ static void generate_filter_coeffs(float * restrict fir_out, float * restrict ii
|
||||
}
|
||||
|
||||
|
||||
static void* dsp_init(const ssnes_dsp_info_t *info)
|
||||
static void *dsp_init(const ssnes_dsp_info_t *info)
|
||||
{
|
||||
(void)info;
|
||||
dsp_state *state = calloc(1, sizeof(*state));
|
||||
|
||||
+15
-27
@@ -20,13 +20,12 @@
|
||||
#include "hermite.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAX_CHANS 8
|
||||
#define CHANNELS 2
|
||||
|
||||
struct hermite_resampler
|
||||
{
|
||||
float chan_data[MAX_CHANS][4];
|
||||
float chan_data[CHANNELS][4];
|
||||
double r_frac;
|
||||
unsigned chans;
|
||||
};
|
||||
|
||||
static inline float hermite_kernel(float mu1, float a, float b, float c, float d)
|
||||
@@ -47,35 +46,30 @@ static inline float hermite_kernel(float mu1, float a, float b, float c, float d
|
||||
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
|
||||
}
|
||||
|
||||
hermite_resampler_t *hermite_new(unsigned channels)
|
||||
hermite_resampler_t *hermite_new(void)
|
||||
{
|
||||
if (channels > MAX_CHANS)
|
||||
return NULL;
|
||||
|
||||
hermite_resampler_t *re = calloc(1, sizeof(*re));
|
||||
if (!re)
|
||||
return NULL;
|
||||
re->chans = channels;
|
||||
return re;
|
||||
}
|
||||
|
||||
// We make sure to allocate enough output data beforehand ... ;)
|
||||
void hermite_process(hermite_resampler_t *re, struct hermite_data *data)
|
||||
{
|
||||
double r_step = 1.0 / data->src_ratio;
|
||||
double r_step = 1.0 / data->ratio;
|
||||
size_t processed_out = 0;
|
||||
size_t processed_in = 0;
|
||||
|
||||
size_t in_frames = data->input_frames;
|
||||
size_t out_frames = data->output_frames;
|
||||
const float *in_data = data->data_in;
|
||||
float *out_data = data->data_out;
|
||||
|
||||
while (processed_in < in_frames && processed_out < out_frames)
|
||||
for (size_t i = 0; i < in_frames; i++)
|
||||
{
|
||||
while (re->r_frac <= 1.0 && processed_out < out_frames)
|
||||
while (re->r_frac <= 1.0)
|
||||
{
|
||||
re->r_frac += r_step;
|
||||
for (unsigned i = 0; i < re->chans; i++)
|
||||
for (unsigned i = 0; i < CHANNELS; i++)
|
||||
{
|
||||
float res = hermite_kernel(re->r_frac,
|
||||
re->chan_data[i][0], re->chan_data[i][1], re->chan_data[i][2], re->chan_data[i][3]);
|
||||
@@ -84,23 +78,17 @@ void hermite_process(hermite_resampler_t *re, struct hermite_data *data)
|
||||
processed_out++;
|
||||
}
|
||||
|
||||
if (re->r_frac >= 1.0)
|
||||
re->r_frac -= 1.0;
|
||||
for (unsigned i = 0; i < CHANNELS; i++)
|
||||
{
|
||||
re->r_frac -= 1.0;
|
||||
|
||||
for (unsigned i = 0; i < re->chans; i++)
|
||||
{
|
||||
re->chan_data[i][0] = re->chan_data[i][1];
|
||||
re->chan_data[i][1] = re->chan_data[i][2];
|
||||
re->chan_data[i][2] = re->chan_data[i][3];
|
||||
re->chan_data[i][3] = *in_data++;
|
||||
}
|
||||
processed_in++;
|
||||
re->chan_data[i][0] = re->chan_data[i][1];
|
||||
re->chan_data[i][1] = re->chan_data[i][2];
|
||||
re->chan_data[i][2] = re->chan_data[i][3];
|
||||
re->chan_data[i][3] = *in_data++;
|
||||
}
|
||||
}
|
||||
|
||||
data->input_frames_used = processed_in;
|
||||
data->output_frames_gen = processed_out;
|
||||
data->output_frames = processed_out;
|
||||
}
|
||||
|
||||
void hermite_free(hermite_resampler_t *re)
|
||||
|
||||
+2
-6
@@ -26,7 +26,7 @@
|
||||
|
||||
typedef struct hermite_resampler hermite_resampler_t;
|
||||
|
||||
hermite_resampler_t *hermite_new(unsigned channels);
|
||||
hermite_resampler_t *hermite_new(void);
|
||||
|
||||
struct hermite_data
|
||||
{
|
||||
@@ -36,11 +36,7 @@ struct hermite_data
|
||||
size_t input_frames;
|
||||
size_t output_frames;
|
||||
|
||||
size_t input_frames_used;
|
||||
size_t output_frames_gen;
|
||||
|
||||
bool end_of_input; // Just used to clone the SRC API.
|
||||
double src_ratio;
|
||||
double ratio;
|
||||
};
|
||||
|
||||
void hermite_process(hermite_resampler_t *re, struct hermite_data *data);
|
||||
|
||||
+14
-14
@@ -124,7 +124,7 @@ static size_t find_buffersize(jack_t *jd, int latency)
|
||||
return buffer_frames * sizeof(jack_default_audio_sample_t);
|
||||
}
|
||||
|
||||
static void* __jack_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *ja_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
jack_t *jd = calloc(1, sizeof(jack_t));
|
||||
if (!jd)
|
||||
@@ -246,32 +246,32 @@ static size_t write_buffer(jack_t *jd, const float *buf, size_t size)
|
||||
return written * sizeof(float) * 2;
|
||||
}
|
||||
|
||||
static ssize_t __jack_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t ja_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
|
||||
return write_buffer(jd, buf, size);
|
||||
}
|
||||
|
||||
static bool __jack_stop(void *data)
|
||||
static bool ja_stop(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __jack_set_nonblock_state(void *data, bool state)
|
||||
static void ja_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
jd->nonblock = state;
|
||||
}
|
||||
|
||||
static bool __jack_start(void *data)
|
||||
static bool ja_start(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __jack_free(void *data)
|
||||
static void ja_free(void *data)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
|
||||
@@ -292,20 +292,20 @@ static void __jack_free(void *data)
|
||||
free(jd);
|
||||
}
|
||||
|
||||
static bool __jack_use_float(void *data)
|
||||
static bool ja_use_float(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
const audio_driver_t audio_jack = {
|
||||
.init = __jack_init,
|
||||
.write = __jack_write,
|
||||
.stop = __jack_stop,
|
||||
.start = __jack_start,
|
||||
.set_nonblock_state = __jack_set_nonblock_state,
|
||||
.free = __jack_free,
|
||||
.use_float = __jack_use_float,
|
||||
.init = ja_init,
|
||||
.write = ja_write,
|
||||
.stop = ja_stop,
|
||||
.start = ja_start,
|
||||
.set_nonblock_state = ja_set_nonblock_state,
|
||||
.free = ja_free,
|
||||
.use_float = ja_use_float,
|
||||
.ident = "jack"
|
||||
};
|
||||
|
||||
|
||||
+16
-17
@@ -56,11 +56,11 @@ typedef struct al
|
||||
|
||||
} al_t;
|
||||
|
||||
static void* __al_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *al_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
(void)device;
|
||||
al_t *al = calloc(1, sizeof(al_t));
|
||||
if ( al == NULL )
|
||||
if (!al)
|
||||
return NULL;
|
||||
|
||||
al->handle = alcOpenDevice(NULL);
|
||||
@@ -133,7 +133,7 @@ static bool al_get_buffer(al_t *al, ALuint *buffer)
|
||||
};
|
||||
#endif
|
||||
|
||||
for(;;)
|
||||
for (;;)
|
||||
{
|
||||
if (al_unqueue_buffers(al))
|
||||
break;
|
||||
@@ -153,7 +153,7 @@ static bool al_get_buffer(al_t *al, ALuint *buffer)
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t al_fill_internal_buf(al_t *al, const void* buf, size_t size)
|
||||
static size_t al_fill_internal_buf(al_t *al, const void *buf, size_t size)
|
||||
{
|
||||
size_t read_size = (BUFSIZE - al->tmpbuf_ptr > size) ? size : (BUFSIZE - al->tmpbuf_ptr);
|
||||
memcpy(al->tmpbuf + al->tmpbuf_ptr, buf, read_size);
|
||||
@@ -161,7 +161,7 @@ static size_t al_fill_internal_buf(al_t *al, const void* buf, size_t size)
|
||||
return read_size;
|
||||
}
|
||||
|
||||
static ssize_t __al_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t al_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
al_t *al = data;
|
||||
|
||||
@@ -196,32 +196,32 @@ static ssize_t __al_write(void* data, const void* buf, size_t size)
|
||||
return size;
|
||||
}
|
||||
|
||||
static bool __al_stop(void *data)
|
||||
static bool al_stop(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __al_set_nonblock_state(void *data, bool state)
|
||||
static void al_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
al_t *al = data;
|
||||
al->nonblock = state;
|
||||
}
|
||||
|
||||
static bool __al_start(void *data)
|
||||
static bool al_start(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __al_free(void *data)
|
||||
static void al_free(void *data)
|
||||
{
|
||||
al_t *al= data;
|
||||
if (al)
|
||||
{
|
||||
alSourceStop(al->source);
|
||||
alDeleteSources(1, &al->source);
|
||||
if ( al->buffers )
|
||||
if (al->buffers)
|
||||
{
|
||||
alDeleteBuffers(al->num_buffers, al->buffers);
|
||||
free(al->buffers);
|
||||
@@ -235,13 +235,12 @@ static void __al_free(void *data)
|
||||
}
|
||||
|
||||
const audio_driver_t audio_openal = {
|
||||
.init = __al_init,
|
||||
.write = __al_write,
|
||||
.stop = __al_stop,
|
||||
.start = __al_start,
|
||||
.set_nonblock_state = __al_set_nonblock_state,
|
||||
.free = __al_free,
|
||||
.init = al_init,
|
||||
.write = al_write,
|
||||
.stop = al_stop,
|
||||
.start = al_start,
|
||||
.set_nonblock_state = al_set_nonblock_state,
|
||||
.free = al_free,
|
||||
.ident = "openal"
|
||||
};
|
||||
|
||||
|
||||
+12
-12
@@ -41,7 +41,7 @@
|
||||
#define DEFAULT_OSS_DEV "/dev/dsp"
|
||||
#endif
|
||||
|
||||
static void* __oss_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *oss_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
int *fd = calloc(1, sizeof(int));
|
||||
if (fd == NULL)
|
||||
@@ -96,7 +96,7 @@ static void* __oss_init(const char* device, unsigned rate, unsigned latency)
|
||||
return fd;
|
||||
}
|
||||
|
||||
static ssize_t __oss_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t oss_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
int *fd = data;
|
||||
|
||||
@@ -114,19 +114,19 @@ static ssize_t __oss_write(void* data, const void* buf, size_t size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool __oss_stop(void *data)
|
||||
static bool oss_stop(void *data)
|
||||
{
|
||||
int *fd = data;
|
||||
ioctl(*fd, SNDCTL_DSP_RESET, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __oss_start(void *data)
|
||||
static bool oss_start(void *data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __oss_set_nonblock_state(void *data, bool state)
|
||||
static void oss_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
int *fd = data;
|
||||
int rc;
|
||||
@@ -138,7 +138,7 @@ static void __oss_set_nonblock_state(void *data, bool state)
|
||||
fprintf(stderr, "SSNES [ERROR]: Could not set nonblocking on OSS file descriptor. Will not be able to fast-forward.\n");
|
||||
}
|
||||
|
||||
static void __oss_free(void *data)
|
||||
static void oss_free(void *data)
|
||||
{
|
||||
int *fd = data;
|
||||
|
||||
@@ -148,12 +148,12 @@ static void __oss_free(void *data)
|
||||
}
|
||||
|
||||
const audio_driver_t audio_oss = {
|
||||
.init = __oss_init,
|
||||
.write = __oss_write,
|
||||
.stop = __oss_stop,
|
||||
.start = __oss_start,
|
||||
.set_nonblock_state = __oss_set_nonblock_state,
|
||||
.free = __oss_free,
|
||||
.init = oss_init,
|
||||
.write = oss_write,
|
||||
.stop = oss_stop,
|
||||
.start = oss_start,
|
||||
.set_nonblock_state = oss_set_nonblock_state,
|
||||
.free = oss_free,
|
||||
.ident = "oss"
|
||||
};
|
||||
|
||||
+15
-15
@@ -32,7 +32,7 @@ typedef struct
|
||||
bool nonblock;
|
||||
} pa_t;
|
||||
|
||||
static void __pulse_free(void *data)
|
||||
static void pulse_free(void *data)
|
||||
{
|
||||
pa_t *pa = data;
|
||||
if (pa)
|
||||
@@ -103,7 +103,7 @@ static void stream_latency_update_cb(pa_stream *s, void *data)
|
||||
pa_threaded_mainloop_signal(pa->mainloop, 0);
|
||||
}
|
||||
|
||||
static void* __pulse_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *pulse_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
pa_t *pa = calloc(1, sizeof(*pa));
|
||||
if (!pa)
|
||||
@@ -168,11 +168,11 @@ static void* __pulse_init(const char* device, unsigned rate, unsigned latency)
|
||||
unlock_error:
|
||||
pa_threaded_mainloop_unlock(pa->mainloop);
|
||||
error:
|
||||
__pulse_free(pa);
|
||||
pulse_free(pa);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t __pulse_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t pulse_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
pa_t *pa = data;
|
||||
|
||||
@@ -198,38 +198,38 @@ static ssize_t __pulse_write(void* data, const void* buf, size_t size)
|
||||
return write_size;
|
||||
}
|
||||
|
||||
static bool __pulse_stop(void *data)
|
||||
static bool pulse_stop(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __pulse_start(void *data)
|
||||
static bool pulse_start(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __pulse_set_nonblock_state(void *data, bool state)
|
||||
static void pulse_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
pa_t *pa = data;
|
||||
pa->nonblock = state;
|
||||
}
|
||||
|
||||
static bool __pulse_use_float(void *data)
|
||||
static bool pulse_use_float(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
const audio_driver_t audio_pulse = {
|
||||
.init = __pulse_init,
|
||||
.write = __pulse_write,
|
||||
.stop = __pulse_stop,
|
||||
.start = __pulse_start,
|
||||
.set_nonblock_state = __pulse_set_nonblock_state,
|
||||
.use_float = __pulse_use_float,
|
||||
.free = __pulse_free,
|
||||
.init = pulse_init,
|
||||
.write = pulse_write,
|
||||
.stop = pulse_stop,
|
||||
.start = pulse_start,
|
||||
.set_nonblock_state = pulse_set_nonblock_state,
|
||||
.use_float = pulse_use_float,
|
||||
.free = pulse_free,
|
||||
.ident = "pulse"
|
||||
};
|
||||
|
||||
|
||||
+13
-12
@@ -30,8 +30,9 @@ typedef struct
|
||||
bool nonblocking;
|
||||
} roar_t;
|
||||
|
||||
static void* __roar_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *ra_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
(void)latency;
|
||||
int err;
|
||||
roar_t *roar = calloc(1, sizeof(roar_t));
|
||||
if (roar == NULL)
|
||||
@@ -51,7 +52,7 @@ static void* __roar_init(const char* device, unsigned rate, unsigned latency)
|
||||
return roar;
|
||||
}
|
||||
|
||||
static ssize_t __roar_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t ra_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
roar_t *roar = data;
|
||||
ssize_t rc;
|
||||
@@ -77,13 +78,13 @@ static ssize_t __roar_write(void* data, const void* buf, size_t size)
|
||||
return size;
|
||||
}
|
||||
|
||||
static bool __roar_stop(void *data)
|
||||
static bool ra_stop(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __roar_set_nonblock_state(void *data, bool state)
|
||||
static void ra_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
roar_t *roar = data;
|
||||
if (roar_vs_blocking(roar->vss, (state) ? ROAR_VS_FALSE : ROAR_VS_TRUE, NULL) < 0)
|
||||
@@ -91,13 +92,13 @@ static void __roar_set_nonblock_state(void *data, bool state)
|
||||
roar->nonblocking = state;
|
||||
}
|
||||
|
||||
static bool __roar_start(void *data)
|
||||
static bool ra_start(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __roar_free(void *data)
|
||||
static void ra_free(void *data)
|
||||
{
|
||||
roar_t *roar = data;
|
||||
roar_vs_close(roar->vss, ROAR_VS_TRUE, NULL);
|
||||
@@ -105,12 +106,12 @@ static void __roar_free(void *data)
|
||||
}
|
||||
|
||||
const audio_driver_t audio_roar = {
|
||||
.init = __roar_init,
|
||||
.write = __roar_write,
|
||||
.stop = __roar_stop,
|
||||
.start = __roar_start,
|
||||
.set_nonblock_state = __roar_set_nonblock_state,
|
||||
.free = __roar_free,
|
||||
.init = ra_init,
|
||||
.write = ra_write,
|
||||
.stop = ra_stop,
|
||||
.start = ra_start,
|
||||
.set_nonblock_state = ra_set_nonblock_state,
|
||||
.free = ra_free,
|
||||
.ident = "roar"
|
||||
};
|
||||
|
||||
|
||||
+17
-17
@@ -54,15 +54,15 @@ static void err_cb(void *userdata)
|
||||
SDL_CondSignal(rsd->cond);
|
||||
}
|
||||
|
||||
static void* __rsd_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *rs_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
rsd_t *rsd = calloc(1, sizeof(rsd_t));
|
||||
if ( rsd == NULL )
|
||||
if (!rsd)
|
||||
return NULL;
|
||||
|
||||
rsound_t *rd;
|
||||
|
||||
if ( rsd_init(&rd) < 0 )
|
||||
if (rsd_init(&rd) < 0)
|
||||
{
|
||||
free(rsd);
|
||||
return NULL;
|
||||
@@ -80,14 +80,14 @@ static void* __rsd_init(const char* device, unsigned rate, unsigned latency)
|
||||
rsd_set_param(rd, RSD_SAMPLERATE, &rate);
|
||||
rsd_set_param(rd, RSD_LATENCY, &latency);
|
||||
|
||||
if ( device != NULL )
|
||||
if (device)
|
||||
rsd_set_param(rd, RSD_HOST, (void*)device);
|
||||
|
||||
rsd_set_param(rd, RSD_FORMAT, &format);
|
||||
|
||||
rsd_set_callback(rd, audio_cb, err_cb, 256, rsd);
|
||||
|
||||
if ( rsd_start(rd) < 0 )
|
||||
if (rsd_start(rd) < 0)
|
||||
{
|
||||
free(rsd);
|
||||
rsd_free(rd);
|
||||
@@ -98,7 +98,7 @@ static void* __rsd_init(const char* device, unsigned rate, unsigned latency)
|
||||
return rsd;
|
||||
}
|
||||
|
||||
static ssize_t __rsd_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t rs_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
|
||||
@@ -144,7 +144,7 @@ static ssize_t __rsd_write(void* data, const void* buf, size_t size)
|
||||
}
|
||||
}
|
||||
|
||||
static bool __rsd_stop(void *data)
|
||||
static bool rs_stop(void *data)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
rsd_stop(rsd->rd);
|
||||
@@ -152,22 +152,22 @@ static bool __rsd_stop(void *data)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __rsd_set_nonblock_state(void *data, bool state)
|
||||
static void rs_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
rsd->nonblock = state;
|
||||
}
|
||||
|
||||
static bool __rsd_start(void *data)
|
||||
static bool rs_start(void *data)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
if ( rsd_start(rsd->rd) < 0)
|
||||
if (rsd_start(rsd->rd) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __rsd_free(void *data)
|
||||
static void rs_free(void *data)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
|
||||
@@ -182,12 +182,12 @@ static void __rsd_free(void *data)
|
||||
}
|
||||
|
||||
const audio_driver_t audio_rsound = {
|
||||
.init = __rsd_init,
|
||||
.write = __rsd_write,
|
||||
.stop = __rsd_stop,
|
||||
.start = __rsd_start,
|
||||
.set_nonblock_state = __rsd_set_nonblock_state,
|
||||
.free = __rsd_free,
|
||||
.init = rs_init,
|
||||
.write = rs_write,
|
||||
.stop = rs_stop,
|
||||
.start = rs_start,
|
||||
.set_nonblock_state = rs_set_nonblock_state,
|
||||
.free = rs_free,
|
||||
.ident = "rsound"
|
||||
};
|
||||
|
||||
+2
-2
@@ -58,7 +58,7 @@ static inline int find_num_frames(int rate, int latency)
|
||||
return next_pow2(frames);
|
||||
}
|
||||
|
||||
static void* sdl_audio_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *sdl_audio_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
(void)device;
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
|
||||
@@ -109,7 +109,7 @@ static void* sdl_audio_init(const char* device, unsigned rate, unsigned latency)
|
||||
return sdl;
|
||||
}
|
||||
|
||||
static ssize_t sdl_audio_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t sdl_audio_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
sdl_audio_t *sdl = data;
|
||||
|
||||
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
#include "utils.h"
|
||||
|
||||
#if __SSE2__
|
||||
#include <emmintrin.h>
|
||||
#elif __ALTIVEC__
|
||||
#include <altivec.h>
|
||||
#endif
|
||||
|
||||
void audio_convert_s16_to_float_C(float *out,
|
||||
const int16_t *in, size_t samples)
|
||||
{
|
||||
for (size_t i = 0; i < samples; i++)
|
||||
out[i] = (float)in[i] / 0x8000;
|
||||
}
|
||||
|
||||
void audio_convert_float_to_s16_C(int16_t *out,
|
||||
const float *in, size_t samples)
|
||||
{
|
||||
for (size_t i = 0; i < samples; i++)
|
||||
{
|
||||
int32_t val = in[i] * 0x8000;
|
||||
out[i] = (val > 0x7FFF) ? 0x7FFF : (val < -0x8000 ? -0x8000 : (int16_t)val);
|
||||
}
|
||||
}
|
||||
|
||||
#if __SSE2__
|
||||
void audio_convert_s16_to_float_SSE2(float *out,
|
||||
const int16_t *in, size_t samples)
|
||||
{
|
||||
__m128 factor = _mm_set1_ps(1.0f / (0x7fff * 0x10000));
|
||||
size_t i;
|
||||
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||
{
|
||||
__m128i input = _mm_loadu_si128((const __m128i *)in);
|
||||
__m128i regs[2] = {
|
||||
_mm_unpacklo_epi16(_mm_setzero_si128(), input),
|
||||
_mm_unpackhi_epi16(_mm_setzero_si128(), input),
|
||||
};
|
||||
|
||||
__m128 output[2] = {
|
||||
_mm_mul_ps(_mm_cvtepi32_ps(regs[0]), factor),
|
||||
_mm_mul_ps(_mm_cvtepi32_ps(regs[1]), factor),
|
||||
};
|
||||
|
||||
_mm_storeu_ps(out + 0, output[0]);
|
||||
_mm_storeu_ps(out + 4, output[1]);
|
||||
}
|
||||
|
||||
audio_convert_s16_to_float_C(out, in, samples - i);
|
||||
}
|
||||
|
||||
void audio_convert_float_to_s16_SSE2(int16_t *out,
|
||||
const float *in, size_t samples)
|
||||
{
|
||||
__m128 factor = _mm_set1_ps((float)0x7fff);
|
||||
size_t i;
|
||||
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||
{
|
||||
__m128 input[2] = { _mm_loadu_ps(in + 0), _mm_loadu_ps(in + 4) };
|
||||
__m128 res[2] = { _mm_mul_ps(input[0], factor), _mm_mul_ps(input[1], factor) };
|
||||
|
||||
__m128i ints[2] = { _mm_cvtps_epi32(res[0]), _mm_cvtps_epi32(res[1]) };
|
||||
__m128i packed = _mm_packs_epi32(ints[0], ints[1]);
|
||||
|
||||
_mm_storeu_si128((__m128i *)out, packed);
|
||||
}
|
||||
|
||||
audio_convert_float_to_s16_C(out, in, samples - i);
|
||||
}
|
||||
#elif __ALTIVEC__
|
||||
void audio_convert_s16_to_float_altivec(float *out,
|
||||
const int16_t *in, size_t samples)
|
||||
{
|
||||
// Unaligned loads/store is a bit expensive, so we optimize for the good path (very likely).
|
||||
if (((uintptr_t)out & 15) + ((uintptr_t)in & 15) == 0)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||
{
|
||||
vector signed short input = vec_ld(0, in);
|
||||
vector signed int hi = vec_unpackh(input);
|
||||
vector signed int lo = vec_unpackl(input);
|
||||
vector float out_hi = vec_ctf(hi, 15);
|
||||
vector float out_lo = vec_ctf(lo, 15);
|
||||
|
||||
vec_st(out_hi, 0, out);
|
||||
vec_st(out_lo, 16, out);
|
||||
}
|
||||
|
||||
audio_convert_s16_to_float_C(out, in, samples - i);
|
||||
}
|
||||
else
|
||||
audio_convert_s16_to_float_C(out, in, samples);
|
||||
}
|
||||
|
||||
void audio_convert_float_to_s16_altivec(int16_t *out,
|
||||
const float *in, size_t samples)
|
||||
{
|
||||
// Unaligned loads/store is a bit expensive, so we optimize for the good path (very likely).
|
||||
if (((uintptr_t)out & 15) + ((uintptr_t)in & 15) == 0)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||
{
|
||||
vector float input0 = vec_ld( 0, in);
|
||||
vector float input1 = vec_ld(16, in);
|
||||
vector signed int result0 = vec_cts(input0, 15);
|
||||
vector signed int result1 = vec_cts(input1, 15);
|
||||
vec_st(vec_packs(result0, result1), 0, out);
|
||||
}
|
||||
|
||||
audio_convert_float_to_s16_C(out, in, samples - i);
|
||||
}
|
||||
else
|
||||
audio_convert_float_to_s16_C(out, in, samples);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef AUDIO_UTILS_H
|
||||
#define AUDIO_UTILS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#if __SSE2__
|
||||
#define audio_convert_s16_to_float audio_convert_s16_to_float_SSE2
|
||||
#define audio_convert_float_to_s16 audio_convert_float_to_s16_SSE2
|
||||
|
||||
void audio_convert_s16_to_float_SSE2(float *out,
|
||||
const int16_t *in, size_t samples);
|
||||
|
||||
void audio_convert_float_to_s16_SSE2(int16_t *out,
|
||||
const float *in, size_t samples);
|
||||
|
||||
#elif __ALTIVEC__
|
||||
#define audio_convert_s16_to_float audio_convert_s16_to_float_altivec
|
||||
#define audio_convert_float_to_s16 audio_convert_float_to_s16_altivec
|
||||
|
||||
void audio_convert_s16_to_float_altivec(float *out,
|
||||
const int16_t *in, size_t samples);
|
||||
|
||||
void audio_convert_float_to_s16_altivec(int16_t *out,
|
||||
const float *in, size_t samples);
|
||||
|
||||
#else
|
||||
#define audio_convert_s16_to_float audio_convert_s16_to_float_C
|
||||
#define audio_convert_float_to_s16 audio_convert_float_to_s16_C
|
||||
#endif
|
||||
|
||||
void audio_convert_s16_to_float_C(float *out,
|
||||
const int16_t *in, size_t samples);
|
||||
void audio_convert_float_to_s16_C(int16_t *out,
|
||||
const float *in, size_t samples);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
Simple C interface for XAudio2
|
||||
Author: Hans-Kristian Arntzen
|
||||
License: Public Domain
|
||||
*/
|
||||
|
||||
#include "xaudio-c.h"
|
||||
#include "xaudio.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define MAX_BUFFERS 16
|
||||
#define MAX_BUFFERS_MASK (MAX_BUFFERS - 1)
|
||||
|
||||
struct xaudio2
|
||||
{
|
||||
const IXAudio2VoiceCallbackVtbl *lpVtbl;
|
||||
|
||||
uint8_t *buf;
|
||||
IXAudio2 *pXAudio2;
|
||||
IXAudio2MasteringVoice *pMasterVoice;
|
||||
IXAudio2SourceVoice *pSourceVoice;
|
||||
HANDLE hEvent;
|
||||
|
||||
volatile long buffers;
|
||||
unsigned bufsize;
|
||||
unsigned bufptr;
|
||||
unsigned write_buffer;
|
||||
};
|
||||
|
||||
static void WINAPI voice_on_buffer_end(void *handle_, void *data)
|
||||
{
|
||||
(void)data;
|
||||
xaudio2_t *handle = handle_;
|
||||
InterlockedDecrement(&handle->buffers);
|
||||
SetEvent(handle->hEvent);
|
||||
}
|
||||
|
||||
static void WINAPI dummy_voidp(void *handle, void *data) { (void)handle; (void)data; }
|
||||
static void WINAPI dummy_nil(void *handle) { (void)handle; }
|
||||
static void WINAPI dummy_uint32(void *handle, UINT32 dummy) { (void)handle; (void)dummy; }
|
||||
static void WINAPI dummy_voidp_hresult(void *handle, void *data, HRESULT dummy) { (void)handle; (void)data; (void)dummy; }
|
||||
|
||||
const struct IXAudio2VoiceCallbackVtbl voice_vtable = {
|
||||
.OnBufferStart = dummy_voidp,
|
||||
.OnBufferEnd = voice_on_buffer_end,
|
||||
.OnLoopEnd = dummy_voidp,
|
||||
.OnVoiceProcessingPassEnd = dummy_nil,
|
||||
.OnVoiceProcessingPassStart = dummy_uint32,
|
||||
.OnVoiceError = dummy_voidp_hresult,
|
||||
.OnStreamEnd = dummy_nil,
|
||||
};
|
||||
|
||||
xaudio2_t *xaudio2_new(unsigned samplerate, unsigned channels, size_t size)
|
||||
{
|
||||
xaudio2_t *handle = calloc(1, sizeof(*handle));
|
||||
if (!handle)
|
||||
return NULL;
|
||||
|
||||
handle->lpVtbl = &voice_vtable;
|
||||
CoInitializeEx(0, COINIT_MULTITHREADED);
|
||||
if (FAILED(XAudio2Create(&handle->pXAudio2)))
|
||||
goto error;
|
||||
if (FAILED(IXAudio2_CreateMasteringVoice(handle->pXAudio2,
|
||||
&handle->pMasterVoice, channels, samplerate, 0, 0, 0)))
|
||||
goto error;
|
||||
|
||||
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(IXAudio2_CreateSourceVoice(handle->pXAudio2,
|
||||
&handle->pSourceVoice, &wfx,
|
||||
XAUDIO2_VOICE_NOSRC, XAUDIO2_DEFAULT_FREQ_RATIO,
|
||||
(IXAudio2VoiceCallback*)handle, 0, 0)))
|
||||
goto error;
|
||||
|
||||
handle->hEvent = CreateEvent(0, FALSE, FALSE, 0);
|
||||
if (!handle->hEvent)
|
||||
goto error;
|
||||
|
||||
IXAudio2SourceVoice_Start(handle->pSourceVoice, 0, XAUDIO2_COMMIT_NOW);
|
||||
|
||||
handle->bufsize = size / MAX_BUFFERS;
|
||||
handle->buf = calloc(1, handle->bufsize * MAX_BUFFERS);
|
||||
if (!handle->buf)
|
||||
goto error;
|
||||
|
||||
return handle;
|
||||
|
||||
error:
|
||||
xaudio2_free(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t xaudio2_write_avail(xaudio2_t *handle)
|
||||
{
|
||||
return handle->bufsize * (MAX_BUFFERS - handle->buffers - 1);
|
||||
}
|
||||
|
||||
size_t xaudio2_write(xaudio2_t *handle, const void *buf, size_t bytes_)
|
||||
{
|
||||
unsigned bytes = bytes_;
|
||||
const uint8_t *buffer = buf;
|
||||
while (bytes)
|
||||
{
|
||||
unsigned need = min(bytes, handle->bufsize - handle->bufptr);
|
||||
memcpy(handle->buf + handle->write_buffer * handle->bufsize + handle->bufptr,
|
||||
buffer, need);
|
||||
|
||||
handle->bufptr += need;
|
||||
buffer += need;
|
||||
bytes -= need;
|
||||
|
||||
if (handle->bufptr == handle->bufsize)
|
||||
{
|
||||
while (handle->buffers == MAX_BUFFERS - 1)
|
||||
WaitForSingleObject(handle->hEvent, INFINITE);
|
||||
|
||||
XAUDIO2_BUFFER xa2buffer = {
|
||||
.AudioBytes = handle->bufsize,
|
||||
.pAudioData = handle->buf + handle->write_buffer * handle->bufsize,
|
||||
};
|
||||
if (FAILED(IXAudio2SourceVoice_SubmitSourceBuffer(handle->pSourceVoice, &xa2buffer, NULL)))
|
||||
return 0;
|
||||
|
||||
InterlockedIncrement(&handle->buffers);
|
||||
handle->bufptr = 0;
|
||||
handle->write_buffer = (handle->write_buffer + 1) & MAX_BUFFERS_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
return bytes_;
|
||||
}
|
||||
|
||||
void xaudio2_free(xaudio2_t *handle)
|
||||
{
|
||||
if (handle)
|
||||
{
|
||||
if (handle->pSourceVoice)
|
||||
{
|
||||
IXAudio2SourceVoice_Stop(handle->pSourceVoice, 0, XAUDIO2_COMMIT_NOW);
|
||||
IXAudio2SourceVoice_DestroyVoice(handle->pSourceVoice);
|
||||
}
|
||||
|
||||
if (handle->pMasterVoice)
|
||||
IXAudio2MasteringVoice_DestroyVoice(handle->pMasterVoice);
|
||||
|
||||
if (handle->pXAudio2)
|
||||
IXAudio2_Release(handle->pXAudio2);
|
||||
|
||||
if (handle->hEvent)
|
||||
CloseHandle(handle->hEvent);
|
||||
|
||||
free(handle->buf);
|
||||
free(handle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
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;
|
||||
}
|
||||
|
||||
Executable → Regular
+52
-159
@@ -1,35 +1,33 @@
|
||||
/*
|
||||
xaudio2.hpp (2010-08-14)
|
||||
author: OV2
|
||||
xaudio.h (2010-08-14) / (2011-12-04)
|
||||
authors: OV2, Themaister
|
||||
*/
|
||||
|
||||
// Kinda stripped down. Only contains the bare essentials used in SSNES.
|
||||
|
||||
#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_GUID_X(n, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
static const GUID n = { 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 X2DEFAULT(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);
|
||||
#ifndef INTERFACE
|
||||
#define INTERFACE void
|
||||
#endif
|
||||
|
||||
#define XAUDIO2_COMMIT_NOW 0
|
||||
#define XAUDIO2_DEFAULT_CHANNELS 0
|
||||
@@ -46,95 +44,33 @@ typedef enum XAUDIO2_DEVICE_ROLE
|
||||
DefaultCommunicationsDevice = 0x4,
|
||||
DefaultGameDevice = 0x8,
|
||||
GlobalDefaultDevice = 0xf,
|
||||
InvalidDeviceRole = ~GlobalDefaultDevice
|
||||
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_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
|
||||
{
|
||||
typedef enum XAUDIO2_FILTER_TYPE {
|
||||
LowPassFilter,
|
||||
BandPassFilter,
|
||||
HighPassFilter
|
||||
HighPassFilter
|
||||
} XAUDIO2_FILTER_TYPE;
|
||||
|
||||
typedef struct XAUDIO2_FILTER_PARAMETERS
|
||||
{
|
||||
XAUDIO2_FILTER_TYPE Type;
|
||||
float Frequency;
|
||||
float OneOverQ;
|
||||
|
||||
} XAUDIO2_FILTER_PARAMETERS;
|
||||
typedef struct XAUDIO2_DEVICE_DETAILS XAUDIO2_DEVICE_DETAILS;
|
||||
typedef struct XAUDIO2_VOICE_DETAILS XAUDIO2_VOICE_DETAILS;
|
||||
typedef struct XAUDIO2_VOICE_SENDS XAUDIO2_VOICE_SENDS;
|
||||
typedef struct XAUDIO2_EFFECT_DESCRIPTOR XAUDIO2_EFFECT_DESCRIPTOR;
|
||||
typedef struct XAUDIO2_EFFECT_CHAIN XAUDIO2_EFFECT_CHAIN;
|
||||
typedef struct XAUDIO2_FILTER_PARAMETERS XAUDIO2_FILTER_PARAMETERS;
|
||||
typedef struct XAUDIO2_BUFFER_WMA XAUDIO2_BUFFER_WMA;
|
||||
typedef struct XAUDIO2_VOICE_STATE XAUDIO2_VOICE_STATE;
|
||||
typedef struct XAUDIO2_PERFORMANCE_DATA XAUDIO2_PERFORMANCE_DATA;
|
||||
typedef struct XAUDIO2_DEBUG_CONFIGURATION XAUDIO2_DEBUG_CONFIGURATION;
|
||||
typedef struct IXAudio2EngineCallback IXAudio2EngineCallback;
|
||||
typedef struct IXAudio2SubmixVoice IXAudio2SubmixVoice;
|
||||
|
||||
typedef struct XAUDIO2_BUFFER
|
||||
{
|
||||
@@ -146,65 +82,18 @@ typedef struct XAUDIO2_BUFFER
|
||||
UINT32 LoopBegin;
|
||||
UINT32 LoopLength;
|
||||
UINT32 LoopCount;
|
||||
void* pContext;
|
||||
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;
|
||||
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)
|
||||
@@ -219,10 +108,10 @@ DECLARE_INTERFACE(IXAudio2Voice)
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
STDMETHOD_(void, GetEffectState) (THIS_ UINT32 EffectIndex, BOOL* pEnabled) PURE; \
|
||||
STDMETHOD(SetEffectParameters) (THIS_ UINT32 EffectIndex, \
|
||||
const void* pParameters, \
|
||||
const void *pParameters, \
|
||||
UINT32 ParametersByteSize, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
STDMETHOD(GetEffectParameters) (THIS_ UINT32 EffectIndex, void* pParameters, \
|
||||
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; \
|
||||
@@ -245,17 +134,11 @@ DECLARE_INTERFACE(IXAudio2Voice)
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
};
|
||||
|
||||
|
||||
DECLARE_INTERFACE_(IXAudio2MasteringVoice, IXAudio2Voice)
|
||||
{
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
};
|
||||
|
||||
DECLARE_INTERFACE_(IXAudio2SubmixVoice, IXAudio2Voice)
|
||||
{
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
};
|
||||
|
||||
DECLARE_INTERFACE_(IXAudio2SourceVoice, IXAudio2Voice)
|
||||
{
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
@@ -304,23 +187,33 @@ DECLARE_INTERFACE_(IXAudio2, IUnknown)
|
||||
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;
|
||||
void *pReserved X2DEFAULT(NULL)) PURE;
|
||||
};
|
||||
|
||||
static inline HRESULT XAudio2Create(IXAudio2** ppXAudio2, UINT32 Flags X2DEFAULT(0),
|
||||
XAUDIO2_PROCESSOR XAudio2Processor X2DEFAULT(XAUDIO2_DEFAULT_PROCESSOR))
|
||||
// C hooks.
|
||||
#define IXAudio2_Initialize(THIS, a, b) (THIS)->lpVtbl->Initialize(THIS, a, b)
|
||||
#define IXAudio2_Release(THIS) (THIS)->lpVtbl->Release(THIS)
|
||||
#define IXAudio2_CreateSourceVoice(THIS, a, b, c, d, e, f, g) (THIS)->lpVtbl->CreateSourceVoice(THIS, a, b, c, d, e, f, g)
|
||||
#define IXAudio2_CreateMasteringVoice(THIS, a, b, c, d, e, f) (THIS)->lpVtbl->CreateMasteringVoice(THIS, a, b, c, d, e, f)
|
||||
|
||||
#define IXAudio2SourceVoice_Start(THIS, a, b) (THIS)->lpVtbl->Start(THIS, a, b)
|
||||
#define IXAudio2SourceVoice_Stop(THIS, a, b) (THIS)->lpVtbl->Stop(THIS, a, b)
|
||||
#define IXAudio2SourceVoice_SubmitSourceBuffer(THIS, a, b) (THIS)->lpVtbl->SubmitSourceBuffer(THIS, a, b)
|
||||
#define IXAudio2SourceVoice_DestroyVoice(THIS) (THIS)->lpVtbl->DestroyVoice(THIS)
|
||||
#define IXAudio2MasteringVoice_DestroyVoice(THIS) (THIS)->lpVtbl->DestroyVoice(THIS)
|
||||
|
||||
static inline HRESULT XAudio2Create(IXAudio2 **ppXAudio2)
|
||||
{
|
||||
IXAudio2* pXAudio2;
|
||||
HRESULT hr = CoCreateInstance((Flags & XAUDIO2_DEBUG_ENGINE) ? CLSID_XAudio2_Debug : CLSID_XAudio2,
|
||||
NULL, CLSCTX_INPROC_SERVER, IID_IXAudio2, reinterpret_cast<void**>(&pXAudio2));
|
||||
IXAudio2 *pXAudio2;
|
||||
HRESULT hr = CoCreateInstance(&CLSID_XAudio2, NULL, CLSCTX_INPROC_SERVER, &IID_IXAudio2, (void**)&pXAudio2);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = pXAudio2->Initialize(Flags, XAudio2Processor);
|
||||
hr = IXAudio2_Initialize(pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
|
||||
if (SUCCEEDED(hr))
|
||||
*ppXAudio2 = pXAudio2;
|
||||
else
|
||||
pXAudio2->Release();
|
||||
IXAudio2_Release(pXAudio2);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
+14
-14
@@ -26,7 +26,7 @@ typedef struct
|
||||
bool nonblock;
|
||||
} xa_t;
|
||||
|
||||
static void* __xa_init(const char* device, unsigned rate, unsigned latency)
|
||||
static void *xa_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
if (latency < 8)
|
||||
latency = 8; // Do not allow shenanigans.
|
||||
@@ -49,7 +49,7 @@ static void* __xa_init(const char* device, unsigned rate, unsigned latency)
|
||||
return xa;
|
||||
}
|
||||
|
||||
static ssize_t __xa_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t xa_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
xa_t *xa = data;
|
||||
if (xa->nonblock)
|
||||
@@ -66,31 +66,31 @@ static ssize_t __xa_write(void* data, const void* buf, size_t size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool __xa_stop(void *data)
|
||||
static bool xa_stop(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __xa_set_nonblock_state(void *data, bool state)
|
||||
static void xa_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
xa_t *xa = data;
|
||||
xa->nonblock = state;
|
||||
}
|
||||
|
||||
static bool __xa_start(void *data)
|
||||
static bool xa_start(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __xa_use_float(void *data)
|
||||
static bool xa_use_float(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __xa_free(void *data)
|
||||
static void xa_free(void *data)
|
||||
{
|
||||
xa_t *xa = data;
|
||||
if (xa)
|
||||
@@ -102,13 +102,13 @@ static void __xa_free(void *data)
|
||||
}
|
||||
|
||||
const audio_driver_t audio_xa = {
|
||||
.init = __xa_init,
|
||||
.write = __xa_write,
|
||||
.stop = __xa_stop,
|
||||
.start = __xa_start,
|
||||
.set_nonblock_state = __xa_set_nonblock_state,
|
||||
.use_float = __xa_use_float,
|
||||
.free = __xa_free,
|
||||
.init = xa_init,
|
||||
.write = xa_write,
|
||||
.stop = xa_stop,
|
||||
.start = xa_start,
|
||||
.set_nonblock_state = xa_set_nonblock_state,
|
||||
.use_float = xa_use_float,
|
||||
.free = xa_free,
|
||||
.ident = "xaudio"
|
||||
};
|
||||
|
||||
+45
-39
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "autosave.h"
|
||||
#include "SDL.h"
|
||||
#include "thread.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
@@ -26,11 +26,11 @@
|
||||
struct autosave
|
||||
{
|
||||
volatile bool quit;
|
||||
SDL_mutex *lock;
|
||||
slock_t *lock;
|
||||
|
||||
SDL_mutex *cond_lock;
|
||||
SDL_cond *cond;
|
||||
SDL_Thread *thread;
|
||||
slock_t *cond_lock;
|
||||
scond_t *cond;
|
||||
sthread_t *thread;
|
||||
|
||||
void *buffer;
|
||||
const void *snes_buffer;
|
||||
@@ -39,7 +39,7 @@ struct autosave
|
||||
unsigned interval;
|
||||
};
|
||||
|
||||
static int autosave_thread(void *data)
|
||||
static void autosave_thread(void *data)
|
||||
{
|
||||
autosave_t *save = data;
|
||||
|
||||
@@ -48,35 +48,40 @@ static int autosave_thread(void *data)
|
||||
while (!save->quit)
|
||||
{
|
||||
autosave_lock(save);
|
||||
memcpy(save->buffer, save->snes_buffer, save->bufsize);
|
||||
bool differ = memcmp(save->buffer, save->snes_buffer, save->bufsize) != 0;
|
||||
if (differ)
|
||||
memcpy(save->buffer, save->snes_buffer, save->bufsize);
|
||||
autosave_unlock(save);
|
||||
|
||||
// Should probably deal with this more elegantly.
|
||||
FILE *file = fopen(save->path, "wb");
|
||||
if (file)
|
||||
if (differ)
|
||||
{
|
||||
// Avoid spamming down stderr ... :)
|
||||
if (first_log)
|
||||
// Should probably deal with this more elegantly.
|
||||
FILE *file = fopen(save->path, "wb");
|
||||
if (file)
|
||||
{
|
||||
SSNES_LOG("Autosaving SRAM to \"%s\", will continue to autosave every %u seconds ...\n", save->path, save->interval);
|
||||
first_log = false;
|
||||
}
|
||||
// Avoid spamming down stderr ... :)
|
||||
if (first_log)
|
||||
{
|
||||
SSNES_LOG("Autosaving SRAM to \"%s\", will continue to check every %u seconds ...\n", save->path, save->interval);
|
||||
first_log = false;
|
||||
}
|
||||
else
|
||||
SSNES_LOG("SRAM changed ... autosaving ...\n");
|
||||
|
||||
bool failed = false;
|
||||
failed |= fwrite(save->buffer, 1, save->bufsize, file) != save->bufsize;
|
||||
failed |= fflush(file) != 0;
|
||||
failed |= fclose(file) != 0;
|
||||
if (failed)
|
||||
SSNES_WARN("Failed to autosave SRAM! Disk might be full.\n");
|
||||
bool failed = false;
|
||||
failed |= fwrite(save->buffer, 1, save->bufsize, file) != save->bufsize;
|
||||
failed |= fflush(file) != 0;
|
||||
failed |= fclose(file) != 0;
|
||||
if (failed)
|
||||
SSNES_WARN("Failed to autosave SRAM! Disk might be full.\n");
|
||||
}
|
||||
}
|
||||
|
||||
SDL_mutexP(save->cond_lock);
|
||||
slock_lock(save->cond_lock);
|
||||
if (!save->quit)
|
||||
SDL_CondWaitTimeout(save->cond, save->cond_lock, save->interval * 1000);
|
||||
SDL_mutexV(save->cond_lock);
|
||||
scond_wait_timeout(save->cond, save->cond_lock, save->interval * 1000);
|
||||
slock_unlock(save->cond_lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
autosave_t *autosave_new(const char *path, const void *data, size_t size, unsigned interval)
|
||||
@@ -96,37 +101,38 @@ autosave_t *autosave_new(const char *path, const void *data, size_t size, unsign
|
||||
free(handle);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(handle->buffer, handle->snes_buffer, handle->bufsize);
|
||||
|
||||
handle->lock = SDL_CreateMutex();
|
||||
handle->cond_lock = SDL_CreateMutex();
|
||||
handle->cond = SDL_CreateCond();
|
||||
handle->lock = slock_new();
|
||||
handle->cond_lock = slock_new();
|
||||
handle->cond = scond_new();
|
||||
|
||||
handle->thread = SDL_CreateThread(autosave_thread, handle);
|
||||
handle->thread = sthread_create(autosave_thread, handle);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void autosave_lock(autosave_t *handle)
|
||||
{
|
||||
SDL_mutexP(handle->lock);
|
||||
slock_lock(handle->lock);
|
||||
}
|
||||
|
||||
void autosave_unlock(autosave_t *handle)
|
||||
{
|
||||
SDL_mutexV(handle->lock);
|
||||
slock_unlock(handle->lock);
|
||||
}
|
||||
|
||||
void autosave_free(autosave_t *handle)
|
||||
{
|
||||
SDL_mutexP(handle->cond_lock);
|
||||
slock_lock(handle->cond_lock);
|
||||
handle->quit = true;
|
||||
SDL_mutexV(handle->cond_lock);
|
||||
SDL_CondSignal(handle->cond);
|
||||
SDL_WaitThread(handle->thread, NULL);
|
||||
slock_unlock(handle->cond_lock);
|
||||
scond_signal(handle->cond);
|
||||
sthread_join(handle->thread);
|
||||
|
||||
SDL_DestroyMutex(handle->lock);
|
||||
SDL_DestroyMutex(handle->cond_lock);
|
||||
SDL_DestroyCond(handle->cond);
|
||||
slock_free(handle->lock);
|
||||
slock_free(handle->cond_lock);
|
||||
scond_free(handle->cond);
|
||||
|
||||
free(handle->buffer);
|
||||
free(handle);
|
||||
|
||||
@@ -21,6 +21,14 @@
|
||||
#include "general.h"
|
||||
#include "strl.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
#include "conf/config_file.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
@@ -43,7 +51,7 @@ struct cheat_manager
|
||||
unsigned buf_size;
|
||||
};
|
||||
|
||||
static char* strcat_alloc(char *dest, const char *input)
|
||||
static char *strcat_alloc(char *dest, const char *input)
|
||||
{
|
||||
size_t dest_len = dest ? strlen(dest) : 0;
|
||||
size_t input_len = strlen(input);
|
||||
@@ -131,8 +139,105 @@ static bool xml_grab_cheats(cheat_manager_t *handle, xmlNodePtr ptr)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void cheat_manager_apply_cheats(cheat_manager_t *handle)
|
||||
{
|
||||
unsigned index = 0;
|
||||
psnes_cheat_reset();
|
||||
for (unsigned i = 0; i < handle->size; i++)
|
||||
{
|
||||
if (handle->cheats[i].state)
|
||||
psnes_cheat_set(index++, true, handle->cheats[i].code);
|
||||
}
|
||||
}
|
||||
|
||||
static void cheat_manager_load_config(cheat_manager_t *handle, const char *path, const char *sha256)
|
||||
{
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
if (!(*path))
|
||||
return;
|
||||
|
||||
config_file_t *conf = config_file_new(path);
|
||||
if (!conf)
|
||||
return;
|
||||
|
||||
char *str;
|
||||
if (!config_get_string(conf, sha256, &str))
|
||||
{
|
||||
config_file_free(conf);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *num = strtok(str, ";");
|
||||
while (num)
|
||||
{
|
||||
unsigned index = strtoul(num, NULL, 0);
|
||||
if (index < handle->size)
|
||||
handle->cheats[index].state = true;
|
||||
|
||||
num = strtok(NULL, ";");
|
||||
}
|
||||
|
||||
free(str);
|
||||
config_file_free(conf);
|
||||
|
||||
cheat_manager_apply_cheats(handle);
|
||||
|
||||
#else
|
||||
(void)handle;
|
||||
(void)path;
|
||||
(void)sha256;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void cheat_manager_save_config(cheat_manager_t *handle, const char *path, const char *sha256)
|
||||
{
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
if (!(*path))
|
||||
return;
|
||||
|
||||
config_file_t *conf = config_file_new(path);
|
||||
if (!conf)
|
||||
conf = config_file_new(NULL);
|
||||
|
||||
if (!conf)
|
||||
{
|
||||
SSNES_ERR("Cannot save XML cheat settings!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char conf_str[512] = {0};
|
||||
char tmp[32] = {0};
|
||||
|
||||
for (unsigned i = 0; i < handle->size; i++)
|
||||
{
|
||||
if (handle->cheats[i].state)
|
||||
{
|
||||
snprintf(tmp, sizeof(tmp), "%u;", i);
|
||||
strlcat(conf_str, tmp, sizeof(conf_str));
|
||||
}
|
||||
}
|
||||
|
||||
if (*conf_str)
|
||||
conf_str[strlen(conf_str) - 1] = '\0'; // Remove the trailing ';'
|
||||
|
||||
config_set_string(conf, sha256, conf_str);
|
||||
|
||||
if (!config_file_write(conf, path))
|
||||
SSNES_ERR("Failed to write XML cheat settings to \"%s\"! Check permissions!\n", path);
|
||||
|
||||
config_file_free(conf);
|
||||
|
||||
#else
|
||||
(void)handle;
|
||||
(void)path;
|
||||
(void)sha256;
|
||||
#endif
|
||||
}
|
||||
|
||||
cheat_manager_t* cheat_manager_new(const char *path)
|
||||
{
|
||||
LIBXML_TEST_VERSION;
|
||||
|
||||
psnes_cheat_reset();
|
||||
|
||||
xmlParserCtxtPtr ctx = NULL;
|
||||
@@ -188,7 +293,7 @@ cheat_manager_t* cheat_manager_new(const char *path)
|
||||
if (!sha256)
|
||||
continue;
|
||||
|
||||
if (memcmp(sha256, g_extern.sha256, 64) == 0)
|
||||
if (strcmp((const char*)sha256, g_extern.sha256) == 0)
|
||||
{
|
||||
xmlFree(sha256);
|
||||
break;
|
||||
@@ -213,6 +318,8 @@ cheat_manager_t* cheat_manager_new(const char *path)
|
||||
goto error;
|
||||
}
|
||||
|
||||
cheat_manager_load_config(handle, g_settings.cheat_settings_path, g_extern.sha256);
|
||||
|
||||
xmlFreeDoc(doc);
|
||||
xmlFreeParserCtxt(ctx);
|
||||
return handle;
|
||||
@@ -233,6 +340,7 @@ void cheat_manager_free(cheat_manager_t *handle)
|
||||
|
||||
if (handle->cheats)
|
||||
{
|
||||
cheat_manager_save_config(handle, g_settings.cheat_settings_path, g_extern.sha256);
|
||||
for (unsigned i = 0; i < handle->size; i++)
|
||||
{
|
||||
xmlFree(handle->cheats[i].desc);
|
||||
@@ -254,18 +362,11 @@ static void cheat_manager_update(cheat_manager_t *handle)
|
||||
SSNES_LOG("%s\n", msg);
|
||||
}
|
||||
|
||||
|
||||
void cheat_manager_toggle(cheat_manager_t *handle)
|
||||
{
|
||||
handle->cheats[handle->ptr].state ^= true;
|
||||
|
||||
unsigned index = 0;
|
||||
psnes_cheat_reset();
|
||||
for (unsigned i = 0; i < handle->size; i++)
|
||||
{
|
||||
if (handle->cheats[handle->ptr].state)
|
||||
psnes_cheat_set(index++, true, handle->cheats[handle->ptr].code);
|
||||
}
|
||||
|
||||
cheat_manager_apply_cheats(handle);
|
||||
cheat_manager_update(handle);
|
||||
}
|
||||
|
||||
|
||||
+336
-77
@@ -21,20 +21,52 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include "strl.h"
|
||||
#include <errno.h>
|
||||
#include "../strl.h"
|
||||
|
||||
#if !defined(_WIN32) && !defined(__CELLOS_LV2__)
|
||||
#include <sys/param.h> // MAXPATHLEN
|
||||
#elif defined(_WIN32)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifndef MAXPATHLEN
|
||||
#ifdef PATH_MAX
|
||||
#define MAXPATHLEN PATH_MAX
|
||||
#else
|
||||
#define MAXPATHLEN 512
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define MAX_INCLUDE_DEPTH 16
|
||||
|
||||
struct entry_list
|
||||
{
|
||||
bool readonly; // If we got this from an #include, do not allow write.
|
||||
char *key;
|
||||
char *value;
|
||||
struct entry_list *next;
|
||||
};
|
||||
|
||||
struct include_list
|
||||
{
|
||||
char *path;
|
||||
struct include_list *next;
|
||||
};
|
||||
|
||||
struct config_file
|
||||
{
|
||||
char *path;
|
||||
struct entry_list *entries;
|
||||
struct entry_list *tail;
|
||||
unsigned include_depth;
|
||||
|
||||
struct include_list *includes;
|
||||
};
|
||||
|
||||
static config_file_t *config_file_new_internal(const char *path, unsigned depth);
|
||||
|
||||
static char *getaline(FILE *file)
|
||||
{
|
||||
char *newline = malloc(9);
|
||||
@@ -57,13 +89,191 @@ static char *getaline(FILE *file)
|
||||
return newline;
|
||||
}
|
||||
|
||||
static bool parse_line(struct entry_list *list, char *line)
|
||||
static char *extract_value(char *line, bool is_value)
|
||||
{
|
||||
if (is_value)
|
||||
{
|
||||
while (isspace(*line))
|
||||
line++;
|
||||
|
||||
// If we don't have an equal sign here, we've got an invalid string...
|
||||
if (*line != '=')
|
||||
return false;
|
||||
|
||||
line++;
|
||||
}
|
||||
|
||||
while (isspace(*line))
|
||||
line++;
|
||||
|
||||
// We have a full string. Read until next ".
|
||||
if (*line == '"')
|
||||
{
|
||||
line++;
|
||||
char *tok = strtok(line, "\"");
|
||||
if (!tok)
|
||||
return false;
|
||||
return strdup(tok);
|
||||
}
|
||||
else if (*line == '\0') // Nothing :(
|
||||
return NULL;
|
||||
else // We don't have that... Read till next space.
|
||||
{
|
||||
char *tok = strtok(line, " \n\t\f\r\v");
|
||||
if (tok)
|
||||
return strdup(tok);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_list_readonly(struct entry_list *list)
|
||||
{
|
||||
while (list)
|
||||
{
|
||||
list->readonly = true;
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
// Move semantics? :)
|
||||
static void add_child_list(config_file_t *parent, config_file_t *child)
|
||||
{
|
||||
if (parent->entries)
|
||||
{
|
||||
struct entry_list *head = parent->entries;
|
||||
while (head->next)
|
||||
head = head->next;
|
||||
|
||||
set_list_readonly(child->entries);
|
||||
head->next = child->entries;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_list_readonly(child->entries);
|
||||
parent->entries = child->entries;
|
||||
}
|
||||
|
||||
child->entries = NULL;
|
||||
|
||||
// Rebase tail.
|
||||
if (parent->entries)
|
||||
{
|
||||
struct entry_list *head = parent->entries;
|
||||
while (head->next)
|
||||
head = head->next;
|
||||
parent->tail = head;
|
||||
}
|
||||
else
|
||||
parent->tail = NULL;
|
||||
}
|
||||
|
||||
static void add_include_list(config_file_t *conf, const char *path)
|
||||
{
|
||||
struct include_list *head = conf->includes;
|
||||
struct include_list *node = calloc(1, sizeof(*node));
|
||||
node->path = strdup(path);
|
||||
|
||||
if (head)
|
||||
{
|
||||
while (head->next)
|
||||
head = head->next;
|
||||
|
||||
head->next = node;
|
||||
}
|
||||
else
|
||||
conf->includes = node;
|
||||
}
|
||||
|
||||
static void add_sub_conf(config_file_t *conf, char *line)
|
||||
{
|
||||
char *path = extract_value(line, false);
|
||||
if (!path)
|
||||
return;
|
||||
|
||||
add_include_list(conf, path);
|
||||
|
||||
char real_path[MAXPATHLEN];
|
||||
|
||||
#ifdef _WIN32
|
||||
// Accomodate POSIX systems on Win32.
|
||||
bool is_full_path = *path == '/';
|
||||
|
||||
if (is_full_path)
|
||||
strlcpy(real_path, path, sizeof(real_path));
|
||||
else
|
||||
GetFullPathNameA(path, sizeof(real_path), real_path, NULL);
|
||||
|
||||
if (strcmp(path, real_path) != 0)
|
||||
{
|
||||
strlcpy(real_path, conf->path, sizeof(real_path));
|
||||
char *split = strrchr(real_path, '/');
|
||||
if (!split)
|
||||
split = strrchr(real_path, '\\');
|
||||
|
||||
split[1] = '\0';
|
||||
strlcat(real_path, path, sizeof(real_path));
|
||||
}
|
||||
#else
|
||||
if (*path == '/')
|
||||
{
|
||||
strlcpy(real_path, path, sizeof(real_path));
|
||||
}
|
||||
#ifndef __CELLOS_LV2__
|
||||
else if (*path == '~')
|
||||
{
|
||||
const char *home = getenv("HOME");
|
||||
strlcpy(real_path, home ? home : "/", sizeof(real_path));
|
||||
strlcat(real_path, path + 1, sizeof(real_path));
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
strlcpy(real_path, conf->path, sizeof(real_path));
|
||||
char *split = strrchr(real_path, '/');
|
||||
if (split)
|
||||
{
|
||||
split[1] = '\0';
|
||||
strlcat(real_path, path, sizeof(real_path));
|
||||
}
|
||||
else
|
||||
strlcpy(real_path, path, sizeof(real_path));
|
||||
}
|
||||
#endif
|
||||
|
||||
config_file_t *sub_conf = config_file_new_internal(real_path, conf->include_depth + 1);
|
||||
if (!sub_conf)
|
||||
{
|
||||
free(path);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pilfer internal list! :D
|
||||
add_child_list(conf, sub_conf);
|
||||
config_file_free(sub_conf);
|
||||
free(path);
|
||||
}
|
||||
|
||||
static bool parse_line(config_file_t *conf, struct entry_list *list, char *line)
|
||||
{
|
||||
// Remove everything after comment.
|
||||
char *comment = strchr(line, '#');
|
||||
if (comment)
|
||||
*comment = '\0';
|
||||
|
||||
// Starting line with # and include includes config files. :)
|
||||
if ((comment == line) && (conf->include_depth < MAX_INCLUDE_DEPTH))
|
||||
{
|
||||
comment++;
|
||||
if (strstr(comment, "include ") == comment)
|
||||
{
|
||||
add_sub_conf(conf, comment + strlen("include "));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (conf->include_depth >= MAX_INCLUDE_DEPTH)
|
||||
fprintf(stderr, "!!! #include depth exceeded for config! Might be a cycle.\n");
|
||||
|
||||
// Skips to first character.
|
||||
while (isspace(*line))
|
||||
line++;
|
||||
@@ -85,66 +295,42 @@ static bool parse_line(struct entry_list *list, char *line)
|
||||
key[index] = '\0';
|
||||
list->key = key;
|
||||
|
||||
while (isspace(*line))
|
||||
line++;
|
||||
|
||||
// If we don't have an equal sign here, we've got an invalid string...
|
||||
if (*line != '=')
|
||||
list->value = extract_value(line, true);
|
||||
if (!list->value)
|
||||
{
|
||||
list->key = NULL;
|
||||
free(key);
|
||||
return false;
|
||||
}
|
||||
line++;
|
||||
|
||||
while (isspace(*line))
|
||||
line++;
|
||||
|
||||
// We have a full string. Read until next ".
|
||||
if (*line == '"')
|
||||
{
|
||||
char *tok = strtok(line + 1, "\"");
|
||||
if (tok == NULL)
|
||||
{
|
||||
list->key = NULL;
|
||||
free(key);
|
||||
return false;
|
||||
}
|
||||
list->value = strdup(tok);
|
||||
}
|
||||
else // We don't have that... Read till next space.
|
||||
{
|
||||
char *tok = strtok(line, " \t\f");
|
||||
if (tok == NULL)
|
||||
{
|
||||
list->key = NULL;
|
||||
free(key);
|
||||
return false;
|
||||
}
|
||||
list->value = strdup(tok);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
config_file_t *config_file_new(const char *path)
|
||||
static config_file_t *config_file_new_internal(const char *path, unsigned depth)
|
||||
{
|
||||
|
||||
struct config_file *conf = calloc(1, sizeof(*conf));
|
||||
if (conf == NULL)
|
||||
if (!conf)
|
||||
return NULL;
|
||||
|
||||
if (path == NULL)
|
||||
if (!path)
|
||||
return conf;
|
||||
|
||||
FILE *file = fopen(path, "r");
|
||||
if (!file)
|
||||
conf->path = strdup(path);
|
||||
if (!conf->path)
|
||||
{
|
||||
free(conf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct entry_list *tail = conf->entries;
|
||||
conf->include_depth = depth;
|
||||
|
||||
FILE *file = fopen(path, "r");
|
||||
if (!file)
|
||||
{
|
||||
free(conf->path);
|
||||
free(conf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (!feof(file))
|
||||
{
|
||||
@@ -153,50 +339,69 @@ config_file_t *config_file_new(const char *path)
|
||||
|
||||
if (line)
|
||||
{
|
||||
if (parse_line(list, line))
|
||||
if (parse_line(conf, list, line))
|
||||
{
|
||||
if (conf->entries == NULL)
|
||||
if (conf->entries)
|
||||
{
|
||||
conf->entries = list;
|
||||
tail = list;
|
||||
conf->tail->next = list;
|
||||
conf->tail = list;
|
||||
}
|
||||
else
|
||||
{
|
||||
tail->next = list;
|
||||
tail = list;
|
||||
conf->entries = list;
|
||||
conf->tail = list;
|
||||
}
|
||||
}
|
||||
|
||||
free(line);
|
||||
}
|
||||
|
||||
if (list != conf->tail)
|
||||
free(list);
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
config_file_t *config_file_new(const char *path)
|
||||
{
|
||||
return config_file_new_internal(path, 0);
|
||||
}
|
||||
|
||||
void config_file_free(config_file_t *conf)
|
||||
{
|
||||
if (conf != NULL)
|
||||
if (!conf)
|
||||
return;
|
||||
|
||||
struct entry_list *tmp = conf->entries;
|
||||
while (tmp)
|
||||
{
|
||||
struct entry_list *tmp = conf->entries;
|
||||
struct entry_list *old = tmp;
|
||||
while (tmp != NULL)
|
||||
{
|
||||
free(tmp->key);
|
||||
free(tmp->value);
|
||||
old = tmp;
|
||||
tmp = tmp->next;
|
||||
free(old);
|
||||
}
|
||||
free(conf);
|
||||
free(tmp->key);
|
||||
free(tmp->value);
|
||||
struct entry_list *hold = tmp;
|
||||
tmp = tmp->next;
|
||||
free(hold);
|
||||
}
|
||||
|
||||
struct include_list *inc_tmp = conf->includes;
|
||||
while (inc_tmp)
|
||||
{
|
||||
free(inc_tmp->path);
|
||||
struct include_list *hold = inc_tmp;
|
||||
inc_tmp = inc_tmp->next;
|
||||
free(hold);
|
||||
}
|
||||
|
||||
free(conf->path);
|
||||
free(conf);
|
||||
}
|
||||
|
||||
bool config_get_double(config_file_t *conf, const char *key, double *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
@@ -212,12 +417,19 @@ bool config_get_int(config_file_t *conf, const char *key, int *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
*in = strtol(list->value, NULL, 0);
|
||||
return true;
|
||||
errno = 0;
|
||||
int val = strtol(list->value, NULL, 0);
|
||||
if (errno == 0)
|
||||
{
|
||||
*in = val;
|
||||
return true;
|
||||
}
|
||||
return
|
||||
false;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
@@ -228,12 +440,19 @@ bool config_get_hex(config_file_t *conf, const char *key, unsigned *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
*in = strtol(list->value, NULL, 16);
|
||||
return true;
|
||||
errno = 0;
|
||||
unsigned val = strtoul(list->value, NULL, 16);
|
||||
if (errno == 0)
|
||||
{
|
||||
*in = val;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
@@ -244,7 +463,7 @@ bool config_get_char(config_file_t *conf, const char *key, char *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
@@ -262,7 +481,7 @@ bool config_get_string(config_file_t *conf, const char *key, char **str)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
@@ -278,7 +497,7 @@ bool config_get_array(config_file_t *conf, const char *key, char *buf, size_t si
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
@@ -294,7 +513,7 @@ bool config_get_bool(config_file_t *conf, const char *key, bool *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
@@ -320,14 +539,15 @@ void config_set_string(config_file_t *conf, const char *key, const char *val)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
struct entry_list *last = list;
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
if (!list->readonly && (strcmp(key, list->key) == 0))
|
||||
{
|
||||
free(list->value);
|
||||
list->value = strdup(val);
|
||||
return;
|
||||
}
|
||||
|
||||
last = list;
|
||||
list = list->next;
|
||||
}
|
||||
@@ -391,11 +611,50 @@ bool config_file_write(config_file_t *conf, const char *path)
|
||||
|
||||
void config_file_dump(config_file_t *conf, FILE *file)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
struct include_list *includes = conf->includes;
|
||||
while (includes)
|
||||
{
|
||||
fprintf(file, "%s = \"%s\"\n", list->key, list->value);
|
||||
fprintf(file, "#include \"%s\"\n", includes->path);
|
||||
includes = includes->next;
|
||||
}
|
||||
|
||||
struct entry_list *list = conf->entries;
|
||||
while (list)
|
||||
{
|
||||
if (!list->readonly)
|
||||
fprintf(file, "%s = \"%s\"\n", list->key, list->value);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
void config_file_dump_all(config_file_t *conf, FILE *file)
|
||||
{
|
||||
struct include_list *includes = conf->includes;
|
||||
while (includes)
|
||||
{
|
||||
fprintf(file, "#include \"%s\"\n", includes->path);
|
||||
includes = includes->next;
|
||||
}
|
||||
|
||||
struct entry_list *list = conf->entries;
|
||||
while (list)
|
||||
{
|
||||
fprintf(file, "%s = \"%s\" %s\n", list->key, list->value, list->readonly ? "(included)" : "");
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
bool config_entry_exists(config_file_t *conf, const char *entry)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(entry, list->key) == 0)
|
||||
return true;
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
+15
-5
@@ -31,13 +31,21 @@ typedef struct config_file config_file_t;
|
||||
// - # are treated as comments. Rest of the line is ignored.
|
||||
// - Format is: key = value. There can be as many spaces as you like in-between.
|
||||
// - Value can be wrapped inside "" for multiword strings. (foo = "hai u")
|
||||
//
|
||||
// - #include includes a config file in-place.
|
||||
// Path is relative to where config file was loaded unless an absolute path is chosen.
|
||||
// Key/value pairs from an #include are read-only, and cannot be modified.
|
||||
|
||||
// Loads a config file. Returns NULL if file doesn't exist. NULL path will create an empty config file.
|
||||
// Loads a config file. Returns NULL if file doesn't exist.
|
||||
// NULL path will create an empty config file.
|
||||
config_file_t *config_file_new(const char *path);
|
||||
// Frees config file.
|
||||
void config_file_free(config_file_t *conf);
|
||||
|
||||
// All extract functions return true when value is valid and exists. Returns false otherwise.
|
||||
// All extract functions return true when value is valid and exists.
|
||||
// Returns false otherwise.
|
||||
|
||||
bool config_entry_exists(config_file_t *conf, const char *entry);
|
||||
|
||||
// Extracts a double from config file.
|
||||
bool config_get_double(config_file_t *conf, const char *entry, double *in);
|
||||
@@ -54,7 +62,8 @@ bool config_get_array(config_file_t *conf, const char *entry, char *in, size_t s
|
||||
// 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);
|
||||
|
||||
// Setters. Similiar to the getters.
|
||||
// Setters. Similar to the getters. Will not write to entry if the entry
|
||||
// was obtained from an #include.
|
||||
void config_set_double(config_file_t *conf, const char *entry, double value);
|
||||
void config_set_int(config_file_t *conf, const char *entry, int val);
|
||||
void config_set_char(config_file_t *conf, const char *entry, char val);
|
||||
@@ -66,7 +75,8 @@ bool config_file_write(config_file_t *conf, const char *path);
|
||||
|
||||
// Dump the current config to an already opened file. Does not close the file.
|
||||
void config_file_dump(config_file_t *conf, FILE *file);
|
||||
|
||||
|
||||
// Also dumps inherited values, useful for logging.
|
||||
void config_file_dump_all(config_file_t *conf, FILE *file);
|
||||
|
||||
#endif
|
||||
|
||||
+89
-74
@@ -30,16 +30,7 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
#include "SDL.h"
|
||||
#else
|
||||
#error "HAVE_SDL is not defined!"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SRC
|
||||
#include <samplerate.h>
|
||||
#endif
|
||||
|
||||
#include "input/keysym.h"
|
||||
|
||||
///////////////// Drivers
|
||||
#define VIDEO_GL 0
|
||||
@@ -59,12 +50,14 @@
|
||||
#define AUDIO_EXT 15
|
||||
#define AUDIO_DSOUND 16
|
||||
#define AUDIO_COREAUDIO 17
|
||||
#define AUDIO_PS3 18
|
||||
////////////////////////
|
||||
#define INPUT_SDL 7
|
||||
#define INPUT_X 12
|
||||
#define INPUT_PS3 19
|
||||
////////////////////////
|
||||
|
||||
#if defined(HAVE_SDL)
|
||||
#if defined(HAVE_OPENGL) || defined(__CELLOS_LV2__)
|
||||
#define VIDEO_DEFAULT_DRIVER VIDEO_GL
|
||||
#elif defined(HAVE_XVIDEO)
|
||||
#define VIDEO_DEFAULT_DRIVER VIDEO_XVIDEO
|
||||
@@ -76,7 +69,9 @@
|
||||
#error "Need at least one video driver!"
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_ALSA)
|
||||
#if defined(__CELLOS_LV2__)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_PS3
|
||||
#elif defined(HAVE_ALSA)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_ALSA
|
||||
#elif defined(HAVE_PULSE)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_PULSE
|
||||
@@ -106,6 +101,8 @@
|
||||
|
||||
#if defined(HAVE_SDL)
|
||||
#define INPUT_DEFAULT_DRIVER INPUT_SDL
|
||||
#elif defined(__CELLOS_LV2__)
|
||||
#define INPUT_DEFAULT_DRIVER INPUT_PS3
|
||||
#elif defined(HAVE_XVIDEO)
|
||||
#define INPUT_DEFAULT_DRIVER INPUT_X
|
||||
#else
|
||||
@@ -118,8 +115,8 @@
|
||||
////////////////
|
||||
|
||||
// Windowed
|
||||
static const float xscale = 3.0; // Real x res = 296 * xscale
|
||||
static const float yscale = 3.0; // Real y res = 224 * yscale
|
||||
static const float xscale = 3.0; // Real x res = aspect * base_size * xscale
|
||||
static const float yscale = 3.0; // Real y res = base_size * yscale
|
||||
|
||||
// Fullscreen
|
||||
static const bool fullscreen = false; // To start in Fullscreen or not
|
||||
@@ -142,7 +139,7 @@ static const bool video_smooth = true;
|
||||
static const bool force_aspect = true;
|
||||
|
||||
// Crop overscanned frames (7/8 or 15/15 for interlaced frames).
|
||||
static const bool crop_overscan = false;
|
||||
static const bool crop_overscan = true;
|
||||
|
||||
// Font size for on-screen messages.
|
||||
static const unsigned font_size = 48;
|
||||
@@ -150,6 +147,8 @@ static const unsigned font_size = 48;
|
||||
// Offset for where messages will be placed on-screen. Values are in range [0.0, 1.0].
|
||||
static const float message_pos_offset_x = 0.05;
|
||||
static const float message_pos_offset_y = 0.05;
|
||||
// Color of the message.
|
||||
static const uint32_t message_color = 0xffff00; // RGB hex value.
|
||||
|
||||
// Render-to-texture before rendering to screen (multi-pass shaders)
|
||||
static const bool render_to_texture = false;
|
||||
@@ -163,7 +162,22 @@ 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)
|
||||
// OSD-messages
|
||||
static const bool font_enable = true;
|
||||
|
||||
// The accurate refresh rate of your monitor (Hz).
|
||||
// This is used to calculate audio input rate with the formula:
|
||||
// audio_input_rate = game_input_rate * display_refresh_rate / game_refresh_rate.
|
||||
// If the implementation does not report any values,
|
||||
// SNES NTSC defaults will be assumed for compatibility.
|
||||
// This value should stay close to 60Hz to avoid large pitch changes.
|
||||
// If your monitor does not run at 60Hz, or something close to it, disable VSync,
|
||||
// and leave this at its default.
|
||||
#ifdef __CELLOS_LV2__
|
||||
static const float refresh_rate = 59.92;
|
||||
#else
|
||||
static const float refresh_rate = 59.95;
|
||||
#endif
|
||||
|
||||
////////////////
|
||||
// Audio
|
||||
@@ -175,15 +189,11 @@ static const bool audio_enable = true;
|
||||
// Output samplerate
|
||||
static const unsigned out_rate = 48000;
|
||||
|
||||
// Input samplerate from libSNES.
|
||||
// Lower this (slightly) if you are experiencing frequent audio dropouts while vsync is enabled.
|
||||
static const float in_rate = 31980.0;
|
||||
|
||||
// When changing input rate on-the-fly
|
||||
static const float audio_rate_step = 0.25;
|
||||
|
||||
// Audio device (e.g. hw:0,0 or /dev/audio). If NULL, will use defaults.
|
||||
static const char* audio_device = NULL;
|
||||
static const char *audio_device = NULL;
|
||||
|
||||
// Desired audio latency in milliseconds. Might not be honored if driver can't provide given latency.
|
||||
static const int out_latency = 64;
|
||||
@@ -191,11 +201,6 @@ static const int out_latency = 64;
|
||||
// Will sync audio. (recommended)
|
||||
static const bool audio_sync = true;
|
||||
|
||||
// Defines the quality (and cpu reqirements) of samplerate conversion.
|
||||
#ifdef HAVE_SRC
|
||||
#define SAMPLERATE_QUALITY SRC_LINEAR
|
||||
#endif
|
||||
|
||||
// Enables use of rewind. This will incur some memory footprint depending on the save state buffer.
|
||||
// This rewind only works when using bSNES core atm.
|
||||
static const bool rewind_enable = false;
|
||||
@@ -207,7 +212,7 @@ static const unsigned rewind_buffer_size = 20 << 20; // 20MiB
|
||||
static const unsigned rewind_granularity = 1;
|
||||
|
||||
// Pause gameplay when gameplay loses focus.
|
||||
static const bool pause_nonactive = true;
|
||||
static const bool pause_nonactive = false;
|
||||
|
||||
// Saves non-volatile SRAM at a regular interval. It is measured in seconds. A value of 0 disables autosave.
|
||||
static const unsigned autosave_interval = 0;
|
||||
@@ -215,6 +220,14 @@ static const unsigned autosave_interval = 0;
|
||||
// When being client over netplay, use keybinds for player 1 rather than player 2.
|
||||
static const bool netplay_client_swap_input = true;
|
||||
|
||||
// On save state load, block SRAM from being overwritten.
|
||||
// This could potentially lead to buggy games.
|
||||
static const bool block_sram_overwrite = false;
|
||||
|
||||
// When saving savestates, state index is automatically incremented before saving.
|
||||
// When the ROM is loaded, state index will be set to the highest existing value.
|
||||
static const bool savestate_auto_index = false;
|
||||
|
||||
|
||||
////////////////////
|
||||
// Keybinds, Joypad
|
||||
@@ -222,64 +235,66 @@ static const bool netplay_client_swap_input = true;
|
||||
|
||||
// Axis threshold (between 0.0 and 1.0)
|
||||
// How far an axis must be tilted to result in a button press
|
||||
#define AXIS_THRESHOLD 0.5
|
||||
static const float axis_threshold = 0.5;
|
||||
|
||||
// To figure out which joypad buttons to use, check jstest or similar.
|
||||
// SDL sometimes reverses the axes for some odd reason, but hey. :D
|
||||
|
||||
// Player 1
|
||||
static const struct snes_keybind snes_keybinds_1[] = {
|
||||
// SNES button | keyboard key | js btn | js axis |
|
||||
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_x, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_z, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_s, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_a, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_q, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_w, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_LEFT, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_RIGHT, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_UP, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_DOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_RETURN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_RSHIFT, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_FAST_FORWARD_KEY, SDLK_SPACE, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_FAST_FORWARD_HOLD_KEY, SDLK_l, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_SAVE_STATE_KEY, SDLK_F2, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_LOAD_STATE_KEY, SDLK_F4, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_FULLSCREEN_TOGGLE_KEY, SDLK_f, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_QUIT_KEY, SDLK_ESCAPE, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_STATE_SLOT_MINUS, SDLK_F6, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_STATE_SLOT_PLUS, SDLK_F7, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_AUDIO_INPUT_RATE_PLUS, SDLK_KP_PLUS, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_AUDIO_INPUT_RATE_MINUS, SDLK_KP_MINUS, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_REWIND, SDLK_r, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_MOVIE_RECORD_TOGGLE, SDLK_o, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_PAUSE_TOGGLE, SDLK_p, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_RESET, SDLK_h, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_SHADER_NEXT, SDLK_m, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_SHADER_PREV, SDLK_n, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_CHEAT_INDEX_PLUS, SDLK_y, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_CHEAT_INDEX_MINUS, SDLK_t, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_CHEAT_TOGGLE, SDLK_u, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_SCREENSHOT, SDLK_PRINT, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_DSP_CONFIG, SDLK_c, NO_BTN, AXIS_NONE },
|
||||
// SNES button | keyboard key | js btn | js axis |
|
||||
{ SNES_DEVICE_ID_JOYPAD_A, SK_x, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_B, SK_z, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_X, SK_s, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_Y, SK_a, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_L, SK_q, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_R, SK_w, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_LEFT, SK_LEFT, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SK_RIGHT, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_UP, SK_UP, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_DOWN, SK_DOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_START, SK_RETURN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_SELECT, SK_RSHIFT, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_FAST_FORWARD_KEY, SK_SPACE, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_FAST_FORWARD_HOLD_KEY, SK_l, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_SAVE_STATE_KEY, SK_F2, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_LOAD_STATE_KEY, SK_F4, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_FULLSCREEN_TOGGLE_KEY, SK_f, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_QUIT_KEY, SK_ESCAPE, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_STATE_SLOT_MINUS, SK_F6, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_STATE_SLOT_PLUS, SK_F7, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_AUDIO_INPUT_RATE_PLUS, SK_KP_PLUS, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_AUDIO_INPUT_RATE_MINUS, SK_KP_MINUS, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_REWIND, SK_r, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_MOVIE_RECORD_TOGGLE, SK_o, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_PAUSE_TOGGLE, SK_p, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_FRAMEADVANCE, SK_k, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_RESET, SK_h, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_SHADER_NEXT, SK_m, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_SHADER_PREV, SK_n, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_CHEAT_INDEX_PLUS, SK_y, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_CHEAT_INDEX_MINUS, SK_t, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_CHEAT_TOGGLE, SK_u, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_SCREENSHOT, SK_PRINT, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_DSP_CONFIG, SK_c, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_MUTE, SK_F9, NO_BTN, AXIS_NONE },
|
||||
{ -1 }
|
||||
};
|
||||
|
||||
// Player 2-5
|
||||
static const struct snes_keybind snes_keybinds_rest[] = {
|
||||
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_A, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_B, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_X, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_Y, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_L, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_R, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_LEFT, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_UP, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_DOWN, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_START, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_SELECT, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ -1 }
|
||||
};
|
||||
|
||||
|
||||
+15
-3
@@ -14,6 +14,18 @@ static const bool _sdl_supp = true;
|
||||
static const bool _sdl_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
static const bool _thread_supp = true;
|
||||
#else
|
||||
static const bool _thread_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENGL
|
||||
static const bool _opengl_supp = true;
|
||||
#else
|
||||
static const bool _opengl_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_XVIDEO
|
||||
static const bool _xvideo_supp = true;
|
||||
#else
|
||||
@@ -110,10 +122,10 @@ static const bool _ffmpeg_supp = true;
|
||||
static const bool _ffmpeg_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SRC
|
||||
static const bool _src_supp = true;
|
||||
#ifdef HAVE_X264RGB
|
||||
static const bool _x264rgb_supp = true;
|
||||
#else
|
||||
static const bool _src_supp = false;
|
||||
static const bool _x264rgb_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" ssnes-joyconfig.1:
|
||||
|
||||
.TH "SSNES-JOYCONFIG" "1" "January 2011" "SSNES-JOYCONFIG" "System Manager's Manual: ssnes-joyconfig"
|
||||
.TH "SSNES-JOYCONFIG" "1" "October 2011" "SSNES-JOYCONFIG" "System Manager's Manual: ssnes-joyconfig"
|
||||
|
||||
.SH NAME
|
||||
|
||||
|
||||
+29
-2
@@ -1,6 +1,6 @@
|
||||
.\" ssnes.1:
|
||||
|
||||
.TH "SSNES" "1" "January 2011" "SSNES" "System Manager's Manual: ssnes"
|
||||
.TH "SSNES" "1" "November 2011" "SSNES" "System Manager's Manual: ssnes"
|
||||
|
||||
.SH NAME
|
||||
|
||||
@@ -27,6 +27,16 @@ If no rom file path is defined on the command line, \fBssnes\fR will try to load
|
||||
\fB--help, -h\fR
|
||||
Prints help text.
|
||||
|
||||
.TP
|
||||
\fB--features\fR
|
||||
Prints available features compiled into SSNES, then exits.
|
||||
|
||||
.TP
|
||||
\fB-L PATH, --libsnes PATH\fR
|
||||
Path to a libsnes implementation which is to be used.
|
||||
This option will override any setting in a config file.
|
||||
This option is only available if SSNES is compiled with dynamic libsnes loading.
|
||||
|
||||
.TP
|
||||
\fB--save PATH, -s PATH\fR
|
||||
Overrides the path used for save ram (*.srm).
|
||||
@@ -91,6 +101,7 @@ When using Sufami, save ram paths will be inferred from the Sufami BIOS path, no
|
||||
\fB--mouse PORT, -m PORT\fR
|
||||
Connects a Super Nintendo Mouse into port number PORT of the emulated SNES. Possible values for PORT are 1 and 2.
|
||||
|
||||
.TP
|
||||
\fB--nodevice PORT, -N PORT\fR
|
||||
Disconnects an input device from port number PORT of the emulated SNES. Possible values for PORT are 1 and 2. This may be needed for some odd games to run properly.
|
||||
|
||||
@@ -113,13 +124,29 @@ 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. Using .mkv extension is recommended.
|
||||
Codecs used are FFV1/FLAC, suitable for processing the material further.
|
||||
Codecs used are (FFV1 or H264 RGB lossless (x264))/FLAC, suitable for processing the material further.
|
||||
|
||||
.TP
|
||||
\fB--size WIDTHxHEIGHT\fR
|
||||
Allows specifying the exact output width and height of FFmpeg recording. This option will override any configuration settings.
|
||||
The video input is scaled with point filtering before being encoded at the correct size.
|
||||
|
||||
.TP
|
||||
\fB--bsvplay PATH, -P PATH\fR
|
||||
Play back a movie recorded in the .bsv format (bSNES). Cart ROM and movie file need to correspond.
|
||||
It also requires to play back with the same libsnes backend that was used for recording.
|
||||
|
||||
.TP
|
||||
\fB--bsvrecord PATH, -R PATH\fR
|
||||
Start recording a .bsv video to PATH immediately after startup.
|
||||
|
||||
.TP
|
||||
\fB--sram-mode MODE, -M MODE\fR
|
||||
MODE designates how to handle SRAM.
|
||||
The format of the second parameter is {no,}load-{no,}save.
|
||||
These two boolean values tell if SRAM loading and SRAM saving should take place.
|
||||
Note that noload-save implies that the SRAM will be overwritten with new data.
|
||||
|
||||
.TP
|
||||
\fB--verbose, -v\fR
|
||||
Activates verbose logging.
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
#endif
|
||||
|
||||
static const audio_driver_t *audio_drivers[] = {
|
||||
#ifdef __CELLOS_LV2__
|
||||
&audio_ps3,
|
||||
#endif
|
||||
#ifdef HAVE_ALSA
|
||||
&audio_alsa,
|
||||
#endif
|
||||
@@ -68,8 +71,10 @@ static const audio_driver_t *audio_drivers[] = {
|
||||
};
|
||||
|
||||
static const video_driver_t *video_drivers[] = {
|
||||
#ifdef HAVE_SDL
|
||||
#ifdef HAVE_OPENGL
|
||||
&video_gl,
|
||||
#endif
|
||||
#ifdef HAVE_SDL
|
||||
&video_sdl,
|
||||
#endif
|
||||
#ifdef HAVE_XVIDEO
|
||||
@@ -81,6 +86,9 @@ static const video_driver_t *video_drivers[] = {
|
||||
};
|
||||
|
||||
static const input_driver_t *input_drivers[] = {
|
||||
#ifdef __CELLOS_LV2__
|
||||
&input_ps3,
|
||||
#endif
|
||||
#ifdef HAVE_SDL
|
||||
&input_sdl,
|
||||
#endif
|
||||
@@ -91,7 +99,7 @@ static const input_driver_t *input_drivers[] = {
|
||||
|
||||
static void find_audio_driver(void)
|
||||
{
|
||||
for (int i = 0; i < sizeof(audio_drivers) / sizeof(audio_driver_t*); i++)
|
||||
for (unsigned i = 0; i < sizeof(audio_drivers) / sizeof(audio_driver_t*); i++)
|
||||
{
|
||||
if (strcasecmp(g_settings.audio.driver, audio_drivers[i]->ident) == 0)
|
||||
{
|
||||
@@ -101,7 +109,7 @@ static void find_audio_driver(void)
|
||||
}
|
||||
SSNES_ERR("Couldn't find any audio driver named \"%s\"\n", g_settings.audio.driver);
|
||||
fprintf(stderr, "Available audio drivers are:\n");
|
||||
for (int i = 0; i < sizeof(audio_drivers) / sizeof(audio_driver_t*); i++)
|
||||
for (unsigned i = 0; i < sizeof(audio_drivers) / sizeof(audio_driver_t*); i++)
|
||||
fprintf(stderr, "\t%s\n", audio_drivers[i]->ident);
|
||||
|
||||
exit(1);
|
||||
@@ -109,7 +117,7 @@ static void find_audio_driver(void)
|
||||
|
||||
static void find_video_driver(void)
|
||||
{
|
||||
for (int i = 0; i < sizeof(video_drivers) / sizeof(video_driver_t*); i++)
|
||||
for (unsigned i = 0; i < sizeof(video_drivers) / sizeof(video_driver_t*); i++)
|
||||
{
|
||||
if (strcasecmp(g_settings.video.driver, video_drivers[i]->ident) == 0)
|
||||
{
|
||||
@@ -127,7 +135,7 @@ static void find_video_driver(void)
|
||||
|
||||
static void find_input_driver(void)
|
||||
{
|
||||
for (int i = 0; i < sizeof(input_drivers) / sizeof(input_driver_t*); i++)
|
||||
for (unsigned i = 0; i < sizeof(input_drivers) / sizeof(input_driver_t*); i++)
|
||||
{
|
||||
if (strcasecmp(g_settings.input.driver, input_drivers[i]->ident) == 0)
|
||||
{
|
||||
@@ -155,6 +163,7 @@ void uninit_drivers(void)
|
||||
uninit_audio();
|
||||
}
|
||||
|
||||
#ifdef HAVE_DYLIB
|
||||
static void init_dsp_plugin(void)
|
||||
{
|
||||
if (!(*g_settings.audio.dsp_plugin))
|
||||
@@ -219,25 +228,74 @@ static void deinit_dsp_plugin(void)
|
||||
dylib_close(g_extern.audio_data.dsp_lib);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void adjust_audio_input_rate(void)
|
||||
{
|
||||
static bool first = true;
|
||||
if (!first)
|
||||
return;
|
||||
|
||||
if (g_extern.system.timing_set)
|
||||
{
|
||||
float timing_skew = fabs(1.0f - g_extern.system.timing.fps / g_settings.video.refresh_rate);
|
||||
if (timing_skew > 0.05f) // We don't want to adjust pitch too much. If we have extreme cases, just don't readjust at all.
|
||||
{
|
||||
SSNES_LOG("Timings deviate too much. Will not adjust. (Display = %.2f Hz, Game = %.2lf Hz)\n",
|
||||
g_settings.video.refresh_rate,
|
||||
g_extern.system.timing.fps);
|
||||
|
||||
g_settings.video.refresh_rate = g_extern.system.timing.fps;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_extern.system.timing_set)
|
||||
{
|
||||
g_settings.audio.in_rate = g_extern.system.timing.sample_rate *
|
||||
(g_settings.video.refresh_rate / g_extern.system.timing.fps);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_settings.audio.in_rate = 32040.5 *
|
||||
(g_settings.video.refresh_rate / (21477272.0 / 357366.0)); // SNES metrics.
|
||||
}
|
||||
|
||||
SSNES_LOG("Set audio input rate to: %.2f Hz.\n", g_settings.audio.in_rate);
|
||||
first = false;
|
||||
}
|
||||
|
||||
#define AUDIO_CHUNK_SIZE_BLOCKING 64
|
||||
#define AUDIO_CHUNK_SIZE_NONBLOCKING 2048 // So we don't get complete line-noise when fast-forwarding audio.
|
||||
#define AUDIO_MAX_RATIO 16
|
||||
void init_audio(void)
|
||||
{
|
||||
// Accomodate rewind since at some point we might have two full buffers.
|
||||
size_t max_bufsamples = AUDIO_CHUNK_SIZE_NONBLOCKING * 2;
|
||||
|
||||
// Used for recording even if audio isn't enabled.
|
||||
assert((g_extern.audio_data.conv_outsamples = malloc(max_bufsamples * sizeof(int16_t) * AUDIO_MAX_RATIO)));
|
||||
g_extern.audio_data.chunk_size = g_extern.audio_data.block_chunk_size;
|
||||
|
||||
// Needs to be able to hold full content of a full max_bufsamples in addition to its own.
|
||||
assert((g_extern.audio_data.rewind_buf = malloc(max_bufsamples * sizeof(int16_t))));
|
||||
g_extern.audio_data.rewind_size = max_bufsamples;
|
||||
|
||||
if (!g_settings.audio.enable)
|
||||
{
|
||||
g_extern.audio_active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
adjust_audio_input_rate();
|
||||
find_audio_driver();
|
||||
|
||||
g_extern.audio_data.block_chunk_size = AUDIO_CHUNK_SIZE_BLOCKING;
|
||||
g_extern.audio_data.nonblock_chunk_size = AUDIO_CHUNK_SIZE_NONBLOCKING;
|
||||
|
||||
driver.audio_data = driver.audio->init(strlen(g_settings.audio.device) ? g_settings.audio.device : NULL, g_settings.audio.out_rate, g_settings.audio.latency);
|
||||
if ( driver.audio_data == NULL )
|
||||
driver.audio_data = driver.audio->init(*g_settings.audio.device ? g_settings.audio.device : NULL,
|
||||
g_settings.audio.out_rate, g_settings.audio.latency);
|
||||
|
||||
if (!driver.audio_data)
|
||||
g_extern.audio_active = false;
|
||||
|
||||
if (g_extern.audio_active && driver.audio->use_float && driver.audio->use_float(driver.audio_data))
|
||||
@@ -248,57 +306,51 @@ void init_audio(void)
|
||||
driver.audio->set_nonblock_state(driver.audio_data, true);
|
||||
g_extern.audio_data.chunk_size = g_extern.audio_data.nonblock_chunk_size;
|
||||
}
|
||||
else
|
||||
g_extern.audio_data.chunk_size = g_extern.audio_data.block_chunk_size;
|
||||
|
||||
#ifdef HAVE_SRC
|
||||
int err;
|
||||
g_extern.audio_data.source = src_new(g_settings.audio.src_quality, 2, &err);
|
||||
g_extern.audio_data.source = hermite_new();
|
||||
if (!g_extern.audio_data.source)
|
||||
g_extern.audio_active = false;
|
||||
#else
|
||||
g_extern.audio_data.source = hermite_new(2);
|
||||
if (!g_extern.audio_data.source)
|
||||
g_extern.audio_active = false;
|
||||
#endif
|
||||
|
||||
size_t max_bufsamples = g_extern.audio_data.block_chunk_size > g_extern.audio_data.nonblock_chunk_size ?
|
||||
g_extern.audio_data.block_chunk_size : g_extern.audio_data.nonblock_chunk_size;
|
||||
|
||||
assert(g_settings.audio.out_rate < g_settings.audio.in_rate * AUDIO_MAX_RATIO);
|
||||
assert((g_extern.audio_data.data = malloc(max_bufsamples * sizeof(float))));
|
||||
g_extern.audio_data.data_ptr = 0;
|
||||
assert(g_settings.audio.out_rate < g_settings.audio.in_rate * AUDIO_MAX_RATIO);
|
||||
assert((g_extern.audio_data.outsamples = malloc(max_bufsamples * sizeof(float) * AUDIO_MAX_RATIO)));
|
||||
assert((g_extern.audio_data.conv_outsamples = malloc(max_bufsamples * sizeof(int16_t) * AUDIO_MAX_RATIO)));
|
||||
|
||||
g_extern.audio_data.src_ratio =
|
||||
(double)g_settings.audio.out_rate / g_settings.audio.in_rate;
|
||||
|
||||
#ifdef HAVE_DYLIB
|
||||
init_dsp_plugin();
|
||||
#endif
|
||||
}
|
||||
|
||||
void uninit_audio(void)
|
||||
{
|
||||
free(g_extern.audio_data.conv_outsamples); g_extern.audio_data.conv_outsamples = NULL;
|
||||
g_extern.audio_data.data_ptr = 0;
|
||||
free(g_extern.audio_data.rewind_buf);
|
||||
g_extern.audio_data.rewind_buf = NULL;
|
||||
|
||||
if (!g_settings.audio.enable)
|
||||
{
|
||||
g_extern.audio_active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( driver.audio_data && driver.audio )
|
||||
if (driver.audio_data && driver.audio)
|
||||
driver.audio->free(driver.audio_data);
|
||||
|
||||
if ( g_extern.audio_data.source )
|
||||
{
|
||||
#ifdef HAVE_SRC
|
||||
src_delete(g_extern.audio_data.source);
|
||||
#else
|
||||
if (g_extern.audio_data.source)
|
||||
hermite_free(g_extern.audio_data.source);
|
||||
#endif
|
||||
}
|
||||
|
||||
free(g_extern.audio_data.data); g_extern.audio_data.data = NULL;
|
||||
free(g_extern.audio_data.outsamples); g_extern.audio_data.outsamples = NULL;
|
||||
free(g_extern.audio_data.conv_outsamples); g_extern.audio_data.conv_outsamples = NULL;
|
||||
free(g_extern.audio_data.data);
|
||||
g_extern.audio_data.data = NULL;
|
||||
free(g_extern.audio_data.outsamples);
|
||||
g_extern.audio_data.outsamples = NULL;
|
||||
|
||||
#ifdef HAVE_DYLIB
|
||||
deinit_dsp_plugin();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_DYLIB
|
||||
@@ -333,17 +385,17 @@ static void init_filter(void)
|
||||
|
||||
g_extern.filter.active = true;
|
||||
|
||||
unsigned width = 512;
|
||||
unsigned height = 512;
|
||||
unsigned width = g_extern.system.geom.max_width;
|
||||
unsigned height = g_extern.system.geom.max_height;
|
||||
g_extern.filter.psize(&width, &height);
|
||||
|
||||
unsigned pow2_x = next_pow2(ceil(width));
|
||||
unsigned pow2_y = next_pow2(ceil(height));
|
||||
unsigned maxsize = pow2_x > pow2_y ? pow2_x : pow2_y;
|
||||
g_extern.filter.scale = maxsize / 256;
|
||||
g_extern.filter.scale = maxsize / SSNES_SCALE_BASE;
|
||||
|
||||
g_extern.filter.buffer = malloc(256 * 256 * g_extern.filter.scale * g_extern.filter.scale * sizeof(uint32_t));
|
||||
g_extern.filter.pitch = 256 * g_extern.filter.scale * sizeof(uint32_t);
|
||||
g_extern.filter.buffer = malloc(SSNES_SCALE_BASE * SSNES_SCALE_BASE * g_extern.filter.scale * g_extern.filter.scale * sizeof(uint32_t));
|
||||
g_extern.filter.pitch = SSNES_SCALE_BASE * g_extern.filter.scale * sizeof(uint32_t);
|
||||
assert(g_extern.filter.buffer);
|
||||
|
||||
g_extern.filter.colormap = malloc(32768 * sizeof(uint32_t));
|
||||
@@ -362,7 +414,6 @@ static void init_filter(void)
|
||||
g_extern.filter.colormap[i] = (r << 16) | (g << 8) | (b << 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void deinit_filter(void)
|
||||
{
|
||||
@@ -375,6 +426,7 @@ static void deinit_filter(void)
|
||||
free(g_extern.filter.colormap);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_XML
|
||||
static void init_shader_dir(void)
|
||||
@@ -399,6 +451,9 @@ static void deinit_shader_dir(void)
|
||||
{
|
||||
// It handles NULL, no worries :D
|
||||
dir_list_free(g_extern.shader_dir.elems);
|
||||
g_extern.shader_dir.elems = NULL;
|
||||
g_extern.shader_dir.size = 0;
|
||||
g_extern.shader_dir.ptr = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -412,8 +467,9 @@ void init_video_input(void)
|
||||
init_shader_dir();
|
||||
#endif
|
||||
|
||||
// We use at least 512x512 textures to accomodate for hi-res games.
|
||||
unsigned scale = 2;
|
||||
unsigned max_dim = max(g_extern.system.geom.max_width, g_extern.system.geom.max_height);
|
||||
unsigned scale = max_dim / SSNES_SCALE_BASE;
|
||||
scale = max(scale, 1);
|
||||
|
||||
find_video_driver();
|
||||
find_input_driver();
|
||||
@@ -430,18 +486,26 @@ void init_video_input(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_settings.video.force_aspect)
|
||||
if (g_settings.video.force_aspect && (g_settings.video.aspect_ratio > 0.0f))
|
||||
{
|
||||
width = 224 * g_settings.video.xscale * g_settings.video.aspect_ratio;
|
||||
height = 224 * g_settings.video.yscale;
|
||||
width = roundf(g_extern.system.geom.base_height * g_settings.video.xscale * g_settings.video.aspect_ratio);
|
||||
height = roundf(g_extern.system.geom.base_height * g_settings.video.yscale);
|
||||
}
|
||||
else
|
||||
{
|
||||
width = 256 * g_settings.video.xscale;
|
||||
height = 224 * g_settings.video.yscale;
|
||||
width = roundf(g_extern.system.geom.base_width * g_settings.video.xscale);
|
||||
height = roundf(g_extern.system.geom.base_height * g_settings.video.yscale);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_settings.video.aspect_ratio < 0.0f)
|
||||
{
|
||||
g_settings.video.aspect_ratio = (float)g_extern.system.geom.base_width / g_extern.system.geom.base_height;
|
||||
SSNES_LOG("Adjusting aspect ratio to %.2f\n", g_settings.video.aspect_ratio);
|
||||
}
|
||||
|
||||
SSNES_LOG("Video @ %ux%u\n", width, height);
|
||||
|
||||
video_info_t video = {
|
||||
.width = width,
|
||||
.height = height,
|
||||
@@ -456,7 +520,7 @@ void init_video_input(void)
|
||||
const input_driver_t *tmp = driver.input;
|
||||
driver.video_data = driver.video->init(&video, &driver.input, &driver.input_data);
|
||||
|
||||
if ( driver.video_data == NULL )
|
||||
if (driver.video_data == NULL)
|
||||
{
|
||||
SSNES_ERR("Cannot open video driver ... Exiting ...\n");
|
||||
exit(1);
|
||||
@@ -469,7 +533,7 @@ void init_video_input(void)
|
||||
if (driver.input != NULL)
|
||||
{
|
||||
driver.input_data = driver.input->init();
|
||||
if ( driver.input_data == NULL )
|
||||
if (driver.input_data == NULL)
|
||||
{
|
||||
SSNES_ERR("Cannot init input driver. Exiting ...\n");
|
||||
exit(1);
|
||||
@@ -485,13 +549,15 @@ void init_video_input(void)
|
||||
|
||||
void uninit_video_input(void)
|
||||
{
|
||||
if ( driver.input_data != driver.video_data && driver.input )
|
||||
if (driver.input_data != driver.video_data && driver.input)
|
||||
driver.input->free(driver.input_data);
|
||||
|
||||
if ( driver.video_data && driver.video )
|
||||
if (driver.video_data && driver.video)
|
||||
driver.video->free(driver.video_data);
|
||||
|
||||
#ifdef HAVE_DYLIB
|
||||
deinit_filter();
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_XML
|
||||
deinit_shader_dir();
|
||||
|
||||
@@ -24,10 +24,13 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include "input/keysym.h"
|
||||
|
||||
// SNES has 12 buttons from 0-11 (libsnes.hpp)
|
||||
#define SSNES_FIRST_META_KEY 12
|
||||
enum
|
||||
{
|
||||
SSNES_FAST_FORWARD_KEY = 0x666, // Hurr, durr
|
||||
SSNES_FAST_FORWARD_KEY = SSNES_FIRST_META_KEY,
|
||||
SSNES_FAST_FORWARD_HOLD_KEY,
|
||||
SSNES_LOAD_STATE_KEY,
|
||||
SSNES_SAVE_STATE_KEY,
|
||||
@@ -40,6 +43,7 @@ enum
|
||||
SSNES_REWIND,
|
||||
SSNES_MOVIE_RECORD_TOGGLE,
|
||||
SSNES_PAUSE_TOGGLE,
|
||||
SSNES_FRAMEADVANCE,
|
||||
SSNES_RESET,
|
||||
SSNES_SHADER_NEXT,
|
||||
SSNES_SHADER_PREV,
|
||||
@@ -47,14 +51,17 @@ enum
|
||||
SSNES_CHEAT_INDEX_MINUS,
|
||||
SSNES_CHEAT_TOGGLE,
|
||||
SSNES_SCREENSHOT,
|
||||
SSNES_DSP_CONFIG
|
||||
SSNES_DSP_CONFIG,
|
||||
SSNES_MUTE,
|
||||
|
||||
SSNES_BIND_LIST_END
|
||||
};
|
||||
|
||||
|
||||
struct snes_keybind
|
||||
{
|
||||
int id;
|
||||
uint16_t key;
|
||||
enum ssnes_key key;
|
||||
uint16_t joykey;
|
||||
uint32_t joyaxis;
|
||||
};
|
||||
@@ -73,12 +80,12 @@ typedef struct video_info
|
||||
|
||||
typedef struct audio_driver
|
||||
{
|
||||
void* (*init)(const char* device, unsigned rate, unsigned latency);
|
||||
ssize_t (*write)(void* data, const void* buf, size_t size);
|
||||
bool (*stop)(void* data);
|
||||
bool (*start)(void* data);
|
||||
void (*set_nonblock_state)(void* data, bool toggle); // Should we care about blocking in audio thread? Fast forwarding.
|
||||
void (*free)(void* data);
|
||||
void *(*init)(const char *device, unsigned rate, unsigned latency);
|
||||
ssize_t (*write)(void *data, const void *buf, size_t size);
|
||||
bool (*stop)(void *data);
|
||||
bool (*start)(void *data);
|
||||
void (*set_nonblock_state)(void *data, bool toggle); // Should we care about blocking in audio thread? Fast forwarding.
|
||||
void (*free)(void *data);
|
||||
bool (*use_float)(void *data); // Defines if driver will take standard floating point samples, or int16_t samples.
|
||||
const char *ident;
|
||||
} audio_driver_t;
|
||||
@@ -104,25 +111,25 @@ typedef struct audio_driver
|
||||
|
||||
typedef struct input_driver
|
||||
{
|
||||
void* (*init)(void);
|
||||
void (*poll)(void* data);
|
||||
int16_t (*input_state)(void* data, const struct snes_keybind **snes_keybinds, bool port, unsigned device, unsigned index, unsigned id);
|
||||
bool (*key_pressed)(void* data, int key);
|
||||
void (*free)(void* data);
|
||||
void *(*init)(void);
|
||||
void (*poll)(void *data);
|
||||
int16_t (*input_state)(void *data, const struct snes_keybind **snes_keybinds, bool port, unsigned device, unsigned index, unsigned id);
|
||||
bool (*key_pressed)(void *data, int key);
|
||||
void (*free)(void *data);
|
||||
const char *ident;
|
||||
} input_driver_t;
|
||||
|
||||
typedef struct video_driver
|
||||
{
|
||||
void* (*init)(const video_info_t *video, const input_driver_t **input, void **input_data);
|
||||
void *(*init)(const video_info_t *video, const input_driver_t **input, void **input_data);
|
||||
// Should the video driver act as an input driver as well? :) The video init might preinitialize an input driver to override the settings in case the video driver relies on input driver for event handling, e.g.
|
||||
bool (*frame)(void* data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg); // msg is for showing a message on the screen along with the video frame.
|
||||
void (*set_nonblock_state)(void* data, bool toggle); // Should we care about syncing to vblank? Fast forwarding.
|
||||
bool (*frame)(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg); // msg is for showing a message on the screen along with the video frame.
|
||||
void (*set_nonblock_state)(void *data, bool toggle); // Should we care about syncing to vblank? Fast forwarding.
|
||||
// Is the window still active?
|
||||
bool (*alive)(void *data);
|
||||
bool (*focus)(void *data); // Does the window have focus?
|
||||
bool (*xml_shader)(void *data, const char *path); // Sets XML-shader. Might not be implemented.
|
||||
void (*free)(void* data);
|
||||
void (*free)(void *data);
|
||||
const char *ident;
|
||||
} video_driver_t;
|
||||
|
||||
@@ -160,12 +167,14 @@ 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 audio_driver_t audio_ps3;
|
||||
extern const video_driver_t video_gl;
|
||||
extern const video_driver_t video_xvideo;
|
||||
extern const video_driver_t video_sdl;
|
||||
extern const video_driver_t video_ext;
|
||||
extern const input_driver_t input_sdl;
|
||||
extern const input_driver_t input_x;
|
||||
extern const input_driver_t input_ps3;
|
||||
////////////////////////////////////////////////
|
||||
|
||||
#endif
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "dynamic.h"
|
||||
#include "general.h"
|
||||
#include "strl.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
@@ -24,29 +25,34 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <libsnes.hpp>
|
||||
#include <stdbool.h>
|
||||
#include "libsnes.hpp"
|
||||
|
||||
#ifdef NEED_DYNAMIC
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DYNAMIC
|
||||
#ifdef NEED_DYNAMIC
|
||||
#define DLSYM(lib, x) dylib_proc(lib, #x)
|
||||
|
||||
#define SYM(type, x) do { \
|
||||
p##x = (type)DLSYM(lib_handle, x); \
|
||||
if (p##x == NULL) { SSNES_ERR("Failed to load symbol: \"%s\"\n", #x); exit(1); } \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define OPT_SYM(type, x) do { \
|
||||
p##x = (type)DLSYM(lib_handle, x); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
static dylib_t lib_handle = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DYNAMIC
|
||||
static dylib_t lib_handle = NULL;
|
||||
#endif
|
||||
|
||||
void (*psnes_init)(void);
|
||||
|
||||
@@ -96,6 +102,11 @@ unsigned (*psnes_get_memory_size)(unsigned);
|
||||
void (*psnes_unload_cartridge)(void);
|
||||
void (*psnes_term)(void);
|
||||
|
||||
#ifdef NEED_DYNAMIC
|
||||
static void set_environment(void);
|
||||
#endif
|
||||
static void set_environment_defaults(void);
|
||||
|
||||
#ifdef HAVE_DYNAMIC
|
||||
static void load_dynamic(void)
|
||||
{
|
||||
@@ -112,7 +123,7 @@ static void load_dynamic(void)
|
||||
SYM(void (*)(snes_audio_sample_t), snes_set_audio_sample);
|
||||
SYM(void (*)(snes_input_poll_t), snes_set_input_poll);
|
||||
SYM(void (*)(snes_input_state_t), snes_set_input_state);
|
||||
OPT_SYM(const char *(*)(void), snes_library_id);
|
||||
SYM(const char *(*)(void), snes_library_id);
|
||||
SYM(unsigned (*)(void), snes_library_revision_minor);
|
||||
SYM(unsigned (*)(void), snes_library_revision_major);
|
||||
SYM(void (*)(void), snes_cheat_reset);
|
||||
@@ -140,13 +151,11 @@ static void load_dynamic(void)
|
||||
SYM(void (*)(void), snes_unload_cartridge);
|
||||
SYM(void (*)(void), snes_term);
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
#define SSYM(x) do { \
|
||||
p##x = x; \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#ifndef HAVE_DYNAMIC
|
||||
static void set_statics(void)
|
||||
{
|
||||
SSYM(snes_init);
|
||||
@@ -156,6 +165,7 @@ static void set_statics(void)
|
||||
SSYM(snes_set_input_state);
|
||||
SSYM(snes_library_revision_minor);
|
||||
SSYM(snes_library_revision_major);
|
||||
SSYM(snes_library_id);
|
||||
SSYM(snes_cheat_reset);
|
||||
SSYM(snes_cheat_set);
|
||||
SSYM(snes_reset);
|
||||
@@ -178,45 +188,47 @@ static void set_statics(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
void init_dlsym(void)
|
||||
void init_libsnes_sym(void)
|
||||
{
|
||||
// Guarantee that we can do "dirty" casting. Every OS that this program supports should pass this ...
|
||||
// Guarantee that we can do "dirty" casting.
|
||||
// Every OS that this program supports should pass this ...
|
||||
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)
|
||||
// Try to verify that -lsnes was not linked in from other modules
|
||||
// since loading it dynamically and with -l will fail hard.
|
||||
function_t sym = dylib_proc(NULL, "snes_init");
|
||||
if (sym)
|
||||
{
|
||||
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
|
||||
{
|
||||
SSNES_ERR("This binary is built to use runtime dynamic binding of libsnes. Set libsnes_path in config to load a libsnes library dynamically.\n");
|
||||
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");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!*g_settings.libsnes)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
strlcpy(g_settings.libsnes, "snes.dll", sizeof(g_settings.libsnes));
|
||||
#elif defined(__APPLE__)
|
||||
strlcpy(g_settings.libsnes, "libsnes.dylib", sizeof(g_settings.libsnes));
|
||||
#else
|
||||
set_statics();
|
||||
strlcpy(g_settings.libsnes, "libsnes.so", sizeof(g_settings.libsnes));
|
||||
#endif
|
||||
}
|
||||
|
||||
load_dynamic();
|
||||
#else
|
||||
set_statics();
|
||||
#endif
|
||||
|
||||
set_environment_defaults();
|
||||
#ifdef NEED_DYNAMIC
|
||||
set_environment();
|
||||
#endif
|
||||
}
|
||||
|
||||
void uninit_dlsym(void)
|
||||
void uninit_libsnes_sym(void)
|
||||
{
|
||||
#ifdef HAVE_DYNAMIC
|
||||
if (lib_handle)
|
||||
@@ -224,6 +236,7 @@ void uninit_dlsym(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef NEED_DYNAMIC
|
||||
// Platform independent dylib loading.
|
||||
dylib_t dylib_load(const char *path)
|
||||
{
|
||||
@@ -237,16 +250,26 @@ dylib_t dylib_load(const char *path)
|
||||
function_t dylib_proc(dylib_t lib, const char *proc)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
function_t sym = (function_t)GetProcAddress(lib, proc);
|
||||
function_t sym = (function_t)GetProcAddress(lib ? lib : GetModuleHandle(NULL), proc);
|
||||
#else
|
||||
// Dirty hack to workaround the non-legality of void* -> fn-pointer casts.
|
||||
void *ptr_sym = dlsym(lib, proc);
|
||||
void *ptr_sym = NULL;
|
||||
if (lib)
|
||||
ptr_sym = dlsym(lib, proc);
|
||||
else
|
||||
{
|
||||
void *handle = dlopen(NULL, RTLD_LAZY);
|
||||
if (handle)
|
||||
{
|
||||
ptr_sym = dlsym(handle, proc);
|
||||
dlclose(handle);
|
||||
}
|
||||
}
|
||||
|
||||
// Dirty hack to workaround the non-legality of (void*) -> fn-pointer casts.
|
||||
function_t sym;
|
||||
memcpy(&sym, &ptr_sym, sizeof(void*));
|
||||
#endif
|
||||
|
||||
if (!sym)
|
||||
SSNES_WARN("Failed to load symbol \"%s\"\n", proc);
|
||||
return sym;
|
||||
}
|
||||
|
||||
@@ -259,3 +282,92 @@ void dylib_close(dylib_t lib)
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool environment_cb(unsigned cmd, void *data)
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case SNES_ENVIRONMENT_GET_FULLPATH:
|
||||
*(const char**)data = g_extern.system.fullpath;
|
||||
SSNES_LOG("Environ FULLPATH: \"%s\"\n", g_extern.system.fullpath);
|
||||
break;
|
||||
|
||||
case SNES_ENVIRONMENT_SET_GEOMETRY:
|
||||
g_extern.system.geom = *(const struct snes_geometry*)data;
|
||||
g_extern.system.geom.max_width = next_pow2(g_extern.system.geom.max_width);
|
||||
g_extern.system.geom.max_height = next_pow2(g_extern.system.geom.max_height);
|
||||
SSNES_LOG("Environ SET_GEOMETRY: (%ux%u) / (%ux%u)\n",
|
||||
g_extern.system.geom.base_width,
|
||||
g_extern.system.geom.base_height,
|
||||
g_extern.system.geom.max_width,
|
||||
g_extern.system.geom.max_height);
|
||||
break;
|
||||
|
||||
case SNES_ENVIRONMENT_SET_PITCH:
|
||||
g_extern.system.pitch = *(const unsigned*)data;
|
||||
SSNES_LOG("Environ SET_PITCH: %u\n", g_extern.system.pitch);
|
||||
break;
|
||||
|
||||
case SNES_ENVIRONMENT_GET_OVERSCAN:
|
||||
*(bool*)data = !g_settings.video.crop_overscan;
|
||||
SSNES_LOG("Environ GET_OVERSCAN: %u\n", (unsigned)!g_settings.video.crop_overscan);
|
||||
break;
|
||||
|
||||
case SNES_ENVIRONMENT_SET_TIMING:
|
||||
g_extern.system.timing = *(const struct snes_system_timing*)data;
|
||||
g_extern.system.timing_set = true;
|
||||
SSNES_LOG("Environ SET_TIMING: %.3lf Hz/ %.3lf Hz\n", g_extern.system.timing.fps, g_extern.system.timing.sample_rate);
|
||||
break;
|
||||
|
||||
case SNES_ENVIRONMENT_GET_CAN_DUPE:
|
||||
*(bool*)data = true;
|
||||
SSNES_LOG("Environ GET_CAN_DUPE: true\n");
|
||||
break;
|
||||
|
||||
case SNES_ENVIRONMENT_SET_NEED_FULLPATH:
|
||||
g_extern.system.need_fullpath = *(const bool*)data;
|
||||
SSNES_LOG("Environ SET_NEED_FULLPATH: %s\n", g_extern.system.need_fullpath ? "true" : "false");
|
||||
break;
|
||||
|
||||
case SNES_ENVIRONMENT_GET_CAN_REWIND:
|
||||
*(bool*)data = g_settings.rewind_enable;
|
||||
SSNES_LOG("Environ GET_CAN_REWIND: %s\n", g_settings.rewind_enable ? "true" : "false");
|
||||
break;
|
||||
|
||||
default:
|
||||
SSNES_LOG("Environ UNSUPPORTED (#%u)!\n", cmd);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// SSNES extension hooks. Totally optional 'n shizz :)
|
||||
static void set_environment(void)
|
||||
{
|
||||
#ifdef HAVE_DYNAMIC
|
||||
dylib_t lib = lib_handle;
|
||||
#else
|
||||
dylib_t lib = NULL;
|
||||
#endif
|
||||
|
||||
void (*psnes_set_environment)(snes_environment_t) =
|
||||
(void (*)(snes_environment_t))dylib_proc(lib, "snes_set_environment");
|
||||
|
||||
if (psnes_set_environment)
|
||||
psnes_set_environment(environment_cb);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Assume SNES as defaults.
|
||||
static void set_environment_defaults(void)
|
||||
{
|
||||
SSNES_LOG("Setting environment defaults (SNES)\n");
|
||||
g_extern.system.pitch = 0; // 0 is classic libsnes semantics.
|
||||
g_extern.system.geom = (struct snes_geometry) {
|
||||
.base_width = 256,
|
||||
.base_height = 224,
|
||||
.max_width = 512,
|
||||
.max_height = 512,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,17 +19,29 @@
|
||||
#define __DYNAMIC_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <libsnes.hpp>
|
||||
#include "libsnes.hpp"
|
||||
|
||||
void init_dlsym(void);
|
||||
void uninit_dlsym(void);
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB)
|
||||
#define NEED_DYNAMIC
|
||||
#else
|
||||
#undef NEED_DYNAMIC
|
||||
#endif
|
||||
|
||||
void init_libsnes_sym(void);
|
||||
void uninit_libsnes_sym(void);
|
||||
|
||||
typedef void *dylib_t;
|
||||
#ifdef NEED_DYNAMIC
|
||||
typedef void (*function_t)(void);
|
||||
|
||||
dylib_t dylib_load(const char *path);
|
||||
void dylib_close(dylib_t lib);
|
||||
function_t dylib_proc(dylib_t lib, const char *proc);
|
||||
#endif
|
||||
|
||||
extern void (*psnes_init)(void);
|
||||
|
||||
@@ -41,7 +53,7 @@ extern void (*psnes_set_input_state)(snes_input_state_t);
|
||||
extern void (*psnes_cheat_reset)(void);
|
||||
extern void (*psnes_cheat_set)(unsigned, bool, const char*);
|
||||
|
||||
extern const char* (*psnes_library_id)(void);
|
||||
extern const char *(*psnes_library_id)(void);
|
||||
extern unsigned (*psnes_library_revision_minor)(void);
|
||||
extern unsigned (*psnes_library_revision_major)(void);
|
||||
|
||||
|
||||
+11
-29
@@ -14,16 +14,17 @@
|
||||
*/
|
||||
|
||||
#include "fifo_buffer.h"
|
||||
#include <stdint.h>
|
||||
|
||||
struct fifo_buffer
|
||||
{
|
||||
char *buffer;
|
||||
uint8_t *buffer;
|
||||
size_t bufsize;
|
||||
size_t first;
|
||||
size_t end;
|
||||
};
|
||||
|
||||
fifo_buffer_t* fifo_new(size_t size)
|
||||
fifo_buffer_t *fifo_new(size_t size)
|
||||
{
|
||||
fifo_buffer_t *buf = calloc(1, sizeof(*buf));
|
||||
if (buf == NULL)
|
||||
@@ -40,20 +41,14 @@ fifo_buffer_t* fifo_new(size_t size)
|
||||
return buf;
|
||||
}
|
||||
|
||||
void fifo_free(fifo_buffer_t* buffer)
|
||||
void fifo_free(fifo_buffer_t *buffer)
|
||||
{
|
||||
assert(buffer);
|
||||
assert(buffer->buffer);
|
||||
|
||||
free(buffer->buffer);
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
size_t fifo_read_avail(fifo_buffer_t* buffer)
|
||||
size_t fifo_read_avail(fifo_buffer_t *buffer)
|
||||
{
|
||||
assert(buffer);
|
||||
assert(buffer->buffer);
|
||||
|
||||
size_t first = buffer->first;
|
||||
size_t end = buffer->end;
|
||||
if (end < first)
|
||||
@@ -61,11 +56,8 @@ size_t fifo_read_avail(fifo_buffer_t* buffer)
|
||||
return end - first;
|
||||
}
|
||||
|
||||
size_t fifo_write_avail(fifo_buffer_t* buffer)
|
||||
size_t fifo_write_avail(fifo_buffer_t *buffer)
|
||||
{
|
||||
assert(buffer);
|
||||
assert(buffer->buffer);
|
||||
|
||||
size_t first = buffer->first;
|
||||
size_t end = buffer->end;
|
||||
if (end < first)
|
||||
@@ -74,13 +66,8 @@ size_t fifo_write_avail(fifo_buffer_t* buffer)
|
||||
return (buffer->bufsize - 1) - (end - first);
|
||||
}
|
||||
|
||||
void fifo_write(fifo_buffer_t* buffer, const void* in_buf, size_t size)
|
||||
void fifo_write(fifo_buffer_t *buffer, const void *in_buf, size_t size)
|
||||
{
|
||||
assert(buffer);
|
||||
assert(buffer->buffer);
|
||||
assert(in_buf);
|
||||
assert(fifo_write_avail(buffer) >= size);
|
||||
|
||||
size_t first_write = size;
|
||||
size_t rest_write = 0;
|
||||
if (buffer->end + size > buffer->bufsize)
|
||||
@@ -91,19 +78,14 @@ void fifo_write(fifo_buffer_t* buffer, const void* in_buf, size_t size)
|
||||
|
||||
memcpy(buffer->buffer + buffer->end, in_buf, first_write);
|
||||
if (rest_write > 0)
|
||||
memcpy(buffer->buffer, (const char*)in_buf + first_write, rest_write);
|
||||
memcpy(buffer->buffer, (const uint8_t*)in_buf + first_write, rest_write);
|
||||
|
||||
buffer->end = (buffer->end + size) % buffer->bufsize;
|
||||
}
|
||||
|
||||
|
||||
void fifo_read(fifo_buffer_t* buffer, void* in_buf, size_t size)
|
||||
void fifo_read(fifo_buffer_t *buffer, void *in_buf, size_t size)
|
||||
{
|
||||
assert(buffer);
|
||||
assert(buffer->buffer);
|
||||
assert(in_buf);
|
||||
assert(fifo_read_avail(buffer) >= size);
|
||||
|
||||
size_t first_read = size;
|
||||
size_t rest_read = 0;
|
||||
if (buffer->first + size > buffer->bufsize)
|
||||
@@ -112,9 +94,9 @@ void fifo_read(fifo_buffer_t* buffer, void* in_buf, size_t size)
|
||||
rest_read = size - first_read;
|
||||
}
|
||||
|
||||
memcpy(in_buf, (const char*)buffer->buffer + buffer->first, first_read);
|
||||
memcpy(in_buf, (const uint8_t*)buffer->buffer + buffer->first, first_read);
|
||||
if (rest_read > 0)
|
||||
memcpy((char*)in_buf + first_read, buffer->buffer, rest_read);
|
||||
memcpy((uint8_t*)in_buf + first_read, buffer->buffer, rest_read);
|
||||
|
||||
buffer->first = (buffer->first + size) % buffer->bufsize;
|
||||
}
|
||||
|
||||
+6
-6
@@ -26,11 +26,11 @@
|
||||
typedef struct fifo_buffer fifo_buffer_t;
|
||||
#endif
|
||||
|
||||
fifo_buffer_t* fifo_new(size_t size);
|
||||
void fifo_write(fifo_buffer_t* buffer, const void* in_buf, size_t size);
|
||||
void fifo_read(fifo_buffer_t* buffer, void* in_buf, size_t size);
|
||||
void fifo_free(fifo_buffer_t* buffer);
|
||||
size_t fifo_read_avail(fifo_buffer_t* buffer);
|
||||
size_t fifo_write_avail(fifo_buffer_t* buffer);
|
||||
fifo_buffer_t *fifo_new(size_t size);
|
||||
void fifo_write(fifo_buffer_t *buffer, const void *in_buf, size_t size);
|
||||
void fifo_read(fifo_buffer_t *buffer, void *in_buf, size_t size);
|
||||
void fifo_free(fifo_buffer_t *buffer);
|
||||
size_t fifo_read_avail(fifo_buffer_t *buffer);
|
||||
size_t fifo_write_avail(fifo_buffer_t *buffer);
|
||||
|
||||
#endif
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "general.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <libsnes.hpp>
|
||||
#include "libsnes.hpp"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
@@ -251,7 +251,7 @@ static ssize_t read_rom_file(FILE* file, void** buf)
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(;;)
|
||||
for (;;)
|
||||
{
|
||||
size_t ret = fread(rom_buf + buf_ptr, 1, buf_size - buf_ptr, stdin);
|
||||
buf_ptr += ret;
|
||||
@@ -361,6 +361,8 @@ static void dump_to_file_desperate(const void *data, size_t size, int type)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const char *base = getenv("APPDATA");
|
||||
#elif defined(__CELLOS_LV2__)
|
||||
const char *base = NULL;
|
||||
#else
|
||||
const char *base = getenv("HOME");
|
||||
#endif
|
||||
@@ -425,7 +427,74 @@ bool load_state(const char *path)
|
||||
else
|
||||
{
|
||||
SSNES_LOG("State size: %d bytes.\n", (int)size);
|
||||
|
||||
uint8_t *block_buf[2] = {NULL, NULL};
|
||||
int block_type[2] = {-1, -1};
|
||||
unsigned block_size[2] = {0};
|
||||
|
||||
if (g_settings.block_sram_overwrite)
|
||||
{
|
||||
SSNES_LOG("Blocking SRAM overwrite!\n");
|
||||
switch (g_extern.game_type)
|
||||
{
|
||||
case SSNES_CART_NORMAL:
|
||||
block_type[0] = SNES_MEMORY_CARTRIDGE_RAM;
|
||||
block_type[1] = SNES_MEMORY_CARTRIDGE_RTC;
|
||||
break;
|
||||
|
||||
case SSNES_CART_BSX:
|
||||
case SSNES_CART_BSX_SLOTTED:
|
||||
block_type[0] = SNES_MEMORY_BSX_RAM;
|
||||
block_type[1] = SNES_MEMORY_BSX_PRAM;
|
||||
break;
|
||||
|
||||
case SSNES_CART_SUFAMI:
|
||||
block_type[0] = SNES_MEMORY_SUFAMI_TURBO_A_RAM;
|
||||
block_type[1] = SNES_MEMORY_SUFAMI_TURBO_B_RAM;
|
||||
break;
|
||||
|
||||
case SSNES_CART_SGB:
|
||||
block_type[0] = SNES_MEMORY_GAME_BOY_RAM;
|
||||
block_type[1] = SNES_MEMORY_GAME_BOY_RTC;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
if (block_type[i] != -1)
|
||||
block_size[i] = psnes_get_memory_size(block_type[i]);
|
||||
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
if (block_size[i])
|
||||
block_buf[i] = malloc(block_size[i]);
|
||||
|
||||
// Backup current SRAM which is overwritten by unserialize.
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
if (block_buf[i])
|
||||
{
|
||||
const uint8_t *ptr = psnes_get_memory_data(block_type[i]);
|
||||
if (ptr)
|
||||
memcpy(block_buf[i], ptr, block_size[i]);
|
||||
}
|
||||
}
|
||||
|
||||
psnes_unserialize(buf, size);
|
||||
|
||||
// Flush back :D
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
if (block_buf[i])
|
||||
{
|
||||
uint8_t *ptr = psnes_get_memory_data(block_type[i]);
|
||||
if (ptr)
|
||||
memcpy(ptr, block_buf[i], block_size[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
if (block_buf[i])
|
||||
free(block_buf[i]);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
@@ -443,7 +512,7 @@ void load_ram_file(const char *path, int type)
|
||||
void *buf = NULL;
|
||||
ssize_t rc = read_file(path, &buf);
|
||||
if (rc <= size)
|
||||
memcpy(data, buf, size);
|
||||
memcpy(data, buf, rc);
|
||||
|
||||
free(buf);
|
||||
}
|
||||
@@ -464,7 +533,7 @@ void save_ram_file(const char *path, int type)
|
||||
}
|
||||
}
|
||||
|
||||
static char* load_xml_map(const char *path)
|
||||
static char *load_xml_map(const char *path)
|
||||
{
|
||||
char *xml_buf = NULL;
|
||||
if (*path)
|
||||
@@ -668,29 +737,43 @@ static bool load_normal_rom(void)
|
||||
void *rom_buf = NULL;
|
||||
ssize_t rom_len = 0;
|
||||
|
||||
if ((rom_len = read_rom_file(g_extern.rom_file, &rom_buf)) == -1)
|
||||
if (!g_extern.system.need_fullpath)
|
||||
{
|
||||
SSNES_ERR("Could not read ROM file.\n");
|
||||
return false;
|
||||
if ((rom_len = read_rom_file(g_extern.rom_file, &rom_buf)) == -1)
|
||||
{
|
||||
SSNES_ERR("Could not read ROM file.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_extern.rom_file)
|
||||
fclose(g_extern.rom_file);
|
||||
|
||||
SSNES_LOG("ROM size: %d bytes\n", (int)rom_len);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!g_extern.rom_file)
|
||||
{
|
||||
SSNES_ERR("Implementation requires a full path to be set, cannot load ROM from stdin. Aborting ...\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_extern.rom_file != NULL)
|
||||
fclose(g_extern.rom_file);
|
||||
|
||||
SSNES_LOG("ROM size: %d bytes\n", (int)rom_len);
|
||||
|
||||
char *xml_buf = load_xml_map(g_extern.xml_name);
|
||||
SSNES_LOG("ROM loading skipped. Implementation will load it on its own.\n");
|
||||
}
|
||||
|
||||
char *xml_buf = load_xml_map(g_extern.xml_name);
|
||||
|
||||
if (!psnes_load_cartridge_normal(xml_buf, rom_buf, rom_len))
|
||||
{
|
||||
SSNES_ERR("ROM file is not valid!\n");
|
||||
free(rom_buf);
|
||||
free(xml_buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xml_buf)
|
||||
free(xml_buf);
|
||||
|
||||
free(xml_buf);
|
||||
free(rom_buf);
|
||||
return true;
|
||||
}
|
||||
@@ -734,7 +817,7 @@ bool init_rom_file(enum ssnes_game_type type)
|
||||
}
|
||||
|
||||
// Yep, this is C alright ;)
|
||||
char** dir_list_new(const char *dir, const char *ext)
|
||||
char **dir_list_new(const char *dir, const char *ext)
|
||||
{
|
||||
size_t cur_ptr = 0;
|
||||
size_t cur_size = 32;
|
||||
@@ -751,8 +834,12 @@ char** dir_list_new(const char *dir, const char *ext)
|
||||
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;
|
||||
|
||||
if (ext)
|
||||
{
|
||||
if (strlcat(utf8_buf, ext, sizeof(utf8_buf)) >= sizeof(utf8_buf))
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (MultiByteToWideChar(CP_UTF8, 0, utf8_buf, -1, wchar_buf, MAXPATHLEN) == 0)
|
||||
goto error;
|
||||
@@ -783,7 +870,7 @@ char** dir_list_new(const char *dir, const char *ext)
|
||||
#ifdef _WIN32
|
||||
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
continue;
|
||||
if (WideCharToMultiByte(CP_UTF8, 0, ffd.cFileName, -1, utf8_buf, MAX_PATH, NULL, NULL) == 0)
|
||||
if (WideCharToMultiByte(CP_UTF8, 0, ffd.cFileName, -1, utf8_buf, MAXPATHLEN, NULL, NULL) == 0)
|
||||
continue;
|
||||
if (ext && !strstr(utf8_buf, ext))
|
||||
continue;
|
||||
@@ -858,6 +945,12 @@ bool path_is_directory(const char *path)
|
||||
if (MultiByteToWideChar(CP_UTF8, 0, path, -1, buf, MAXPATHLEN) == 0)
|
||||
return false;
|
||||
return PathIsDirectoryW(buf) == FILE_ATTRIBUTE_DIRECTORY;
|
||||
#elif defined(__CELLOS_LV2__)
|
||||
return false; // STUB
|
||||
#elif defined(XENON)
|
||||
// Dummy
|
||||
(void)path;
|
||||
return false;
|
||||
#else
|
||||
struct stat buf;
|
||||
if (stat(path, &buf) < 0)
|
||||
@@ -880,8 +973,8 @@ bool path_file_exists(const char *path)
|
||||
|
||||
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 tmp_path[MAXPATHLEN];
|
||||
assert(strlcpy(tmp_path, in_path, sizeof(tmp_path)) < sizeof(tmp_path));
|
||||
char *tok = strrchr(tmp_path, '.');
|
||||
if (tok != NULL)
|
||||
*tok = '\0';
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <sys/types.h>
|
||||
#include "general.h"
|
||||
|
||||
// Generic file, path and directory handling.
|
||||
|
||||
ssize_t read_file(const char *path, void **buf);
|
||||
|
||||
bool load_state(const char *path);
|
||||
@@ -37,7 +39,9 @@ void save_ram_file(const char *path, int type);
|
||||
bool init_rom_file(enum ssnes_game_type type);
|
||||
|
||||
// Returns a NULL-terminated list of files in a directory with full paths.
|
||||
char** dir_list_new(const char *dir, const char *ext);
|
||||
// If ext is NULL, any file will be picked.
|
||||
// If non-NULL, only files with extension ext are added.
|
||||
char **dir_list_new(const char *dir, const char *ext);
|
||||
void dir_list_free(char **dir_list);
|
||||
|
||||
bool path_is_directory(const char *path);
|
||||
|
||||
@@ -20,30 +20,37 @@
|
||||
#define __SSNES_GENERAL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "driver.h"
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include "driver.h"
|
||||
#include "record/ffemu.h"
|
||||
#include "message.h"
|
||||
#include "rewind.h"
|
||||
#include "movie.h"
|
||||
#include "autosave.h"
|
||||
#include "netplay.h"
|
||||
#include "dynamic.h"
|
||||
#include "cheats.h"
|
||||
#include "audio/ext/ssnes_dsp.h"
|
||||
|
||||
#ifdef __CELLOS_LV2__
|
||||
#include <sys/timer.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SRC
|
||||
#include <samplerate.h>
|
||||
#else
|
||||
#include "audio/hermite.h"
|
||||
#ifdef HAVE_NETPLAY
|
||||
#include "netplay.h"
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#include "audio/hermite.h"
|
||||
|
||||
#if !defined(_WIN32) && !defined(__CELLOS_LV2__)
|
||||
#include <sys/param.h> // MAXPATHLEN
|
||||
#elif defined(_WIN32)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifndef MAXPATHLEN
|
||||
@@ -55,7 +62,7 @@
|
||||
#endif
|
||||
|
||||
#define MAX_PLAYERS 5
|
||||
#define MAX_BINDS 34 // Needs to be increased every time there are new binds added.
|
||||
#define MAX_BINDS (SSNES_BIND_LIST_END + 1)
|
||||
#define SSNES_NO_JOYPAD 0xFFFF
|
||||
|
||||
enum ssnes_shader_type
|
||||
@@ -86,6 +93,7 @@ struct settings
|
||||
char bsnes_shader_path[MAXPATHLEN];
|
||||
char filter_path[MAXPATHLEN];
|
||||
enum ssnes_shader_type shader_type;
|
||||
float refresh_rate;
|
||||
|
||||
bool render_to_texture;
|
||||
double fbo_scale_x;
|
||||
@@ -96,8 +104,12 @@ struct settings
|
||||
|
||||
char font_path[MAXPATHLEN];
|
||||
unsigned font_size;
|
||||
bool font_enable;
|
||||
float msg_pos_x;
|
||||
float msg_pos_y;
|
||||
float msg_color_r;
|
||||
float msg_color_g;
|
||||
float msg_color_b;
|
||||
|
||||
bool force_16bit;
|
||||
bool disable_composition;
|
||||
@@ -118,7 +130,6 @@ struct settings
|
||||
char device[MAXPATHLEN];
|
||||
unsigned latency;
|
||||
bool sync;
|
||||
int src_quality;
|
||||
|
||||
char dsp_plugin[MAXPATHLEN];
|
||||
char external_driver[MAXPATHLEN];
|
||||
@@ -135,15 +146,19 @@ struct settings
|
||||
|
||||
char libsnes[MAXPATHLEN];
|
||||
char cheat_database[MAXPATHLEN];
|
||||
char cheat_settings_path[MAXPATHLEN];
|
||||
|
||||
char screenshot_directory[MAXPATHLEN];
|
||||
|
||||
bool rewind_enable;
|
||||
unsigned rewind_buffer_size;
|
||||
size_t rewind_buffer_size;
|
||||
unsigned rewind_granularity;
|
||||
|
||||
bool pause_nonactive;
|
||||
unsigned autosave_interval;
|
||||
|
||||
bool block_sram_overwrite;
|
||||
bool savestate_auto_index;
|
||||
};
|
||||
|
||||
enum ssnes_game_type
|
||||
@@ -203,11 +218,17 @@ struct global
|
||||
|
||||
struct
|
||||
{
|
||||
#ifdef HAVE_SRC
|
||||
SRC_STATE *source;
|
||||
#else
|
||||
struct snes_geometry geom;
|
||||
unsigned pitch; // If 0, has classic libsnes semantics.
|
||||
char fullpath[MAXPATHLEN];
|
||||
struct snes_system_timing timing;
|
||||
bool timing_set;
|
||||
bool need_fullpath;
|
||||
} system;
|
||||
|
||||
struct
|
||||
{
|
||||
hermite_resampler_t *source;
|
||||
#endif
|
||||
|
||||
float *data;
|
||||
size_t data_ptr;
|
||||
@@ -215,11 +236,18 @@ struct global
|
||||
size_t nonblock_chunk_size;
|
||||
size_t block_chunk_size;
|
||||
|
||||
double src_ratio;
|
||||
|
||||
bool use_float;
|
||||
bool mute;
|
||||
|
||||
float *outsamples;
|
||||
int16_t *conv_outsamples;
|
||||
|
||||
int16_t *rewind_buf;
|
||||
size_t rewind_ptr;
|
||||
size_t rewind_size;
|
||||
|
||||
dylib_t dsp_lib;
|
||||
const ssnes_dsp_plugin_t *dsp_plugin;
|
||||
void *dsp_handle;
|
||||
@@ -246,46 +274,67 @@ struct global
|
||||
void *state_buf;
|
||||
bool frame_is_reverse;
|
||||
|
||||
// Movie record support
|
||||
bsv_movie_t *bsv_movie;
|
||||
char bsv_movie_path[MAXPATHLEN];
|
||||
bool bsv_movie_end;
|
||||
bool bsv_movie_playback;
|
||||
// Movie playback/recording support.
|
||||
struct
|
||||
{
|
||||
bsv_movie_t *movie;
|
||||
char movie_path[MAXPATHLEN];
|
||||
bool movie_playback;
|
||||
|
||||
// Immediate playback/recording.
|
||||
char movie_start_path[MAXPATHLEN];
|
||||
bool movie_start_recording;
|
||||
bool movie_start_playback;
|
||||
bool movie_end;
|
||||
} bsv;
|
||||
|
||||
bool sram_load_disable;
|
||||
bool sram_save_disable;
|
||||
|
||||
// Pausing support
|
||||
bool is_paused;
|
||||
bool is_oneshot;
|
||||
|
||||
// Autosave support.
|
||||
autosave_t *autosave[2];
|
||||
|
||||
// Netplay.
|
||||
#ifdef HAVE_NETPLAY
|
||||
netplay_t *netplay;
|
||||
char netplay_server[MAXPATHLEN];
|
||||
bool netplay_enable;
|
||||
bool netplay_is_client;
|
||||
unsigned netplay_sync_frames;
|
||||
uint16_t netplay_port;
|
||||
#endif
|
||||
|
||||
// FFmpeg record.
|
||||
#ifdef HAVE_FFMPEG
|
||||
ffemu_t *rec;
|
||||
char record_path[MAXPATHLEN];
|
||||
bool recording;
|
||||
unsigned record_width;
|
||||
unsigned record_height;
|
||||
#endif
|
||||
|
||||
struct
|
||||
{
|
||||
const uint16_t *data;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
} frame_cache;
|
||||
|
||||
char title_buf[64];
|
||||
|
||||
struct
|
||||
{
|
||||
char** elems;
|
||||
char **elems;
|
||||
size_t size;
|
||||
size_t ptr;
|
||||
} shader_dir;
|
||||
|
||||
char sha256[64 + 1];
|
||||
|
||||
bool do_screenshot;
|
||||
|
||||
#ifdef HAVE_XML
|
||||
cheat_manager_t *cheat;
|
||||
#endif
|
||||
@@ -300,17 +349,27 @@ extern struct global g_extern;
|
||||
if (g_extern.verbose) \
|
||||
fprintf(stderr, "SSNES: " __VA_ARGS__); \
|
||||
fflush(stderr); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define SSNES_ERR(...) do { \
|
||||
fprintf(stderr, "SSNES [ERROR] :: " __VA_ARGS__); \
|
||||
fflush(stderr); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#define SSNES_WARN(...) do { \
|
||||
fprintf(stderr, "SSNES [WARN] :: " __VA_ARGS__); \
|
||||
fflush(stderr); \
|
||||
} while(0)
|
||||
} while (0)
|
||||
|
||||
#ifndef max
|
||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef min
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#define SSNES_SCALE_BASE 256
|
||||
|
||||
static inline uint32_t next_pow2(uint32_t v)
|
||||
{
|
||||
@@ -336,6 +395,24 @@ static inline uint8_t is_little_endian(void)
|
||||
return u.y[0];
|
||||
}
|
||||
|
||||
static inline void ssnes_sleep(unsigned msec)
|
||||
{
|
||||
#ifdef __CELLOS_LV2__
|
||||
sys_timer_usleep(1000 * msec);
|
||||
#elif defined(_WIN32)
|
||||
Sleep(msec);
|
||||
#elif defined(XENON)
|
||||
// Dummy
|
||||
(void)msec;
|
||||
#else
|
||||
struct timespec tv = {
|
||||
.tv_sec = msec / 1000,
|
||||
.tv_nsec = (msec % 1000) * 1000000,
|
||||
};
|
||||
nanosleep(&tv, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
/* 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 "getopt_ssnes.h"
|
||||
|
||||
#ifndef HAVE_GETOPT_LONG
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
|
||||
char *optarg;
|
||||
int optind, opterr, optopt;
|
||||
|
||||
static bool is_short_option(const char *str)
|
||||
{
|
||||
return str[0] == '-' && str[1] != '-';
|
||||
}
|
||||
|
||||
static bool is_long_option(const char *str)
|
||||
{
|
||||
return str[0] == '-' && str[1] == '-';
|
||||
}
|
||||
|
||||
static int find_short_index(char * const *argv)
|
||||
{
|
||||
for (int index = 0; argv[index]; index++)
|
||||
{
|
||||
if (is_short_option(argv[index]))
|
||||
return index;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int find_long_index(char * const *argv)
|
||||
{
|
||||
for (int index = 0; argv[index]; index++)
|
||||
{
|
||||
if (is_long_option(argv[index]))
|
||||
return index;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parse_short(const char *optstring, char * const *argv)
|
||||
{
|
||||
char arg = argv[0][1];
|
||||
if (arg == ':')
|
||||
return '?';
|
||||
|
||||
const char *opt = strchr(optstring, arg);
|
||||
if (!opt)
|
||||
return '?';
|
||||
|
||||
bool multiple_opt = argv[0][2];
|
||||
bool takes_arg = opt[1] == ':';
|
||||
if (multiple_opt && takes_arg) // Makes little sense to allow.
|
||||
return '?';
|
||||
|
||||
if (takes_arg)
|
||||
{
|
||||
optarg = argv[1];
|
||||
optind += 2;
|
||||
return optarg ? opt[0] : '?';
|
||||
}
|
||||
else if (multiple_opt)
|
||||
{
|
||||
memmove(&argv[0][1], &argv[0][2], strlen(&argv[0][2]) + 1);
|
||||
return opt[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
optind++;
|
||||
return opt[0];
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_long(const struct option *longopts, char * const *argv)
|
||||
{
|
||||
const struct option *opt = NULL;
|
||||
for (size_t indice = 0; longopts[indice].name; indice++)
|
||||
{
|
||||
if (strcmp(longopts[indice].name, &argv[0][2]) == 0)
|
||||
{
|
||||
opt = &longopts[indice];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!opt)
|
||||
return '?';
|
||||
|
||||
// getopt_long has an "optional" arg, but we don't bother with that.
|
||||
if (opt->has_arg && !argv[1])
|
||||
return '?';
|
||||
|
||||
if (opt->has_arg)
|
||||
{
|
||||
optarg = argv[1];
|
||||
optind += 2;
|
||||
}
|
||||
else
|
||||
optind++;
|
||||
|
||||
if (opt->flag)
|
||||
{
|
||||
*opt->flag = opt->val;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return opt->val;
|
||||
}
|
||||
|
||||
static void shuffle_block(char **begin, char **last, char **end)
|
||||
{
|
||||
ptrdiff_t len = last - begin;
|
||||
const char *tmp[len];
|
||||
memcpy(tmp, begin, sizeof(tmp));
|
||||
memmove(begin, last, (end - last) * sizeof(char*));
|
||||
memcpy(end - len, tmp, sizeof(tmp));
|
||||
}
|
||||
|
||||
int getopt_long(int argc, char *argv[],
|
||||
const char *optstring, const struct option *longopts, int *longindex)
|
||||
{
|
||||
(void)longindex;
|
||||
if (argc == 1)
|
||||
return -1;
|
||||
|
||||
if (optind == 0)
|
||||
optind = 1;
|
||||
|
||||
int short_index = find_short_index(&argv[optind]);
|
||||
int long_index = find_long_index(&argv[optind]);
|
||||
|
||||
// We're done here.
|
||||
if (short_index == -1 && long_index == -1)
|
||||
return -1;
|
||||
|
||||
// Reorder argv so that non-options come last.
|
||||
// Non-POSIXy, but that's what getopt does by default.
|
||||
if ((short_index > 0) && ((short_index < long_index) || (long_index == -1)))
|
||||
{
|
||||
shuffle_block(&argv[optind], &argv[optind + short_index], &argv[argc]);
|
||||
short_index = 0;
|
||||
}
|
||||
else if ((long_index > 0) && ((long_index < short_index) || (short_index == -1)))
|
||||
{
|
||||
shuffle_block(&argv[optind], &argv[optind + long_index], &argv[argc]);
|
||||
long_index = 0;
|
||||
}
|
||||
|
||||
assert(short_index == 0 || long_index == 0);
|
||||
|
||||
if (short_index == 0)
|
||||
return parse_short(optstring, &argv[optind]);
|
||||
else if (long_index == 0)
|
||||
return parse_long(longopts, &argv[optind]);
|
||||
else
|
||||
return '?';
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/* 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 __SSNES_GETOPT_H
|
||||
#define __SSNES_GETOPT_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
// Custom implementation of the GNU getopt_long for portability.
|
||||
// Not designed to be fully compatible,
|
||||
// but compatible with the features SSNES uses.
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
#include <getopt.h>
|
||||
#else
|
||||
// Avoid possible naming collitions during link since we prefer to use the actual name.
|
||||
#define getopt_long(argc, argv, optstring, longopts, longindex) __getopt_long_ssnes(argc, argv, optstring, longopts, longindex)
|
||||
|
||||
struct option
|
||||
{
|
||||
const char *name;
|
||||
int has_arg;
|
||||
int *flag;
|
||||
int val;
|
||||
};
|
||||
|
||||
// argv[] is declared with char * const argv[] in GNU,
|
||||
// but this makes no sense, as non-POSIX getopt_long mutates argv (non-opts are moved to the end).
|
||||
int getopt_long(int argc, char *argv[],
|
||||
const char *optstring, const struct option *longopts, int *longindex);
|
||||
extern char *optarg;
|
||||
extern int optind, opterr, optopt;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -24,6 +24,17 @@
|
||||
#include <stdint.h>
|
||||
#include "dynamic.h"
|
||||
#include "general.h"
|
||||
#include "sdlwrap.h"
|
||||
#include "gfx_common.h"
|
||||
|
||||
#ifdef HAVE_FREETYPE
|
||||
#include "fonts.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
#define PY_STATE_OMIT_DECLARATION
|
||||
#include "py_state/py_state.h"
|
||||
#endif
|
||||
|
||||
static bool g_input_dead = true;
|
||||
static bool g_video_dead = true;
|
||||
@@ -37,7 +48,7 @@ typedef struct
|
||||
void *handle;
|
||||
} input_ext_t;
|
||||
|
||||
static void* input_ext_init(void)
|
||||
static void *input_ext_init(void)
|
||||
{
|
||||
g_input_dead = false;
|
||||
return calloc(1, sizeof(input_ext_t));
|
||||
@@ -193,7 +204,7 @@ static bool video_ext_frame(void *data, const void *frame, unsigned width, unsig
|
||||
return ext->driver->frame(ext->handle, frame, width, height, pitch, msg);
|
||||
}
|
||||
|
||||
static void* setup_input(ext_t *ext, const ssnes_input_driver_t *driver)
|
||||
static void *setup_input(ext_t *ext, const ssnes_input_driver_t *driver)
|
||||
{
|
||||
int joypad_index[5];
|
||||
for (unsigned i = 0; i < 5; i++)
|
||||
@@ -232,6 +243,30 @@ static bool setup_video(ext_t *ext, const video_info_t *video, const input_drive
|
||||
else if ((type == SSNES_SHADER_BSNES || type == SSNES_SHADER_AUTO) && *g_settings.video.bsnes_shader_path)
|
||||
xml_shader = g_settings.video.bsnes_shader_path;
|
||||
|
||||
int font_color_r = g_settings.video.msg_color_r * 255;
|
||||
int font_color_g = g_settings.video.msg_color_g * 255;
|
||||
int font_color_b = g_settings.video.msg_color_b * 255;
|
||||
font_color_r = font_color_r > 255 ? 255 : (font_color_r < 0 ? 0 : font_color_r);
|
||||
font_color_g = font_color_g > 255 ? 255 : (font_color_g < 0 ? 0 : font_color_g);
|
||||
font_color_b = font_color_b > 255 ? 255 : (font_color_b < 0 ? 0 : font_color_b);
|
||||
|
||||
const char *font = NULL;
|
||||
if (g_settings.video.font_enable)
|
||||
{
|
||||
#ifdef HAVE_FREETYPE
|
||||
if (*g_settings.video.font_path)
|
||||
font = g_settings.video.font_path;
|
||||
else
|
||||
font = font_renderer_get_default_font();
|
||||
#else
|
||||
font = *g_settings.video.font_path ?
|
||||
g_settings.video.font_path : NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
char title_buf[128];
|
||||
gfx_window_title_reset();
|
||||
gfx_window_title(title_buf, sizeof(title_buf));
|
||||
|
||||
ssnes_video_info_t info = {
|
||||
.width = video->width,
|
||||
@@ -245,8 +280,16 @@ static bool setup_video(ext_t *ext, const video_info_t *video, const input_drive
|
||||
.color_format = video->rgb32 ? SSNES_COLOR_FORMAT_ARGB8888 : SSNES_COLOR_FORMAT_XRGB1555,
|
||||
.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
|
||||
.ttf_font = font,
|
||||
.ttf_font_size = g_settings.video.font_size,
|
||||
.ttf_font_color = (font_color_r << 16) | (font_color_g << 8) | (font_color_b << 0),
|
||||
.title_hint = title_buf,
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
.python_state_new = py_state_new,
|
||||
.python_state_get = py_state_get,
|
||||
.python_state_free = py_state_free,
|
||||
#endif
|
||||
};
|
||||
|
||||
const ssnes_input_driver_t *input_driver = NULL;
|
||||
@@ -263,7 +306,7 @@ static bool setup_video(ext_t *ext, const video_info_t *video, const input_drive
|
||||
return true;
|
||||
}
|
||||
|
||||
static void* video_ext_init(const video_info_t *video, const input_driver_t **input, void **input_data)
|
||||
static void *video_ext_init(const video_info_t *video, const input_driver_t **input, void **input_data)
|
||||
{
|
||||
ext_t *ext = calloc(1, sizeof(*ext));
|
||||
if (!ext)
|
||||
|
||||
+44
-17
@@ -6,6 +6,8 @@
|
||||
#ifndef __SSNES_VIDEO_DRIVER_H
|
||||
#define __SSNES_VIDEO_DRIVER_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -22,10 +24,10 @@ extern "C" {
|
||||
#define SSNES_API_CALLTYPE
|
||||
#endif
|
||||
|
||||
#define SSNES_GRAPHICS_API_VERSION 1
|
||||
#define SSNES_GRAPHICS_API_VERSION 2
|
||||
|
||||
// Since we don't want to rely on C++ or C99 for a proper boolean type,
|
||||
// and make sure return semantics are perfectly clear ... ;)
|
||||
// make sure return semantics are perfectly clear ... ;)
|
||||
|
||||
#ifndef SSNES_OK
|
||||
#define SSNES_OK 1
|
||||
@@ -46,6 +48,23 @@ extern "C" {
|
||||
#define SSNES_COLOR_FORMAT_XRGB1555 0
|
||||
#define SSNES_COLOR_FORMAT_ARGB8888 1
|
||||
|
||||
#define SSNES_INPUT_SCALE_BASE 256
|
||||
|
||||
typedef struct py_state py_state_t;
|
||||
|
||||
// Create a new runtime for Python.
|
||||
// py_script: The python script to be loaded. If is_file is true, this will be the full path to a file.
|
||||
// If false, it will be an UTF-8 encoded string of the script.
|
||||
// is_file: Tells if py_script is the path to a script, or a script itself.
|
||||
// py_class: name of the class being instantiated.
|
||||
typedef py_state_t *(*python_state_new_cb)(const char *py_script, unsigned is_file, const char *py_class);
|
||||
// Grabs a value from the Python runtime.
|
||||
// id: The uniform (class method) to be called.
|
||||
// frame_count: Passes frame_count as an argument to the script.
|
||||
typedef float (*python_state_get_cb)(py_state_t *handle, const char *id, unsigned frame_count);
|
||||
// Frees the runtime.
|
||||
typedef void (*python_state_free_cb)(py_state_t *handle);
|
||||
|
||||
typedef struct ssnes_video_info
|
||||
{
|
||||
// Width of window.
|
||||
@@ -79,12 +98,9 @@ typedef struct ssnes_video_info
|
||||
|
||||
// input_scale defines the maximum size of the picture that will
|
||||
// ever be used with the frame callback.
|
||||
// The maximum resolution is a multiple of 256x256 size,
|
||||
// The maximum resolution is a multiple of 256x256 size (SSNES_INPUT_SCALE_BASE),
|
||||
// so an input scale of 2
|
||||
// means you should allocate a texture or of 512x512.
|
||||
// It is normally set to 2 due to the possibility of
|
||||
// pseudo-hires on the SNES.
|
||||
// CPU filters or otherwise might increase this value.
|
||||
unsigned input_scale;
|
||||
|
||||
// Defines the coloring format used of the input frame.
|
||||
@@ -108,7 +124,17 @@ typedef struct ssnes_video_info
|
||||
// Can be disregarded.
|
||||
const char *ttf_font;
|
||||
unsigned ttf_font_size;
|
||||
unsigned ttf_font_color; // Font color, in format ARGB8888. Alpha should be disregarded.
|
||||
|
||||
// A title that should be displayed in the title bar of the window.
|
||||
const char *title_hint;
|
||||
|
||||
// Functions to peek into the python runtime for shaders.
|
||||
// Check typedefs above for explanation.
|
||||
// These may be NULL if SSNES is not built with Python support.
|
||||
python_state_new_cb python_state_new;
|
||||
python_state_get_cb python_state_get;
|
||||
python_state_free_cb python_state_free;
|
||||
} ssnes_video_info_t;
|
||||
|
||||
// Some convenience macros.
|
||||
@@ -119,7 +145,7 @@ typedef struct ssnes_video_info
|
||||
|
||||
// I hope no joypad will ever have this many buttons or axes ... ;)
|
||||
// If joykey is this value, do not check that button.
|
||||
#define SSNES_NO_AXIS ((unsigned short)0xFFFFU)
|
||||
#define SSNES_NO_AXIS (0xFFFFFFFFU)
|
||||
#define SSNES_NO_BTN ((unsigned short)0xFFFFU)
|
||||
|
||||
// Masks to test on joykey which hat direction is to be tested for.
|
||||
@@ -155,8 +181,9 @@ struct ssnes_keybind
|
||||
// last frame and current frame in the Y-axis.
|
||||
int analog_y;
|
||||
|
||||
// Keyboard key. The key values use the SDL keysyms,
|
||||
// Keyboard key. The key values use the SDL 1.2 keysyms,
|
||||
// which probably need to be transformed to the native format.
|
||||
// The actual keysyms SSNES uses are found in input/keysym.h.
|
||||
unsigned short key;
|
||||
|
||||
// Joypad key. Joypad POV (hats) are embedded into this key as well.
|
||||
@@ -177,20 +204,20 @@ typedef struct ssnes_input_driver
|
||||
// The range of this is [0, 1],
|
||||
// where 0 means any displacement will register,
|
||||
// and 1 means the axis has to be pressed all the way to register.
|
||||
void* (*init)(const int joypad_index[5], float axis_threshold);
|
||||
void *(*init)(const int joypad_index[5], float axis_threshold);
|
||||
|
||||
// Polls input. Called once every frame.
|
||||
void (*poll)(void* data);
|
||||
void (*poll)(void *data);
|
||||
|
||||
// Queries input state for a certain key on a certain player.
|
||||
// Players are 1 - 5.
|
||||
// For digital inputs, pressed key is 1, not pressed key is 0.
|
||||
// Analog values have same range as a signed 16-bit integer.
|
||||
int (*input_state)(void* data, const struct ssnes_keybind *bind,
|
||||
int (*input_state)(void *data, const struct ssnes_keybind *bind,
|
||||
unsigned player);
|
||||
|
||||
// Frees the input struct.
|
||||
void (*free)(void* data);
|
||||
void (*free)(void *data);
|
||||
|
||||
// Human readable indentification string.
|
||||
const char *ident;
|
||||
@@ -204,7 +231,7 @@ typedef struct ssnes_video_driver
|
||||
// Should the video driver request that a certain input driver is used,
|
||||
// it is possible to set the driver to *input.
|
||||
// If no certain driver is desired, set *input to NULL.
|
||||
void* (*init)(const ssnes_video_info_t *video,
|
||||
void *(*init)(const ssnes_video_info_t *video,
|
||||
const ssnes_input_driver_t **input);
|
||||
|
||||
// Updates frame on the screen.
|
||||
@@ -214,13 +241,13 @@ typedef struct ssnes_video_driver
|
||||
//
|
||||
// When msg is non-NULL,
|
||||
// it's a message that should be displayed to the user.
|
||||
int (*frame)(void* data, const void* frame,
|
||||
int (*frame)(void *data, const void *frame,
|
||||
unsigned width, unsigned height, unsigned pitch, const char *msg);
|
||||
|
||||
// Requests nonblocking operation.
|
||||
// True = VSync is turned off.
|
||||
// False = VSync is turned on.
|
||||
void (*set_nonblock_state)(void* data, int toggle);
|
||||
void (*set_nonblock_state)(void *data, int toggle);
|
||||
|
||||
// This must return false when the user exits the emulator.
|
||||
int (*alive)(void *data);
|
||||
@@ -229,13 +256,13 @@ typedef struct ssnes_video_driver
|
||||
int (*focus)(void *data);
|
||||
|
||||
// Frees the video driver.
|
||||
void (*free)(void* data);
|
||||
void (*free)(void *data);
|
||||
|
||||
// A human-readable identification of the video driver.
|
||||
const char *ident;
|
||||
|
||||
// Needs to be defined to SSNES_GRAPHICS_API_VERSION.
|
||||
// This is used to detect API mismatches.
|
||||
// This is used to detect API/ABI mismatches.
|
||||
int api_version;
|
||||
} ssnes_video_driver_t;
|
||||
|
||||
|
||||
+41
-5
@@ -16,10 +16,10 @@
|
||||
*/
|
||||
|
||||
#include "fonts.h"
|
||||
#include "file.h"
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
@@ -79,12 +79,16 @@ void font_renderer_msg(font_renderer_t *handle, const char *msg, struct font_out
|
||||
if (!err)
|
||||
{
|
||||
struct font_output *tmp = calloc(1, sizeof(*tmp));
|
||||
assert(tmp);
|
||||
|
||||
//fprintf(stderr, "Char: %c, off_x: %d, off_y: %d, bmp_left: %d, bmp_top: %d\n", msg[i], off_x, off_y, slot->bitmap_left, slot->bitmap_top);
|
||||
if (!tmp)
|
||||
break;
|
||||
|
||||
tmp->output = malloc(slot->bitmap.pitch * slot->bitmap.rows);
|
||||
assert(tmp->output);
|
||||
if (!tmp->output)
|
||||
{
|
||||
free(tmp);
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(tmp->output, slot->bitmap.buffer, slot->bitmap.pitch * slot->bitmap.rows);
|
||||
|
||||
tmp->width = slot->bitmap.width;
|
||||
@@ -128,3 +132,35 @@ void font_renderer_free(font_renderer_t *handle)
|
||||
if (handle->lib)
|
||||
FT_Done_FreeType(handle->lib);
|
||||
}
|
||||
|
||||
// Not the cleanest way to do things for sure, but should hopefully work ... :)
|
||||
|
||||
#if defined(_WIN32)
|
||||
static const char *font_paths[] = {
|
||||
"C:\\Windows\\Fonts\\consola.ttf",
|
||||
"C:\\Windows\\Fonts\\verdana.ttf",
|
||||
#elif defined(__APPLE__)
|
||||
static const char *font_paths[] = {
|
||||
"/Library/Fonts/Microsoft/Candara.ttf",
|
||||
"/Library/Fonts/Verdana.ttf",
|
||||
"/Library/Fonts/Tahoma.ttf",
|
||||
#else
|
||||
static const char *font_paths[] = {
|
||||
"/usr/share/fonts/TTF/DejaVuSansMono.ttf",
|
||||
"/usr/share/fonts/TTF/DejaVuSans.ttf",
|
||||
#endif
|
||||
"osd-font.ttf", // Magic font to search for, useful for distribution.
|
||||
};
|
||||
|
||||
// Highly OS/platform dependent.
|
||||
const char *font_renderer_get_default_font(void)
|
||||
{
|
||||
for (unsigned i = 0; i < sizeof(font_paths) / sizeof(font_paths[0]); i++)
|
||||
{
|
||||
if (path_file_exists(font_paths[i]))
|
||||
return font_paths[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+7
-3
@@ -25,10 +25,10 @@ typedef struct font_renderer font_renderer_t;
|
||||
|
||||
struct font_output
|
||||
{
|
||||
uint8_t *output;
|
||||
uint8_t *output; // 8-bit intensity.
|
||||
unsigned width, height, pitch;
|
||||
int off_x, off_y;
|
||||
struct font_output *next;
|
||||
struct font_output *next; // linked list.
|
||||
};
|
||||
|
||||
struct font_output_list
|
||||
@@ -37,8 +37,12 @@ struct font_output_list
|
||||
};
|
||||
|
||||
font_renderer_t *font_renderer_new(const char *font_path, unsigned font_size);
|
||||
void font_renderer_msg(font_renderer_t *handle, const char *msg, struct font_output_list *output);
|
||||
void font_renderer_msg(font_renderer_t *handle, const char *msg,
|
||||
struct font_output_list *output);
|
||||
|
||||
void font_renderer_free_output(struct font_output_list *list);
|
||||
void font_renderer_free(font_renderer_t *handle);
|
||||
|
||||
const char *font_renderer_get_default_font(void);
|
||||
|
||||
#endif
|
||||
+13
-22
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "gfx_common.h"
|
||||
#include "general.h"
|
||||
#include "../general.h"
|
||||
#include <sys/time.h>
|
||||
|
||||
static float tv_to_fps(const struct timeval *tv, const struct timeval *new_tv, int frames)
|
||||
@@ -32,6 +32,17 @@ void gfx_window_title_reset(void)
|
||||
gl_frames = 0;
|
||||
}
|
||||
|
||||
#ifdef __CELLOS_LV2__
|
||||
#include <sys/sys_time.h>
|
||||
static void gettimeofday(struct timeval *val, void *dummy)
|
||||
{
|
||||
(void)dummy;
|
||||
uint64_t usec = sys_time_get_system_time();
|
||||
val->tv_sec = usec / 1000000;
|
||||
val->tv_usec = usec % 1000000;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool gfx_window_title(char *buf, size_t size)
|
||||
{
|
||||
static struct timeval tv;
|
||||
@@ -84,7 +95,7 @@ void gfx_set_dwm(void)
|
||||
dwmlib = dylib_load("dwmapi.dll");
|
||||
if (!dwmlib)
|
||||
{
|
||||
SSNES_LOG("Did not find dwmapi.dll");
|
||||
SSNES_LOG("Did not find dwmapi.dll.\n");
|
||||
return;
|
||||
}
|
||||
atexit(gfx_dwm_shutdown);
|
||||
@@ -112,23 +123,3 @@ void gfx_set_dwm(void)
|
||||
}
|
||||
#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
|
||||
@@ -28,8 +28,4 @@ void gfx_window_title_reset(void);
|
||||
void gfx_set_dwm(void);
|
||||
#endif
|
||||
|
||||
#if !defined(__APPLE__) && !defined(_WIN32)
|
||||
void gfx_get_window_size(unsigned *width, unsigned *height);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -16,14 +16,14 @@
|
||||
*/
|
||||
|
||||
|
||||
#include "driver.h"
|
||||
#include "../driver.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include "libsnes.hpp"
|
||||
#include "../libsnes.hpp"
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <string.h>
|
||||
#include "general.h"
|
||||
#include "../general.h"
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
|
||||
#include "gl_common.h"
|
||||
#include "gfx_common.h"
|
||||
#include "sdlwrap.h"
|
||||
#include "../strl.h"
|
||||
|
||||
#define NO_SDL_GLEXT
|
||||
#include "SDL.h"
|
||||
@@ -76,6 +78,13 @@ static const GLfloat tex_coords[] = {
|
||||
1, 1
|
||||
};
|
||||
|
||||
static const GLfloat white_color[] = {
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
};
|
||||
|
||||
|
||||
#ifdef HAVE_FBO
|
||||
#ifdef _WIN32
|
||||
@@ -122,12 +131,19 @@ static inline bool load_gl_proc(void) { return true; }
|
||||
|
||||
#define MAX_SHADERS 16
|
||||
|
||||
#if defined(HAVE_XML) || defined(HAVE_CG)
|
||||
#define TEXTURES 8
|
||||
#else
|
||||
#define TEXTURES 1
|
||||
#endif
|
||||
#define TEXTURES_MASK (TEXTURES - 1)
|
||||
|
||||
typedef struct gl
|
||||
{
|
||||
bool vsync;
|
||||
GLuint texture[2];
|
||||
GLuint texture[TEXTURES];
|
||||
unsigned tex_index; // For use with PREV.
|
||||
struct gl_tex_info prev_info;
|
||||
struct gl_tex_info prev_info[TEXTURES];
|
||||
GLuint tex_filter;
|
||||
|
||||
void *empty_buf;
|
||||
@@ -156,8 +172,8 @@ typedef struct gl
|
||||
unsigned win_height;
|
||||
unsigned vp_width, vp_out_width;
|
||||
unsigned vp_height, vp_out_height;
|
||||
unsigned last_width[2];
|
||||
unsigned last_height[2];
|
||||
unsigned last_width[TEXTURES];
|
||||
unsigned last_height[TEXTURES];
|
||||
unsigned tex_w, tex_h;
|
||||
GLfloat tex_coords[8];
|
||||
#ifdef HAVE_FBO
|
||||
@@ -171,6 +187,12 @@ typedef struct gl
|
||||
#ifdef HAVE_FREETYPE
|
||||
font_renderer_t *font;
|
||||
GLuint font_tex;
|
||||
int font_tex_w, font_tex_h;
|
||||
void *font_tex_empty_buf;
|
||||
char font_last_msg[256];
|
||||
int font_last_width, font_last_height;
|
||||
GLfloat font_color[16];
|
||||
GLfloat font_color_dark[16];
|
||||
#endif
|
||||
|
||||
} gl_t;
|
||||
@@ -331,9 +353,16 @@ static void gl_shader_scale(unsigned index, struct gl_fbo_scale *scale)
|
||||
static inline void gl_init_font(gl_t *gl, const char *font_path, unsigned font_size)
|
||||
{
|
||||
#ifdef HAVE_FREETYPE
|
||||
if (strlen(font_path) > 0)
|
||||
if (!g_settings.video.font_enable)
|
||||
return;
|
||||
|
||||
const char *path = font_path;
|
||||
if (!*path)
|
||||
path = font_renderer_get_default_font();
|
||||
|
||||
if (path)
|
||||
{
|
||||
gl->font = font_renderer_new(font_path, font_size);
|
||||
gl->font = font_renderer_new(path, font_size);
|
||||
if (gl->font)
|
||||
{
|
||||
glGenTextures(1, &gl->font_tex);
|
||||
@@ -347,6 +376,28 @@ static inline void gl_init_font(gl_t *gl, const char *font_path, unsigned font_s
|
||||
else
|
||||
SSNES_WARN("Couldn't init font renderer with font \"%s\"...\n", font_path);
|
||||
}
|
||||
else
|
||||
SSNES_LOG("Did not find default font.\n");
|
||||
|
||||
for (unsigned i = 0; i < 4; i++)
|
||||
{
|
||||
gl->font_color[4 * i + 0] = g_settings.video.msg_color_r;
|
||||
gl->font_color[4 * i + 1] = g_settings.video.msg_color_g;
|
||||
gl->font_color[4 * i + 2] = g_settings.video.msg_color_b;
|
||||
gl->font_color[4 * i + 3] = 1.0;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 4; i++)
|
||||
{
|
||||
for (unsigned j = 0; j < 3; j++)
|
||||
gl->font_color_dark[4 * i + j] = 0.3 * gl->font_color[4 * i + j];
|
||||
gl->font_color_dark[4 * i + 3] = 1.0;
|
||||
}
|
||||
|
||||
#else
|
||||
(void)gl;
|
||||
(void)font_path;
|
||||
(void)font_size;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -522,7 +573,7 @@ static void gl_init_fbo(gl_t *gl, unsigned width, unsigned height)
|
||||
error:
|
||||
glDeleteTextures(gl->fbo_pass, gl->fbo_texture);
|
||||
pglDeleteFramebuffers(gl->fbo_pass, gl->fbo);
|
||||
SSNES_WARN("Failed to set up FBO. Two-pass shading will not work.\n");
|
||||
SSNES_ERR("Failed to set up frame buffer objects. Multi-pass shading will not work.\n");
|
||||
#else
|
||||
(void)gl;
|
||||
(void)width;
|
||||
@@ -537,7 +588,12 @@ static inline void gl_deinit_font(gl_t *gl)
|
||||
{
|
||||
font_renderer_free(gl->font);
|
||||
glDeleteTextures(1, &gl->font_tex);
|
||||
|
||||
if (gl->font_tex_empty_buf)
|
||||
free(gl->font_tex_empty_buf);
|
||||
}
|
||||
#else
|
||||
(void)gl;
|
||||
#endif
|
||||
}
|
||||
////////////
|
||||
@@ -604,63 +660,203 @@ static void set_viewport(gl_t *gl, unsigned width, unsigned height, bool force_f
|
||||
//SSNES_LOG("Setting viewport @ %ux%u\n", width, height);
|
||||
}
|
||||
|
||||
#ifdef HAVE_FREETYPE
|
||||
|
||||
// Somewhat overwhelming code just to render some damn fonts.
|
||||
// We aim to use NPOT textures for compatibility with old and shitty cards.
|
||||
// Also, we want to avoid reallocating a texture for each glyph (performance dips), so we
|
||||
// contruct the whole texture using one call, and copy straight to it with
|
||||
// glTexSubImage.
|
||||
|
||||
struct font_rect
|
||||
{
|
||||
int x, y;
|
||||
int width, height;
|
||||
int pot_width, pot_height;
|
||||
};
|
||||
|
||||
static void calculate_msg_geometry(const struct font_output *head, struct font_rect *rect)
|
||||
{
|
||||
int x_min = head->off_x;
|
||||
int x_max = head->off_x + head->width;
|
||||
int y_min = head->off_y;
|
||||
int y_max = head->off_y + head->height;
|
||||
|
||||
while ((head = head->next))
|
||||
{
|
||||
int left = head->off_x;
|
||||
int right = head->off_x + head->width;
|
||||
int bottom = head->off_y;
|
||||
int top = head->off_y + head->height;
|
||||
|
||||
if (left < x_min)
|
||||
x_min = left;
|
||||
if (right > x_max)
|
||||
x_max = right;
|
||||
|
||||
if (bottom < y_min)
|
||||
y_min = bottom;
|
||||
if (top > y_max)
|
||||
y_max = top;
|
||||
}
|
||||
|
||||
rect->x = x_min;
|
||||
rect->y = y_min;
|
||||
rect->width = x_max - x_min;
|
||||
rect->height = y_max - y_min;
|
||||
}
|
||||
|
||||
static void adjust_power_of_two(gl_t *gl, struct font_rect *geom)
|
||||
{
|
||||
// Some systems really hate NPOT textures.
|
||||
geom->pot_width = next_pow2(geom->width);
|
||||
geom->pot_height = next_pow2(geom->height);
|
||||
|
||||
if ((geom->pot_width > gl->font_tex_w) || (geom->pot_height > gl->font_tex_h))
|
||||
{
|
||||
gl->font_tex_empty_buf = realloc(gl->font_tex_empty_buf, geom->pot_width * geom->pot_height);
|
||||
memset(gl->font_tex_empty_buf, 0, geom->pot_width * geom->pot_height);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 8);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, geom->pot_width);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_INTENSITY8, geom->pot_width, geom->pot_height,
|
||||
0, GL_LUMINANCE, GL_UNSIGNED_BYTE, gl->font_tex_empty_buf);
|
||||
|
||||
gl->font_tex_w = geom->pot_width;
|
||||
gl->font_tex_h = geom->pot_height;
|
||||
}
|
||||
}
|
||||
|
||||
// Old style "blitting", so we can render all the fonts in one go.
|
||||
// TODO: Is it possible that fonts could overlap if we blit without alpha blending?
|
||||
static void blit_fonts(gl_t *gl, const struct font_output *head, const struct font_rect *geom)
|
||||
{
|
||||
// Clear out earlier fonts.
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 8);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, gl->font_tex_w);
|
||||
glTexSubImage2D(GL_TEXTURE_2D,
|
||||
0, 0, 0, gl->font_tex_w, gl->font_tex_h,
|
||||
GL_LUMINANCE, GL_UNSIGNED_BYTE, gl->font_tex_empty_buf);
|
||||
|
||||
while (head)
|
||||
{
|
||||
// head has top-left oriented coords.
|
||||
int x = head->off_x - geom->x;
|
||||
int y = head->off_y - geom->y;
|
||||
y = gl->font_tex_h - head->height - y - 1;
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(head->pitch));
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, head->pitch);
|
||||
glTexSubImage2D(GL_TEXTURE_2D,
|
||||
0, x, y, head->width, head->height,
|
||||
GL_LUMINANCE, GL_UNSIGNED_BYTE, head->output);
|
||||
|
||||
head = head->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void calculate_font_coords(gl_t *gl,
|
||||
GLfloat font_vertex[8], GLfloat font_vertex_dark[8], GLfloat font_tex_coords[8])
|
||||
{
|
||||
GLfloat lx = g_settings.video.msg_pos_x;
|
||||
GLfloat hx = (GLfloat)gl->font_last_width / gl->vp_width + lx;
|
||||
GLfloat ly = g_settings.video.msg_pos_y;
|
||||
GLfloat hy = (GLfloat)gl->font_last_height / gl->vp_height + ly;
|
||||
|
||||
font_vertex[0] = lx;
|
||||
font_vertex[1] = ly;
|
||||
font_vertex[2] = lx;
|
||||
font_vertex[3] = hy;
|
||||
font_vertex[4] = hx;
|
||||
font_vertex[5] = hy;
|
||||
font_vertex[6] = hx;
|
||||
font_vertex[7] = ly;
|
||||
|
||||
GLfloat shift_x = 2.0f / gl->vp_width;
|
||||
GLfloat shift_y = 2.0f / gl->vp_height;
|
||||
for (unsigned i = 0; i < 4; i++)
|
||||
{
|
||||
font_vertex_dark[2 * i + 0] = font_vertex[2 * i + 0] - shift_x;
|
||||
font_vertex_dark[2 * i + 1] = font_vertex[2 * i + 1] - shift_y;
|
||||
}
|
||||
|
||||
lx = 0.0f;
|
||||
hx = (GLfloat)gl->font_last_width / gl->font_tex_w;
|
||||
ly = 1.0f - (GLfloat)gl->font_last_height / gl->font_tex_h;
|
||||
hy = 1.0f;
|
||||
|
||||
font_tex_coords[0] = lx;
|
||||
font_tex_coords[1] = hy;
|
||||
font_tex_coords[2] = lx;
|
||||
font_tex_coords[3] = ly;
|
||||
font_tex_coords[4] = hx;
|
||||
font_tex_coords[5] = ly;
|
||||
font_tex_coords[6] = hx;
|
||||
font_tex_coords[7] = hy;
|
||||
}
|
||||
|
||||
static void gl_render_msg(gl_t *gl, const char *msg)
|
||||
{
|
||||
#ifdef HAVE_FREETYPE
|
||||
if (!gl->font)
|
||||
return;
|
||||
|
||||
GLfloat font_vertex[8];
|
||||
GLfloat font_vertex_dark[8];
|
||||
GLfloat font_tex_coords[8];
|
||||
|
||||
// Deactivate custom shaders. Enable the font texture.
|
||||
gl_shader_use(0);
|
||||
set_viewport(gl, gl->win_width, gl->win_height, false);
|
||||
glBindTexture(GL_TEXTURE_2D, gl->font_tex);
|
||||
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), font_vertex);
|
||||
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), tex_coords); // Use the static one (uses whole texture).
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, font_tex_coords);
|
||||
|
||||
// Need blending.
|
||||
// Using fixed function pipeline here since we cannot guarantee presence of shaders (would be kinda overkill anyways).
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
struct font_output_list out;
|
||||
font_renderer_msg(gl->font, msg, &out);
|
||||
struct font_output *head = out.head;
|
||||
|
||||
while (head != NULL)
|
||||
// If we get the same message, there's obviously no need to render fonts again ...
|
||||
if (strcmp(gl->font_last_msg, msg) != 0)
|
||||
{
|
||||
GLfloat lx = (GLfloat)head->off_x / gl->vp_width + g_settings.video.msg_pos_x;
|
||||
GLfloat hx = (GLfloat)(head->off_x + head->width) / gl->vp_width + g_settings.video.msg_pos_x;
|
||||
GLfloat ly = (GLfloat)head->off_y / gl->vp_height + g_settings.video.msg_pos_y;
|
||||
GLfloat hy = (GLfloat)(head->off_y + head->height) / gl->vp_height + g_settings.video.msg_pos_y;
|
||||
font_renderer_msg(gl->font, msg, &out);
|
||||
struct font_output *head = out.head;
|
||||
|
||||
font_vertex[0] = lx;
|
||||
font_vertex[1] = ly;
|
||||
font_vertex[2] = lx;
|
||||
font_vertex[3] = hy;
|
||||
font_vertex[4] = hx;
|
||||
font_vertex[5] = hy;
|
||||
font_vertex[6] = hx;
|
||||
font_vertex[7] = ly;
|
||||
struct font_rect geom;
|
||||
calculate_msg_geometry(head, &geom);
|
||||
adjust_power_of_two(gl, &geom);
|
||||
blit_fonts(gl, head, &geom);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(head->pitch));
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, head->pitch);
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0, GL_INTENSITY8, head->width, head->height, 0, GL_LUMINANCE,
|
||||
GL_UNSIGNED_BYTE, head->output);
|
||||
font_renderer_free_output(&out);
|
||||
strlcpy(gl->font_last_msg, msg, sizeof(gl->font_last_msg));
|
||||
|
||||
head = head->next;
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
gl->font_last_width = geom.width;
|
||||
gl->font_last_height = geom.height;
|
||||
}
|
||||
font_renderer_free_output(&out);
|
||||
calculate_font_coords(gl, font_vertex, font_vertex_dark, font_tex_coords);
|
||||
|
||||
glVertexPointer(2, GL_FLOAT, 0, font_vertex_dark);
|
||||
glColorPointer(4, GL_FLOAT, 0, gl->font_color_dark);
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
glVertexPointer(2, GL_FLOAT, 0, font_vertex);
|
||||
glColorPointer(4, GL_FLOAT, 0, gl->font_color);
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
|
||||
// Go back to old rendering path.
|
||||
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->tex_coords);
|
||||
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes_flipped);
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, gl->tex_coords);
|
||||
glVertexPointer(2, GL_FLOAT, 0, vertexes_flipped);
|
||||
glColorPointer(4, GL_FLOAT, 0, white_color);
|
||||
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
static void gl_render_msg(gl_t *gl, const char *msg)
|
||||
{
|
||||
(void)gl;
|
||||
(void)msg;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void set_lut_texture_coords(const GLfloat *coords)
|
||||
{
|
||||
@@ -668,7 +864,7 @@ static inline void set_lut_texture_coords(const GLfloat *coords)
|
||||
// For texture images.
|
||||
pglClientActiveTexture(GL_TEXTURE1);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), coords);
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, coords);
|
||||
pglClientActiveTexture(GL_TEXTURE0);
|
||||
#else
|
||||
(void)coords;
|
||||
@@ -685,49 +881,21 @@ static inline void set_texture_coords(GLfloat *coords, GLfloat xamt, GLfloat yam
|
||||
|
||||
static void check_window(gl_t *gl)
|
||||
{
|
||||
SDL_Event event;
|
||||
bool quit, resize;
|
||||
|
||||
// Search for events we care about ...
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_QUIT:
|
||||
gl->quitting = true;
|
||||
break;
|
||||
sdlwrap_check_window(&quit,
|
||||
&resize, &gl->win_width, &gl->win_height,
|
||||
gl->frame_count);
|
||||
|
||||
case SDL_VIDEORESIZE:
|
||||
gl->should_resize = true;
|
||||
gl->win_width = event.resize.w;
|
||||
gl->win_height = event.resize.h;
|
||||
break;
|
||||
|
||||
default:
|
||||
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
|
||||
if (quit)
|
||||
gl->quitting = true;
|
||||
else if (resize)
|
||||
gl->should_resize = true;
|
||||
}
|
||||
|
||||
static bool gl_frame(void *data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
|
||||
static bool gl_frame(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
|
||||
{
|
||||
gl_t *gl = data;
|
||||
check_window(gl);
|
||||
|
||||
gl_shader_use(1);
|
||||
gl->frame_count++;
|
||||
@@ -801,7 +969,8 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
|
||||
if (gl->should_resize)
|
||||
{
|
||||
gl->should_resize = false;
|
||||
SDL_SetVideoMode(gl->win_width, gl->win_height, 0, SDL_OPENGL | SDL_RESIZABLE | (g_settings.video.fullscreen ? SDL_FULLSCREEN : 0));
|
||||
|
||||
sdlwrap_set_resize(gl->win_width, gl->win_height);
|
||||
|
||||
#ifdef HAVE_FBO
|
||||
if (!gl->render_to_tex)
|
||||
@@ -864,7 +1033,8 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
|
||||
set_texture_coords(gl->tex_coords, xamt, yamt);
|
||||
}
|
||||
#if defined(HAVE_XML) || defined(HAVE_CG)
|
||||
else if (width != gl->last_width[1 - gl->tex_index] || height != gl->last_height[1 - gl->tex_index])
|
||||
// We might have used different texture coordinates last frame. Edge case if resolution changes very rapidly.
|
||||
else if (width != gl->last_width[(gl->tex_index - 1) & TEXTURES_MASK] || height != gl->last_height[(gl->tex_index - 1) & TEXTURES_MASK])
|
||||
{
|
||||
GLfloat xamt = (GLfloat)width / gl->tex_w;
|
||||
GLfloat yamt = (GLfloat)height / gl->tex_h;
|
||||
@@ -873,10 +1043,10 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FBO
|
||||
// Need to preserve the "flipped" state when in FBO too to have
|
||||
// Need to preserve the "flipped" state when in FBO as well to have
|
||||
// consistent texture coordinates.
|
||||
if (gl->render_to_tex)
|
||||
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes);
|
||||
glVertexPointer(2, GL_FLOAT, 0, vertexes);
|
||||
#endif
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch / gl->base_size);
|
||||
@@ -895,7 +1065,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
gl_shader_set_params(width, height, gl->tex_w, gl->tex_h, gl->vp_width, gl->vp_height, gl->frame_count,
|
||||
&tex_info, &gl->prev_info, fbo_tex_info, fbo_tex_info_cnt);
|
||||
&tex_info, gl->prev_info, fbo_tex_info, fbo_tex_info_cnt);
|
||||
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
|
||||
@@ -903,7 +1073,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
|
||||
if (gl->fbo_inited)
|
||||
{
|
||||
// Render the rest of our passes.
|
||||
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->fbo_tex_coords);
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, gl->fbo_tex_coords);
|
||||
|
||||
// It's kinda handy ... :)
|
||||
const struct gl_fbo_rect *prev_rect;
|
||||
@@ -940,7 +1110,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
|
||||
gl_shader_set_params(prev_rect->img_width, prev_rect->img_height,
|
||||
prev_rect->width, prev_rect->height,
|
||||
gl->vp_width, gl->vp_height, gl->frame_count,
|
||||
&tex_info, &gl->prev_info, fbo_tex_info, fbo_tex_info_cnt);
|
||||
&tex_info, gl->prev_info, fbo_tex_info, fbo_tex_info_cnt);
|
||||
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
|
||||
@@ -966,18 +1136,19 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
|
||||
gl_shader_set_params(prev_rect->img_width, prev_rect->img_height,
|
||||
prev_rect->width, prev_rect->height,
|
||||
gl->vp_width, gl->vp_height, gl->frame_count,
|
||||
&tex_info, &gl->prev_info, fbo_tex_info, fbo_tex_info_cnt);
|
||||
&tex_info, gl->prev_info, fbo_tex_info, fbo_tex_info_cnt);
|
||||
|
||||
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes_flipped);
|
||||
glVertexPointer(2, GL_FLOAT, 0, vertexes_flipped);
|
||||
glDrawArrays(GL_QUADS, 0, 4);
|
||||
|
||||
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->tex_coords);
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, gl->tex_coords);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_XML) || defined(HAVE_CG)
|
||||
memcpy(&gl->prev_info, &tex_info, sizeof(tex_info));
|
||||
gl->tex_index = 1 - gl->tex_index;
|
||||
memmove(gl->prev_info + 1, gl->prev_info, sizeof(tex_info) * (TEXTURES - 1));
|
||||
memcpy(&gl->prev_info[0], &tex_info, sizeof(tex_info));
|
||||
gl->tex_index = (gl->tex_index + 1) & TEXTURES_MASK;
|
||||
#endif
|
||||
|
||||
if (msg)
|
||||
@@ -985,8 +1156,9 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
|
||||
|
||||
char buf[128];
|
||||
if (gfx_window_title(buf, sizeof(buf)))
|
||||
SDL_WM_SetCaption(buf, NULL);
|
||||
SDL_GL_SwapBuffers();
|
||||
sdlwrap_wm_set_caption(buf);
|
||||
|
||||
sdlwrap_swap_buffers();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -999,7 +1171,8 @@ static void gl_free(void *data)
|
||||
gl_shader_deinit();
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glDeleteTextures(2, gl->texture);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDeleteTextures(TEXTURES, gl->texture);
|
||||
|
||||
#ifdef HAVE_FBO
|
||||
if (gl->fbo_inited)
|
||||
@@ -1008,7 +1181,7 @@ static void gl_free(void *data)
|
||||
pglDeleteFramebuffers(gl->fbo_pass, gl->fbo);
|
||||
}
|
||||
#endif
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||
sdlwrap_destroy();
|
||||
|
||||
if (gl->empty_buf)
|
||||
free(gl->empty_buf);
|
||||
@@ -1016,48 +1189,23 @@ static void gl_free(void *data)
|
||||
free(gl);
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <OpenGL/OpenGL.h>
|
||||
#endif
|
||||
|
||||
static void gl_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
gl_t *gl = data;
|
||||
if (gl->vsync)
|
||||
{
|
||||
SSNES_LOG("GL VSync => %s\n", state ? "off" : "on");
|
||||
#if defined(_WIN32)
|
||||
static BOOL (APIENTRY *wgl_swap_interval)(int) = NULL;
|
||||
if (!wgl_swap_interval)
|
||||
{
|
||||
SDL_SYM_WRAP(wgl_swap_interval, "wglSwapIntervalEXT");
|
||||
}
|
||||
if (wgl_swap_interval) wgl_swap_interval(state ? 0 : 1);
|
||||
#elif defined(__APPLE__)
|
||||
GLint val = state ? 0 : 1;
|
||||
CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &val);
|
||||
#else
|
||||
static int (*glx_swap_interval)(int) = NULL;
|
||||
if (!glx_swap_interval)
|
||||
{
|
||||
SDL_SYM_WRAP(glx_swap_interval, "glXSwapIntervalSGI");
|
||||
}
|
||||
if (!glx_swap_interval)
|
||||
{
|
||||
SDL_SYM_WRAP(glx_swap_interval, "glXSwapIntervalMESA");
|
||||
}
|
||||
if (glx_swap_interval) glx_swap_interval(state ? 0 : 1);
|
||||
#endif
|
||||
sdlwrap_set_swap_interval(state ? 0 : 1, true);
|
||||
}
|
||||
}
|
||||
|
||||
static void* gl_init(const video_info_t *video, const input_driver_t **input, void **input_data)
|
||||
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)
|
||||
if (!sdlwrap_init())
|
||||
return NULL;
|
||||
|
||||
const SDL_VideoInfo *video_info = SDL_GetVideoInfo();
|
||||
@@ -1066,16 +1214,24 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
unsigned full_y = video_info->current_h;
|
||||
SSNES_LOG("Detecting desktop resolution %ux%u.\n", full_x, full_y);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, video->vsync ? 1 : 0);
|
||||
sdlwrap_set_swap_interval(video->vsync ? 1 : 0, false);
|
||||
|
||||
if (!SDL_SetVideoMode(video->width, video->height, g_settings.video.force_16bit ? 16 : 0, SDL_OPENGL | SDL_RESIZABLE | (video->fullscreen ? SDL_FULLSCREEN : 0)))
|
||||
unsigned win_width = video->width;
|
||||
unsigned win_height = video->height;
|
||||
if (video->fullscreen && (win_width == 0) && (win_height == 0))
|
||||
{
|
||||
win_width = full_x;
|
||||
win_height = full_y;
|
||||
}
|
||||
|
||||
if (!sdlwrap_set_video_mode(win_width, win_height,
|
||||
g_settings.video.force_16bit ? 15 : 0, video->fullscreen))
|
||||
return NULL;
|
||||
|
||||
gfx_window_title_reset();
|
||||
char buf[128];
|
||||
if (gfx_window_title(buf, sizeof(buf)))
|
||||
SDL_WM_SetCaption(buf, NULL);
|
||||
sdlwrap_wm_set_caption(buf);
|
||||
|
||||
// Remove that ugly mouse :D
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
@@ -1086,7 +1242,7 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
// Need to load dynamically :(
|
||||
if (!load_gl_proc())
|
||||
{
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||
sdlwrap_destroy();
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
@@ -1094,44 +1250,24 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
gl_t *gl = calloc(1, sizeof(gl_t));
|
||||
if (!gl)
|
||||
{
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||
sdlwrap_destroy();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gl->vsync = video->vsync;
|
||||
gl->fullscreen = video->fullscreen;
|
||||
int attr = 0;
|
||||
SDL_GL_GetAttribute(SDL_GL_SWAP_CONTROL, &attr);
|
||||
if (attr <= 0 && video->vsync)
|
||||
{
|
||||
SSNES_WARN("SDL failed to setup VSync, attempting to recover using native calls!\n");
|
||||
gl_set_nonblock_state(gl, false);
|
||||
}
|
||||
attr = 0;
|
||||
SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &attr);
|
||||
if (attr <= 0)
|
||||
SSNES_WARN("GL double buffer has not been enabled!\n");
|
||||
|
||||
|
||||
gl->full_x = full_x;
|
||||
gl->full_y = full_y;
|
||||
|
||||
if (video->fullscreen)
|
||||
{
|
||||
gl->win_width = video->width ? video->width : gl->full_x;
|
||||
gl->win_height = video->height ? video->height : gl->full_y;
|
||||
}
|
||||
else
|
||||
{
|
||||
gl->win_width = video->width;
|
||||
gl->win_height = video->height;
|
||||
}
|
||||
gl->win_width = win_width;
|
||||
gl->win_height = win_height;
|
||||
|
||||
SSNES_LOG("GL: Using resolution %ux%u\n", gl->win_width, gl->win_height);
|
||||
|
||||
if (!gl_shader_init())
|
||||
{
|
||||
SSNES_ERR("Shader init failed.\n");
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||
sdlwrap_destroy();
|
||||
free(gl);
|
||||
return NULL;
|
||||
}
|
||||
@@ -1139,7 +1275,8 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
SSNES_LOG("GL: Loaded %u program(s).\n", gl_shader_num());
|
||||
|
||||
// Set up render to texture.
|
||||
gl_init_fbo(gl, 256 * video->input_scale, 256 * video->input_scale);
|
||||
gl_init_fbo(gl, SSNES_SCALE_BASE * video->input_scale,
|
||||
SSNES_SCALE_BASE * video->input_scale);
|
||||
|
||||
gl->keep_aspect = video->force_aspect;
|
||||
|
||||
@@ -1162,15 +1299,14 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_DITHER);
|
||||
glColor4f(1, 1, 1, 1);
|
||||
glClearColor(0, 0, 0, 1);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
glGenTextures(2, gl->texture);
|
||||
glGenTextures(TEXTURES, gl->texture);
|
||||
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
for (unsigned i = 0; i < TEXTURES; i++)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, gl->texture[i]);
|
||||
|
||||
@@ -1183,20 +1319,23 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes_flipped);
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glVertexPointer(2, GL_FLOAT, 0, vertexes_flipped);
|
||||
|
||||
memcpy(gl->tex_coords, tex_coords, sizeof(tex_coords));
|
||||
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->tex_coords);
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, gl->tex_coords);
|
||||
|
||||
glColorPointer(4, GL_FLOAT, 0, white_color);
|
||||
|
||||
set_lut_texture_coords(tex_coords);
|
||||
|
||||
gl->tex_w = 256 * video->input_scale;
|
||||
gl->tex_h = 256 * video->input_scale;
|
||||
gl->tex_w = SSNES_SCALE_BASE * video->input_scale;
|
||||
gl->tex_h = SSNES_SCALE_BASE * video->input_scale;
|
||||
|
||||
// Empty buffer that we use to clear out the texture with on res change.
|
||||
gl->empty_buf = calloc(gl->tex_w * gl->tex_h, gl->base_size);
|
||||
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
for (unsigned i = 0; i < TEXTURES; i++)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, gl->texture[i]);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, gl->tex_w);
|
||||
@@ -1206,18 +1345,21 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
|
||||
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
for (unsigned i = 0; i < TEXTURES; i++)
|
||||
{
|
||||
gl->last_width[i] = gl->tex_w;
|
||||
gl->last_height[i] = gl->tex_h;
|
||||
}
|
||||
|
||||
gl->prev_info.tex = gl->texture[1 - gl->tex_index];
|
||||
gl->prev_info.input_size[0] = gl->tex_w;
|
||||
gl->prev_info.tex_size[0] = gl->tex_w;
|
||||
gl->prev_info.input_size[1] = gl->tex_h;
|
||||
gl->prev_info.tex_size[1] = gl->tex_h;
|
||||
memcpy(gl->prev_info.coord, tex_coords, sizeof(tex_coords));
|
||||
for (unsigned i = 0; i < TEXTURES; i++)
|
||||
{
|
||||
gl->prev_info[i].tex = gl->texture[(gl->tex_index - (i + 1)) & TEXTURES_MASK];
|
||||
gl->prev_info[i].input_size[0] = gl->tex_w;
|
||||
gl->prev_info[i].tex_size[0] = gl->tex_w;
|
||||
gl->prev_info[i].input_size[1] = gl->tex_h;
|
||||
gl->prev_info[i].tex_size[1] = gl->tex_h;
|
||||
memcpy(gl->prev_info[i].coord, tex_coords, sizeof(tex_coords));
|
||||
}
|
||||
|
||||
// Hook up SDL input driver to get SDL_QUIT events and RESIZE.
|
||||
sdl_input_t *sdl_input = input_sdl.init();
|
||||
@@ -1233,7 +1375,7 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
|
||||
if (!gl_check_error())
|
||||
{
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||
sdlwrap_destroy();
|
||||
free(gl);
|
||||
return NULL;
|
||||
}
|
||||
@@ -1244,13 +1386,14 @@ static void* gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
static bool gl_alive(void *data)
|
||||
{
|
||||
gl_t *gl = data;
|
||||
check_window(gl);
|
||||
return !gl->quitting;
|
||||
}
|
||||
|
||||
static bool gl_focus(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return (SDL_GetAppState() & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) == (SDL_APPINPUTFOCUS | SDL_APPACTIVE);
|
||||
return sdlwrap_window_has_focus();
|
||||
}
|
||||
|
||||
#ifdef HAVE_XML
|
||||
|
||||
+7
-13
@@ -18,16 +18,20 @@
|
||||
#ifndef __GL_COMMON_H
|
||||
#define __GL_COMMON_H
|
||||
|
||||
#include "general.h"
|
||||
#include "../general.h"
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#include "../config.h"
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#if defined(__APPLE__)
|
||||
#include <OpenGL/gl.h>
|
||||
#include <OpenGL/glext.h>
|
||||
#elif defined(__CELLOS_LV2__)
|
||||
#include <PSGL/psgl.h>
|
||||
#include <PSGL/psglu.h>
|
||||
#include <GLES/glext.h>
|
||||
#else
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
@@ -57,9 +61,6 @@ static inline bool gl_check_error(void)
|
||||
case GL_OUT_OF_MEMORY:
|
||||
SSNES_ERR("GL: Out of memory. Harhar.\n");
|
||||
break;
|
||||
case GL_TABLE_TOO_LARGE:
|
||||
SSNES_ERR("GL: Table too large. Big tables scare you! :(\n");
|
||||
break;
|
||||
case GL_NO_ERROR:
|
||||
return true;
|
||||
break;
|
||||
@@ -106,13 +107,6 @@ struct gl_tex_info
|
||||
GLfloat coord[8];
|
||||
};
|
||||
|
||||
// Not legal to cast void* to fn-pointer. Need dirty hack to be compilant.
|
||||
// sizeof(void*) == sizeof(void (*)(void)) is asserted earlier.
|
||||
#define SDL_SYM_WRAP(sym, symbol) { \
|
||||
void *sym__ = SDL_GL_GetProcAddress(symbol); \
|
||||
memcpy(&(sym), &sym__, sizeof(void*)); \
|
||||
}
|
||||
|
||||
// Windows ... <_<
|
||||
#if (defined(HAVE_XML) || defined(HAVE_CG)) && defined(_WIN32)
|
||||
extern PFNGLCLIENTACTIVETEXTUREPROC pglClientActiveTexture;
|
||||
|
||||
+3
-3
@@ -16,16 +16,16 @@
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#include "../config.h"
|
||||
#endif
|
||||
|
||||
#include "image.h"
|
||||
#include "file.h"
|
||||
#include "../file.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include "general.h"
|
||||
#include "../general.h"
|
||||
|
||||
#ifdef HAVE_SDL_IMAGE
|
||||
|
||||
|
||||
+1
-1
@@ -30,5 +30,5 @@ struct texture_image
|
||||
|
||||
bool texture_image_load(const char *path, struct texture_image* img);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
+108
-45
@@ -26,6 +26,7 @@
|
||||
#include "py_state.h"
|
||||
#include "general.h"
|
||||
#include "strl.h"
|
||||
#include "file.h"
|
||||
|
||||
#define PY_READ_FUNC_DECL(RAMTYPE) py_read_##RAMTYPE
|
||||
#define PY_READ_FUNC(RAMTYPE) \
|
||||
@@ -60,7 +61,7 @@ PY_READ_FUNC(APURAM)
|
||||
PY_READ_FUNC(CGRAM)
|
||||
PY_READ_FUNC(OAM)
|
||||
|
||||
static PyObject* py_read_input(PyObject *self, PyObject *args)
|
||||
static PyObject *py_read_input(PyObject *self, PyObject *args)
|
||||
{
|
||||
(void)self;
|
||||
|
||||
@@ -72,12 +73,16 @@ static PyObject* py_read_input(PyObject *self, PyObject *args)
|
||||
if (!PyArg_ParseTuple(args, "II", &player, &key))
|
||||
return NULL;
|
||||
|
||||
if (player > MAX_PLAYERS || player < 1)
|
||||
if (player > MAX_PLAYERS || player < 1 || key >= SSNES_FIRST_META_KEY)
|
||||
return NULL;
|
||||
|
||||
const struct snes_keybind *binds[MAX_PLAYERS];
|
||||
for (int i = 0; i < MAX_PLAYERS; i++)
|
||||
binds[i] = g_settings.input.binds[i];
|
||||
static const struct snes_keybind *binds[MAX_PLAYERS] = {
|
||||
g_settings.input.binds[0],
|
||||
g_settings.input.binds[1],
|
||||
g_settings.input.binds[2],
|
||||
g_settings.input.binds[3],
|
||||
g_settings.input.binds[4],
|
||||
};
|
||||
|
||||
int16_t res = driver.input->input_state(driver.input_data,
|
||||
binds, player > 1,
|
||||
@@ -85,7 +90,25 @@ static PyObject* py_read_input(PyObject *self, PyObject *args)
|
||||
player > 2 ? player - 2 : 0,
|
||||
key);
|
||||
|
||||
return PyBool_FromLong((long)res);
|
||||
return PyBool_FromLong(res);
|
||||
}
|
||||
|
||||
static PyObject *py_read_input_meta(PyObject *self, PyObject *args)
|
||||
{
|
||||
(void)self;
|
||||
|
||||
if (!driver.input_data)
|
||||
return PyBool_FromLong(0);
|
||||
|
||||
unsigned key;
|
||||
if (!PyArg_ParseTuple(args, "I", &key))
|
||||
return NULL;
|
||||
|
||||
if (key < SSNES_FIRST_META_KEY)
|
||||
return NULL;
|
||||
|
||||
bool ret = driver.input->key_pressed(driver.input_data, key);
|
||||
return PyBool_FromLong(ret);
|
||||
}
|
||||
|
||||
static PyMethodDef SNESMethods[] = {
|
||||
@@ -95,23 +118,50 @@ static PyMethodDef SNESMethods[] = {
|
||||
{ "read_cgram", PY_READ_FUNC_DECL(CGRAM), METH_VARARGS, "Read CGRAM from SNES." },
|
||||
{ "read_oam", PY_READ_FUNC_DECL(OAM), METH_VARARGS, "Read OAM from SNES." },
|
||||
{ "input", py_read_input, METH_VARARGS, "Read input state from SNES." },
|
||||
{ "input_meta", py_read_input_meta, METH_VARARGS, "Read SSNES specific input." },
|
||||
{ NULL, NULL, 0, NULL }
|
||||
};
|
||||
|
||||
#define DECL_ATTR_SNES(attr) PyObject_SetAttrString(mod, #attr, PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_##attr))
|
||||
#define DECL_ATTR_SSNES(attr) PyObject_SetAttrString(mod, #attr, PyLong_FromLong(SSNES_##attr))
|
||||
static void py_set_attrs(PyObject *mod)
|
||||
{
|
||||
PyObject_SetAttrString(mod, "B", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_B));
|
||||
PyObject_SetAttrString(mod, "Y", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_Y));
|
||||
PyObject_SetAttrString(mod, "SELECT", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_SELECT));
|
||||
PyObject_SetAttrString(mod, "START", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_START));
|
||||
PyObject_SetAttrString(mod, "UP", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_UP));
|
||||
PyObject_SetAttrString(mod, "DOWN", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_DOWN));
|
||||
PyObject_SetAttrString(mod, "LEFT", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_LEFT));
|
||||
PyObject_SetAttrString(mod, "RIGHT", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_RIGHT));
|
||||
PyObject_SetAttrString(mod, "A", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_A));
|
||||
PyObject_SetAttrString(mod, "X", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_X));
|
||||
PyObject_SetAttrString(mod, "L", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_L));
|
||||
PyObject_SetAttrString(mod, "R", PyLong_FromLong(SNES_DEVICE_ID_JOYPAD_R));
|
||||
DECL_ATTR_SNES(B);
|
||||
DECL_ATTR_SNES(Y);
|
||||
DECL_ATTR_SNES(SELECT);
|
||||
DECL_ATTR_SNES(START);
|
||||
DECL_ATTR_SNES(UP);
|
||||
DECL_ATTR_SNES(DOWN);
|
||||
DECL_ATTR_SNES(LEFT);
|
||||
DECL_ATTR_SNES(RIGHT);
|
||||
DECL_ATTR_SNES(A);
|
||||
DECL_ATTR_SNES(X);
|
||||
DECL_ATTR_SNES(L);
|
||||
DECL_ATTR_SNES(R);
|
||||
|
||||
DECL_ATTR_SSNES(FAST_FORWARD_KEY);
|
||||
DECL_ATTR_SSNES(FAST_FORWARD_HOLD_KEY);
|
||||
DECL_ATTR_SSNES(LOAD_STATE_KEY);
|
||||
DECL_ATTR_SSNES(SAVE_STATE_KEY);
|
||||
DECL_ATTR_SSNES(FULLSCREEN_TOGGLE_KEY);
|
||||
DECL_ATTR_SSNES(QUIT_KEY);
|
||||
DECL_ATTR_SSNES(STATE_SLOT_PLUS);
|
||||
DECL_ATTR_SSNES(STATE_SLOT_MINUS);
|
||||
DECL_ATTR_SSNES(AUDIO_INPUT_RATE_PLUS);
|
||||
DECL_ATTR_SSNES(AUDIO_INPUT_RATE_MINUS);
|
||||
DECL_ATTR_SSNES(REWIND);
|
||||
DECL_ATTR_SSNES(MOVIE_RECORD_TOGGLE);
|
||||
DECL_ATTR_SSNES(PAUSE_TOGGLE);
|
||||
DECL_ATTR_SSNES(FRAMEADVANCE);
|
||||
DECL_ATTR_SSNES(RESET);
|
||||
DECL_ATTR_SSNES(SHADER_NEXT);
|
||||
DECL_ATTR_SSNES(SHADER_PREV);
|
||||
DECL_ATTR_SSNES(CHEAT_INDEX_PLUS);
|
||||
DECL_ATTR_SSNES(CHEAT_INDEX_MINUS);
|
||||
DECL_ATTR_SSNES(CHEAT_TOGGLE);
|
||||
DECL_ATTR_SSNES(SCREENSHOT);
|
||||
DECL_ATTR_SSNES(DSP_CONFIG);
|
||||
DECL_ATTR_SSNES(MUTE);
|
||||
}
|
||||
|
||||
static PyModuleDef SNESModule = {
|
||||
@@ -156,7 +206,7 @@ static char *dupe_newline(const char *str)
|
||||
}
|
||||
|
||||
// Need to make sure that first-line indentation is 0. :(
|
||||
static char* align_program(const char *program)
|
||||
static char *align_program(const char *program)
|
||||
{
|
||||
char *prog = strdup(program);
|
||||
if (!prog)
|
||||
@@ -190,28 +240,38 @@ static char* align_program(const char *program)
|
||||
}
|
||||
|
||||
free(prog);
|
||||
|
||||
return new_prog;
|
||||
}
|
||||
|
||||
py_state_t *py_state_new(const char *script, bool is_file, const char *pyclass)
|
||||
py_state_t *py_state_new(const char *script, unsigned is_file, const char *pyclass)
|
||||
{
|
||||
SSNES_LOG("Initializing Python runtime ...\n");
|
||||
PyImport_AppendInittab("snes", &PyInit_SNES);
|
||||
Py_Initialize();
|
||||
SSNES_LOG("Initialized Python runtime!\n");
|
||||
|
||||
py_state_t *handle = calloc(1, sizeof(*handle));
|
||||
|
||||
handle->main = PyImport_AddModule("__main__");
|
||||
if (!handle->main)
|
||||
goto error;
|
||||
Py_INCREF(handle->main);
|
||||
|
||||
if (is_file)
|
||||
{
|
||||
FILE *file = fopen(script, "r");
|
||||
if (!file)
|
||||
// Have to hack around the fact that the
|
||||
// FILE struct isn't standardized across environments.
|
||||
// PyRun_SimpleFile() breaks on Windows.
|
||||
|
||||
char *script_ = NULL;
|
||||
if (read_file(script, (void**)&script_) < 0)
|
||||
{
|
||||
SSNES_ERR("Python: Failed to read script\n");
|
||||
goto error;
|
||||
PyRun_SimpleFile(file, script);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
PyRun_SimpleString(script_);
|
||||
free(script_);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -223,22 +283,35 @@ py_state_t *py_state_new(const char *script, bool is_file, const char *pyclass)
|
||||
}
|
||||
}
|
||||
|
||||
SSNES_LOG("Python: Script loaded.\n");
|
||||
handle->dict = PyModule_GetDict(handle->main);
|
||||
if (!handle->dict)
|
||||
{
|
||||
SSNES_ERR("Python: PyModule_GetDict() failed.\n");
|
||||
goto error;
|
||||
}
|
||||
Py_INCREF(handle->dict);
|
||||
|
||||
PyObject *hook = PyDict_GetItemString(handle->dict, pyclass);
|
||||
if (!hook)
|
||||
{
|
||||
SSNES_ERR("Python: PyDict_GetItemString() failed.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
handle->inst = PyObject_CallFunction(hook, NULL);
|
||||
if (!handle->inst)
|
||||
{
|
||||
SSNES_ERR("Python: PyObject_CallFunction() failed.\n");
|
||||
goto error;
|
||||
}
|
||||
Py_INCREF(handle->inst);
|
||||
|
||||
return handle;
|
||||
|
||||
error:
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
py_state_free(handle);
|
||||
return NULL;
|
||||
}
|
||||
@@ -247,20 +320,19 @@ void py_state_free(py_state_t *handle)
|
||||
{
|
||||
if (handle)
|
||||
{
|
||||
if (handle->main)
|
||||
Py_DECREF(handle->main);
|
||||
if (handle->dict)
|
||||
Py_DECREF(handle->dict);
|
||||
if (handle->inst)
|
||||
Py_DECREF(handle->inst);
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
|
||||
Py_CLEAR(handle->inst);
|
||||
Py_CLEAR(handle->dict);
|
||||
Py_CLEAR(handle->main);
|
||||
|
||||
free(handle);
|
||||
Py_Finalize();
|
||||
}
|
||||
|
||||
Py_Finalize();
|
||||
}
|
||||
|
||||
int py_state_get(py_state_t *handle, const char *id,
|
||||
float py_state_get(py_state_t *handle, const char *id,
|
||||
unsigned frame_count)
|
||||
{
|
||||
PyObject *ret = PyObject_CallMethod(handle->inst, (char*)id, (char*)"I", frame_count);
|
||||
@@ -269,19 +341,10 @@ int py_state_get(py_state_t *handle, const char *id,
|
||||
if (!handle->warned_ret)
|
||||
SSNES_WARN("Didn't get return value from script! Bug?\n");
|
||||
handle->warned_ret = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int retval = 0;
|
||||
if (PyLong_Check(ret))
|
||||
retval = (int)PyLong_AsLong(ret);
|
||||
else
|
||||
{
|
||||
if (!handle->warned_type)
|
||||
SSNES_WARN("Didn't get long compatible value from script! Bug?\n");
|
||||
handle->warned_type = true;
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float retval = (float)PyFloat_AsDouble(ret);
|
||||
Py_DECREF(ret);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -21,12 +21,14 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef PY_STATE_OMIT_DECLARATION
|
||||
typedef struct py_state py_state_t;
|
||||
#endif
|
||||
|
||||
py_state_t *py_state_new(const char *program, bool is_file, const char *pyclass);
|
||||
py_state_t *py_state_new(const char *program, unsigned is_file, const char *pyclass);
|
||||
void py_state_free(py_state_t *handle);
|
||||
|
||||
int py_state_get(py_state_t *handle,
|
||||
float py_state_get(py_state_t *handle,
|
||||
const char *id, unsigned frame_count);
|
||||
|
||||
#endif
|
||||
@@ -45,11 +45,16 @@ struct sdl_video
|
||||
bool rgb32;
|
||||
bool upsample;
|
||||
|
||||
bool render32;
|
||||
|
||||
void (*convert_15_func)(uint16_t*, unsigned, const uint16_t*, unsigned, unsigned, unsigned, const SDL_PixelFormat*);
|
||||
void (*convert_32_func)(uint32_t*, unsigned, const uint32_t*, unsigned, unsigned, unsigned, const SDL_PixelFormat*);
|
||||
|
||||
#ifdef HAVE_FREETYPE
|
||||
font_renderer_t *font;
|
||||
uint8_t font_r;
|
||||
uint8_t font_g;
|
||||
uint8_t font_b;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -75,12 +80,43 @@ static void sdl_gfx_free(void *data)
|
||||
static void sdl_init_font(sdl_video_t *vid, const char *font_path, unsigned font_size)
|
||||
{
|
||||
#ifdef HAVE_FREETYPE
|
||||
if (*font_path)
|
||||
if (!g_settings.video.font_enable)
|
||||
return;
|
||||
|
||||
const char *path = font_path;
|
||||
if (!*path)
|
||||
path = font_renderer_get_default_font();
|
||||
|
||||
if (path)
|
||||
{
|
||||
vid->font = font_renderer_new(font_path, font_size);
|
||||
if (!vid->font)
|
||||
vid->font = font_renderer_new(path, font_size);
|
||||
if (vid->font)
|
||||
{
|
||||
int r = g_settings.video.msg_color_r * 255;
|
||||
int g = g_settings.video.msg_color_g * 255;
|
||||
int b = g_settings.video.msg_color_b * 255;
|
||||
|
||||
r = r < 0 ? 0 : (r > 255 ? 255 : r);
|
||||
g = g < 0 ? 0 : (g > 255 ? 255 : g);
|
||||
b = b < 0 ? 0 : (b > 255 ? 255 : b);
|
||||
|
||||
// RGB888 -> RGB555
|
||||
if (!vid->render32)
|
||||
{
|
||||
r >>= 3;
|
||||
g >>= 3;
|
||||
b >>= 3;
|
||||
}
|
||||
|
||||
vid->font_r = r;
|
||||
vid->font_g = g;
|
||||
vid->font_b = b;
|
||||
}
|
||||
else
|
||||
SSNES_WARN("Failed to init font.\n");
|
||||
}
|
||||
else
|
||||
SSNES_LOG("Did not find default font.\n");
|
||||
#else
|
||||
(void)vid;
|
||||
(void)font_path;
|
||||
@@ -128,9 +164,9 @@ static void sdl_render_msg_15(sdl_video_t *vid, SDL_Surface *buffer, const char
|
||||
unsigned g = (out_pix >> gshift) & 0x1f;
|
||||
unsigned b = (out_pix >> bshift) & 0x1f;
|
||||
|
||||
unsigned out_r = (r * (256 - blend) + 0x1f * blend) >> 8;
|
||||
unsigned out_g = (g * (256 - blend) + 0x1f * blend) >> 8;
|
||||
unsigned out_b = (b * (256 - blend) + 0x1f * blend) >> 8;
|
||||
unsigned out_r = (r * (256 - blend) + vid->font_r * blend) >> 8;
|
||||
unsigned out_g = (g * (256 - blend) + vid->font_g * blend) >> 8;
|
||||
unsigned out_b = (b * (256 - blend) + vid->font_b * blend) >> 8;
|
||||
out[x] = (out_r << rshift) | (out_g << gshift) | (out_b << bshift);
|
||||
}
|
||||
}
|
||||
@@ -189,9 +225,9 @@ static void sdl_render_msg_32(sdl_video_t *vid, SDL_Surface *buffer, const char
|
||||
unsigned g = (out_pix >> gshift) & 0xff;
|
||||
unsigned b = (out_pix >> bshift) & 0xff;
|
||||
|
||||
unsigned out_r = (r * (256 - blend) + 0xff * blend) >> 8;
|
||||
unsigned out_g = (g * (256 - blend) + 0xff * blend) >> 8;
|
||||
unsigned out_b = (b * (256 - blend) + 0xff * blend) >> 8;
|
||||
unsigned out_r = (r * (256 - blend) + vid->font_r * blend) >> 8;
|
||||
unsigned out_g = (g * (256 - blend) + vid->font_g * blend) >> 8;
|
||||
unsigned out_b = (b * (256 - blend) + vid->font_b * blend) >> 8;
|
||||
out[x] = (out_r << rshift) | (out_g << gshift) | (out_b << bshift);
|
||||
}
|
||||
}
|
||||
@@ -211,7 +247,7 @@ static void sdl_render_msg_32(sdl_video_t *vid, SDL_Surface *buffer, const char
|
||||
#endif
|
||||
}
|
||||
|
||||
static void* sdl_gfx_init(const video_info_t *video, const input_driver_t **input, void **input_data)
|
||||
static void *sdl_gfx_init(const video_info_t *video, const input_driver_t **input, void **input_data)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
gfx_set_dwm();
|
||||
@@ -232,13 +268,15 @@ static void* sdl_gfx_init(const video_info_t *video, const input_driver_t **inpu
|
||||
if (!video->fullscreen)
|
||||
SSNES_LOG("Creating window @ %ux%u\n", video->width, video->height);
|
||||
|
||||
vid->screen = SDL_SetVideoMode(video->width, video->height, (g_settings.video.force_16bit || !video->rgb32) ? 15 : 32, SDL_HWSURFACE | SDL_HWACCEL | SDL_DOUBLEBUF | (video->fullscreen ? SDL_FULLSCREEN : 0));
|
||||
vid->render32 = video->rgb32 && !g_settings.video.force_16bit;
|
||||
vid->screen = SDL_SetVideoMode(video->width, video->height, vid->render32 ? 32 : 15, SDL_HWSURFACE | SDL_HWACCEL | SDL_DOUBLEBUF | (video->fullscreen ? SDL_FULLSCREEN : 0));
|
||||
|
||||
if (!vid->screen && !g_settings.video.force_16bit && !video->rgb32)
|
||||
{
|
||||
vid->upsample = true;
|
||||
vid->screen = SDL_SetVideoMode(video->width, video->height, 32, SDL_HWSURFACE | SDL_HWACCEL | SDL_DOUBLEBUF | (video->fullscreen ? SDL_FULLSCREEN : 0));
|
||||
SSNES_WARN("SDL: 15-bit colors failed, attempting 32-bit colors.\n");
|
||||
vid->render32 = true;
|
||||
}
|
||||
|
||||
if (!vid->screen)
|
||||
@@ -250,16 +288,18 @@ static void* sdl_gfx_init(const video_info_t *video, const input_driver_t **inpu
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
|
||||
const SDL_PixelFormat *fmt = vid->screen->format;
|
||||
if (!g_settings.video.force_16bit && (video->rgb32 || vid->upsample))
|
||||
if (vid->render32)
|
||||
{
|
||||
SSNES_LOG("SDL: Creating 32-bit buffer.\n");
|
||||
vid->buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, 256 * video->input_scale, 256 * video->input_scale, 32,
|
||||
vid->buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, SSNES_SCALE_BASE * video->input_scale,
|
||||
SSNES_SCALE_BASE * video->input_scale, 32,
|
||||
fmt->Rmask, fmt->Bmask, fmt->Gmask, fmt->Amask);
|
||||
}
|
||||
else
|
||||
{
|
||||
SSNES_LOG("SDL: Creating 15-bit buffer.\n");
|
||||
vid->buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, 256 * video->input_scale, 256 * video->input_scale, 15,
|
||||
vid->buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, SSNES_SCALE_BASE * video->input_scale,
|
||||
SSNES_SCALE_BASE * video->input_scale, 15,
|
||||
fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
|
||||
}
|
||||
SSNES_LOG("[Debug]: SDL Pixel format: Rshift = %u, Gshift = %u, Bshift = %u\n",
|
||||
@@ -285,7 +325,7 @@ static void* sdl_gfx_init(const video_info_t *video, const input_driver_t **inpu
|
||||
sdl_init_font(vid, g_settings.video.font_path, g_settings.video.font_size);
|
||||
|
||||
|
||||
if (fmt->Rshift == 10 && fmt->Gshift == 5 && fmt->Bshift == 0)
|
||||
if (fmt->Rshift == 10 && fmt->Gshift == 5 && fmt->Bshift == 0) // XRGB1555
|
||||
{
|
||||
SSNES_LOG("SDL: 15-bit format matches. Fast blit.\n");
|
||||
vid->convert_15_func = convert_15bit_15bit_direct;
|
||||
@@ -296,7 +336,7 @@ static void* sdl_gfx_init(const video_info_t *video, const input_driver_t **inpu
|
||||
vid->convert_15_func = convert_15bit_15bit_shift;
|
||||
}
|
||||
|
||||
if (fmt->Rshift == 16 && fmt->Gshift == 8 && fmt->Bshift == 0)
|
||||
if (fmt->Rshift == 16 && fmt->Gshift == 8 && fmt->Bshift == 0) // ARGB8888
|
||||
{
|
||||
SSNES_LOG("SDL: 32-bit format matches. Fast blit.\n");
|
||||
vid->convert_32_func = convert_32bit_32bit_direct;
|
||||
@@ -427,10 +467,9 @@ static void check_window(sdl_video_t *vid)
|
||||
}
|
||||
}
|
||||
|
||||
static bool sdl_gfx_frame(void *data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
|
||||
static bool sdl_gfx_frame(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
|
||||
{
|
||||
sdl_video_t *vid = data;
|
||||
check_window(vid);
|
||||
|
||||
if (SDL_MUSTLOCK(vid->buffer))
|
||||
SDL_LockSurface(vid->buffer);
|
||||
@@ -456,15 +495,7 @@ static bool sdl_gfx_frame(void *data, const void* frame, unsigned width, unsigne
|
||||
{
|
||||
vid->convert_32_func(vid->buffer->pixels, vid->buffer->pitch, frame, width, height, pitch, vid->screen->format);
|
||||
}
|
||||
|
||||
if (msg)
|
||||
{
|
||||
if ((!vid->rgb32 || g_settings.video.force_16bit) && !vid->upsample)
|
||||
sdl_render_msg_15(vid, vid->buffer, msg, width, height, vid->screen->format);
|
||||
else
|
||||
sdl_render_msg_32(vid, vid->buffer, msg, width, height, vid->screen->format);
|
||||
}
|
||||
|
||||
|
||||
if (SDL_MUSTLOCK(vid->buffer))
|
||||
SDL_UnlockSurface(vid->buffer);
|
||||
|
||||
@@ -484,6 +515,14 @@ static bool sdl_gfx_frame(void *data, const void* frame, unsigned width, unsigne
|
||||
|
||||
SDL_SoftStretch(vid->buffer, &src, vid->screen, &dest);
|
||||
|
||||
if (msg)
|
||||
{
|
||||
if ((!vid->rgb32 || g_settings.video.force_16bit) && !vid->upsample)
|
||||
sdl_render_msg_15(vid, vid->screen, msg, vid->screen->w, vid->screen->h, vid->screen->format);
|
||||
else
|
||||
sdl_render_msg_32(vid, vid->screen, msg, vid->screen->w, vid->screen->h, vid->screen->format);
|
||||
}
|
||||
|
||||
char buf[128];
|
||||
if (gfx_window_title(buf, sizeof(buf)))
|
||||
SDL_WM_SetCaption(buf, NULL);
|
||||
@@ -502,6 +541,7 @@ static void sdl_gfx_set_nonblock_state(void *data, bool state)
|
||||
static bool sdl_gfx_alive(void *data)
|
||||
{
|
||||
sdl_video_t *vid = data;
|
||||
check_window(vid);
|
||||
return !vid->quitting;
|
||||
}
|
||||
|
||||
|
||||
+354
@@ -0,0 +1,354 @@
|
||||
/* 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 "sdlwrap.h"
|
||||
#include "SDL_syswm.h"
|
||||
#include "general.h"
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <OpenGL/OpenGL.h>
|
||||
#include <OpenGL/gl.h>
|
||||
#endif
|
||||
|
||||
// SDL 1.2 is portable, sure, but you still need some platform specific workarounds ;)
|
||||
// Hopefully SDL 1.3 will solve this more cleanly :D
|
||||
// Welcome to #ifdef HELL! :D
|
||||
//
|
||||
|
||||
#if SDL_MODERN
|
||||
static SDL_Window* g_window;
|
||||
static SDL_GLContext g_ctx;
|
||||
#endif
|
||||
|
||||
static bool g_fullscreen;
|
||||
static unsigned g_interval;
|
||||
|
||||
void sdlwrap_set_swap_interval(unsigned interval, bool inited)
|
||||
{
|
||||
g_interval = interval;
|
||||
|
||||
#if SDL_MODERN
|
||||
if (g_window)
|
||||
SDL_GL_SetSwapInterval(g_interval);
|
||||
#else
|
||||
if (inited)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
static BOOL (APIENTRY *wgl_swap_interval)(int) = NULL;
|
||||
if (!wgl_swap_interval)
|
||||
{
|
||||
SDL_SYM_WRAP(wgl_swap_interval, "wglSwapIntervalEXT");
|
||||
}
|
||||
if (wgl_swap_interval) wgl_swap_interval(g_interval);
|
||||
|
||||
#elif defined(__APPLE__) && defined(HAVE_OPENGL)
|
||||
GLint val = g_interval;
|
||||
CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &val);
|
||||
#else
|
||||
static int (*glx_swap_interval)(int) = NULL;
|
||||
if (!glx_swap_interval)
|
||||
{
|
||||
SDL_SYM_WRAP(glx_swap_interval, "glXSwapIntervalSGI");
|
||||
}
|
||||
if (!glx_swap_interval)
|
||||
{
|
||||
SDL_SYM_WRAP(glx_swap_interval, "glXSwapIntervalMESA");
|
||||
}
|
||||
if (glx_swap_interval)
|
||||
glx_swap_interval(g_interval);
|
||||
else
|
||||
SSNES_WARN("Could not find GLX VSync call. :(\n");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool sdlwrap_init(void)
|
||||
{
|
||||
#if SDL_MODERN
|
||||
return SDL_VideoInit(NULL) == 0;
|
||||
#else
|
||||
if (SDL_WasInit(SDL_INIT_VIDEO))
|
||||
return true;
|
||||
|
||||
return SDL_Init(SDL_INIT_VIDEO) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if SDL_MODERN
|
||||
void sdlwrap_destroy(void)
|
||||
{
|
||||
if (g_ctx)
|
||||
SDL_GL_DeleteContext(g_ctx);
|
||||
if (g_window)
|
||||
SDL_DestroyWindow(g_window);
|
||||
|
||||
g_ctx = NULL;
|
||||
g_window = NULL;
|
||||
SDL_VideoQuit();
|
||||
}
|
||||
#else
|
||||
void sdlwrap_destroy(void)
|
||||
{
|
||||
SDL_QuitSubSystem(SDL_INIT_VIDEO);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool sdlwrap_set_video_mode(
|
||||
unsigned width, unsigned height,
|
||||
unsigned bits, bool fullscreen)
|
||||
{
|
||||
#if SDL_MODERN
|
||||
if (g_window)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
#if SDL_MODERN
|
||||
static const int resizable = SDL_WINDOW_RESIZABLE;
|
||||
#else
|
||||
#ifndef __APPLE__ // Resizing on OSX is broken in 1.2 it seems :)
|
||||
static const int resizable = SDL_RESIZABLE;
|
||||
#else
|
||||
static const int resizable = 0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
|
||||
#if !SDL_MODERN
|
||||
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, g_interval);
|
||||
#endif
|
||||
|
||||
#if SDL_MODERN
|
||||
if (bits == 15)
|
||||
{
|
||||
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
|
||||
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
|
||||
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
|
||||
}
|
||||
g_window = SDL_CreateWindow("SSNES", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||
width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | (fullscreen ? SDL_WINDOW_FULLSCREEN : resizable));
|
||||
if (!g_window)
|
||||
return false;
|
||||
g_ctx = SDL_GL_CreateContext(g_window);
|
||||
#else
|
||||
if (!SDL_SetVideoMode(width, height, bits,
|
||||
SDL_OPENGL | (fullscreen ? SDL_FULLSCREEN : resizable)))
|
||||
return false;
|
||||
#endif
|
||||
|
||||
int attr = 0;
|
||||
#if SDL_MODERN
|
||||
SDL_GL_SetSwapInterval(g_interval);
|
||||
#else
|
||||
SDL_GL_GetAttribute(SDL_GL_SWAP_CONTROL, &attr);
|
||||
if (attr <= 0 && g_interval)
|
||||
{
|
||||
SSNES_WARN("SDL failed to setup VSync, attempting to recover using native calls!\n");
|
||||
sdlwrap_set_swap_interval(g_interval, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
g_fullscreen = fullscreen;
|
||||
|
||||
attr = 0;
|
||||
SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &attr);
|
||||
if (attr <= 0)
|
||||
SSNES_WARN("GL double buffer has not been enabled!\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// SDL 1.2 has an awkward model where you need to "confirm" window resizing.
|
||||
// SDL 1.3 luckily removes this quirk.
|
||||
void sdlwrap_set_resize(unsigned width, unsigned height)
|
||||
{
|
||||
#if SDL_MODERN
|
||||
(void)width;
|
||||
(void)height;
|
||||
#else
|
||||
#ifndef __APPLE__ // Resizing on OSX is broken in 1.2 it seems :)
|
||||
static const int resizable = SDL_RESIZABLE;
|
||||
#else
|
||||
static const int resizable = 0;
|
||||
#endif
|
||||
SDL_SetVideoMode(width, height, 0, SDL_OPENGL | (g_fullscreen ? SDL_FULLSCREEN : resizable));
|
||||
#endif
|
||||
}
|
||||
|
||||
void sdlwrap_wm_set_caption(const char *str)
|
||||
{
|
||||
#if SDL_MODERN
|
||||
SDL_SetWindowTitle(g_window, str);
|
||||
#else
|
||||
SDL_WM_SetCaption(str, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void sdlwrap_swap_buffers(void)
|
||||
{
|
||||
#if SDL_MODERN
|
||||
SDL_GL_SwapWindow(g_window);
|
||||
#else
|
||||
SDL_GL_SwapBuffers();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool sdlwrap_key_pressed(int key)
|
||||
{
|
||||
int num_keys;
|
||||
#if SDL_MODERN
|
||||
Uint8 *keymap = SDL_GetKeyboardState(&num_keys);
|
||||
key = SDL_GetScancodeFromKey(key);
|
||||
if (key >= num_keys)
|
||||
return false;
|
||||
|
||||
return keymap[key];
|
||||
#else
|
||||
Uint8 *keymap = SDL_GetKeyState(&num_keys);
|
||||
if (key >= num_keys)
|
||||
return false;
|
||||
|
||||
return keymap[key];
|
||||
#endif
|
||||
}
|
||||
|
||||
// 1.2 specific workaround for tiling WMs. In 1.3 we call GetSize directly, so we don't need to rely on
|
||||
// proper event handling (I hope).
|
||||
#if !defined(__APPLE__) && !defined(_WIN32) && !SDL_MODERN && !defined(XENON)
|
||||
static void sdlwrap_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
|
||||
|
||||
void sdlwrap_check_window(bool *quit,
|
||||
bool *resize, unsigned *width, unsigned *height, unsigned frame_count)
|
||||
{
|
||||
*quit = false;
|
||||
*resize = false;
|
||||
SDL_Event event;
|
||||
#if SDL_MODERN
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_QUIT:
|
||||
*quit = true;
|
||||
return;
|
||||
|
||||
case SDL_WINDOWEVENT:
|
||||
switch (event.window.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_CLOSE:
|
||||
*quit = true;
|
||||
return;
|
||||
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
*resize = true;
|
||||
*width = event.window.data1;
|
||||
*height = event.window.data2;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!*resize)
|
||||
{
|
||||
int w, h;
|
||||
SDL_GetWindowSize(g_window, &w, &h);
|
||||
if (*width != (unsigned)w || *height != (unsigned)h)
|
||||
{
|
||||
*resize = true;
|
||||
*width = w;
|
||||
*height = h;
|
||||
}
|
||||
}
|
||||
#else
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_QUIT:
|
||||
*quit = true;
|
||||
break;
|
||||
|
||||
case SDL_VIDEORESIZE:
|
||||
*resize = true;
|
||||
*width = event.resize.w;
|
||||
*height = event.resize.h;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(__APPLE__) && !defined(_WIN32) && !defined(XENON)
|
||||
// Hack to workaround limitations in tiling WMs ...
|
||||
if (!*resize && !g_fullscreen)
|
||||
{
|
||||
unsigned new_width, new_height;
|
||||
sdlwrap_get_window_size(&new_width, &new_height);
|
||||
if ((new_width != *width || new_height != *height) || (frame_count == 10)) // Ugly hack :D
|
||||
{
|
||||
*resize = true;
|
||||
*width = new_width;
|
||||
*height = new_height;
|
||||
SSNES_LOG("GL: Verified window size: %u x %u\n", *width, *height);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
bool sdlwrap_get_wm_info(SDL_SysWMinfo *info)
|
||||
{
|
||||
#ifdef XENON
|
||||
(void)info;
|
||||
return false;
|
||||
#elif SDL_MODERN
|
||||
if (g_window)
|
||||
return SDL_GetWindowWMInfo(g_window, info);
|
||||
else
|
||||
return SDL_GetWMInfo(info) == 1;
|
||||
#else
|
||||
return SDL_GetWMInfo(info) == 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool sdlwrap_window_has_focus(void)
|
||||
{
|
||||
#if SDL_MODERN
|
||||
Uint32 flags = SDL_GetWindowFlags(g_window);
|
||||
flags &= SDL_WINDOW_INPUT_FOCUS;
|
||||
return flags == SDL_WINDOW_INPUT_FOCUS;
|
||||
#else
|
||||
return (SDL_GetAppState() & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) == (SDL_APPINPUTFOCUS | SDL_APPACTIVE);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
// Compatibility wrapper between SDL 1.2/1.3 for OpenGL.
|
||||
// Wraps functions which differ in 1.2 and 1.3.
|
||||
|
||||
#ifndef __SDLWRAP_H
|
||||
#define __SDLWRAP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "SDL.h"
|
||||
#include "SDL_version.h"
|
||||
#include "SDL_syswm.h"
|
||||
|
||||
#if SDL_VERSION_ATLEAST(1, 3, 0)
|
||||
#define SDL_MODERN 1
|
||||
#else
|
||||
#define SDL_MODERN 0
|
||||
#endif
|
||||
|
||||
// Not legal to cast (void*) to fn-pointer. Need workaround to be compliant.
|
||||
#define SDL_SYM_WRAP(sym, symbol) { \
|
||||
assert(sizeof(void*) == sizeof(void (*)(void))); \
|
||||
void *sym__ = SDL_GL_GetProcAddress(symbol); \
|
||||
memcpy(&(sym), &sym__, sizeof(void*)); \
|
||||
}
|
||||
|
||||
void sdlwrap_set_swap_interval(unsigned interval, bool inited);
|
||||
|
||||
bool sdlwrap_set_video_mode(
|
||||
unsigned width, unsigned height,
|
||||
unsigned bits, bool fullscreen);
|
||||
|
||||
bool sdlwrap_init(void);
|
||||
void sdlwrap_destroy(void);
|
||||
|
||||
void sdlwrap_wm_set_caption(const char *str);
|
||||
|
||||
void sdlwrap_swap_buffers(void);
|
||||
|
||||
bool sdlwrap_key_pressed(int key);
|
||||
|
||||
void sdlwrap_check_window(bool *quit,
|
||||
bool *resize, unsigned *width, unsigned *height, unsigned frame_count);
|
||||
|
||||
void sdlwrap_set_resize(unsigned width, unsigned height);
|
||||
|
||||
bool sdlwrap_get_wm_info(SDL_SysWMinfo *info);
|
||||
|
||||
bool sdlwrap_window_has_focus(void);
|
||||
|
||||
#endif
|
||||
|
||||
+230
-136
@@ -18,12 +18,12 @@
|
||||
#include "shader_cg.h"
|
||||
#include <Cg/cg.h>
|
||||
#include <Cg/cgGL.h>
|
||||
#include "general.h"
|
||||
#include "../general.h"
|
||||
#include <string.h>
|
||||
#include "strl.h"
|
||||
#include "conf/config_file.h"
|
||||
#include "../strl.h"
|
||||
#include "../conf/config_file.h"
|
||||
#include "image.h"
|
||||
#include "dynamic.h"
|
||||
#include "../dynamic.h"
|
||||
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
#include "snes_state.h"
|
||||
@@ -32,25 +32,28 @@
|
||||
//#define SSNES_CG_DEBUG
|
||||
|
||||
// Used when we call deactivate() since just unbinding the program didn't seem to work... :(
|
||||
static const char* stock_cg_program =
|
||||
static const char *stock_cg_program =
|
||||
"void main_vertex"
|
||||
"("
|
||||
" float4 position : POSITION,"
|
||||
" float2 texCoord : TEXCOORD0,"
|
||||
" float4 color : COLOR,"
|
||||
""
|
||||
" uniform float4x4 modelViewProj,"
|
||||
""
|
||||
" out float4 oPosition : POSITION,"
|
||||
" out float2 otexCoord : TEXCOORD0"
|
||||
" out float2 otexCoord : TEXCOORD0,"
|
||||
" out float4 oColor : COLOR"
|
||||
")"
|
||||
"{"
|
||||
" oPosition = mul(modelViewProj, position);"
|
||||
" otexCoord = texCoord;"
|
||||
" oColor = color;"
|
||||
"}"
|
||||
""
|
||||
"float4 main_fragment(float2 tex : TEXCOORD0, uniform sampler2D s0 : TEXUNIT0) : COLOR"
|
||||
"float4 main_fragment(in float4 color : COLOR, float2 tex : TEXCOORD0, uniform sampler2D s0 : TEXUNIT0) : COLOR"
|
||||
"{"
|
||||
" return tex2D(s0, tex);"
|
||||
" return color * tex2D(s0, tex);"
|
||||
"}";
|
||||
|
||||
#ifdef SSNES_CG_DEBUG
|
||||
@@ -92,6 +95,7 @@ struct cg_fbo_params
|
||||
#define MAX_SHADERS 16
|
||||
#define MAX_TEXTURES 8
|
||||
#define MAX_VARIABLES 64
|
||||
#define PREV_TEXTURES 7
|
||||
|
||||
struct cg_program
|
||||
{
|
||||
@@ -101,15 +105,17 @@ struct cg_program
|
||||
CGparameter tex_size_f;
|
||||
CGparameter out_size_f;
|
||||
CGparameter frame_cnt_f;
|
||||
CGparameter frame_dir_f;
|
||||
CGparameter vid_size_v;
|
||||
CGparameter tex_size_v;
|
||||
CGparameter out_size_v;
|
||||
CGparameter frame_cnt_v;
|
||||
CGparameter frame_dir_v;
|
||||
CGparameter mvp;
|
||||
|
||||
struct cg_fbo_params fbo[MAX_SHADERS];
|
||||
struct cg_fbo_params orig;
|
||||
struct cg_fbo_params prev;
|
||||
struct cg_fbo_params prev[PREV_TEXTURES];
|
||||
};
|
||||
|
||||
#define FILTER_UNSPEC 0
|
||||
@@ -128,7 +134,19 @@ static unsigned lut_textures[MAX_TEXTURES];
|
||||
static unsigned lut_textures_num = 0;
|
||||
static char lut_textures_uniform[MAX_TEXTURES][64];
|
||||
|
||||
static CGparameter cg_attribs[PREV_TEXTURES + 1 + MAX_SHADERS];
|
||||
static unsigned cg_attrib_index;
|
||||
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
static snes_tracker_t *snes_tracker = NULL;
|
||||
#endif
|
||||
|
||||
static void gl_cg_reset_attrib(void)
|
||||
{
|
||||
for (unsigned i = 0; i < cg_attrib_index; i++)
|
||||
cgGLDisableClientState(cg_attribs[i]);
|
||||
cg_attrib_index = 0;
|
||||
}
|
||||
|
||||
void gl_cg_set_proj_matrix(void)
|
||||
{
|
||||
@@ -150,113 +168,124 @@ void gl_cg_set_params(unsigned width, unsigned height,
|
||||
const struct gl_tex_info *fbo_info,
|
||||
unsigned fbo_info_cnt)
|
||||
{
|
||||
if (cg_active && active_index > 0)
|
||||
if (!cg_active || (active_index == 0))
|
||||
return;
|
||||
|
||||
// Set frame.
|
||||
set_param_2f(prg[active_index].vid_size_f, width, height);
|
||||
set_param_2f(prg[active_index].tex_size_f, tex_width, tex_height);
|
||||
set_param_2f(prg[active_index].out_size_f, out_width, out_height);
|
||||
set_param_1f(prg[active_index].frame_cnt_f, (float)frame_count);
|
||||
set_param_1f(prg[active_index].frame_dir_f, g_extern.frame_is_reverse ? -1.0 : 1.0);
|
||||
|
||||
set_param_2f(prg[active_index].vid_size_v, width, height);
|
||||
set_param_2f(prg[active_index].tex_size_v, tex_width, tex_height);
|
||||
set_param_2f(prg[active_index].out_size_v, out_width, out_height);
|
||||
set_param_1f(prg[active_index].frame_cnt_v, (float)frame_count);
|
||||
set_param_1f(prg[active_index].frame_dir_v, g_extern.frame_is_reverse ? -1.0 : 1.0);
|
||||
|
||||
// Set orig texture.
|
||||
CGparameter param = prg[active_index].orig.tex;
|
||||
if (param)
|
||||
{
|
||||
// Set frame.
|
||||
set_param_2f(prg[active_index].vid_size_f, width, height);
|
||||
set_param_2f(prg[active_index].tex_size_f, tex_width, tex_height);
|
||||
set_param_2f(prg[active_index].out_size_f, out_width, out_height);
|
||||
set_param_1f(prg[active_index].frame_cnt_f, (float)frame_count);
|
||||
cgGLSetTextureParameter(param, info->tex);
|
||||
cgGLEnableTextureParameter(param);
|
||||
}
|
||||
|
||||
set_param_2f(prg[active_index].vid_size_v, width, height);
|
||||
set_param_2f(prg[active_index].tex_size_v, tex_width, tex_height);
|
||||
set_param_2f(prg[active_index].out_size_v, out_width, out_height);
|
||||
set_param_1f(prg[active_index].frame_cnt_v, (float)frame_count);
|
||||
set_param_2f(prg[active_index].orig.vid_size_v, info->input_size[0], info->input_size[1]);
|
||||
set_param_2f(prg[active_index].orig.vid_size_f, info->input_size[0], info->input_size[1]);
|
||||
set_param_2f(prg[active_index].orig.tex_size_v, info->tex_size[0], info->tex_size[1]);
|
||||
set_param_2f(prg[active_index].orig.tex_size_f, info->tex_size[0], info->tex_size[1]);
|
||||
if (prg[active_index].orig.coord)
|
||||
{
|
||||
cgGLSetParameterPointer(prg[active_index].orig.coord, 2, GL_FLOAT, 0, info->coord);
|
||||
cgGLEnableClientState(prg[active_index].orig.coord);
|
||||
cg_attribs[cg_attrib_index++] = prg[active_index].orig.coord;
|
||||
}
|
||||
|
||||
// Set orig texture.
|
||||
CGparameter param = prg[active_index].orig.tex;
|
||||
// Set prev textures.
|
||||
for (unsigned i = 0; i < PREV_TEXTURES; i++)
|
||||
{
|
||||
param = prg[active_index].prev[i].tex;
|
||||
if (param)
|
||||
{
|
||||
cgGLSetTextureParameter(param, info->tex);
|
||||
cgGLSetTextureParameter(param, prev_info[i].tex);
|
||||
cgGLEnableTextureParameter(param);
|
||||
}
|
||||
|
||||
set_param_2f(prg[active_index].orig.vid_size_v, info->input_size[0], info->input_size[1]);
|
||||
set_param_2f(prg[active_index].orig.vid_size_f, info->input_size[0], info->input_size[1]);
|
||||
set_param_2f(prg[active_index].orig.tex_size_v, info->tex_size[0], info->tex_size[1]);
|
||||
set_param_2f(prg[active_index].orig.tex_size_f, info->tex_size[0], info->tex_size[1]);
|
||||
if (prg[active_index].orig.coord)
|
||||
{
|
||||
cgGLSetParameterPointer(prg[active_index].orig.coord, 2, GL_FLOAT, 0, info->coord);
|
||||
cgGLEnableClientState(prg[active_index].orig.coord);
|
||||
}
|
||||
set_param_2f(prg[active_index].prev[i].vid_size_v, prev_info[i].input_size[0], prev_info[i].input_size[1]);
|
||||
set_param_2f(prg[active_index].prev[i].vid_size_f, prev_info[i].input_size[0], prev_info[i].input_size[1]);
|
||||
set_param_2f(prg[active_index].prev[i].tex_size_v, prev_info[i].tex_size[0], prev_info[i].tex_size[1]);
|
||||
set_param_2f(prg[active_index].prev[i].tex_size_f, prev_info[i].tex_size[0], prev_info[i].tex_size[1]);
|
||||
|
||||
// Set prev texture
|
||||
param = prg[active_index].prev.tex;
|
||||
if (prg[active_index].prev[i].coord)
|
||||
{
|
||||
cgGLSetParameterPointer(prg[active_index].prev[i].coord, 2, GL_FLOAT, 0, prev_info[i].coord);
|
||||
cgGLEnableClientState(prg[active_index].prev[i].coord);
|
||||
cg_attribs[cg_attrib_index++] = prg[active_index].prev[i].coord;
|
||||
}
|
||||
}
|
||||
|
||||
// Set lookup textures.
|
||||
for (unsigned i = 0; i < lut_textures_num; i++)
|
||||
{
|
||||
CGparameter param = cgGetNamedParameter(prg[active_index].fprg, lut_textures_uniform[i]);
|
||||
if (param)
|
||||
{
|
||||
cgGLSetTextureParameter(param, prev_info->tex);
|
||||
cgGLSetTextureParameter(param, lut_textures[i]);
|
||||
cgGLEnableTextureParameter(param);
|
||||
}
|
||||
}
|
||||
|
||||
set_param_2f(prg[active_index].prev.vid_size_v, prev_info->input_size[0], prev_info->input_size[1]);
|
||||
set_param_2f(prg[active_index].prev.vid_size_f, prev_info->input_size[0], prev_info->input_size[1]);
|
||||
set_param_2f(prg[active_index].prev.tex_size_v, prev_info->tex_size[0], prev_info->tex_size[1]);
|
||||
set_param_2f(prg[active_index].prev.tex_size_f, prev_info->tex_size[0], prev_info->tex_size[1]);
|
||||
if (prg[active_index].prev.coord)
|
||||
// Set FBO textures.
|
||||
if (active_index > 2)
|
||||
{
|
||||
for (unsigned i = 0; i < fbo_info_cnt; i++)
|
||||
{
|
||||
cgGLSetParameterPointer(prg[active_index].prev.coord, 2, GL_FLOAT, 0, prev_info->coord);
|
||||
cgGLEnableClientState(prg[active_index].prev.coord);
|
||||
}
|
||||
|
||||
// Set lookup textures.
|
||||
for (unsigned i = 0; i < lut_textures_num; i++)
|
||||
{
|
||||
CGparameter param = cgGetNamedParameter(prg[active_index].fprg, lut_textures_uniform[i]);
|
||||
if (param)
|
||||
if (prg[active_index].fbo[i].tex)
|
||||
{
|
||||
cgGLSetTextureParameter(param, lut_textures[i]);
|
||||
cgGLEnableTextureParameter(param);
|
||||
cgGLSetTextureParameter(prg[active_index].fbo[i].tex, fbo_info[i].tex);
|
||||
cgGLEnableTextureParameter(prg[active_index].fbo[i].tex);
|
||||
}
|
||||
}
|
||||
|
||||
// Set FBO textures.
|
||||
if (active_index > 2)
|
||||
{
|
||||
for (unsigned i = 0; i < fbo_info_cnt; i++)
|
||||
|
||||
set_param_2f(prg[active_index].fbo[i].vid_size_v,
|
||||
fbo_info[i].input_size[0], fbo_info[i].input_size[1]);
|
||||
set_param_2f(prg[active_index].fbo[i].vid_size_f,
|
||||
fbo_info[i].input_size[0], fbo_info[i].input_size[1]);
|
||||
|
||||
set_param_2f(prg[active_index].fbo[i].tex_size_v,
|
||||
fbo_info[i].tex_size[0], fbo_info[i].tex_size[1]);
|
||||
set_param_2f(prg[active_index].fbo[i].tex_size_f,
|
||||
fbo_info[i].tex_size[0], fbo_info[i].tex_size[1]);
|
||||
|
||||
if (prg[active_index].fbo[i].coord)
|
||||
{
|
||||
if (prg[active_index].fbo[i].tex)
|
||||
{
|
||||
cgGLSetTextureParameter(prg[active_index].fbo[i].tex, fbo_info[i].tex);
|
||||
cgGLEnableTextureParameter(prg[active_index].fbo[i].tex);
|
||||
}
|
||||
|
||||
set_param_2f(prg[active_index].fbo[i].vid_size_v,
|
||||
fbo_info[i].input_size[0], fbo_info[i].input_size[1]);
|
||||
set_param_2f(prg[active_index].fbo[i].vid_size_f,
|
||||
fbo_info[i].input_size[0], fbo_info[i].input_size[1]);
|
||||
|
||||
set_param_2f(prg[active_index].fbo[i].tex_size_v,
|
||||
fbo_info[i].tex_size[0], fbo_info[i].tex_size[1]);
|
||||
set_param_2f(prg[active_index].fbo[i].tex_size_f,
|
||||
fbo_info[i].tex_size[0], fbo_info[i].tex_size[1]);
|
||||
|
||||
if (prg[active_index].fbo[i].coord)
|
||||
{
|
||||
cgGLSetParameterPointer(prg[active_index].fbo[i].coord, 2, GL_FLOAT, 0, fbo_info[i].coord);
|
||||
cgGLEnableClientState(prg[active_index].fbo[i].coord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set state parameters
|
||||
if (snes_tracker)
|
||||
{
|
||||
static struct snes_tracker_uniform info[MAX_VARIABLES];
|
||||
static unsigned cnt = 0;
|
||||
|
||||
if (active_index == 1)
|
||||
cnt = snes_get_uniform(snes_tracker, info, MAX_VARIABLES, frame_count);
|
||||
|
||||
for (unsigned i = 0; i < cnt; i++)
|
||||
{
|
||||
CGparameter param_v = cgGetNamedParameter(prg[active_index].vprg, info[i].id);
|
||||
CGparameter param_f = cgGetNamedParameter(prg[active_index].fprg, info[i].id);
|
||||
set_param_1f(param_v, info[i].value);
|
||||
set_param_1f(param_f, info[i].value);
|
||||
cgGLSetParameterPointer(prg[active_index].fbo[i].coord, 2, GL_FLOAT, 0, fbo_info[i].coord);
|
||||
cgGLEnableClientState(prg[active_index].fbo[i].coord);
|
||||
cg_attribs[cg_attrib_index++] = prg[active_index].fbo[i].coord;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
// Set state parameters
|
||||
if (snes_tracker)
|
||||
{
|
||||
static struct snes_tracker_uniform info[MAX_VARIABLES];
|
||||
static unsigned cnt = 0;
|
||||
|
||||
if (active_index == 1)
|
||||
cnt = snes_get_uniform(snes_tracker, info, MAX_VARIABLES, frame_count);
|
||||
|
||||
for (unsigned i = 0; i < cnt; i++)
|
||||
{
|
||||
CGparameter param_v = cgGetNamedParameter(prg[active_index].vprg, info[i].id);
|
||||
CGparameter param_f = cgGetNamedParameter(prg[active_index].fprg, info[i].id);
|
||||
set_param_1f(param_v, info[i].value);
|
||||
set_param_1f(param_f, info[i].value);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void gl_cg_deinit(void)
|
||||
@@ -264,6 +293,8 @@ void gl_cg_deinit(void)
|
||||
if (!cg_active)
|
||||
return;
|
||||
|
||||
gl_cg_reset_attrib();
|
||||
|
||||
cg_active = false;
|
||||
cg_shader_num = 0;
|
||||
memset(prg, 0, sizeof(prg));
|
||||
@@ -273,46 +304,58 @@ void gl_cg_deinit(void)
|
||||
glDeleteTextures(lut_textures_num, lut_textures);
|
||||
lut_textures_num = 0;
|
||||
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
if (snes_tracker)
|
||||
{
|
||||
snes_tracker_free(snes_tracker);
|
||||
snes_tracker = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef __CELLOS_LV2__
|
||||
cgDestroyContext(cgCtx);
|
||||
#endif
|
||||
}
|
||||
|
||||
#define SET_LISTING_INDEX(type, index) \
|
||||
{ \
|
||||
const char *list = cgGetLastListing(cgCtx); \
|
||||
if (list) \
|
||||
listing_##type[index] = strdup(list); \
|
||||
}
|
||||
|
||||
#define SET_LISTING(type) \
|
||||
{ \
|
||||
const char *list = cgGetLastListing(cgCtx); \
|
||||
if (list) \
|
||||
listing_##type = strdup(list); \
|
||||
}
|
||||
|
||||
static bool load_plain(const char *path)
|
||||
{
|
||||
SSNES_LOG("Loading Cg file: %s\n", path);
|
||||
if (strlen(g_settings.video.second_pass_shader) > 0)
|
||||
SSNES_LOG("Loading 2nd pass: %s\n", g_settings.video.second_pass_shader);
|
||||
|
||||
char *listing[3] = {NULL};
|
||||
const char *list = NULL;
|
||||
char *listing_v[3] = {NULL};
|
||||
char *listing_f[3] = {NULL};
|
||||
|
||||
prg[0].fprg = cgCreateProgram(cgCtx, CG_SOURCE, stock_cg_program, cgFProf, "main_fragment", 0);
|
||||
SET_LISTING_INDEX(f, 0);
|
||||
prg[0].vprg = cgCreateProgram(cgCtx, CG_SOURCE, stock_cg_program, cgVProf, "main_vertex", 0);
|
||||
|
||||
list = cgGetLastListing(cgCtx);
|
||||
if (list)
|
||||
listing[0] = strdup(list);
|
||||
SET_LISTING_INDEX(v, 0);
|
||||
|
||||
prg[1].fprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, path, cgFProf, "main_fragment", 0);
|
||||
SET_LISTING_INDEX(f, 1);
|
||||
prg[1].vprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, path, cgVProf, "main_vertex", 0);
|
||||
SET_LISTING_INDEX(v, 1);
|
||||
|
||||
list = cgGetLastListing(cgCtx);
|
||||
if (list)
|
||||
listing[1] = strdup(list);
|
||||
|
||||
if (strlen(g_settings.video.second_pass_shader) > 0)
|
||||
if (*g_settings.video.second_pass_shader && g_settings.video.render_to_texture)
|
||||
{
|
||||
SSNES_LOG("Loading 2nd pass: %s\n", g_settings.video.second_pass_shader);
|
||||
prg[2].fprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, g_settings.video.second_pass_shader, cgFProf, "main_fragment", 0);
|
||||
SET_LISTING_INDEX(f, 2);
|
||||
prg[2].vprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, g_settings.video.second_pass_shader, cgVProf, "main_vertex", 0);
|
||||
SET_LISTING_INDEX(v, 2);
|
||||
|
||||
list = cgGetLastListing(cgCtx);
|
||||
if (list)
|
||||
listing[2] = strdup(list);
|
||||
cg_shader_num = 2;
|
||||
}
|
||||
else
|
||||
@@ -327,8 +370,10 @@ static bool load_plain(const char *path)
|
||||
{
|
||||
CGerror err = cgGetError();
|
||||
SSNES_ERR("CG error: %s\n", cgGetErrorString(err));
|
||||
if (listing[i])
|
||||
SSNES_ERR("%s\n", listing[i]);
|
||||
if (listing_v[i])
|
||||
SSNES_ERR("Vertex:\n%s\n", listing_v[i]);
|
||||
else if (listing_f[i])
|
||||
SSNES_ERR("Fragment:\n%s\n", listing_f[i]);
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -336,13 +381,19 @@ static bool load_plain(const char *path)
|
||||
cgGLLoadProgram(prg[i].vprg);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 3; i++)
|
||||
{
|
||||
free(listing_v[i]);
|
||||
free(listing_f[i]);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
for (unsigned i = 0; i < 3; i++)
|
||||
{
|
||||
if (listing[i])
|
||||
free(listing[i]);
|
||||
free(listing_v[i]);
|
||||
free(listing_f[i]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -395,8 +446,10 @@ static bool load_textures(const char *dir_path, config_file_t *conf)
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, smooth ? GL_LINEAR : GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, smooth ? GL_LINEAR : GL_NEAREST);
|
||||
|
||||
#ifndef __CELLOS_LV2__
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, img.width);
|
||||
#endif
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0, GL_RGBA, img.width, img.height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, img.pixels);
|
||||
|
||||
@@ -488,8 +541,8 @@ static bool load_imports(const char *dir_path, config_file_t *conf)
|
||||
unsigned addr = 0;
|
||||
#ifdef HAVE_PYTHON
|
||||
if (tracker_type != SSNES_STATE_PYTHON)
|
||||
{
|
||||
#endif
|
||||
{
|
||||
unsigned input_slot = 0;
|
||||
if (config_get_hex(conf, input_slot_buf, &input_slot))
|
||||
{
|
||||
@@ -523,11 +576,9 @@ static bool load_imports(const char *dir_path, config_file_t *conf)
|
||||
SSNES_ERR("No address assigned to semantic.\n");
|
||||
goto error;
|
||||
}
|
||||
#ifdef HAVE_PYTHON
|
||||
}
|
||||
#endif
|
||||
|
||||
int memtype = 0;
|
||||
unsigned memtype;
|
||||
switch (ram_type)
|
||||
{
|
||||
case SSNES_STATE_WRAM:
|
||||
@@ -547,10 +598,10 @@ static bool load_imports(const char *dir_path, config_file_t *conf)
|
||||
break;
|
||||
|
||||
default:
|
||||
memtype = SNES_MEMORY_WRAM;
|
||||
memtype = -1u;
|
||||
}
|
||||
|
||||
if (addr >= psnes_get_memory_size(memtype))
|
||||
if ((memtype != -1u) && (addr >= psnes_get_memory_size(memtype)))
|
||||
{
|
||||
SSNES_ERR("Address out of bounds.\n");
|
||||
goto error;
|
||||
@@ -579,7 +630,7 @@ static bool load_imports(const char *dir_path, config_file_t *conf)
|
||||
.oam = psnes_get_memory_data(SNES_MEMORY_OAM),
|
||||
.apuram = psnes_get_memory_data(SNES_MEMORY_APURAM),
|
||||
.info = info,
|
||||
.info_elem = info_cnt
|
||||
.info_elem = info_cnt,
|
||||
};
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
@@ -611,7 +662,6 @@ static bool load_imports(const char *dir_path, config_file_t *conf)
|
||||
#endif
|
||||
|
||||
free(imports);
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
@@ -721,8 +771,8 @@ static bool load_preset(const char *path)
|
||||
scale->type_y = SSNES_SCALE_INPUT;
|
||||
scale->scale_x = 1.0;
|
||||
scale->scale_y = 1.0;
|
||||
scale->abs_x = 256;
|
||||
scale->abs_y = 224;
|
||||
scale->abs_x = g_extern.system.geom.base_width;
|
||||
scale->abs_y = g_extern.system.geom.base_height;
|
||||
|
||||
if (strcmp(scale_type_x, "source") == 0)
|
||||
scale->type_x = SSNES_SCALE_INPUT;
|
||||
@@ -805,7 +855,7 @@ static bool load_preset(const char *path)
|
||||
}
|
||||
|
||||
// Basedir.
|
||||
char dir_path[256];
|
||||
char dir_path[MAXPATHLEN];
|
||||
strlcpy(dir_path, path, sizeof(dir_path));
|
||||
char *ptr = strrchr(dir_path, '/');
|
||||
if (!ptr) ptr = strrchr(dir_path, '\\');
|
||||
@@ -819,7 +869,7 @@ static bool load_preset(const char *path)
|
||||
{
|
||||
char *shader_path;
|
||||
char attr_buf[64];
|
||||
char path_buf[512];
|
||||
char path_buf[MAXPATHLEN];
|
||||
|
||||
print_buf(attr_buf, "shader%u", i);
|
||||
if (config_get_string(conf, attr_buf, &shader_path))
|
||||
@@ -837,19 +887,31 @@ static bool load_preset(const char *path)
|
||||
SSNES_LOG("Loading Cg shader: \"%s\".\n", path_buf);
|
||||
|
||||
struct cg_program *prog = &prg[i + 1];
|
||||
|
||||
char *listing_f = NULL;
|
||||
prog->fprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, path_buf, cgFProf, "main_fragment", 0);
|
||||
SET_LISTING(f);
|
||||
|
||||
char *listing_v = NULL;
|
||||
prog->vprg = cgCreateProgramFromFile(cgCtx, CG_SOURCE, path_buf, cgVProf, "main_vertex", 0);
|
||||
SET_LISTING(v);
|
||||
|
||||
if (!prog->fprg || !prog->vprg)
|
||||
{
|
||||
const char *listing = cgGetLastListing(cgCtx);
|
||||
CGerror err = cgGetError();
|
||||
SSNES_ERR("CG error: %s\n", cgGetErrorString(err));
|
||||
if (listing)
|
||||
SSNES_ERR("%s\n", listing);
|
||||
if (listing_v)
|
||||
SSNES_ERR("Vertex:\n%s\n", listing_v);
|
||||
else if (listing_f)
|
||||
SSNES_ERR("Fragment:\n%s\n", listing_f);
|
||||
|
||||
free(listing_f);
|
||||
free(listing_v);
|
||||
goto error;
|
||||
}
|
||||
|
||||
free(listing_f);
|
||||
free(listing_v);
|
||||
cgGLLoadProgram(prog->fprg);
|
||||
cgGLLoadProgram(prog->vprg);
|
||||
}
|
||||
@@ -882,6 +944,9 @@ error:
|
||||
|
||||
bool gl_cg_init(const char *path)
|
||||
{
|
||||
#ifdef __CELLOS_LV2__
|
||||
cgRTCgcInit();
|
||||
#endif
|
||||
cgCtx = cgCreateContext();
|
||||
if (cgCtx == NULL)
|
||||
{
|
||||
@@ -930,10 +995,12 @@ bool gl_cg_init(const char *path)
|
||||
prg[i].tex_size_f = cgGetNamedParameter(prg[i].fprg, "IN.texture_size");
|
||||
prg[i].out_size_f = cgGetNamedParameter(prg[i].fprg, "IN.output_size");
|
||||
prg[i].frame_cnt_f = cgGetNamedParameter(prg[i].fprg, "IN.frame_count");
|
||||
prg[i].frame_dir_f = cgGetNamedParameter(prg[i].fprg, "IN.frame_direction");
|
||||
prg[i].vid_size_v = cgGetNamedParameter(prg[i].vprg, "IN.video_size");
|
||||
prg[i].tex_size_v = cgGetNamedParameter(prg[i].vprg, "IN.texture_size");
|
||||
prg[i].out_size_v = cgGetNamedParameter(prg[i].vprg, "IN.output_size");
|
||||
prg[i].frame_cnt_v = cgGetNamedParameter(prg[i].vprg, "IN.frame_count");
|
||||
prg[i].frame_dir_v = cgGetNamedParameter(prg[i].vprg, "IN.frame_direction");
|
||||
prg[i].mvp = cgGetNamedParameter(prg[i].vprg, "modelViewProj");
|
||||
if (prg[i].mvp)
|
||||
cgGLSetStateMatrixParameter(prg[i].mvp, CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);
|
||||
@@ -945,12 +1012,37 @@ bool gl_cg_init(const char *path)
|
||||
prg[i].orig.tex_size_f = cgGetNamedParameter(prg[i].fprg, "ORIG.texture_size");
|
||||
prg[i].orig.coord = cgGetNamedParameter(prg[i].vprg, "ORIG.tex_coord");
|
||||
|
||||
prg[i].prev.tex = cgGetNamedParameter(prg[i].fprg, "PREV.texture");
|
||||
prg[i].prev.vid_size_v = cgGetNamedParameter(prg[i].vprg, "PREV.video_size");
|
||||
prg[i].prev.vid_size_f = cgGetNamedParameter(prg[i].fprg, "PREV.video_size");
|
||||
prg[i].prev.tex_size_v = cgGetNamedParameter(prg[i].vprg, "PREV.texture_size");
|
||||
prg[i].prev.tex_size_f = cgGetNamedParameter(prg[i].fprg, "PREV.texture_size");
|
||||
prg[i].prev.coord = cgGetNamedParameter(prg[i].vprg, "PREV.tex_coord");
|
||||
for (unsigned j = 0; j < PREV_TEXTURES; j++)
|
||||
{
|
||||
char attr_buf_tex[64];
|
||||
char attr_buf_vid_size[64];
|
||||
char attr_buf_tex_size[64];
|
||||
char attr_buf_coord[64];
|
||||
static const char *prev_names[PREV_TEXTURES] = {
|
||||
"PREV",
|
||||
"PREV1",
|
||||
"PREV2",
|
||||
"PREV3",
|
||||
"PREV4",
|
||||
"PREV5",
|
||||
"PREV6",
|
||||
};
|
||||
|
||||
snprintf(attr_buf_tex, sizeof(attr_buf_tex), "%s.texture", prev_names[j]);
|
||||
snprintf(attr_buf_vid_size, sizeof(attr_buf_vid_size), "%s.video_size", prev_names[j]);
|
||||
snprintf(attr_buf_tex_size, sizeof(attr_buf_tex_size), "%s.texture_size", prev_names[j]);
|
||||
snprintf(attr_buf_coord, sizeof(attr_buf_coord), "%s.tex_coord", prev_names[j]);
|
||||
|
||||
prg[i].prev[j].tex = cgGetNamedParameter(prg[i].fprg, attr_buf_tex);
|
||||
|
||||
prg[i].prev[j].vid_size_v = cgGetNamedParameter(prg[i].vprg, attr_buf_vid_size);
|
||||
prg[i].prev[j].vid_size_f = cgGetNamedParameter(prg[i].fprg, attr_buf_vid_size);
|
||||
|
||||
prg[i].prev[j].tex_size_v = cgGetNamedParameter(prg[i].vprg, attr_buf_tex_size);
|
||||
prg[i].prev[j].tex_size_f = cgGetNamedParameter(prg[i].fprg, attr_buf_tex_size);
|
||||
|
||||
prg[i].prev[j].coord = cgGetNamedParameter(prg[i].vprg, attr_buf_coord);
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < i - 1; j++)
|
||||
{
|
||||
@@ -983,6 +1075,8 @@ void gl_cg_use(unsigned index)
|
||||
{
|
||||
if (cg_active && prg[index].vprg && prg[index].fprg)
|
||||
{
|
||||
gl_cg_reset_attrib();
|
||||
|
||||
active_index = index;
|
||||
cgGLBindProgram(prg[index].vprg);
|
||||
cgGLBindProgram(prg[index].fprg);
|
||||
|
||||
+207
-130
@@ -17,11 +17,11 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include "general.h"
|
||||
#include "../general.h"
|
||||
#include "shader_glsl.h"
|
||||
#include "strl.h"
|
||||
#include "../strl.h"
|
||||
#include "snes_state.h"
|
||||
#include "dynamic.h"
|
||||
#include "../dynamic.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <OpenGL/gl.h>
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
|
||||
#define NO_SDL_GLEXT
|
||||
#include "SDL.h"
|
||||
#include "sdlwrap.h"
|
||||
#include "SDL_opengl.h"
|
||||
#include <stdlib.h>
|
||||
#include <libxml/parser.h>
|
||||
@@ -55,6 +55,7 @@
|
||||
#define pglLinkProgram glLinkProgram
|
||||
#define pglGetUniformLocation glGetUniformLocation
|
||||
#define pglUniform1i glUniform1i
|
||||
#define pglUniform1f glUniform1f
|
||||
#define pglUniform2fv glUniform2fv
|
||||
#define pglUniform4fv glUniform4fv
|
||||
#define pglGetShaderiv glGetShaderiv
|
||||
@@ -65,6 +66,7 @@
|
||||
#define pglGetAttachedShaders glGetAttachedShaders
|
||||
#define pglGetAttribLocation glGetAttribLocation
|
||||
#define pglEnableVertexAttribArray glEnableVertexAttribArray
|
||||
#define pglDisableVertexAttribArray glDisableVertexAttribArray
|
||||
#define pglVertexAttribPointer glVertexAttribPointer
|
||||
#else
|
||||
static PFNGLCREATEPROGRAMPROC pglCreateProgram = NULL;
|
||||
@@ -78,6 +80,7 @@ static PFNGLDETACHSHADERPROC pglDetachShader = NULL;
|
||||
static PFNGLLINKPROGRAMPROC pglLinkProgram = NULL;
|
||||
static PFNGLGETUNIFORMLOCATIONPROC pglGetUniformLocation = NULL;
|
||||
static PFNGLUNIFORM1IPROC pglUniform1i = NULL;
|
||||
static PFNGLUNIFORM1FPROC pglUniform1f = NULL;
|
||||
static PFNGLUNIFORM2FVPROC pglUniform2fv = NULL;
|
||||
static PFNGLUNIFORM4FVPROC pglUniform4fv = NULL;
|
||||
static PFNGLGETSHADERIVPROC pglGetShaderiv = NULL;
|
||||
@@ -88,12 +91,14 @@ static PFNGLDELETEPROGRAMPROC pglDeleteProgram = NULL;
|
||||
static PFNGLGETATTACHEDSHADERSPROC pglGetAttachedShaders = NULL;
|
||||
static PFNGLGETATTRIBLOCATIONPROC pglGetAttribLocation = NULL;
|
||||
static PFNGLENABLEVERTEXATTRIBARRAYPROC pglEnableVertexAttribArray = NULL;
|
||||
static PFNGLDISABLEVERTEXATTRIBARRAYPROC pglDisableVertexAttribArray = NULL;
|
||||
static PFNGLVERTEXATTRIBPOINTERPROC pglVertexAttribPointer = NULL;
|
||||
#endif
|
||||
|
||||
#define MAX_PROGRAMS 16
|
||||
#define MAX_TEXTURES 8
|
||||
#define MAX_VARIABLES 256
|
||||
#define PREV_TEXTURES 7
|
||||
|
||||
enum filter_type
|
||||
{
|
||||
@@ -116,10 +121,14 @@ static char gl_teximage_uniforms[MAX_TEXTURES][64];
|
||||
static snes_tracker_t *gl_snes_tracker = NULL;
|
||||
static struct snes_tracker_uniform_info gl_tracker_info[MAX_VARIABLES];
|
||||
static unsigned gl_tracker_info_cnt = 0;
|
||||
static char gl_tracker_script[256];
|
||||
static char gl_tracker_script[MAXPATHLEN];
|
||||
static char gl_tracker_script_class[64];
|
||||
static xmlChar *gl_script_program = NULL;
|
||||
|
||||
static GLint gl_attribs[PREV_TEXTURES + 1 + MAX_PROGRAMS];
|
||||
static unsigned gl_attrib_index = 0;
|
||||
|
||||
|
||||
struct shader_program
|
||||
{
|
||||
char *vertex;
|
||||
@@ -136,6 +145,21 @@ struct shader_program
|
||||
bool valid_scale;
|
||||
};
|
||||
|
||||
static const char *stock_vertex =
|
||||
"varying vec4 color;\n"
|
||||
"void main() {\n"
|
||||
" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
|
||||
" gl_TexCoord[0] = gl_MultiTexCoord0;\n"
|
||||
" color = gl_Color;\n"
|
||||
"}";
|
||||
|
||||
static const char *stock_fragment =
|
||||
"uniform sampler2D rubyTexture;\n"
|
||||
"varying vec4 color;\n"
|
||||
"void main() {\n"
|
||||
" gl_FragColor = color * texture2D(rubyTexture, gl_TexCoord[0].xy);\n"
|
||||
"}";
|
||||
|
||||
static bool get_xml_attrs(struct shader_program *prog, xmlNodePtr ptr)
|
||||
{
|
||||
prog->scale_x = 1.0;
|
||||
@@ -325,7 +349,7 @@ static bool get_texture_image(const char *shader_path, xmlNodePtr ptr)
|
||||
if (filter && strcmp((const char*)filter, "nearest") == 0)
|
||||
linear = false;
|
||||
|
||||
char tex_path[256];
|
||||
char tex_path[MAXPATHLEN];
|
||||
strlcpy(tex_path, shader_path, sizeof(tex_path));
|
||||
|
||||
char *last = strrchr(tex_path, '/');
|
||||
@@ -481,13 +505,13 @@ static bool get_import_value(xmlNodePtr ptr)
|
||||
goto error;
|
||||
}
|
||||
|
||||
enum snes_ram_type ram_type = SSNES_STATE_WRAM;
|
||||
enum snes_ram_type ram_type = SSNES_STATE_NONE;
|
||||
uint32_t addr = 0;
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
if (tracker_type != SSNES_STATE_PYTHON)
|
||||
{
|
||||
#endif
|
||||
{
|
||||
if (input)
|
||||
{
|
||||
unsigned slot = strtoul((const char*)input, NULL, 0);
|
||||
@@ -515,11 +539,9 @@ static bool get_import_value(xmlNodePtr ptr)
|
||||
SSNES_ERR("No RAM address specificed for import value.\n");
|
||||
goto error;
|
||||
}
|
||||
#ifdef HAVE_PYTHON
|
||||
}
|
||||
#endif
|
||||
|
||||
int memtype = 0;
|
||||
unsigned memtype;
|
||||
switch (ram_type)
|
||||
{
|
||||
case SSNES_STATE_WRAM:
|
||||
@@ -539,10 +561,10 @@ static bool get_import_value(xmlNodePtr ptr)
|
||||
break;
|
||||
|
||||
default:
|
||||
memtype = SNES_MEMORY_WRAM;
|
||||
memtype = -1u;
|
||||
}
|
||||
|
||||
if (addr >= psnes_get_memory_size(memtype))
|
||||
if ((memtype != -1u) && (addr >= psnes_get_memory_size(memtype)))
|
||||
{
|
||||
SSNES_ERR("Address out of bounds.\n");
|
||||
goto error;
|
||||
@@ -825,6 +847,13 @@ static bool compile_programs(GLuint *gl_prog, struct shader_program *progs, size
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gl_glsl_reset_attrib(void)
|
||||
{
|
||||
for (unsigned i = 0; i < gl_attrib_index; i++)
|
||||
pglDisableVertexAttribArray(gl_attribs[i]);
|
||||
gl_attrib_index = 0;
|
||||
}
|
||||
|
||||
#define LOAD_GL_SYM(SYM) if (!pgl##SYM) { SDL_SYM_WRAP(pgl##SYM, "gl" #SYM) }
|
||||
|
||||
bool gl_glsl_init(const char *path)
|
||||
@@ -842,6 +871,7 @@ bool gl_glsl_init(const char *path)
|
||||
LOAD_GL_SYM(LinkProgram);
|
||||
LOAD_GL_SYM(GetUniformLocation);
|
||||
LOAD_GL_SYM(Uniform1i);
|
||||
LOAD_GL_SYM(Uniform1f);
|
||||
LOAD_GL_SYM(Uniform2fv);
|
||||
LOAD_GL_SYM(Uniform4fv);
|
||||
LOAD_GL_SYM(GetShaderiv);
|
||||
@@ -852,6 +882,7 @@ bool gl_glsl_init(const char *path)
|
||||
LOAD_GL_SYM(GetAttachedShaders);
|
||||
LOAD_GL_SYM(GetAttribLocation);
|
||||
LOAD_GL_SYM(EnableVertexAttribArray);
|
||||
LOAD_GL_SYM(DisableVertexAttribArray);
|
||||
LOAD_GL_SYM(VertexAttribPointer);
|
||||
#endif
|
||||
|
||||
@@ -862,19 +893,26 @@ bool gl_glsl_init(const char *path)
|
||||
bool shader_support = pglCreateProgram && pglUseProgram && pglCreateShader
|
||||
&& pglDeleteShader && pglShaderSource && pglCompileShader && pglAttachShader
|
||||
&& pglDetachShader && pglLinkProgram && pglGetUniformLocation
|
||||
&& pglUniform1i && pglUniform2fv && pglUniform4fv
|
||||
&& pglUniform1i && pglUniform1f && pglUniform2fv && pglUniform4fv
|
||||
&& pglGetShaderiv && pglGetShaderInfoLog && pglGetProgramiv && pglGetProgramInfoLog
|
||||
&& pglDeleteProgram && pglGetAttachedShaders
|
||||
&& pglGetAttribLocation && pglEnableVertexAttribArray
|
||||
&& pglGetAttribLocation && pglEnableVertexAttribArray && pglDisableVertexAttribArray
|
||||
&& pglVertexAttribPointer;
|
||||
#endif
|
||||
|
||||
if (!shader_support)
|
||||
{
|
||||
SSNES_ERR("GLSL shaders aren't supported by your GL driver.\n");
|
||||
SSNES_ERR("GLSL shaders aren't supported by your OpenGL driver.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct shader_program stock_prog = {
|
||||
.vertex = strdup(stock_vertex),
|
||||
.fragment = strdup(stock_fragment),
|
||||
};
|
||||
if (!compile_programs(&gl_program[0], &stock_prog, 1))
|
||||
return false;
|
||||
|
||||
struct shader_program progs[MAX_PROGRAMS];
|
||||
unsigned num_progs = get_xml_shaders(path, progs, MAX_PROGRAMS - 1);
|
||||
|
||||
@@ -900,7 +938,7 @@ bool gl_glsl_init(const char *path)
|
||||
return false;
|
||||
|
||||
// SSNES custom two-pass with two different files.
|
||||
if (num_progs == 1 && *g_settings.video.second_pass_shader)
|
||||
if (num_progs == 1 && *g_settings.video.second_pass_shader && g_settings.video.render_to_texture)
|
||||
{
|
||||
unsigned secondary_progs = get_xml_shaders(g_settings.video.second_pass_shader, progs, 1);
|
||||
if (secondary_progs == 1)
|
||||
@@ -949,6 +987,10 @@ bool gl_glsl_init(const char *path)
|
||||
|
||||
glsl_enable = true;
|
||||
gl_num_programs = num_progs;
|
||||
gl_program[gl_num_programs + 1] = gl_program[0];
|
||||
|
||||
gl_glsl_reset_attrib();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -957,10 +999,10 @@ void gl_glsl_deinit(void)
|
||||
if (glsl_enable)
|
||||
{
|
||||
pglUseProgram(0);
|
||||
for (int i = 1; i < MAX_PROGRAMS; i++)
|
||||
for (int i = 0; i <= gl_num_programs; i++)
|
||||
{
|
||||
if (gl_program[i] == 0)
|
||||
break;
|
||||
continue;
|
||||
|
||||
GLsizei count;
|
||||
GLuint shaders[2];
|
||||
@@ -998,6 +1040,8 @@ void gl_glsl_deinit(void)
|
||||
snes_tracker_free(gl_snes_tracker);
|
||||
gl_snes_tracker = NULL;
|
||||
}
|
||||
|
||||
gl_glsl_reset_attrib();
|
||||
}
|
||||
|
||||
void gl_glsl_set_params(unsigned width, unsigned height,
|
||||
@@ -1009,147 +1053,178 @@ void gl_glsl_set_params(unsigned width, unsigned height,
|
||||
const struct gl_tex_info *fbo_info, unsigned fbo_info_cnt)
|
||||
{
|
||||
// We enforce a certain layout for our various texture types in the texunits.
|
||||
// Unit 0: Regular SNES frame (rubyTexture).
|
||||
// Unit 1-A: LUT textures.
|
||||
// Unit A+1: Previous texture.
|
||||
// Unit A+2: Original texture.
|
||||
// Unit A+3-B: FBO textures.
|
||||
// - Regular SNES frame (rubyTexture) (always bound).
|
||||
// - LUT textures (always bound).
|
||||
// - Original texture (always bound if meaningful).
|
||||
// - FBO textures (always bound if available).
|
||||
// - Previous textures.
|
||||
|
||||
if (glsl_enable && gl_program[active_index] > 0)
|
||||
if (!glsl_enable || (gl_program[active_index] == 0))
|
||||
return;
|
||||
|
||||
GLint location;
|
||||
|
||||
float inputSize[2] = {width, height};
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyInputSize");
|
||||
pglUniform2fv(location, 1, inputSize);
|
||||
|
||||
float outputSize[2] = {out_width, out_height};
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyOutputSize");
|
||||
pglUniform2fv(location, 1, outputSize);
|
||||
|
||||
float textureSize[2] = {tex_width, tex_height};
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyTextureSize");
|
||||
pglUniform2fv(location, 1, textureSize);
|
||||
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyFrameCount");
|
||||
pglUniform1i(location, frame_count);
|
||||
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyFrameDirection");
|
||||
pglUniform1i(location, g_extern.frame_is_reverse ? -1 : 1);
|
||||
|
||||
for (unsigned i = 0; i < gl_teximage_cnt; i++)
|
||||
{
|
||||
GLint location;
|
||||
location = pglGetUniformLocation(gl_program[active_index], gl_teximage_uniforms[i]);
|
||||
pglUniform1i(location, i + 1);
|
||||
}
|
||||
|
||||
float inputSize[2] = {width, height};
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyInputSize");
|
||||
pglUniform2fv(location, 1, inputSize);
|
||||
unsigned texunit = gl_teximage_cnt + 1;
|
||||
|
||||
float outputSize[2] = {out_width, out_height};
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyOutputSize");
|
||||
pglUniform2fv(location, 1, outputSize);
|
||||
// Set original texture unless we're in first pass (pointless).
|
||||
if (active_index > 1)
|
||||
{
|
||||
// Bind original texture.
|
||||
pglActiveTexture(GL_TEXTURE0 + texunit);
|
||||
|
||||
float textureSize[2] = {tex_width, tex_height};
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyTextureSize");
|
||||
pglUniform2fv(location, 1, textureSize);
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyOrigTexture");
|
||||
pglUniform1i(location, texunit++);
|
||||
glBindTexture(GL_TEXTURE_2D, info->tex);
|
||||
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyFrameCount");
|
||||
pglUniform1i(location, frame_count);
|
||||
|
||||
for (unsigned i = 0; i < gl_teximage_cnt; i++)
|
||||
{
|
||||
location = pglGetUniformLocation(gl_program[active_index], gl_teximage_uniforms[i]);
|
||||
pglUniform1i(location, i + 1);
|
||||
}
|
||||
|
||||
// Set previous texture.
|
||||
pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 1);
|
||||
glBindTexture(GL_TEXTURE_2D, prev_info->tex);
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyPrevTexture");
|
||||
pglUniform1i(location, gl_teximage_cnt + 1);
|
||||
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyPrevTextureSize");
|
||||
pglUniform2fv(location, 1, prev_info->tex_size);
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyPrevInputSize");
|
||||
pglUniform2fv(location, 1, prev_info->input_size);
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyOrigTextureSize");
|
||||
pglUniform2fv(location, 1, info->tex_size);
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyOrigInputSize");
|
||||
pglUniform2fv(location, 1, info->input_size);
|
||||
|
||||
// Pass texture coordinates.
|
||||
location = pglGetAttribLocation(gl_program[active_index], "rubyPrevTexCoord");
|
||||
location = pglGetAttribLocation(gl_program[active_index], "rubyOrigTexCoord");
|
||||
if (location >= 0)
|
||||
{
|
||||
pglEnableVertexAttribArray(location);
|
||||
pglVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, prev_info->coord);
|
||||
pglVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, info->coord);
|
||||
gl_attribs[gl_attrib_index++] = location;
|
||||
}
|
||||
|
||||
// Set original texture unless we're in first pass (pointless).
|
||||
if (active_index > 1)
|
||||
// Bind new texture in the chain.
|
||||
if (fbo_info_cnt > 0)
|
||||
{
|
||||
// Bind original texture.
|
||||
pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 2);
|
||||
glBindTexture(GL_TEXTURE_2D, info->tex);
|
||||
pglActiveTexture(GL_TEXTURE0 + texunit + fbo_info_cnt - 1);
|
||||
glBindTexture(GL_TEXTURE_2D, fbo_info[fbo_info_cnt - 1].tex);
|
||||
}
|
||||
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyOrigTexture");
|
||||
pglUniform1i(location, gl_teximage_cnt + 2);
|
||||
// Bind FBO textures.
|
||||
for (unsigned i = 0; i < fbo_info_cnt; i++)
|
||||
{
|
||||
char attrib_buf[64];
|
||||
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyOrigTextureSize");
|
||||
pglUniform2fv(location, 1, info->tex_size);
|
||||
location = pglGetUniformLocation(gl_program[active_index], "rubyOrigInputSize");
|
||||
pglUniform2fv(location, 1, info->input_size);
|
||||
snprintf(attrib_buf, sizeof(attrib_buf), "rubyPass%uTexture", i + 1);
|
||||
location = pglGetUniformLocation(gl_program[active_index], attrib_buf);
|
||||
pglUniform1i(location, texunit++);
|
||||
|
||||
// Pass texture coordinates.
|
||||
location = pglGetAttribLocation(gl_program[active_index], "rubyOrigTexCoord");
|
||||
snprintf(attrib_buf, sizeof(attrib_buf), "rubyPass%uTextureSize", i + 1);
|
||||
location = pglGetUniformLocation(gl_program[active_index], attrib_buf);
|
||||
pglUniform2fv(location, 1, fbo_info[i].tex_size);
|
||||
|
||||
snprintf(attrib_buf, sizeof(attrib_buf), "rubyPass%uInputSize", i + 1);
|
||||
location = pglGetUniformLocation(gl_program[active_index], attrib_buf);
|
||||
pglUniform2fv(location, 1, fbo_info[i].input_size);
|
||||
|
||||
snprintf(attrib_buf, sizeof(attrib_buf), "rubyPass%uTexCoord", i + 1);
|
||||
location = pglGetAttribLocation(gl_program[active_index], attrib_buf);
|
||||
if (location >= 0)
|
||||
{
|
||||
pglEnableVertexAttribArray(location);
|
||||
pglVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, info->coord);
|
||||
}
|
||||
|
||||
GLuint base_tex = GL_TEXTURE0 + gl_teximage_cnt + 3;
|
||||
|
||||
// Bind new texture in the chain.
|
||||
if (fbo_info_cnt > 0)
|
||||
{
|
||||
pglActiveTexture(base_tex + fbo_info_cnt - 1);
|
||||
glBindTexture(GL_TEXTURE_2D, fbo_info[fbo_info_cnt - 1].tex);
|
||||
}
|
||||
|
||||
// Bind FBO textures.
|
||||
for (unsigned i = 0; i < fbo_info_cnt; i++)
|
||||
{
|
||||
char attrib_buf[64];
|
||||
|
||||
snprintf(attrib_buf, sizeof(attrib_buf), "rubyPass%uTexture", i + 1);
|
||||
location = pglGetUniformLocation(gl_program[active_index], attrib_buf);
|
||||
pglUniform1i(location, base_tex + i - GL_TEXTURE0);
|
||||
|
||||
snprintf(attrib_buf, sizeof(attrib_buf), "rubyPass%uTextureSize", i + 1);
|
||||
location = pglGetUniformLocation(gl_program[active_index], attrib_buf);
|
||||
pglUniform2fv(location, 1, fbo_info[i].tex_size);
|
||||
|
||||
snprintf(attrib_buf, sizeof(attrib_buf), "rubyPass%uInputSize", i + 1);
|
||||
location = pglGetUniformLocation(gl_program[active_index], attrib_buf);
|
||||
pglUniform2fv(location, 1, fbo_info[i].input_size);
|
||||
|
||||
snprintf(attrib_buf, sizeof(attrib_buf), "rubyPass%uTexCoord", i + 1);
|
||||
location = pglGetAttribLocation(gl_program[active_index], attrib_buf);
|
||||
if (location >= 0)
|
||||
{
|
||||
pglEnableVertexAttribArray(location);
|
||||
pglVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, fbo_info[i].coord);
|
||||
}
|
||||
pglVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, fbo_info[i].coord);
|
||||
gl_attribs[gl_attrib_index++] = location;
|
||||
}
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
// First pass, so unbind everything to avoid collitions.
|
||||
// Unbind ORIG.
|
||||
pglActiveTexture(GL_TEXTURE0 + texunit);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
GLuint base_tex = texunit + 1;
|
||||
// Unbind any lurking FBO passes.
|
||||
// Rendering to a texture that is bound to a texture unit
|
||||
// sounds very shaky ... ;)
|
||||
for (unsigned i = 0; i < gl_num_programs; i++)
|
||||
{
|
||||
// First pass, so unbind everything to avoid collitions.
|
||||
// Unbind ORIG.
|
||||
pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 2);
|
||||
pglActiveTexture(GL_TEXTURE0 + base_tex + i);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
|
||||
GLuint base_tex = GL_TEXTURE0 + gl_teximage_cnt + 3;
|
||||
// Unbind any lurking FBO passes.
|
||||
// Rendering to a texture that is bound to a texture unit
|
||||
// sounds very shaky ... ;)
|
||||
for (unsigned i = 0; i < gl_num_programs; i++)
|
||||
{
|
||||
pglActiveTexture(GL_TEXTURE0 + base_tex + i);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
// Set previous textures. Only bind if they're actually used.
|
||||
for (unsigned i = 0; i < PREV_TEXTURES; i++)
|
||||
{
|
||||
char attr_buf_tex[64];
|
||||
char attr_buf_tex_size[64];
|
||||
char attr_buf_input_size[64];
|
||||
char attr_buf_coord[64];
|
||||
static const char *prev_names[PREV_TEXTURES] = {
|
||||
"Prev",
|
||||
"Prev1",
|
||||
"Prev2",
|
||||
"Prev3",
|
||||
"Prev4",
|
||||
"Prev5",
|
||||
"Prev6",
|
||||
};
|
||||
|
||||
snprintf(attr_buf_tex, sizeof(attr_buf_tex), "ruby%sTexture", prev_names[i]);
|
||||
snprintf(attr_buf_tex_size, sizeof(attr_buf_tex_size), "ruby%sTextureSize", prev_names[i]);
|
||||
snprintf(attr_buf_input_size, sizeof(attr_buf_input_size), "ruby%sInputSize", prev_names[i]);
|
||||
snprintf(attr_buf_coord, sizeof(attr_buf_coord), "ruby%sTexCoord", prev_names[i]);
|
||||
|
||||
location = pglGetUniformLocation(gl_program[active_index], attr_buf_tex);
|
||||
if (location >= 0)
|
||||
{
|
||||
pglActiveTexture(GL_TEXTURE0 + texunit);
|
||||
glBindTexture(GL_TEXTURE_2D, prev_info[i].tex);
|
||||
pglUniform1i(location, texunit++);
|
||||
}
|
||||
|
||||
pglActiveTexture(GL_TEXTURE0);
|
||||
location = pglGetUniformLocation(gl_program[active_index], attr_buf_tex_size);
|
||||
pglUniform2fv(location, 1, prev_info[i].tex_size);
|
||||
location = pglGetUniformLocation(gl_program[active_index], attr_buf_input_size);
|
||||
pglUniform2fv(location, 1, prev_info[i].input_size);
|
||||
|
||||
if (gl_snes_tracker)
|
||||
// Pass texture coordinates.
|
||||
location = pglGetAttribLocation(gl_program[active_index], attr_buf_coord);
|
||||
if (location >= 0)
|
||||
{
|
||||
static struct snes_tracker_uniform info[MAX_VARIABLES];
|
||||
static unsigned cnt = 0;
|
||||
|
||||
if (active_index == 1)
|
||||
cnt = snes_get_uniform(gl_snes_tracker, info, MAX_VARIABLES, frame_count);
|
||||
pglEnableVertexAttribArray(location);
|
||||
pglVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, prev_info[i].coord);
|
||||
gl_attribs[gl_attrib_index++] = location;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < cnt; i++)
|
||||
{
|
||||
location = pglGetUniformLocation(gl_program[active_index], info[i].id);
|
||||
pglUniform1i(location, info[i].value);
|
||||
}
|
||||
pglActiveTexture(GL_TEXTURE0);
|
||||
|
||||
if (gl_snes_tracker)
|
||||
{
|
||||
static struct snes_tracker_uniform info[MAX_VARIABLES];
|
||||
static unsigned cnt = 0;
|
||||
|
||||
if (active_index == 1)
|
||||
cnt = snes_get_uniform(gl_snes_tracker, info, MAX_VARIABLES, frame_count);
|
||||
|
||||
for (unsigned i = 0; i < cnt; i++)
|
||||
{
|
||||
location = pglGetUniformLocation(gl_program[active_index], info[i].id);
|
||||
pglUniform1f(location, info[i].value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1161,6 +1236,8 @@ void gl_glsl_use(unsigned index)
|
||||
{
|
||||
if (glsl_enable)
|
||||
{
|
||||
gl_glsl_reset_attrib();
|
||||
|
||||
active_index = index;
|
||||
pglUseProgram(gl_program[index]);
|
||||
}
|
||||
|
||||
+12
-12
@@ -18,9 +18,9 @@
|
||||
#include "snes_state.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "strl.h"
|
||||
#include "general.h"
|
||||
#include <libsnes.hpp>
|
||||
#include "../strl.h"
|
||||
#include "../general.h"
|
||||
#include "../libsnes.hpp"
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
#include "py_state/py_state.h"
|
||||
@@ -88,32 +88,32 @@ snes_tracker_t* snes_tracker_init(const struct snes_tracker_info *info)
|
||||
strlcpy(tracker->info[i].id, info->info[i].id, sizeof(tracker->info[i].id));
|
||||
tracker->info[i].addr = info->info[i].addr;
|
||||
tracker->info[i].type = info->info[i].type;
|
||||
tracker->info[i].mask = (info->info[i].mask == 0) ? 0xffffffffu : info->info[i].mask;
|
||||
tracker->info[i].mask = (info->info[i].mask == 0) ? -1u : info->info[i].mask;
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
if (info->info[i].type == SSNES_STATE_PYTHON)
|
||||
tracker->info[i].py = tracker->py;
|
||||
#endif
|
||||
|
||||
assert(info->wram && info->vram && info->cgram &&
|
||||
info->oam && info->apuram);
|
||||
// If we don't have a valid pointer.
|
||||
static const uint8_t empty = 0;
|
||||
|
||||
switch (info->info[i].ram_type)
|
||||
{
|
||||
case SSNES_STATE_WRAM:
|
||||
tracker->info[i].ptr = info->wram;
|
||||
tracker->info[i].ptr = info->wram ? info->wram : ∅
|
||||
break;
|
||||
case SSNES_STATE_APURAM:
|
||||
tracker->info[i].ptr = info->apuram;
|
||||
tracker->info[i].ptr = info->apuram ? info->apuram : ∅
|
||||
break;
|
||||
case SSNES_STATE_OAM:
|
||||
tracker->info[i].ptr = info->oam;
|
||||
tracker->info[i].ptr = info->oam ? info->oam : ∅
|
||||
break;
|
||||
case SSNES_STATE_CGRAM:
|
||||
tracker->info[i].ptr = info->cgram;
|
||||
tracker->info[i].ptr = info->cgram ? info->cgram : ∅
|
||||
break;
|
||||
case SSNES_STATE_VRAM:
|
||||
tracker->info[i].ptr = info->vram;
|
||||
tracker->info[i].ptr = info->vram ? info->vram : ∅
|
||||
break;
|
||||
case SSNES_STATE_INPUT_SLOT1:
|
||||
tracker->info[i].input_ptr = &tracker->input_state[0];
|
||||
@@ -125,7 +125,7 @@ snes_tracker_t* snes_tracker_init(const struct snes_tracker_info *info)
|
||||
break;
|
||||
|
||||
default:
|
||||
tracker->info[i].ptr = NULL;
|
||||
tracker->info[i].ptr = ∅
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -39,6 +39,7 @@ enum snes_tracker_type
|
||||
|
||||
enum snes_ram_type
|
||||
{
|
||||
SSNES_STATE_NONE,
|
||||
SSNES_STATE_WRAM,
|
||||
SSNES_STATE_APURAM,
|
||||
SSNES_STATE_OAM,
|
||||
@@ -78,7 +79,7 @@ struct snes_tracker_info
|
||||
struct snes_tracker_uniform
|
||||
{
|
||||
const char *id;
|
||||
int value;
|
||||
float value;
|
||||
};
|
||||
|
||||
typedef struct snes_tracker snes_tracker_t;
|
||||
|
||||
+168
-90
@@ -63,11 +63,17 @@ struct xv
|
||||
uint8_t *ytable;
|
||||
uint8_t *utable;
|
||||
uint8_t *vtable;
|
||||
unsigned luma_i;
|
||||
unsigned chroma_i;
|
||||
|
||||
#ifdef HAVE_FREETYPE
|
||||
font_renderer_t *font;
|
||||
|
||||
unsigned luma_index[2];
|
||||
unsigned chroma_u_index;
|
||||
unsigned chroma_v_index;
|
||||
|
||||
uint8_t font_y;
|
||||
uint8_t font_u;
|
||||
uint8_t font_v;
|
||||
#endif
|
||||
|
||||
void (*render_func)(xv_t*, const void *frame, unsigned width, unsigned height, unsigned pitch);
|
||||
@@ -89,6 +95,17 @@ static void sighandler(int sig)
|
||||
g_quit = 1;
|
||||
}
|
||||
|
||||
static inline void calculate_yuv(uint8_t *y, uint8_t *u, uint8_t *v, unsigned r, unsigned g, unsigned b)
|
||||
{
|
||||
int y_ = (int)(+((double)r * 0.257) + ((double)g * 0.504) + ((double)b * 0.098) + 16.0);
|
||||
int u_ = (int)(-((double)r * 0.148) - ((double)g * 0.291) + ((double)b * 0.439) + 128.0);
|
||||
int v_ = (int)(+((double)r * 0.439) - ((double)g * 0.368) - ((double)b * 0.071) + 128.0);
|
||||
|
||||
*y = y_ < 0 ? 0 : (y_ > 255 ? 255 : y_);
|
||||
*u = y_ < 0 ? 0 : (u_ > 255 ? 255 : u_);
|
||||
*v = v_ < 0 ? 0 : (v_ > 255 ? 255 : v_);
|
||||
}
|
||||
|
||||
static void init_yuv_tables(xv_t *xv)
|
||||
{
|
||||
xv->ytable = malloc(0x8000);
|
||||
@@ -98,18 +115,12 @@ static void init_yuv_tables(xv_t *xv)
|
||||
for (unsigned i = 0; i < 0x8000; i++)
|
||||
{
|
||||
// Extract RGB555 color data from i
|
||||
uint8_t r = (i >> 10) & 0x1F, g = (i >> 5) & 0x1F, b = (i) & 0x1F;
|
||||
r = (r << 3) | (r >> 2); //R5->R8
|
||||
g = (g << 3) | (g >> 2); //G5->G8
|
||||
b = (b << 3) | (b >> 2); //B5->B8
|
||||
unsigned r = (i >> 10) & 0x1F, g = (i >> 5) & 0x1F, b = (i) & 0x1F;
|
||||
r = (r << 3) | (r >> 2); // R5->R8
|
||||
g = (g << 3) | (g >> 2); // G5->G8
|
||||
b = (b << 3) | (b >> 2); // B5->B8
|
||||
|
||||
int y = (int)(+((double)r * 0.257) + ((double)g * 0.504) + ((double)b * 0.098) + 16.0);
|
||||
int u = (int)(-((double)r * 0.148) - ((double)g * 0.291) + ((double)b * 0.439) + 128.0);
|
||||
int v = (int)(+((double)r * 0.439) - ((double)g * 0.368) - ((double)b * 0.071) + 128.0);
|
||||
|
||||
xv->ytable[i] = y < 0 ? 0 : y > 255 ? 255 : y;
|
||||
xv->utable[i] = u < 0 ? 0 : u > 255 ? 255 : u;
|
||||
xv->vtable[i] = v < 0 ? 0 : v > 255 ? 255 : v;
|
||||
calculate_yuv(&xv->ytable[i], &xv->utable[i], &xv->vtable[i], r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,12 +184,33 @@ static void set_fullscreen(xv_t *xv)
|
||||
static void xv_init_font(xv_t *xv, const char *font_path, unsigned font_size)
|
||||
{
|
||||
#ifdef HAVE_FREETYPE
|
||||
if (*font_path)
|
||||
if (!g_settings.video.font_enable)
|
||||
return;
|
||||
|
||||
const char *path = font_path;
|
||||
if (!*path)
|
||||
path = font_renderer_get_default_font();
|
||||
|
||||
if (path)
|
||||
{
|
||||
xv->font = font_renderer_new(font_path, font_size);
|
||||
if (!xv->font)
|
||||
xv->font = font_renderer_new(path, font_size);
|
||||
if (xv->font)
|
||||
{
|
||||
int r = g_settings.video.msg_color_r * 255;
|
||||
r = (r < 0 ? 0 : (r > 255 ? 255 : r));
|
||||
int g = g_settings.video.msg_color_g * 255;
|
||||
g = (g < 0 ? 0 : (g > 255 ? 255 : g));
|
||||
int b = g_settings.video.msg_color_b * 255;
|
||||
b = (b < 0 ? 0 : (b > 255 ? 255 : b));
|
||||
|
||||
calculate_yuv(&xv->font_y, &xv->font_u, &xv->font_v,
|
||||
r, g, b);
|
||||
}
|
||||
else
|
||||
SSNES_WARN("Failed to init font.\n");
|
||||
}
|
||||
else
|
||||
SSNES_LOG("Did not find default font.\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -297,8 +329,76 @@ static void render32_uyvy(xv_t *xv, const void *input_, unsigned width, unsigned
|
||||
}
|
||||
}
|
||||
|
||||
struct format_desc
|
||||
{
|
||||
void (*render_16)(xv_t *xv, const void *input,
|
||||
unsigned width, unsigned height, unsigned pitch);
|
||||
void (*render_32)(xv_t *xv, const void *input,
|
||||
unsigned width, unsigned height, unsigned pitch);
|
||||
char components[4];
|
||||
unsigned luma_index[2];
|
||||
unsigned u_index;
|
||||
unsigned v_index;
|
||||
};
|
||||
|
||||
static void* xv_init(const video_info_t *video, const input_driver_t **input, void **input_data)
|
||||
static const struct format_desc formats[] = {
|
||||
{
|
||||
.render_16 = render16_yuy2,
|
||||
.render_32 = render32_yuy2,
|
||||
.components = { 'Y', 'U', 'Y', 'V' },
|
||||
.luma_index = { 0, 2 },
|
||||
.u_index = 1,
|
||||
.v_index = 3,
|
||||
},
|
||||
{
|
||||
.render_16 = render16_uyvy,
|
||||
.render_32 = render32_uyvy,
|
||||
.components = { 'U', 'Y', 'V', 'Y' },
|
||||
.luma_index = { 1, 3 },
|
||||
.u_index = 0,
|
||||
.v_index = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static bool adaptor_set_format(xv_t *xv, Display *dpy, XvPortID port, const video_info_t *video)
|
||||
{
|
||||
int format_count;
|
||||
XvImageFormatValues *format = XvListImageFormats(xv->display, port, &format_count);
|
||||
if (!format)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < format_count; i++)
|
||||
{
|
||||
for (unsigned j = 0; j < sizeof(formats) / sizeof(formats[0]); j++)
|
||||
{
|
||||
if (format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked)
|
||||
{
|
||||
if (format[i].component_order[0] == formats[j].components[0] &&
|
||||
format[i].component_order[1] == formats[j].components[1] &&
|
||||
format[i].component_order[2] == formats[j].components[2] &&
|
||||
format[i].component_order[3] == formats[j].components[3])
|
||||
{
|
||||
xv->fourcc = format[i].id;
|
||||
xv->render_func = video->rgb32 ? formats[j].render_32 : formats[j].render_16;
|
||||
|
||||
#ifdef HAVE_FREETYPE
|
||||
xv->luma_index[0] = formats[j].luma_index[0];
|
||||
xv->luma_index[1] = formats[j].luma_index[1];
|
||||
xv->chroma_u_index = formats[j].u_index;
|
||||
xv->chroma_v_index = formats[j].v_index;
|
||||
#endif
|
||||
XFree(format);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XFree(format);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void *xv_init(const video_info_t *video, const input_driver_t **input, void **input_data)
|
||||
{
|
||||
xv_t *xv = calloc(1, sizeof(*xv));
|
||||
if (!xv)
|
||||
@@ -325,17 +425,20 @@ static void* xv_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
if (adaptor_info[i].num_formats < 1) continue;
|
||||
if (!(adaptor_info[i].type & XvInputMask)) continue;
|
||||
if (!(adaptor_info[i].type & XvImageMask)) continue;
|
||||
if (!adaptor_set_format(xv, xv->display, adaptor_info[i].base_id, video)) continue;
|
||||
|
||||
xv->port = adaptor_info[i].base_id;
|
||||
xv->depth = adaptor_info[i].formats->depth;
|
||||
xv->visualid = adaptor_info[i].formats->visual_id;
|
||||
|
||||
SSNES_LOG("XVideo: Found suitable XvPort #%u\n", (unsigned)xv->port);
|
||||
break;
|
||||
}
|
||||
XvFreeAdaptorInfo(adaptor_info);
|
||||
|
||||
if (xv->port == 0)
|
||||
{
|
||||
SSNES_ERR("XVideo: Failed to find valid XvPort.\n");
|
||||
SSNES_ERR("XVideo: Failed to find valid XvPort or format.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -359,8 +462,8 @@ static void* xv_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
attributes.border_pixel = 0;
|
||||
attributes.event_mask = StructureNotifyMask | DestroyNotify | ClientMessage;
|
||||
|
||||
unsigned width = video->fullscreen ? ((video->width == 0) ? 256 : video->width) : video->width;
|
||||
unsigned height = video->fullscreen ? ((video->height == 0) ? 224 : video->height) : video->height;
|
||||
unsigned width = video->fullscreen ? ((video->width == 0) ? g_extern.system.geom.base_width : video->width) : video->width;
|
||||
unsigned height = video->fullscreen ? ((video->height == 0) ? g_extern.system.geom.base_height : video->height) : video->height;
|
||||
xv->window = XCreateWindow(xv->display, DefaultRootWindow(xv->display),
|
||||
0, 0, width, height,
|
||||
0, xv->depth, InputOutput, visualinfo->visual,
|
||||
@@ -383,54 +486,10 @@ static void* xv_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
|
||||
// Set colorkey to auto paint, so that Xv video output is always visible
|
||||
Atom atom = XInternAtom(xv->display, "XV_AUTOPAINT_COLORKEY", true);
|
||||
if(atom != None) XvSetPortAttribute(xv->display, xv->port, atom, 1);
|
||||
if (atom != None) XvSetPortAttribute(xv->display, xv->port, atom, 1);
|
||||
|
||||
int format_count;
|
||||
XvImageFormatValues *format = XvListImageFormats(xv->display, xv->port, &format_count);
|
||||
|
||||
bool has_format = false;
|
||||
for (int i = 0; i < format_count; i++)
|
||||
{
|
||||
if (format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked)
|
||||
{
|
||||
if (format[i].component_order[0] == 'Y' && format[i].component_order[1] == 'U'
|
||||
&& format[i].component_order[2] == 'Y' && format[i].component_order[3] == 'V')
|
||||
{
|
||||
has_format = true;
|
||||
xv->fourcc = format[i].id;
|
||||
xv->render_func = video->rgb32 ? render32_yuy2 : render16_yuy2;
|
||||
xv->luma_i = 0; xv->chroma_i = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_format) for (int i = 0; i < format_count; i++)
|
||||
{
|
||||
if (format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked)
|
||||
{
|
||||
if (format[i].component_order[0] == 'U' && format[i].component_order[1] == 'Y'
|
||||
&& format[i].component_order[2] == 'V' && format[i].component_order[3] == 'Y')
|
||||
{
|
||||
has_format = true;
|
||||
xv->fourcc = format[i].id;
|
||||
xv->render_func = video->rgb32 ? render32_uyvy : render16_uyvy;
|
||||
xv->chroma_i = 0; xv->luma_i = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
free(format);
|
||||
if (!has_format)
|
||||
{
|
||||
SSNES_ERR("XVideo: unable to find a supported image format.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
xv->width = 512;
|
||||
xv->height = 512;
|
||||
xv->width = g_extern.system.geom.max_width;
|
||||
xv->height = g_extern.system.geom.max_height;
|
||||
|
||||
xv->image = XvShmCreateImage(xv->display, xv->port, xv->fourcc, NULL, xv->width, xv->height, &xv->shminfo);
|
||||
if (!xv->image)
|
||||
@@ -466,8 +525,6 @@ static void* xv_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
xv_set_nonblock_state(xv, !video->vsync);
|
||||
xv->focus = true;
|
||||
|
||||
xv_init_font(xv, g_settings.video.font_path, g_settings.video.font_size);
|
||||
|
||||
void *xinput = input_x.init();
|
||||
if (xinput)
|
||||
{
|
||||
@@ -478,6 +535,8 @@ static void* xv_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
*input = NULL;
|
||||
|
||||
init_yuv_tables(xv);
|
||||
xv_init_font(xv, g_settings.video.font_path, g_settings.video.font_size);
|
||||
|
||||
return xv;
|
||||
|
||||
error:
|
||||
@@ -561,6 +620,7 @@ static void calc_out_rect(bool keep_aspect, unsigned *x, unsigned *y, unsigned *
|
||||
}
|
||||
|
||||
// TODO: Is there some way to render directly like GL? :(
|
||||
// Hacky C code is hacky :D Yay.
|
||||
static void xv_render_msg(xv_t *xv, const char *msg, unsigned width, unsigned height)
|
||||
{
|
||||
#ifdef HAVE_FREETYPE
|
||||
@@ -574,12 +634,17 @@ static void xv_render_msg(xv_t *xv, const char *msg, unsigned width, unsigned he
|
||||
int _base_x = g_settings.video.msg_pos_x * width;
|
||||
int _base_y = height - g_settings.video.msg_pos_y * height;
|
||||
|
||||
unsigned luma_i = xv->luma_i;
|
||||
unsigned chroma_i = xv->chroma_i;
|
||||
unsigned luma_index[2] = { xv->luma_index[0], xv->luma_index[1] };
|
||||
unsigned chroma_u_index = xv->chroma_u_index;
|
||||
unsigned chroma_v_index = xv->chroma_v_index;
|
||||
|
||||
unsigned pitch = width << 1; // YUV formats used are 16 bpp.
|
||||
|
||||
while (head)
|
||||
{
|
||||
int base_x = _base_x + head->off_x;
|
||||
int base_x = (_base_x + head->off_x) << 1;
|
||||
base_x &= ~3; // Make sure we always start on the correct boundary so the indices are correct.
|
||||
|
||||
int base_y = _base_y - head->off_y;
|
||||
if (base_y >= 0)
|
||||
{
|
||||
@@ -588,21 +653,33 @@ static void xv_render_msg(xv_t *xv, const char *msg, unsigned width, unsigned he
|
||||
if (base_x < 0)
|
||||
continue;
|
||||
|
||||
const uint8_t *a = head->output + head->pitch * y;
|
||||
uint8_t *out = (uint8_t*)xv->image->data + (base_y - head->height + y) * (width << 1) + (base_x << 1);
|
||||
const uint8_t * restrict a = head->output + head->pitch * y;
|
||||
uint8_t * restrict out = (uint8_t*)xv->image->data + (base_y - head->height + y) * pitch + base_x;
|
||||
|
||||
for (int x = 0; x < (head->width << 1) && (base_x + x) < width; x += 2)
|
||||
for (int x = 0; x < (head->width << 1) && (base_x + x) < pitch; x += 4)
|
||||
{
|
||||
// Blend luma
|
||||
uint8_t blend = a[x >> 1];
|
||||
unsigned blended = blend + (((256 - blend) * (unsigned)out[x + luma_i]) >> 8);
|
||||
blended = blended > 255 ? 255 : blended;
|
||||
out[x + luma_i] = blended;
|
||||
unsigned alpha[2];
|
||||
alpha[0] = a[(x >> 1) + 0];
|
||||
|
||||
// Blend chroma
|
||||
blended = (128 * blend + ((256 - blend) * (unsigned)out[x + chroma_i])) >> 8;
|
||||
blended = blended > 255 ? 255 : blended;
|
||||
out[x + chroma_i] = blended;
|
||||
if (((x >> 1) + 1) == head->width) // We reached the end, uhoh! Branching like a BOSS! :D
|
||||
alpha[1] = 0;
|
||||
else
|
||||
alpha[1] = a[(x >> 1) + 1];
|
||||
|
||||
unsigned alpha_sub = (alpha[0] + alpha[1]) >> 1; // Blended alpha for the sub-samples U/V channels.
|
||||
|
||||
for (unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
unsigned blended = (xv->font_y * alpha[i] + ((256 - alpha[i]) * out[x + luma_index[i]])) >> 8;
|
||||
out[x + luma_index[i]] = blended;
|
||||
}
|
||||
|
||||
// Blend chroma channels
|
||||
unsigned blended = (xv->font_u * alpha_sub + ((256 - alpha_sub) * out[x + chroma_u_index])) >> 8;
|
||||
out[x + chroma_u_index] = blended;
|
||||
|
||||
blended = (xv->font_v * alpha_sub + ((256 - alpha_sub) * out[x + chroma_v_index])) >> 8;
|
||||
out[x + chroma_v_index] = blended;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -619,7 +696,7 @@ static void xv_render_msg(xv_t *xv, const char *msg, unsigned width, unsigned he
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool xv_frame(void *data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
|
||||
static bool xv_frame(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
|
||||
{
|
||||
xv_t *xv = data;
|
||||
|
||||
@@ -630,11 +707,12 @@ static bool xv_frame(void *data, const void* frame, unsigned width, unsigned hei
|
||||
XGetWindowAttributes(xv->display, xv->window, &target);
|
||||
xv->render_func(xv, frame, width, height, pitch);
|
||||
|
||||
if (msg)
|
||||
xv_render_msg(xv, msg, xv->width, xv->height);
|
||||
|
||||
unsigned x, y, owidth, oheight;
|
||||
calc_out_rect(xv->keep_aspect, &x, &y, &owidth, &oheight, target.width, target.height);
|
||||
|
||||
if (msg)
|
||||
xv_render_msg(xv, msg, width << 1, height << 1);
|
||||
|
||||
XvShmPutImage(xv->display, xv->port, xv->window, xv->gc, xv->image,
|
||||
0, 0, width << 1, height << 1,
|
||||
x, y, owidth, oheight,
|
||||
@@ -693,9 +771,9 @@ static void xv_free(void *data)
|
||||
shmctl(xv->shminfo.shmid, IPC_RMID, NULL);
|
||||
XFree(xv->image);
|
||||
|
||||
if(xv->window)
|
||||
if (xv->window)
|
||||
XUnmapWindow(xv->display, xv->window);
|
||||
if(xv->colormap)
|
||||
if (xv->colormap)
|
||||
XFreeColormap(xv->display, xv->colormap);
|
||||
|
||||
XCloseDisplay(xv->display);
|
||||
|
||||
+9
-1
@@ -22,6 +22,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "../gfx/sdlwrap.h"
|
||||
|
||||
void sdl_dinput_free(sdl_dinput_t *di)
|
||||
{
|
||||
@@ -103,12 +104,19 @@ sdl_dinput_t* sdl_dinput_init(void)
|
||||
CoInitialize(NULL);
|
||||
|
||||
SDL_SysWMinfo info;
|
||||
if (!SDL_GetWMInfo(&info))
|
||||
SDL_VERSION(&info.version);
|
||||
|
||||
if (!sdlwrap_get_wm_info(&info))
|
||||
{
|
||||
SSNES_ERR("Failed to get SysWM info.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
#if SDL_MODERN
|
||||
di->hWnd = info.info.win.window;
|
||||
#else
|
||||
di->hWnd = info.window;
|
||||
#endif
|
||||
|
||||
if (FAILED(DirectInput8Create(
|
||||
GetModuleHandle(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8,
|
||||
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
/* 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 __SSNES_KEYSYM_H
|
||||
#define __SSNES_KEYSYM_H
|
||||
|
||||
// Global keysym table for SSNES.
|
||||
// As you may have noticed, it's the same as SDL 1.2 since I'm lazy.
|
||||
|
||||
enum ssnes_key
|
||||
{
|
||||
SK_UNKNOWN = 0,
|
||||
SK_FIRST = 0,
|
||||
SK_BACKSPACE = 8,
|
||||
SK_TAB = 9,
|
||||
SK_CLEAR = 12,
|
||||
SK_RETURN = 13,
|
||||
SK_PAUSE = 19,
|
||||
SK_ESCAPE = 27,
|
||||
SK_SPACE = 32,
|
||||
SK_EXCLAIM = 33,
|
||||
SK_QUOTEDBL = 34,
|
||||
SK_HASH = 35,
|
||||
SK_DOLLAR = 36,
|
||||
SK_AMPERSAND = 38,
|
||||
SK_QUOTE = 39,
|
||||
SK_LEFTPAREN = 40,
|
||||
SK_RIGHTPAREN = 41,
|
||||
SK_ASTERISK = 42,
|
||||
SK_PLUS = 43,
|
||||
SK_COMMA = 44,
|
||||
SK_MINUS = 45,
|
||||
SK_PERIOD = 46,
|
||||
SK_SLASH = 47,
|
||||
SK_0 = 48,
|
||||
SK_1 = 49,
|
||||
SK_2 = 50,
|
||||
SK_3 = 51,
|
||||
SK_4 = 52,
|
||||
SK_5 = 53,
|
||||
SK_6 = 54,
|
||||
SK_7 = 55,
|
||||
SK_8 = 56,
|
||||
SK_9 = 57,
|
||||
SK_COLON = 58,
|
||||
SK_SEMICOLON = 59,
|
||||
SK_LESS = 60,
|
||||
SK_EQUALS = 61,
|
||||
SK_GREATER = 62,
|
||||
SK_QUESTION = 63,
|
||||
SK_AT = 64,
|
||||
SK_LEFTBRACKET = 91,
|
||||
SK_BACKSLASH = 92,
|
||||
SK_RIGHTBRACKET = 93,
|
||||
SK_CARET = 94,
|
||||
SK_UNDERSCORE = 95,
|
||||
SK_BACKQUOTE = 96,
|
||||
SK_a = 97,
|
||||
SK_b = 98,
|
||||
SK_c = 99,
|
||||
SK_d = 100,
|
||||
SK_e = 101,
|
||||
SK_f = 102,
|
||||
SK_g = 103,
|
||||
SK_h = 104,
|
||||
SK_i = 105,
|
||||
SK_j = 106,
|
||||
SK_k = 107,
|
||||
SK_l = 108,
|
||||
SK_m = 109,
|
||||
SK_n = 110,
|
||||
SK_o = 111,
|
||||
SK_p = 112,
|
||||
SK_q = 113,
|
||||
SK_r = 114,
|
||||
SK_s = 115,
|
||||
SK_t = 116,
|
||||
SK_u = 117,
|
||||
SK_v = 118,
|
||||
SK_w = 119,
|
||||
SK_x = 120,
|
||||
SK_y = 121,
|
||||
SK_z = 122,
|
||||
SK_DELETE = 127,
|
||||
|
||||
SK_KP0 = 256,
|
||||
SK_KP1 = 257,
|
||||
SK_KP2 = 258,
|
||||
SK_KP3 = 259,
|
||||
SK_KP4 = 260,
|
||||
SK_KP5 = 261,
|
||||
SK_KP6 = 262,
|
||||
SK_KP7 = 263,
|
||||
SK_KP8 = 264,
|
||||
SK_KP9 = 265,
|
||||
SK_KP_PERIOD = 266,
|
||||
SK_KP_DIVIDE = 267,
|
||||
SK_KP_MULTIPLY = 268,
|
||||
SK_KP_MINUS = 269,
|
||||
SK_KP_PLUS = 270,
|
||||
SK_KP_ENTER = 271,
|
||||
SK_KP_EQUALS = 272,
|
||||
|
||||
SK_UP = 273,
|
||||
SK_DOWN = 274,
|
||||
SK_RIGHT = 275,
|
||||
SK_LEFT = 276,
|
||||
SK_INSERT = 277,
|
||||
SK_HOME = 278,
|
||||
SK_END = 279,
|
||||
SK_PAGEUP = 280,
|
||||
SK_PAGEDOWN = 281,
|
||||
|
||||
SK_F1 = 282,
|
||||
SK_F2 = 283,
|
||||
SK_F3 = 284,
|
||||
SK_F4 = 285,
|
||||
SK_F5 = 286,
|
||||
SK_F6 = 287,
|
||||
SK_F7 = 288,
|
||||
SK_F8 = 289,
|
||||
SK_F9 = 290,
|
||||
SK_F10 = 291,
|
||||
SK_F11 = 292,
|
||||
SK_F12 = 293,
|
||||
SK_F13 = 294,
|
||||
SK_F14 = 295,
|
||||
SK_F15 = 296,
|
||||
|
||||
SK_NUMLOCK = 300,
|
||||
SK_CAPSLOCK = 301,
|
||||
SK_SCROLLOCK = 302,
|
||||
SK_RSHIFT = 303,
|
||||
SK_LSHIFT = 304,
|
||||
SK_RCTRL = 305,
|
||||
SK_LCTRL = 306,
|
||||
SK_RALT = 307,
|
||||
SK_LALT = 308,
|
||||
SK_RMETA = 309,
|
||||
SK_LMETA = 310,
|
||||
SK_LSUPER = 311,
|
||||
SK_RSUPER = 312,
|
||||
SK_MODE = 313,
|
||||
SK_COMPOSE = 314,
|
||||
|
||||
SK_HELP = 315,
|
||||
SK_PRINT = 316,
|
||||
SK_SYSREQ = 317,
|
||||
SK_BREAK = 318,
|
||||
SK_MENU = 319,
|
||||
SK_POWER = 320,
|
||||
SK_EURO = 321,
|
||||
SK_UNDO = 322,
|
||||
|
||||
SK_LAST
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+103
-12
@@ -17,17 +17,115 @@
|
||||
|
||||
#include "driver.h"
|
||||
|
||||
#include "SDL.h"
|
||||
#include "../gfx/sdlwrap.h"
|
||||
#include <stdbool.h>
|
||||
#include "general.h"
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <libsnes.hpp>
|
||||
#include "../libsnes.hpp"
|
||||
#include "ssnes_sdl_input.h"
|
||||
#include "keysym.h"
|
||||
|
||||
|
||||
static void* sdl_input_init(void)
|
||||
struct key_bind
|
||||
{
|
||||
unsigned sdl;
|
||||
enum ssnes_key sk;
|
||||
};
|
||||
|
||||
static unsigned keysym_lut[SK_LAST];
|
||||
static const struct key_bind lut_binds[] = {
|
||||
{ SDLK_LEFT, SK_LEFT },
|
||||
{ SDLK_RIGHT, SK_RIGHT },
|
||||
{ SDLK_UP, SK_UP },
|
||||
{ SDLK_DOWN, SK_DOWN },
|
||||
{ SDLK_RETURN, SK_RETURN },
|
||||
{ SDLK_TAB, SK_TAB },
|
||||
{ SDLK_INSERT, SK_INSERT },
|
||||
{ SDLK_DELETE, SK_DELETE },
|
||||
{ SDLK_RSHIFT, SK_RSHIFT },
|
||||
{ SDLK_LSHIFT, SK_LSHIFT },
|
||||
{ SDLK_LCTRL, SK_LCTRL },
|
||||
{ SDLK_LALT, SK_LALT },
|
||||
{ SDLK_SPACE, SK_SPACE },
|
||||
{ SDLK_ESCAPE, SK_ESCAPE },
|
||||
{ SDLK_BACKSPACE, SK_BACKSPACE },
|
||||
{ SDLK_KP_ENTER, SK_KP_ENTER },
|
||||
{ SDLK_KP_PLUS, SK_KP_PLUS },
|
||||
{ SDLK_KP_MINUS, SK_KP_MINUS },
|
||||
{ SDLK_KP_MULTIPLY, SK_KP_MULTIPLY },
|
||||
{ SDLK_KP_DIVIDE, SK_KP_DIVIDE },
|
||||
{ SDLK_BACKQUOTE, SK_BACKQUOTE },
|
||||
{ SDLK_PAUSE, SK_PAUSE },
|
||||
{ SDLK_KP0, SK_KP0 },
|
||||
{ SDLK_KP1, SK_KP1 },
|
||||
{ SDLK_KP2, SK_KP2 },
|
||||
{ SDLK_KP3, SK_KP3 },
|
||||
{ SDLK_KP4, SK_KP4 },
|
||||
{ SDLK_KP5, SK_KP5 },
|
||||
{ SDLK_KP6, SK_KP6 },
|
||||
{ SDLK_KP7, SK_KP7 },
|
||||
{ SDLK_KP8, SK_KP8 },
|
||||
{ SDLK_KP9, SK_KP9 },
|
||||
{ SDLK_0, SK_0 },
|
||||
{ SDLK_1, SK_1 },
|
||||
{ SDLK_2, SK_2 },
|
||||
{ SDLK_3, SK_3 },
|
||||
{ SDLK_4, SK_4 },
|
||||
{ SDLK_5, SK_5 },
|
||||
{ SDLK_6, SK_6 },
|
||||
{ SDLK_7, SK_7 },
|
||||
{ SDLK_8, SK_8 },
|
||||
{ SDLK_9, SK_9 },
|
||||
{ SDLK_F1, SK_F1 },
|
||||
{ SDLK_F2, SK_F2 },
|
||||
{ SDLK_F3, SK_F3 },
|
||||
{ SDLK_F4, SK_F4 },
|
||||
{ SDLK_F5, SK_F5 },
|
||||
{ SDLK_F6, SK_F6 },
|
||||
{ SDLK_F7, SK_F7 },
|
||||
{ SDLK_F8, SK_F8 },
|
||||
{ SDLK_F9, SK_F9 },
|
||||
{ SDLK_F10, SK_F10 },
|
||||
{ SDLK_F11, SK_F11 },
|
||||
{ SDLK_F12, SK_F12 },
|
||||
{ SDLK_a, SK_a },
|
||||
{ SDLK_b, SK_b },
|
||||
{ SDLK_c, SK_c },
|
||||
{ SDLK_d, SK_d },
|
||||
{ SDLK_e, SK_e },
|
||||
{ SDLK_f, SK_f },
|
||||
{ SDLK_g, SK_g },
|
||||
{ SDLK_h, SK_h },
|
||||
{ SDLK_i, SK_i },
|
||||
{ SDLK_j, SK_j },
|
||||
{ SDLK_k, SK_k },
|
||||
{ SDLK_l, SK_l },
|
||||
{ SDLK_m, SK_m },
|
||||
{ SDLK_n, SK_n },
|
||||
{ SDLK_o, SK_o },
|
||||
{ SDLK_p, SK_p },
|
||||
{ SDLK_q, SK_q },
|
||||
{ SDLK_r, SK_r },
|
||||
{ SDLK_s, SK_s },
|
||||
{ SDLK_t, SK_t },
|
||||
{ SDLK_u, SK_u },
|
||||
{ SDLK_v, SK_v },
|
||||
{ SDLK_w, SK_w },
|
||||
{ SDLK_x, SK_x },
|
||||
{ SDLK_y, SK_y },
|
||||
{ SDLK_z, SK_z },
|
||||
};
|
||||
|
||||
static void init_lut(void)
|
||||
{
|
||||
memset(keysym_lut, 0, sizeof(keysym_lut));
|
||||
for (unsigned i = 0; i < sizeof(lut_binds) / sizeof(lut_binds[0]); i++)
|
||||
keysym_lut[lut_binds[i].sk] = lut_binds[i].sdl;
|
||||
}
|
||||
|
||||
static void *sdl_input_init(void)
|
||||
{
|
||||
init_lut();
|
||||
sdl_input_t *sdl = calloc(1, sizeof(*sdl));
|
||||
if (!sdl)
|
||||
return NULL;
|
||||
@@ -76,16 +174,9 @@ static void* sdl_input_init(void)
|
||||
return sdl;
|
||||
}
|
||||
|
||||
|
||||
static bool sdl_key_pressed(int key)
|
||||
{
|
||||
int num_keys;
|
||||
Uint8 *keymap = SDL_GetKeyState(&num_keys);
|
||||
|
||||
if (key >= num_keys)
|
||||
return false;
|
||||
|
||||
return keymap[key];
|
||||
return sdlwrap_key_pressed(keysym_lut[key]);
|
||||
}
|
||||
|
||||
#ifndef HAVE_DINPUT
|
||||
|
||||
+86
-86
@@ -23,6 +23,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "ssnes_sdl_input.h"
|
||||
#include "keysym.h"
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
@@ -37,103 +38,102 @@ typedef struct x11_input
|
||||
|
||||
struct key_bind
|
||||
{
|
||||
int x;
|
||||
int sdl;
|
||||
unsigned x;
|
||||
enum ssnes_key sk;
|
||||
};
|
||||
|
||||
static int keysym_lut[SDLK_LAST];
|
||||
static unsigned keysym_lut[SK_LAST];
|
||||
static const struct key_bind lut_binds[] = {
|
||||
{ XK_Left, SDLK_LEFT },
|
||||
{ XK_Right, SDLK_RIGHT },
|
||||
{ XK_Up, SDLK_UP },
|
||||
{ XK_Down, SDLK_DOWN },
|
||||
{ XK_Return, SDLK_RETURN },
|
||||
{ XK_Tab, SDLK_TAB },
|
||||
{ XK_Insert, SDLK_INSERT },
|
||||
{ XK_Delete, SDLK_DELETE },
|
||||
{ XK_Shift_R, SDLK_RSHIFT },
|
||||
{ XK_Shift_L, SDLK_LSHIFT },
|
||||
{ XK_Control_L, SDLK_LCTRL },
|
||||
{ XK_Alt_L, SDLK_LALT },
|
||||
{ XK_space, SDLK_SPACE },
|
||||
{ XK_Escape, SDLK_ESCAPE },
|
||||
{ XK_KP_Add, SDLK_KP_PLUS },
|
||||
{ XK_KP_Subtract, SDLK_KP_MINUS },
|
||||
{ XK_BackSpace, SDLK_BACKSPACE },
|
||||
{ XK_KP_Enter, SDLK_KP_ENTER },
|
||||
{ XK_KP_Add, SDLK_KP_PLUS },
|
||||
{ XK_KP_Subtract, SDLK_KP_MINUS },
|
||||
{ XK_KP_Multiply, SDLK_KP_MULTIPLY },
|
||||
{ XK_KP_Divide, SDLK_KP_DIVIDE },
|
||||
{ XK_grave, SDLK_BACKQUOTE },
|
||||
{ XK_KP_0, SDLK_KP0 },
|
||||
{ XK_KP_1, SDLK_KP1 },
|
||||
{ XK_KP_2, SDLK_KP2 },
|
||||
{ XK_KP_3, SDLK_KP3 },
|
||||
{ XK_KP_4, SDLK_KP4 },
|
||||
{ XK_KP_5, SDLK_KP5 },
|
||||
{ XK_KP_6, SDLK_KP6 },
|
||||
{ XK_KP_7, SDLK_KP7 },
|
||||
{ XK_KP_8, SDLK_KP8 },
|
||||
{ XK_KP_9, SDLK_KP9 },
|
||||
{ XK_0, SDLK_0 },
|
||||
{ XK_1, SDLK_1 },
|
||||
{ XK_2, SDLK_2 },
|
||||
{ XK_3, SDLK_3 },
|
||||
{ XK_4, SDLK_4 },
|
||||
{ XK_5, SDLK_5 },
|
||||
{ XK_6, SDLK_6 },
|
||||
{ XK_7, SDLK_7 },
|
||||
{ XK_8, SDLK_8 },
|
||||
{ XK_9, SDLK_9 },
|
||||
{ XK_F1, SDLK_F1 },
|
||||
{ XK_F2, SDLK_F2 },
|
||||
{ XK_F3, SDLK_F3 },
|
||||
{ XK_F4, SDLK_F4 },
|
||||
{ XK_F5, SDLK_F5 },
|
||||
{ XK_F6, SDLK_F6 },
|
||||
{ XK_F7, SDLK_F7 },
|
||||
{ XK_F8, SDLK_F8 },
|
||||
{ XK_F9, SDLK_F9 },
|
||||
{ XK_F10, SDLK_F10 },
|
||||
{ XK_F11, SDLK_F11 },
|
||||
{ XK_F12, SDLK_F12 },
|
||||
{ XK_a, SDLK_a },
|
||||
{ XK_b, SDLK_b },
|
||||
{ XK_c, SDLK_c },
|
||||
{ XK_d, SDLK_d },
|
||||
{ XK_e, SDLK_e },
|
||||
{ XK_f, SDLK_f },
|
||||
{ XK_g, SDLK_g },
|
||||
{ XK_h, SDLK_h },
|
||||
{ XK_i, SDLK_i },
|
||||
{ XK_j, SDLK_j },
|
||||
{ XK_k, SDLK_k },
|
||||
{ XK_l, SDLK_l },
|
||||
{ XK_m, SDLK_m },
|
||||
{ XK_n, SDLK_n },
|
||||
{ XK_o, SDLK_o },
|
||||
{ XK_p, SDLK_p },
|
||||
{ XK_q, SDLK_q },
|
||||
{ XK_r, SDLK_r },
|
||||
{ XK_s, SDLK_s },
|
||||
{ XK_t, SDLK_t },
|
||||
{ XK_u, SDLK_u },
|
||||
{ XK_v, SDLK_v },
|
||||
{ XK_w, SDLK_w },
|
||||
{ XK_x, SDLK_x },
|
||||
{ XK_y, SDLK_y },
|
||||
{ XK_z, SDLK_z },
|
||||
{ XK_Left, SK_LEFT },
|
||||
{ XK_Right, SK_RIGHT },
|
||||
{ XK_Up, SK_UP },
|
||||
{ XK_Down, SK_DOWN },
|
||||
{ XK_Return, SK_RETURN },
|
||||
{ XK_Tab, SK_TAB },
|
||||
{ XK_Insert, SK_INSERT },
|
||||
{ XK_Delete, SK_DELETE },
|
||||
{ XK_Shift_R, SK_RSHIFT },
|
||||
{ XK_Shift_L, SK_LSHIFT },
|
||||
{ XK_Control_L, SK_LCTRL },
|
||||
{ XK_Alt_L, SK_LALT },
|
||||
{ XK_space, SK_SPACE },
|
||||
{ XK_Escape, SK_ESCAPE },
|
||||
{ XK_BackSpace, SK_BACKSPACE },
|
||||
{ XK_KP_Enter, SK_KP_ENTER },
|
||||
{ XK_KP_Add, SK_KP_PLUS },
|
||||
{ XK_KP_Subtract, SK_KP_MINUS },
|
||||
{ XK_KP_Multiply, SK_KP_MULTIPLY },
|
||||
{ XK_KP_Divide, SK_KP_DIVIDE },
|
||||
{ XK_grave, SK_BACKQUOTE },
|
||||
{ XK_Pause, SK_PAUSE },
|
||||
{ XK_KP_0, SK_KP0 },
|
||||
{ XK_KP_1, SK_KP1 },
|
||||
{ XK_KP_2, SK_KP2 },
|
||||
{ XK_KP_3, SK_KP3 },
|
||||
{ XK_KP_4, SK_KP4 },
|
||||
{ XK_KP_5, SK_KP5 },
|
||||
{ XK_KP_6, SK_KP6 },
|
||||
{ XK_KP_7, SK_KP7 },
|
||||
{ XK_KP_8, SK_KP8 },
|
||||
{ XK_KP_9, SK_KP9 },
|
||||
{ XK_0, SK_0 },
|
||||
{ XK_1, SK_1 },
|
||||
{ XK_2, SK_2 },
|
||||
{ XK_3, SK_3 },
|
||||
{ XK_4, SK_4 },
|
||||
{ XK_5, SK_5 },
|
||||
{ XK_6, SK_6 },
|
||||
{ XK_7, SK_7 },
|
||||
{ XK_8, SK_8 },
|
||||
{ XK_9, SK_9 },
|
||||
{ XK_F1, SK_F1 },
|
||||
{ XK_F2, SK_F2 },
|
||||
{ XK_F3, SK_F3 },
|
||||
{ XK_F4, SK_F4 },
|
||||
{ XK_F5, SK_F5 },
|
||||
{ XK_F6, SK_F6 },
|
||||
{ XK_F7, SK_F7 },
|
||||
{ XK_F8, SK_F8 },
|
||||
{ XK_F9, SK_F9 },
|
||||
{ XK_F10, SK_F10 },
|
||||
{ XK_F11, SK_F11 },
|
||||
{ XK_F12, SK_F12 },
|
||||
{ XK_a, SK_a },
|
||||
{ XK_b, SK_b },
|
||||
{ XK_c, SK_c },
|
||||
{ XK_d, SK_d },
|
||||
{ XK_e, SK_e },
|
||||
{ XK_f, SK_f },
|
||||
{ XK_g, SK_g },
|
||||
{ XK_h, SK_h },
|
||||
{ XK_i, SK_i },
|
||||
{ XK_j, SK_j },
|
||||
{ XK_k, SK_k },
|
||||
{ XK_l, SK_l },
|
||||
{ XK_m, SK_m },
|
||||
{ XK_n, SK_n },
|
||||
{ XK_o, SK_o },
|
||||
{ XK_p, SK_p },
|
||||
{ XK_q, SK_q },
|
||||
{ XK_r, SK_r },
|
||||
{ XK_s, SK_s },
|
||||
{ XK_t, SK_t },
|
||||
{ XK_u, SK_u },
|
||||
{ XK_v, SK_v },
|
||||
{ XK_w, SK_w },
|
||||
{ XK_x, SK_x },
|
||||
{ XK_y, SK_y },
|
||||
{ XK_z, SK_z },
|
||||
};
|
||||
|
||||
static void init_lut(void)
|
||||
{
|
||||
memset(keysym_lut, 0, sizeof(keysym_lut));
|
||||
for (unsigned i = 0; i < sizeof(lut_binds) / sizeof(lut_binds[0]); i++)
|
||||
keysym_lut[lut_binds[i].sdl] = lut_binds[i].x;
|
||||
keysym_lut[lut_binds[i].sk] = lut_binds[i].x;
|
||||
}
|
||||
|
||||
static void* x_input_init(void)
|
||||
static void *x_input_init(void)
|
||||
{
|
||||
x11_input_t *x11 = calloc(1, sizeof(*x11));
|
||||
if (!x11)
|
||||
|
||||
+56
@@ -66,6 +66,60 @@ extern "C" {
|
||||
#define SNES_MEMORY_OAM 103
|
||||
#define SNES_MEMORY_CGRAM 104
|
||||
|
||||
// SSNES extension. Not required to be implemented for a working implementation.
|
||||
#define SNES_ENVIRONMENT_GET_FULLPATH 0 // const char ** --
|
||||
// Full path of game loaded.
|
||||
//
|
||||
#define SNES_ENVIRONMENT_SET_GEOMETRY 1 // const struct snes_geometry * --
|
||||
// Window geometry information for the system/game.
|
||||
//
|
||||
#define SNES_ENVIRONMENT_SET_PITCH 2 // const unsigned * --
|
||||
// Pitch of game image.
|
||||
//
|
||||
#define SNES_ENVIRONMENT_GET_OVERSCAN 3 // bool * --
|
||||
// Boolean value whether or not the implementation should use overscan.
|
||||
//
|
||||
#define SNES_ENVIRONMENT_SET_TIMING 4 // const struct snes_system_timing * --
|
||||
// Set exact timings of the system. Used primarily for video recording.
|
||||
//
|
||||
#define SNES_ENVIRONMENT_GET_CAN_DUPE 5 // bool * --
|
||||
// Boolean value whether or not SSNES supports frame duping,
|
||||
// passing NULL to video frame callback.
|
||||
//
|
||||
//
|
||||
#define SNES_ENVIRONMENT_SET_NEED_FULLPATH 6 // const bool * --
|
||||
// Boolean value telling if implementation needs a valid fullpath to be able to run.
|
||||
// If this is the case, SSNES will not open the rom directly,
|
||||
// and pass NULL to rom data.
|
||||
// Implementation must then use SNES_ENVIRONMENT_GET_FULLPATH.
|
||||
// This is useful for implementations with very large roms,
|
||||
// which are impractical to load fully into RAM.
|
||||
//
|
||||
#define SNES_ENVIRONMENT_GET_CAN_REWIND 7 // bool * --
|
||||
// Boolean value telling if SSNES is able to rewind.
|
||||
// Some implementations might need to take extra precautions
|
||||
// to allow this as smoothly as possible.
|
||||
|
||||
struct snes_geometry
|
||||
{
|
||||
unsigned base_width; // Nominal video width of system.
|
||||
unsigned base_height; // Nominal video height of system.
|
||||
unsigned max_width; // Maximum possible width of system.
|
||||
unsigned max_height; // Maximum possible height of system.
|
||||
};
|
||||
|
||||
struct snes_system_timing
|
||||
{
|
||||
double fps;
|
||||
double sample_rate;
|
||||
};
|
||||
|
||||
typedef bool (*snes_environment_t)(unsigned cmd, void *data);
|
||||
|
||||
// Must be called before calling snes_init().
|
||||
void snes_set_environment(snes_environment_t);
|
||||
////
|
||||
|
||||
typedef void (*snes_video_refresh_t)(const uint16_t *data, unsigned width, unsigned height);
|
||||
typedef void (*snes_audio_sample_t)(uint16_t left, uint16_t right);
|
||||
typedef void (*snes_input_poll_t)(void);
|
||||
@@ -74,6 +128,8 @@ typedef int16_t (*snes_input_state_t)(bool port, unsigned device, unsigned index
|
||||
unsigned snes_library_revision_major(void);
|
||||
unsigned snes_library_revision_minor(void);
|
||||
|
||||
const char *snes_library_id(void);
|
||||
|
||||
void snes_set_video_refresh(snes_video_refresh_t);
|
||||
void snes_set_audio_sample(snes_audio_sample_t);
|
||||
void snes_set_input_poll(snes_input_poll_t);
|
||||
|
||||
@@ -78,7 +78,7 @@ uint32_t crc32_adjust(uint32_t crc32, uint8_t input)
|
||||
uint32_t crc32_calculate(const uint8_t *data, unsigned length)
|
||||
{
|
||||
uint32_t crc32 = ~0;
|
||||
for(unsigned i = 0; i < length; i++)
|
||||
for (unsigned i = 0; i < length; i++)
|
||||
crc32 = crc32_adjust(crc32, data[i]);
|
||||
return ~crc32;
|
||||
}
|
||||
@@ -88,13 +88,12 @@ struct bsv_movie
|
||||
FILE *file;
|
||||
uint8_t *state;
|
||||
|
||||
long *frame_pos; // A ring buffer keeping track of many key presses were requested per frame.
|
||||
size_t *frame_pos; // A ring buffer keeping track of positions in the file for each frame.
|
||||
size_t frame_mask;
|
||||
uint16_t current_frame_count;
|
||||
size_t frame_ptr;
|
||||
|
||||
bool playback;
|
||||
unsigned min_file_pos;
|
||||
size_t min_file_pos;
|
||||
|
||||
bool first_rewind;
|
||||
bool did_rewind;
|
||||
@@ -103,7 +102,7 @@ struct bsv_movie
|
||||
#define BSV_MAGIC 0x42535631
|
||||
|
||||
#define MAGIC_INDEX 0
|
||||
#define SERIALIZER_INDEX 1
|
||||
#define SERIALIZER_INDEX 1 // Not current used.
|
||||
#define CRC_INDEX 2
|
||||
#define STATE_SIZE_INDEX 3
|
||||
|
||||
@@ -116,6 +115,14 @@ static inline uint32_t swap_if_big32(uint32_t val)
|
||||
return (val >> 24) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | (val << 24);
|
||||
}
|
||||
|
||||
static inline uint32_t swap_if_little32(uint32_t val)
|
||||
{
|
||||
if (!is_little_endian()) // Big-endian
|
||||
return val;
|
||||
else
|
||||
return (val >> 24) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | (val << 24);
|
||||
}
|
||||
|
||||
static inline uint16_t swap_if_big16(uint16_t val)
|
||||
{
|
||||
if (is_little_endian())
|
||||
@@ -141,7 +148,8 @@ static bool init_playback(bsv_movie_t *handle, const char *path)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (swap_if_big32(header[MAGIC_INDEX]) != BSV_MAGIC)
|
||||
// Compatibility with old implementation that used incorrect documentation.
|
||||
if (swap_if_little32(header[MAGIC_INDEX]) != BSV_MAGIC && swap_if_big32(header[MAGIC_INDEX]) != BSV_MAGIC)
|
||||
{
|
||||
SSNES_ERR("Movie file is not a valid BSV1 file!\n");
|
||||
return false;
|
||||
@@ -154,25 +162,32 @@ static bool init_playback(bsv_movie_t *handle, const char *path)
|
||||
}
|
||||
|
||||
uint32_t state_size = swap_if_big32(header[STATE_SIZE_INDEX]);
|
||||
handle->state = malloc(state_size);
|
||||
if (!handle->state)
|
||||
return false;
|
||||
|
||||
// If we're playing back from the start, state_size is 0.
|
||||
if (state_size)
|
||||
{
|
||||
handle->state = malloc(state_size);
|
||||
if (!handle->state)
|
||||
return false;
|
||||
|
||||
if (fread(handle->state, 1, state_size, handle->file) != state_size)
|
||||
{
|
||||
SSNES_ERR("Couldn't read state from movie.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (psnes_serialize_size() != state_size)
|
||||
{
|
||||
SSNES_ERR("Movie format seems to have a different serializer version. Cannot continue.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Unserialize to start playback.
|
||||
psnes_unserialize(handle->state, state_size);
|
||||
}
|
||||
|
||||
handle->min_file_pos = sizeof(header) + state_size;
|
||||
|
||||
if (fread(handle->state, 1, state_size, handle->file) != state_size)
|
||||
{
|
||||
SSNES_ERR("Couldn't read state from movie.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (psnes_serialize_size() != state_size)
|
||||
{
|
||||
SSNES_ERR("Movie format seems to have a different serializer version. Cannot continue.\n");
|
||||
exit(1);
|
||||
}
|
||||
// Unserialize to start playback.
|
||||
psnes_unserialize(handle->state, state_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -186,11 +201,14 @@ static bool init_record(bsv_movie_t *handle, const char *path)
|
||||
}
|
||||
|
||||
uint32_t header[4] = {0};
|
||||
header[MAGIC_INDEX] = swap_if_big32(BSV_MAGIC);
|
||||
//header[SERIALIZER_INDEX] = 0; // TODO: Fix this.
|
||||
|
||||
// This value is supposed to show up as BSV1 in a HEX editor.
|
||||
header[MAGIC_INDEX] = swap_if_little32(BSV_MAGIC);
|
||||
|
||||
header[CRC_INDEX] = swap_if_big32(g_extern.cart_crc);
|
||||
|
||||
uint32_t state_size = psnes_serialize_size();
|
||||
|
||||
header[STATE_SIZE_INDEX] = swap_if_big32(state_size);
|
||||
fwrite(header, 4, sizeof(uint32_t), handle->file);
|
||||
|
||||
@@ -200,8 +218,12 @@ static bool init_record(bsv_movie_t *handle, const char *path)
|
||||
|
||||
handle->min_file_pos = sizeof(header) + state_size;
|
||||
|
||||
psnes_serialize(handle->state, state_size);
|
||||
fwrite(handle->state, 1, state_size, handle->file);
|
||||
if (state_size > 0)
|
||||
{
|
||||
psnes_serialize(handle->state, state_size);
|
||||
fwrite(handle->state, 1, state_size, handle->file);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -245,8 +267,11 @@ bsv_movie_t *bsv_movie_init(const char *path, enum ssnes_movie_type type)
|
||||
else if (!init_record(handle, path))
|
||||
goto error;
|
||||
|
||||
if (!(handle->frame_pos = calloc((1 << 20), sizeof(long)))) // Just pick something really large :D
|
||||
// Just pick something really large :D ~1 million frames rewind should do the trick.
|
||||
if (!(handle->frame_pos = calloc((1 << 20), sizeof(size_t))))
|
||||
goto error;
|
||||
|
||||
handle->frame_pos[0] = handle->min_file_pos;
|
||||
handle->frame_mask = (1 << 20) - 1;
|
||||
|
||||
return handle;
|
||||
@@ -258,14 +283,11 @@ error:
|
||||
|
||||
void bsv_movie_set_frame_start(bsv_movie_t *handle)
|
||||
{
|
||||
//fprintf(stderr, "Starting frame: %u, Pos: %ld\n", (unsigned)handle->frame_ptr, ftell(handle->file));
|
||||
handle->frame_pos[handle->frame_ptr] = ftell(handle->file);
|
||||
|
||||
}
|
||||
|
||||
void bsv_movie_set_frame_end(bsv_movie_t *handle)
|
||||
{
|
||||
//fprintf(stderr, "Frame++\n");
|
||||
handle->frame_ptr = (handle->frame_ptr + 1) & handle->frame_mask;
|
||||
|
||||
handle->first_rewind = !handle->did_rewind;
|
||||
@@ -274,22 +296,27 @@ void bsv_movie_set_frame_end(bsv_movie_t *handle)
|
||||
|
||||
void bsv_movie_frame_rewind(bsv_movie_t *handle)
|
||||
{
|
||||
//fprintf(stderr, "Frame--\n");
|
||||
|
||||
handle->did_rewind = true;
|
||||
if (handle->frame_ptr <= 1)
|
||||
|
||||
// If we're at the beginning ... :)
|
||||
if ((handle->frame_ptr <= 1) && (handle->frame_pos[0] == handle->min_file_pos))
|
||||
{
|
||||
handle->frame_ptr = 0;
|
||||
fseek(handle->file, handle->min_file_pos, SEEK_SET);
|
||||
}
|
||||
else
|
||||
{
|
||||
// First time rewind is performed, the old frame is simply replayed.
|
||||
// However, playing back that frame caused us to read data, and push data to the ring buffer.
|
||||
// Sucessively rewinding frames, we need to rewind past the read data, plus another.
|
||||
handle->frame_ptr = (handle->frame_ptr - (handle->first_rewind ? 1 : 2)) & handle->frame_mask;
|
||||
fseek(handle->file, handle->frame_pos[handle->frame_ptr], SEEK_SET);
|
||||
}
|
||||
|
||||
// We rewound past the beginning. :O
|
||||
if (ftell(handle->file) <= (long)handle->min_file_pos)
|
||||
{
|
||||
// If recording, we simply reset the starting point. Nice and easy.
|
||||
if (!handle->playback)
|
||||
{
|
||||
fseek(handle->file, 4 * sizeof(uint32_t), SEEK_SET);
|
||||
|
||||
@@ -15,15 +15,6 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "netplay.h"
|
||||
#include "general.h"
|
||||
#include "autosave.h"
|
||||
#include "dynamic.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0501
|
||||
@@ -32,10 +23,6 @@
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
// Woohoo, Winsock has headers from the STONE AGE! :D
|
||||
#define close(x) closesocket(x)
|
||||
#define CONST_CAST (const char*)
|
||||
#define NONCONST_CAST (char*)
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#include <sys/types.h>
|
||||
@@ -45,11 +32,29 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#include "netplay.h"
|
||||
#include "general.h"
|
||||
#include "autosave.h"
|
||||
#include "dynamic.h"
|
||||
#include "message.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
// Woohoo, Winsock has headers from the STONE AGE! :D
|
||||
#define close(x) closesocket(x)
|
||||
#define CONST_CAST (const char*)
|
||||
#define NONCONST_CAST (char*)
|
||||
#else
|
||||
#define CONST_CAST
|
||||
#define NONCONST_CAST
|
||||
#endif
|
||||
|
||||
|
||||
#define PREV_PTR(x) ((x) == 0 ? handle->buffer_size - 1 : (x) - 1)
|
||||
#define NEXT_PTR(x) ((x + 1) % handle->buffer_size)
|
||||
|
||||
@@ -64,7 +69,7 @@ struct delta_frame
|
||||
bool used_real;
|
||||
};
|
||||
|
||||
#define UDP_FRAME_PACKETS 8
|
||||
#define UDP_FRAME_PACKETS 16
|
||||
|
||||
struct netplay
|
||||
{
|
||||
@@ -90,11 +95,21 @@ struct netplay
|
||||
uint32_t packet_buffer[UDP_FRAME_PACKETS * 2]; // To compat UDP packet loss we also send old data along with the packets.
|
||||
uint32_t frame_count;
|
||||
uint32_t read_frame_count;
|
||||
uint32_t other_frame_count;
|
||||
struct addrinfo *addr;
|
||||
struct sockaddr_storage their_addr;
|
||||
bool has_client_addr;
|
||||
|
||||
unsigned timeout_cnt;
|
||||
};
|
||||
|
||||
static void warn_hangup(void)
|
||||
{
|
||||
SSNES_WARN("Netplay has disconnected. Will continue without connection ...\n");
|
||||
if (g_extern.msg_queue)
|
||||
msg_queue_push(g_extern.msg_queue, "Netplay has disconnected. Will continue without connection.", 0, 480);
|
||||
}
|
||||
|
||||
void input_poll_net(void)
|
||||
{
|
||||
if (!netplay_should_skip(g_extern.netplay) && netplay_can_poll(g_extern.netplay))
|
||||
@@ -263,9 +278,27 @@ bool netplay_can_poll(netplay_t *handle)
|
||||
return handle->can_poll;
|
||||
}
|
||||
|
||||
// Not really a hash, but should be enough to differentiate implementations from each other.
|
||||
// Subtle differences in the implementation will not be possible to spot.
|
||||
// The alternative would have been checking serialization sizes, but it was troublesome for cross platform compat.
|
||||
static uint32_t implementation_magic_value(void)
|
||||
{
|
||||
uint32_t res = 0;
|
||||
res |= (psnes_library_revision_major() & 0xf) << 0;
|
||||
res |= (psnes_library_revision_minor() & 0xf) << 4;
|
||||
|
||||
// Shouldn't really use this, but oh well :) It'll do the job.
|
||||
const char *lib = psnes_library_id();
|
||||
size_t len = strlen(lib);
|
||||
for (size_t i = 0; i < len; i++)
|
||||
res ^= lib[i] << (i & 0xf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool send_info(netplay_t *handle)
|
||||
{
|
||||
uint32_t header[3] = { htonl(g_extern.cart_crc), htonl(psnes_serialize_size()), htonl(psnes_get_memory_size(SNES_MEMORY_CARTRIDGE_RAM)) };
|
||||
uint32_t header[3] = { htonl(g_extern.cart_crc), htonl(implementation_magic_value()), htonl(psnes_get_memory_size(SNES_MEMORY_CARTRIDGE_RAM)) };
|
||||
if (send(handle->fd, CONST_CAST header, sizeof(header), 0) != sizeof(header))
|
||||
return false;
|
||||
|
||||
@@ -300,9 +333,9 @@ static bool get_info(netplay_t *handle)
|
||||
SSNES_ERR("Cart CRC32s differ! Cannot use different games!\n");
|
||||
return false;
|
||||
}
|
||||
if (psnes_serialize_size() != ntohl(header[1]))
|
||||
if (implementation_magic_value() != ntohl(header[1]))
|
||||
{
|
||||
SSNES_ERR("Serialization sizes differ, make sure you're using exact same libsnes implementations!\n");
|
||||
SSNES_ERR("Implementations differ, make sure you're using exact same libsnes implementations!\n");
|
||||
return false;
|
||||
}
|
||||
if (psnes_get_memory_size(SNES_MEMORY_CARTRIDGE_RAM) != ntohl(header[2]))
|
||||
@@ -342,6 +375,9 @@ static void init_buffers(netplay_t *handle)
|
||||
|
||||
netplay_t *netplay_new(const char *server, uint16_t port, unsigned frames, const struct snes_callbacks *cb)
|
||||
{
|
||||
if (frames > UDP_FRAME_PACKETS)
|
||||
frames = UDP_FRAME_PACKETS;
|
||||
|
||||
netplay_t *handle = calloc(1, sizeof(*handle));
|
||||
if (!handle)
|
||||
return NULL;
|
||||
@@ -400,7 +436,7 @@ static bool send_chunk(netplay_t *handle)
|
||||
{
|
||||
if (sendto(handle->udp_fd, CONST_CAST handle->packet_buffer, sizeof(handle->packet_buffer), 0, addr, sizeof(struct sockaddr)) != sizeof(handle->packet_buffer))
|
||||
{
|
||||
SSNES_WARN("Netplay connection hung up. Will continue without netplay.\n");
|
||||
warn_hangup();
|
||||
handle->has_connection = false;
|
||||
return false;
|
||||
}
|
||||
@@ -408,26 +444,32 @@ static bool send_chunk(netplay_t *handle)
|
||||
return true;
|
||||
}
|
||||
|
||||
#define MAX_RETRIES 64
|
||||
#define MAX_RETRIES 16
|
||||
#define RETRY_MS 500
|
||||
|
||||
static int poll_input(netplay_t *handle, bool block)
|
||||
{
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(handle->udp_fd, &fds);
|
||||
FD_SET(handle->fd, &fds);
|
||||
int max_fd = (handle->fd > handle->udp_fd ? handle->fd : handle->udp_fd) + 1;
|
||||
|
||||
struct timeval tv = {
|
||||
const struct timeval tv = {
|
||||
.tv_sec = 0,
|
||||
.tv_usec = block ? (RETRY_MS * 1000) : 0
|
||||
};
|
||||
|
||||
int i = 0;
|
||||
do
|
||||
{
|
||||
if (select(max_fd, &fds, NULL, NULL, &tv) < 0)
|
||||
{
|
||||
handle->timeout_cnt++;
|
||||
|
||||
// select() does not take pointer to const struct timeval.
|
||||
// Technically possible for select() to modify tmp_tv, so we go paranoia mode.
|
||||
struct timeval tmp_tv = tv;
|
||||
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(handle->udp_fd, &fds);
|
||||
FD_SET(handle->fd, &fds);
|
||||
|
||||
if (select(max_fd, &fds, NULL, NULL, &tmp_tv) < 0)
|
||||
return -1;
|
||||
|
||||
// Somewhat hacky, but we aren't using the TCP connection for anything useful atm.
|
||||
@@ -440,20 +482,17 @@ static int poll_input(netplay_t *handle, bool block)
|
||||
|
||||
if (block && !send_chunk(handle))
|
||||
{
|
||||
SSNES_WARN("Netplay connection hung up. Will continue without netplay.\n");
|
||||
warn_hangup();
|
||||
handle->has_connection = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (block)
|
||||
SSNES_LOG("Network lag of %d ms, resending packet... Attempt %d of %d ...\n", RETRY_MS, i, MAX_RETRIES);
|
||||
|
||||
// Seems to be necessary on Win32.
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(handle->udp_fd, &fds);
|
||||
FD_SET(handle->fd, &fds);
|
||||
|
||||
} while ((i++ < MAX_RETRIES) && block);
|
||||
{
|
||||
SSNES_LOG("Network is stalling, resending packet... Count %u of %d ...\n",
|
||||
handle->timeout_cnt, MAX_RETRIES);
|
||||
}
|
||||
} while ((handle->timeout_cnt < MAX_RETRIES) && block);
|
||||
|
||||
if (block)
|
||||
return -1;
|
||||
@@ -482,7 +521,7 @@ static bool get_self_input_state(netplay_t *handle)
|
||||
|
||||
if (!send_chunk(handle))
|
||||
{
|
||||
SSNES_WARN("Netplay connection hung up. Will continue without netplay.\n");
|
||||
warn_hangup();
|
||||
handle->has_connection = false;
|
||||
return false;
|
||||
}
|
||||
@@ -510,7 +549,7 @@ static void parse_packet(netplay_t *handle, uint32_t *buffer, unsigned size)
|
||||
|
||||
for (unsigned i = 0; i < size && handle->read_frame_count <= handle->frame_count; i++)
|
||||
{
|
||||
uint32_t frame = buffer[2 * i];
|
||||
uint32_t frame = buffer[2 * i + 0];
|
||||
uint32_t state = buffer[2 * i + 1];
|
||||
|
||||
if (frame == handle->read_frame_count)
|
||||
@@ -519,6 +558,7 @@ static void parse_packet(netplay_t *handle, uint32_t *buffer, unsigned size)
|
||||
handle->buffer[handle->read_ptr].real_input_state = state;
|
||||
handle->read_ptr = NEXT_PTR(handle->read_ptr);
|
||||
handle->read_frame_count++;
|
||||
handle->timeout_cnt = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -543,7 +583,7 @@ bool netplay_poll(netplay_t *handle)
|
||||
if (!get_self_input_state(handle))
|
||||
return false;
|
||||
|
||||
// We skip reading the first frame so the host has a change to grab our host info so we don't block forever :')
|
||||
// We skip reading the first frame so the host has a chance to grab our host info so we don't block forever :')
|
||||
if (handle->frame_count == 0)
|
||||
{
|
||||
handle->buffer[0].used_real = true;
|
||||
@@ -554,20 +594,22 @@ bool netplay_poll(netplay_t *handle)
|
||||
return true;
|
||||
}
|
||||
|
||||
//fprintf(stderr, "Other ptr: %lu, Read ptr: %lu, Self ptr: %lu\n", handle->other_ptr, handle->read_ptr, handle->self_ptr);
|
||||
//fprintf(stderr, "Before poll: Other ptr: %lu, Read ptr: %lu, Self ptr: %lu\n", handle->other_ptr, handle->read_ptr, handle->self_ptr);
|
||||
/*
|
||||
if (handle->buffer_size > 1)
|
||||
{
|
||||
assert(handle->other_ptr != handle->self_ptr);
|
||||
assert(handle->read_ptr != handle->self_ptr);
|
||||
}
|
||||
assert(handle->other_ptr == handle->read_ptr);
|
||||
*/
|
||||
|
||||
// We might have reached the end of the buffer, where we simply have to block.
|
||||
int res = poll_input(handle, handle->other_ptr == NEXT_PTR(handle->self_ptr));
|
||||
int res = poll_input(handle, handle->other_ptr == handle->self_ptr);
|
||||
if (res == -1)
|
||||
{
|
||||
handle->has_connection = false;
|
||||
SSNES_WARN("Netplay connection timed out. Will continue without netplay.\n");
|
||||
warn_hangup();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -579,26 +621,28 @@ bool netplay_poll(netplay_t *handle)
|
||||
uint32_t buffer[UDP_FRAME_PACKETS * 2];
|
||||
if (!receive_data(handle, buffer, sizeof(buffer)))
|
||||
{
|
||||
SSNES_WARN("Netplay connection hung up. Will continue without netplay.\n");
|
||||
warn_hangup();
|
||||
handle->has_connection = false;
|
||||
return false;
|
||||
}
|
||||
parse_packet(handle, buffer, UDP_FRAME_PACKETS);
|
||||
|
||||
} while ((handle->read_frame_count <= handle->frame_count) &&
|
||||
poll_input(handle, (handle->other_ptr == NEXT_PTR(handle->self_ptr)) &&
|
||||
poll_input(handle, (handle->other_ptr == handle->self_ptr) &&
|
||||
(first_read == handle->read_frame_count)) == 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cannot allow this. Should not happen though.
|
||||
if (NEXT_PTR(handle->self_ptr) == handle->other_ptr)
|
||||
if (handle->self_ptr == handle->other_ptr)
|
||||
{
|
||||
SSNES_WARN("Netplay connection hung up. Will continue without netplay.\n");
|
||||
warn_hangup();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//fprintf(stderr, "After poll: Other ptr: %lu, Read ptr: %lu, Self ptr: %lu\n", handle->other_ptr, handle->read_ptr, handle->self_ptr);
|
||||
|
||||
if (handle->read_ptr != handle->self_ptr)
|
||||
simulate_input(handle);
|
||||
else
|
||||
@@ -668,36 +712,42 @@ void netplay_post_frame(netplay_t *handle)
|
||||
handle->frame_count++;
|
||||
|
||||
// Nothing to do...
|
||||
if (handle->other_ptr == handle->read_ptr)
|
||||
if (handle->other_frame_count == handle->read_frame_count)
|
||||
return;
|
||||
|
||||
// Skip ahead if we predicted correctly. Skip until our simulation failed.
|
||||
while (handle->other_ptr != handle->read_ptr)
|
||||
while (handle->other_frame_count < handle->read_frame_count)
|
||||
{
|
||||
struct delta_frame *ptr = &handle->buffer[handle->other_ptr];
|
||||
if ((ptr->simulated_input_state != ptr->real_input_state) && !ptr->used_real)
|
||||
break;
|
||||
handle->other_ptr = NEXT_PTR(handle->other_ptr);
|
||||
handle->other_frame_count++;
|
||||
}
|
||||
|
||||
if (handle->other_ptr != handle->read_ptr)
|
||||
if (handle->other_frame_count < handle->read_frame_count)
|
||||
{
|
||||
// Replay frames
|
||||
handle->is_replay = true;
|
||||
handle->tmp_ptr = handle->other_ptr;
|
||||
psnes_unserialize(handle->buffer[handle->other_ptr].state, handle->state_size);
|
||||
while (handle->tmp_ptr != handle->self_ptr)
|
||||
bool first = true;
|
||||
while (first || (handle->tmp_ptr != handle->self_ptr))
|
||||
{
|
||||
psnes_serialize(handle->buffer[handle->tmp_ptr].state, handle->state_size);
|
||||
#ifdef HAVE_THREADS
|
||||
lock_autosave();
|
||||
#endif
|
||||
psnes_run();
|
||||
#ifdef HAVE_THREADS
|
||||
unlock_autosave();
|
||||
#endif
|
||||
handle->tmp_ptr = NEXT_PTR(handle->tmp_ptr);
|
||||
first = false;
|
||||
}
|
||||
handle->other_ptr = handle->read_ptr;
|
||||
handle->other_frame_count = handle->read_frame_count;
|
||||
handle->is_replay = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <libsnes.hpp>
|
||||
#include "libsnes.hpp"
|
||||
|
||||
void input_poll_net(void);
|
||||
int16_t input_state_net(bool port, unsigned device, unsigned index, unsigned id);
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011 - Daniel De Matteis
|
||||
*
|
||||
* 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 <sys/process.h>
|
||||
#include <sysutil/sysutil_common.h>
|
||||
#include <sys/spu_initialize.h>
|
||||
#include <stddef.h>
|
||||
|
||||
int ssnes_main(int argc, char *argv[]);
|
||||
|
||||
SYS_PROCESS_PARAM(1001, 0x100000)
|
||||
|
||||
#undef main
|
||||
// Temporary, a more sane implementation should go here.
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
sys_spu_initialize(4, 3);
|
||||
char arg1[] = "ssnes";
|
||||
char arg2[] = "/dev_hdd0/game/SNES90000/USRDIR/main.sfc";
|
||||
char arg3[] = "-v";
|
||||
char arg4[] = "-c";
|
||||
char arg5[] = "/dev_hdd0/game/SSNE10000/USRDIR/ssnes.cfg";
|
||||
char *argv_[] = { arg1, arg2, arg3, arg4, arg5, NULL };
|
||||
return ssnes_main(sizeof(argv_) / sizeof(argv_[0]) - 1, argv_);
|
||||
}
|
||||
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011 - Daniel De Matteis
|
||||
*
|
||||
* 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 <cell/pad.h>
|
||||
#include <sdk_version.h>
|
||||
#include "pad_input.h"
|
||||
|
||||
#define LOWER_BUTTONS 2
|
||||
#define HIGHER_BUTTONS 3
|
||||
#define RSTICK_X 4
|
||||
#define RSTICK_Y 5
|
||||
#define LSTICK_X 6
|
||||
#define LSTICK_Y 7
|
||||
|
||||
#define DEADZONE_LOW 55
|
||||
#define DEADZONE_HIGH 210
|
||||
|
||||
#define PRESSED_LEFT_LSTICK(state) (CTRL_AXIS_LSTICK_X(state) <= DEADZONE_LOW)
|
||||
#define PRESSED_RIGHT_LSTICK(state) (CTRL_AXIS_LSTICK_X(state) >= DEADZONE_HIGH)
|
||||
#define PRESSED_UP_LSTICK(state) (CTRL_AXIS_LSTICK_Y(state) <= DEADZONE_LOW)
|
||||
#define PRESSED_DOWN_LSTICK(state) (CTRL_AXIS_LSTICK_Y(state) >= DEADZONE_HIGH)
|
||||
#define PRESSED_LEFT_RSTICK(state) (CTRL_AXIS_RSTICK_X(state) <= DEADZONE_LOW)
|
||||
#define PRESSED_RIGHT_RSTICK(state) (CTRL_AXIS_RSTICK_X(state) >= DEADZONE_HIGH)
|
||||
#define PRESSED_UP_RSTICK(state) (CTRL_AXIS_RSTICK_Y(state) <= DEADZONE_LOW)
|
||||
#define PRESSED_DOWN_RSTICK(state) (CTRL_AXIS_RSTICK_Y(state) >= DEADZONE_HIGH)
|
||||
|
||||
#define LSTICK_LEFT_SHIFT 48
|
||||
#define LSTICK_RIGHT_SHIFT 49
|
||||
#define LSTICK_UP_SHIFT 50
|
||||
#define LSTICK_DOWN_SHIFT 51
|
||||
|
||||
#define RSTICK_LEFT_SHIFT 52
|
||||
#define RSTICK_RIGHT_SHIFT 53
|
||||
#define RSTICK_UP_SHIFT 54
|
||||
#define RSTICK_DOWN_SHIFT 55
|
||||
|
||||
int cell_pad_input_init(void)
|
||||
{
|
||||
return cellPadInit(MAX_PADS);
|
||||
}
|
||||
|
||||
void cell_pad_input_deinit(void)
|
||||
{
|
||||
cellPadEnd();
|
||||
}
|
||||
|
||||
uint32_t cell_pad_input_pads_connected(void)
|
||||
{
|
||||
#if(CELL_SDK_VERSION > 0x340000)
|
||||
CellPadInfo2 pad_info;
|
||||
cellPadGetInfo2(&pad_info);
|
||||
#else
|
||||
CellPadInfo pad_info;
|
||||
cellPadGetInfo(&pad_info);
|
||||
#endif
|
||||
return pad_info.now_connect;
|
||||
}
|
||||
|
||||
#define M(x) (x & 0xFF)
|
||||
|
||||
uint64_t cell_pad_input_poll_device(uint32_t id)
|
||||
{
|
||||
CellPadData pad_data;
|
||||
static uint64_t ret[MAX_PADS];
|
||||
|
||||
// Get new pad data
|
||||
cellPadGetData(id, &pad_data);
|
||||
|
||||
if (pad_data.len == 0)
|
||||
return ret[id];
|
||||
else
|
||||
{
|
||||
ret[id] = 0;
|
||||
|
||||
// Build the return value.
|
||||
ret[id] |= (uint64_t)M(pad_data.button[LOWER_BUTTONS]);
|
||||
ret[id] |= (uint64_t)M(pad_data.button[HIGHER_BUTTONS]) << 8;
|
||||
ret[id] |= (uint64_t)M(pad_data.button[RSTICK_X]) << 32;
|
||||
ret[id] |= (uint64_t)M(pad_data.button[RSTICK_Y]) << 40;
|
||||
ret[id] |= (uint64_t)M(pad_data.button[LSTICK_X]) << 16;
|
||||
ret[id] |= (uint64_t)M(pad_data.button[LSTICK_Y]) << 24;
|
||||
|
||||
ret[id] |= (uint64_t)(PRESSED_LEFT_LSTICK(ret[id]) ? 1 : 0) << LSTICK_LEFT_SHIFT;
|
||||
ret[id] |= (uint64_t)(PRESSED_RIGHT_LSTICK(ret[id]) ? 1 : 0) << LSTICK_RIGHT_SHIFT;
|
||||
ret[id] |= (uint64_t)(PRESSED_UP_LSTICK(ret[id]) ? 1 : 0) << LSTICK_UP_SHIFT;
|
||||
ret[id] |= (uint64_t)(PRESSED_DOWN_LSTICK(ret[id]) ? 1 : 0) << LSTICK_DOWN_SHIFT;
|
||||
|
||||
ret[id] |= (uint64_t)(PRESSED_LEFT_RSTICK(ret[id]) ? 1 : 0) << RSTICK_LEFT_SHIFT;
|
||||
ret[id] |= (uint64_t)(PRESSED_RIGHT_RSTICK(ret[id]) ? 1 : 0) << RSTICK_RIGHT_SHIFT;
|
||||
ret[id] |= (uint64_t)(PRESSED_UP_RSTICK(ret[id]) ? 1 : 0) << RSTICK_UP_SHIFT;
|
||||
ret[id] |= (uint64_t)(PRESSED_DOWN_RSTICK(ret[id]) ? 1 : 0) << RSTICK_DOWN_SHIFT;
|
||||
return ret[id];
|
||||
}
|
||||
}
|
||||
#undef M
|
||||
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011 - Daniel De Matteis
|
||||
*
|
||||
* 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 __CELL_PAD_INPUT_H
|
||||
#define __CELL_PAD_INPUT_H
|
||||
|
||||
#define MAX_PADS 7
|
||||
|
||||
#define CTRL_SELECT_MASK 0x01
|
||||
#define CTRL_L3_MASK 0x2
|
||||
#define CTRL_R3_MASK 0x4
|
||||
#define CTRL_START_MASK 0x8
|
||||
#define CTRL_UP_MASK 0x10
|
||||
#define CTRL_RIGHT_MASK 0x20
|
||||
#define CTRL_DOWN_MASK 0x40
|
||||
#define CTRL_LEFT_MASK 0x80
|
||||
|
||||
#define CTRL_L2_MASK 0x100
|
||||
#define CTRL_R2_MASK 0x200
|
||||
#define CTRL_L1_MASK 0x400
|
||||
#define CTRL_R1_MASK 0x800
|
||||
#define CTRL_TRIANGLE_MASK 0x1000
|
||||
#define CTRL_CIRCLE_MASK 0x2000
|
||||
#define CTRL_CROSS_MASK 0x4000
|
||||
#define CTRL_SQUARE_MASK 0x8000
|
||||
|
||||
// Big numbers, harhar.
|
||||
#define CTRL_LSTICK_LEFT_MASK 0x1000000000000LLU
|
||||
#define CTRL_LSTICK_RIGHT_MASK 0x2000000000000LLU
|
||||
#define CTRL_LSTICK_UP_MASK 0x4000000000000LLU
|
||||
#define CTRL_LSTICK_DOWN_MASK 0x8000000000000LLU
|
||||
#define CTRL_RSTICK_LEFT_MASK 0x10000000000000LLU
|
||||
#define CTRL_RSTICK_RIGHT_MASK 0x20000000000000LLU
|
||||
#define CTRL_RSTICK_UP_MASK 0x40000000000000LLU
|
||||
#define CTRL_RSTICK_DOWN_MASK 0x80000000000000LLU
|
||||
|
||||
|
||||
#define CTRL_SELECT(state) (CTRL_SELECT_MASK & state)
|
||||
#define CTRL_L3(state) (CTRL_L3_MASK & state)
|
||||
#define CTRL_R3(state) (CTRL_R3_MASK & state)
|
||||
#define CTRL_START(state) (CTRL_START_MASK & state)
|
||||
#define CTRL_UP(state) (CTRL_UP_MASK & state)
|
||||
#define CTRL_RIGHT(state) (CTRL_RIGHT_MASK & state)
|
||||
#define CTRL_DOWN(state) (CTRL_DOWN_MASK & state)
|
||||
#define CTRL_LEFT(state) (CTRL_LEFT_MASK & state)
|
||||
|
||||
#define CTRL_L2(state) (CTRL_L2_MASK & state)
|
||||
#define CTRL_R2(state) (CTRL_R2_MASK & state)
|
||||
#define CTRL_L1(state) (CTRL_L1_MASK & state)
|
||||
#define CTRL_R1(state) (CTRL_R1_MASK & state)
|
||||
#define CTRL_TRIANGLE(state) (CTRL_TRIANGLE_MASK & state)
|
||||
#define CTRL_CIRCLE(state) (CTRL_CIRCLE_MASK & state)
|
||||
#define CTRL_CROSS(state) (CTRL_CROSS_MASK & state)
|
||||
#define CTRL_SQUARE(state) (CTRL_SQUARE_MASK & state)
|
||||
|
||||
#define CTRL_LSTICK_LEFT(state) (CTRL_LSTICK_LEFT_MASK & state)
|
||||
#define CTRL_LSTICK_RIGHT(state) (CTRL_LSTICK_RIGHT_MASK & state)
|
||||
#define CTRL_LSTICK_UP(state) (CTRL_LSTICK_UP_MASK & state)
|
||||
#define CTRL_LSTICK_DOWN(state) (CTRL_LSTICK_DOWN_MASK & state)
|
||||
#define CTRL_RSTICK_LEFT(state) (CTRL_RSTICK_LEFT_MASK & state)
|
||||
#define CTRL_RSTICK_RIGHT(state) (CTRL_RSTICK_RIGHT_MASK & state)
|
||||
#define CTRL_RSTICK_UP(state) (CTRL_RSTICK_UP_MASK & state)
|
||||
#define CTRL_RSTICK_DOWN(state) (CTRL_RSTICK_DOWN_MASK & state)
|
||||
|
||||
#define CTRL_MASK(state, mask) (state & mask)
|
||||
|
||||
#define CTRL_AXIS_LSTICK_X(state) ((uint8_t)(((0xFF0000LLU & state) >> 16) & 0xFF))
|
||||
#define CTRL_AXIS_LSTICK_Y(state) ((uint8_t)(((0xFF000000LLU & state) >> 24) & 0xFF))
|
||||
#define CTRL_AXIS_RSTICK_X(state) ((uint8_t)(((0xFF00000000LLU & state) >> 32) & 0xFF))
|
||||
#define CTRL_AXIS_RSTICK_Y(state) ((uint8_t)(((0xFF0000000000LLU & state) >> 40) & 0xFF))
|
||||
|
||||
typedef uint64_t cell_input_state_t;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Init and destruction of device.
|
||||
int cell_pad_input_init(void);
|
||||
void cell_pad_input_deinit(void);
|
||||
|
||||
// Get number of pads connected
|
||||
uint32_t cell_pad_input_pads_connected(void);
|
||||
|
||||
cell_input_state_t cell_pad_input_poll_device(uint32_t id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
Whitespace-only changes.
@@ -0,0 +1,335 @@
|
||||
##### 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"
|
||||
|
||||
#### Video
|
||||
|
||||
# Video driver to use. "gl", "xvideo", "sdl" or "ext" (external API driver)
|
||||
# video_driver = "gl"
|
||||
|
||||
# Path to external video driver using the SSNES driver API.
|
||||
# video_external_driver =
|
||||
|
||||
# Windowed xscale and yscale
|
||||
# (Real x res: base_size * xscale * aspect_ratio, real y res: base_size * yscale)
|
||||
# video_xscale = 3.0
|
||||
# video_yscale = 3.0
|
||||
|
||||
# Fullscreen resolution. Resolution of 0 uses the resolution of the desktop.
|
||||
# video_fullscreen_x = 0
|
||||
# video_fullscreen_y = 0
|
||||
|
||||
# Start in fullscreen. Can be changed at runtime.
|
||||
# video_fullscreen = false
|
||||
|
||||
# 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
|
||||
|
||||
# Smoothens picture with bilinear filtering. Should be disabled if using pixel shaders.
|
||||
# video_smooth = true
|
||||
|
||||
# Forces rendering area to stay equal to SNES aspect ratio 4:3 or as defined in video_aspect_ratio.
|
||||
# video_force_aspect = true
|
||||
|
||||
# A floating point value for video aspect ratio (width / height)
|
||||
# video_aspect_ratio = 1.333
|
||||
|
||||
# Forces cropping of overscanned frames. Crops away top 7 scanlines and 8 bottom scanlines. (15/15 for interlaced frames).
|
||||
# video_crop_overscan = false
|
||||
|
||||
# Path to Cg shader.
|
||||
# video_cg_shader = "/path/to/cg/shader.cg"
|
||||
|
||||
# Path to bSNES XML shader (GLSL only). If both Cg shader path and XML shader path are defined,
|
||||
# Cg shader will take priority unless overridden in video_shader_type.
|
||||
# video_bsnes_shader = "/path/to/bsnes/xml/shader.shader"
|
||||
|
||||
# Which shader type to use. Valid values are "cg", "bsnes", "none" and "auto"
|
||||
# video_shader_type = auto
|
||||
|
||||
# Defines a directory where XML shaders are kept.
|
||||
# video_shader_dir =
|
||||
|
||||
# Render to texture first. Useful when doing multi-pass shaders or control the output of shaders better.
|
||||
# video_render_to_texture = false
|
||||
|
||||
# Defines the video scale of render-to-texture.
|
||||
# The output FBO size is scaled by these amounts against the input size (typically 256 * 224 for SNES).
|
||||
# video_fbo_scale_x = 2.0
|
||||
# video_fbo_scale_y = 2.0
|
||||
|
||||
# Define shader to use for second pass (needs render-to-texture).
|
||||
# video_second_pass_shader = "/path/to/second/shader.{cg,shader}"
|
||||
|
||||
# Defines if bilinear filtering is used during second pass (needs render-to-texture).
|
||||
# video_second_pass_smooth = true
|
||||
|
||||
# CPU-based filter. Path to a bSNES CPU filter (*.filter)
|
||||
# video_filter =
|
||||
|
||||
# Path to a TTF font used for rendering messages. This path must be defined to enable fonts.
|
||||
# Do note that the _full_ path of the font is necessary!
|
||||
# video_font_path =
|
||||
|
||||
# Size of the TTF font rendered.
|
||||
# video_font_size = 48
|
||||
|
||||
# Enable usage of OSD messages.
|
||||
# video_font_enable = true
|
||||
|
||||
# Offset for where messages will be placed on screen. Values are in range 0.0 to 1.0 for both x and y values.
|
||||
# [0.0, 0.0] maps to the lower left corner of the screen.
|
||||
# video_message_pos_x = 0.05
|
||||
# video_message_pos_y = 0.05
|
||||
|
||||
# Color for message. The value is treated as a hexadecimal value.
|
||||
# It is a regular RGB hex number, i.e. red is "ff0000".
|
||||
# video_message_color = ffffff
|
||||
|
||||
# Video refresh rate of your monitor.
|
||||
# Used to calculate a suitable audio input rate.
|
||||
# video_refresh_rate = 59.95
|
||||
|
||||
#### Audio
|
||||
|
||||
# Enable audio.
|
||||
# audio_enable = true
|
||||
|
||||
# Audio output samplerate.
|
||||
# audio_out_rate = 48000
|
||||
|
||||
# When altering audio_in_rate on-the-fly, define by how much each time.
|
||||
# audio_rate_step = 0.25
|
||||
|
||||
# Audio driver backend. Depending on configuration possible candidates are: alsa, pulse, oss, jack, rsound, roar, openal, sdl, xaudio and ext (external driver).
|
||||
# audio_driver =
|
||||
|
||||
# Path to external audio driver using the SSNES audio driver API.
|
||||
# audio_external_driver =
|
||||
|
||||
# Override the default audio device the audio_driver uses. This is driver dependant. E.g. ALSA wants a PCM device, OSS wants a path (e.g. /dev/dsp), Jack wants portnames (e.g. system:playback1,system:playback_2), and so on ...
|
||||
# audio_device =
|
||||
|
||||
# External DSP plugin that processes audio before it's sent to the driver.
|
||||
# audio_dsp_plugin =
|
||||
|
||||
# Will sync (block) on audio. Recommended.
|
||||
# audio_sync = true
|
||||
|
||||
# Desired audio latency in milliseconds. Might not be honored if driver can't provide given latency.
|
||||
# audio_latency = 64
|
||||
|
||||
|
||||
### Input
|
||||
|
||||
# Input driver. Depending on video driver, it might force a different input driver.
|
||||
# input_driver = sdl
|
||||
|
||||
# Defines axis threshold. Possible values are [0.0, 1.0]
|
||||
# input_axis_threshold = 0.5
|
||||
|
||||
# Keyboard input. Will recognize normal keypresses and special keys like "left", "right", and so on.
|
||||
# Keyboard input, Joypad and Joyaxis will all obey the "nul" bind, which disables the bind completely,
|
||||
# rather than relying on a default.
|
||||
# input_player1_a = x
|
||||
# input_player1_b = z
|
||||
# input_player1_y = a
|
||||
# input_player1_x = s
|
||||
# input_player1_start = enter
|
||||
# input_player1_select = rshift
|
||||
# input_player1_l = q
|
||||
# input_player1_r = w
|
||||
# input_player1_left = left
|
||||
# input_player1_right = right
|
||||
# input_player1_up = up
|
||||
# input_player1_down = down
|
||||
|
||||
# If desired, it is possible to override which joypads are being used for player 1 through 5. First joypad available is 0.
|
||||
# input_player1_joypad_index = 0
|
||||
# input_player2_joypad_index = 1
|
||||
# input_player3_joypad_index = 2
|
||||
# input_player4_joypad_index = 3
|
||||
# input_player5_joypad_index = 4
|
||||
|
||||
# Joypad buttons. Figure these out by looking at jstest /dev/input/js0 output, or use ssnes-joyconfig.
|
||||
# You can use joypad hats with hnxx, where n is the hat, and xx is a string representing direction.
|
||||
# E.g. "h0up"
|
||||
# input_player1_a_btn = 1
|
||||
# input_player1_b_btn = 0
|
||||
# input_player1_y_btn = 2
|
||||
# input_player1_x_btn = 3
|
||||
# input_player1_start_btn = 7
|
||||
# input_player1_select_btn = 6
|
||||
# input_player1_l_btn = 4
|
||||
# input_player1_r_btn = 5
|
||||
# input_player1_left_btn = 11
|
||||
# input_player1_right_btn = 12
|
||||
# input_player1_up_btn = 13
|
||||
# input_player1_down_btn = 14
|
||||
|
||||
# Axis for SNES DPAD.
|
||||
# Needs to be either '+' or '-' in the first character signaling either positive or negative direction of the axis, then the axis number.
|
||||
# Do note that every other input option has the corresponding _btn and _axis binds as well; they are omitted here for clarity.
|
||||
# input_player1_left_axis = -0
|
||||
# input_player1_right_axis = +0
|
||||
# input_player1_up_axis = +1
|
||||
# input_player1_down_axis = -1
|
||||
|
||||
# Same stuff, just for player two.
|
||||
# input_player2_a =
|
||||
# input_player2_b =
|
||||
# input_player2_y =
|
||||
# input_player2_x =
|
||||
# input_player2_start =
|
||||
# input_player2_select =
|
||||
# input_player2_l =
|
||||
# input_player2_r =
|
||||
# input_player2_left =
|
||||
# input_player2_right =
|
||||
# input_player2_up =
|
||||
# input_player2_down =
|
||||
|
||||
# input_player2_a_btn = 1
|
||||
# input_player2_b_btn = 0
|
||||
# input_player2_y_btn = 2
|
||||
# input_player2_x_btn = 3
|
||||
# input_player2_start_btn = 7
|
||||
# input_player2_select_btn = 6
|
||||
# input_player2_l_btn = 4
|
||||
# input_player2_r_btn = 5
|
||||
# input_player2_left_btn = 11
|
||||
# input_player2_right_btn = 12
|
||||
# input_player2_up_btn = 13
|
||||
# input_player2_down_btn = 14
|
||||
|
||||
|
||||
# input_player2_left_axis = -0
|
||||
# input_player2_right_axis = +0
|
||||
# input_player2_up_axis = -1
|
||||
# input_player2_down_axis = +1
|
||||
|
||||
# This goes all the way to player 5, but again omitted for clarity.
|
||||
|
||||
# Toggles fullscreen.
|
||||
# input_toggle_fullscreen = f
|
||||
# Saves state.
|
||||
# input_save_state = f2
|
||||
# Loads state.
|
||||
# input_load_state = f4
|
||||
|
||||
# State slots. With slot set to 0, save state name is *.state (or whatever defined on commandline).
|
||||
# When slot is != 0, path will be $path%d, where %d is slot number.
|
||||
# input_state_slot_increase = f7
|
||||
# input_state_slot_decrease = f6
|
||||
|
||||
# Toggles between fast-forwarding and normal speed.
|
||||
# input_toggle_fast_forward = space
|
||||
|
||||
# Hold for fast-forward. Releasing button disables fast-forward.
|
||||
# input_hold_fast_forward = l
|
||||
|
||||
# Key to exit emulator cleanly.
|
||||
# Killing it in any hard way (SIGTERM, SIGKILL, etc, will terminate emulator without saving RAM, etc.)
|
||||
# input_exit_emulator = escape
|
||||
|
||||
# Decrease/increase input sample rate on the fly. Amount to decrease/increase is defined by audio_rate_step.
|
||||
# input_rate_step_up = kp_plus
|
||||
# input_rate_step_down = kp_minus
|
||||
|
||||
# Applies next and previous XML shader in directory.
|
||||
# input_shader_next = m
|
||||
# input_shader_prev = n
|
||||
|
||||
# Enable rewinding. This will take a performance hit when playing, so it is disabled by default.
|
||||
# Do note that rewinding will only work properly when using bSNES libsnes core atm.
|
||||
# rewind_enable = true
|
||||
|
||||
# Rewinding buffer size in megabytes. Bigger rewinding buffer means you can rewind longer.
|
||||
# The buffer should be approx. 20MB per minute of buffer time.
|
||||
# rewind_buffer_size = 20
|
||||
|
||||
# Rewind granularity. When rewinding defined number of frames, you can rewind several frames at a time, increasing the rewinding speed.
|
||||
# rewind_granularity = 1
|
||||
|
||||
# Hold button down to rewind. Rewinding must be enabled.
|
||||
# input_rewind = r
|
||||
|
||||
# Toggle between recording and not.
|
||||
# input_movie_record_toggle = o
|
||||
|
||||
# Toggle between paused and non-paused state
|
||||
# input_pause_toggle = p
|
||||
|
||||
# Frame advance when game is paused
|
||||
# input_frame_advance = k
|
||||
|
||||
# Reset the emulated SNES.
|
||||
# input_reset = h
|
||||
|
||||
# Configures DSP plugin
|
||||
# input_dsp_config = c
|
||||
|
||||
# Pause gameplay when window focus is lost.
|
||||
# pause_nonactive = true
|
||||
|
||||
# Autosaves the non-volatile SRAM at a regular interval. This is disabled by default unless set otherwise.
|
||||
# The interval is measured in seconds. A value of 0 disables autosave.
|
||||
# autosave_interval =
|
||||
|
||||
# When being client over netplay, use keybinds for player 1.
|
||||
# netplay_client_swap_input = false
|
||||
|
||||
|
||||
# Path to XML cheat database (as used by bSNES).
|
||||
# cheat_database_path =
|
||||
|
||||
# Path to XML cheat config, a file which keeps track of which
|
||||
# cheat settings are used for individual games.
|
||||
# If the file does not exist, it will be created.
|
||||
# cheat_settings_path =
|
||||
|
||||
# Cheats.
|
||||
# input_cheat_index_plus = y
|
||||
# input_cheat_index_minus = t
|
||||
# input_cheat_toggle = u
|
||||
|
||||
# Mute/unmute audio
|
||||
# input_audio_mute = f9
|
||||
|
||||
# Take screenshot
|
||||
# input_screenshot = print_screen
|
||||
|
||||
# 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
|
||||
|
||||
# Block SRAM from being overwritten when loading save states.
|
||||
# Might potentially lead to buggy games.
|
||||
# block_sram_overwrite = false
|
||||
|
||||
# When saving a savestate, save state index is automatically increased before
|
||||
# it is saved.
|
||||
# Also, when loading a ROM, the index will be set to the highest existing index.
|
||||
# There is no upper bound on the index.
|
||||
# savestate_auto_index = false
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
Content-ID = UP0001-SSNE10000_00-0000000000000001
|
||||
k_licensee = 0x00000000000000000000000000000000
|
||||
DRM_Type = Local
|
||||
Content_Type = Game_Exec
|
||||
PackageVersion = 00.01
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011 - Daniel De Matteis
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <cell/audio.h>
|
||||
#include <sys/timer.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/synchronization.h>
|
||||
#include "../fifo_buffer.h"
|
||||
#include <sys/event.h>
|
||||
|
||||
#define AUDIO_BLOCKS 8 // 8 or 16. Guess what we choose? :)
|
||||
#define AUDIO_CHANNELS 2 // All hail glorious stereo!
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t audio_port;
|
||||
bool nonblocking;
|
||||
volatile bool quit_thread;
|
||||
fifo_buffer_t *buffer;
|
||||
|
||||
pthread_t thread;
|
||||
sys_lwmutex_t lock;
|
||||
sys_lwmutex_t cond_lock;
|
||||
sys_lwcond_t cond;
|
||||
} ps3_audio_t;
|
||||
|
||||
static void *event_loop(void *data)
|
||||
{
|
||||
ps3_audio_t *aud = data;
|
||||
sys_event_queue_t id;
|
||||
sys_ipc_key_t key;
|
||||
sys_event_t event;
|
||||
|
||||
cellAudioCreateNotifyEventQueue(&id, &key);
|
||||
cellAudioSetNotifyEventQueue(key);
|
||||
|
||||
float out_tmp[CELL_AUDIO_BLOCK_SAMPLES * AUDIO_CHANNELS] __attribute__((aligned(16)));
|
||||
|
||||
while (!aud->quit_thread)
|
||||
{
|
||||
sys_event_queue_receive(id, &event, SYS_NO_TIMEOUT);
|
||||
|
||||
sys_lwmutex_lock(&aud->lock, SYS_NO_TIMEOUT);
|
||||
if (fifo_read_avail(aud->buffer) >= sizeof(out_tmp))
|
||||
fifo_read(aud->buffer, out_tmp, sizeof(out_tmp));
|
||||
else
|
||||
memset(out_tmp, 0, sizeof(out_tmp));
|
||||
sys_lwmutex_unlock(&aud->lock);
|
||||
sys_lwcond_signal(&aud->cond);
|
||||
|
||||
cellAudioAddData(aud->audio_port, out_tmp, CELL_AUDIO_BLOCK_SAMPLES, 1.0);
|
||||
}
|
||||
|
||||
cellAudioRemoveNotifyEventQueue(key);
|
||||
pthread_exit(NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *ps3_audio_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
(void)latency;
|
||||
(void)device;
|
||||
(void)rate; // Always use 48kHz.
|
||||
g_settings.audio.out_rate = 48000.0;
|
||||
|
||||
ps3_audio_t *data = calloc(1, sizeof(*data));
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
CellAudioPortParam params;
|
||||
cellAudioInit();
|
||||
params.nChannel = AUDIO_CHANNELS;
|
||||
params.nBlock = AUDIO_BLOCKS;
|
||||
params.attr = 0;
|
||||
|
||||
if (cellAudioPortOpen(¶ms, &data->audio_port) != CELL_OK)
|
||||
{
|
||||
cellAudioQuit();
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data->buffer = fifo_new(CELL_AUDIO_BLOCK_SAMPLES * AUDIO_CHANNELS * AUDIO_BLOCKS * sizeof(float));
|
||||
|
||||
sys_lwmutex_attribute_t lock_attr, cond_lock_attr;
|
||||
sys_lwcond_attribute_t cond_attr;
|
||||
|
||||
sys_lwmutex_attribute_initialize(lock_attr);
|
||||
sys_lwmutex_attribute_initialize(cond_lock_attr);
|
||||
sys_lwcond_attribute_initialize(cond_attr);
|
||||
|
||||
sys_lwmutex_create(&data->lock, &lock_attr);
|
||||
sys_lwmutex_create(&data->cond_lock, &cond_lock_attr);
|
||||
sys_lwcond_create(&data->cond, &data->cond_lock, &cond_attr);
|
||||
|
||||
cellAudioPortStart(data->audio_port);
|
||||
pthread_create(&data->thread, NULL, event_loop, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
static ssize_t ps3_audio_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
ps3_audio_t *aud = data;
|
||||
|
||||
if (aud->nonblocking)
|
||||
{
|
||||
if (fifo_write_avail(aud->buffer) < size)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (fifo_write_avail(aud->buffer) < size)
|
||||
sys_lwcond_wait(&aud->cond, 0);
|
||||
}
|
||||
|
||||
sys_lwmutex_lock(&aud->lock, SYS_NO_TIMEOUT);
|
||||
fifo_write(aud->buffer, buf, size);
|
||||
sys_lwmutex_unlock(&aud->lock);
|
||||
return size;
|
||||
}
|
||||
|
||||
static bool ps3_audio_stop(void *data)
|
||||
{
|
||||
ps3_audio_t *aud = data;
|
||||
cellAudioPortStop(aud->audio_port);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ps3_audio_start(void *data)
|
||||
{
|
||||
ps3_audio_t *aud = data;
|
||||
cellAudioPortStart(aud->audio_port);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ps3_audio_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
ps3_audio_t *aud = data;
|
||||
aud->nonblocking = state;
|
||||
}
|
||||
|
||||
static void ps3_audio_free(void *data)
|
||||
{
|
||||
ps3_audio_t *aud = data;
|
||||
|
||||
aud->quit_thread = true;
|
||||
cellAudioPortStart(aud->audio_port);
|
||||
pthread_join(aud->thread, NULL);
|
||||
|
||||
cellAudioPortStop(aud->audio_port);
|
||||
cellAudioPortClose(aud->audio_port);
|
||||
cellAudioQuit();
|
||||
fifo_free(aud->buffer);
|
||||
|
||||
sys_lwmutex_destroy(&aud->lock);
|
||||
sys_lwmutex_destroy(&aud->cond_lock);
|
||||
sys_lwcond_destroy(&aud->cond);
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
static bool ps3_audio_use_float(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
const audio_driver_t audio_ps3 = {
|
||||
.init = ps3_audio_init,
|
||||
.write = ps3_audio_write,
|
||||
.stop = ps3_audio_stop,
|
||||
.start = ps3_audio_start,
|
||||
.set_nonblock_state = ps3_audio_set_nonblock_state,
|
||||
.use_float = ps3_audio_use_float,
|
||||
.free = ps3_audio_free,
|
||||
.ident = "ps3"
|
||||
};
|
||||
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
/* SSNES - A Super Ninteno Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011 - Daniel De Matteis
|
||||
*
|
||||
* 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 "pad_input.h"
|
||||
#include <stdint.h>
|
||||
#include "../libsnes.hpp"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
static uint64_t state[5];
|
||||
static void ps3_input_poll(void *data)
|
||||
{
|
||||
(void)data;
|
||||
for (unsigned i = 0; i < 5; i++)
|
||||
state[i] = cell_pad_input_poll_device(i);
|
||||
}
|
||||
|
||||
static int16_t ps3_input_state(void *data, const struct snes_keybind **binds,
|
||||
bool port, unsigned device,
|
||||
unsigned index, unsigned id)
|
||||
{
|
||||
(void)data;
|
||||
(void)binds;
|
||||
(void)index;
|
||||
|
||||
if (device != SNES_DEVICE_JOYPAD)
|
||||
return 0;
|
||||
|
||||
unsigned player = 0;
|
||||
if (port == SNES_PORT_2 && device == SNES_DEVICE_MULTITAP)
|
||||
player = index + 1;
|
||||
else if (port == SNES_PORT_2)
|
||||
player = 1;
|
||||
|
||||
// Hardcoded binds.
|
||||
uint64_t button;
|
||||
switch (id)
|
||||
{
|
||||
case SNES_DEVICE_ID_JOYPAD_A:
|
||||
button = CTRL_CIRCLE_MASK;
|
||||
break;
|
||||
case SNES_DEVICE_ID_JOYPAD_B:
|
||||
button = CTRL_CROSS_MASK;
|
||||
break;
|
||||
case SNES_DEVICE_ID_JOYPAD_X:
|
||||
button = CTRL_TRIANGLE_MASK;
|
||||
break;
|
||||
case SNES_DEVICE_ID_JOYPAD_Y:
|
||||
button = CTRL_SQUARE_MASK;
|
||||
break;
|
||||
case SNES_DEVICE_ID_JOYPAD_LEFT:
|
||||
button = CTRL_LEFT_MASK;
|
||||
break;
|
||||
case SNES_DEVICE_ID_JOYPAD_RIGHT:
|
||||
button = CTRL_RIGHT_MASK;
|
||||
break;
|
||||
case SNES_DEVICE_ID_JOYPAD_UP:
|
||||
button = CTRL_UP_MASK;
|
||||
break;
|
||||
case SNES_DEVICE_ID_JOYPAD_DOWN:
|
||||
button = CTRL_DOWN_MASK;
|
||||
break;
|
||||
case SNES_DEVICE_ID_JOYPAD_START:
|
||||
button = CTRL_START_MASK;
|
||||
break;
|
||||
case SNES_DEVICE_ID_JOYPAD_SELECT:
|
||||
button = CTRL_SELECT_MASK;
|
||||
break;
|
||||
case SNES_DEVICE_ID_JOYPAD_L:
|
||||
button = CTRL_L1_MASK;
|
||||
break;
|
||||
case SNES_DEVICE_ID_JOYPAD_R:
|
||||
button = CTRL_R1_MASK;
|
||||
break;
|
||||
default:
|
||||
button = 0;
|
||||
}
|
||||
|
||||
return CTRL_MASK(state[player], button) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void ps3_free_input(void *data)
|
||||
{
|
||||
(void)data;
|
||||
cell_pad_input_deinit();
|
||||
}
|
||||
|
||||
static void* ps3_input_init(void)
|
||||
{
|
||||
cell_pad_input_init();
|
||||
return (void*)-1;
|
||||
}
|
||||
|
||||
static bool ps3_key_pressed(void *data, int key)
|
||||
{
|
||||
(void)data;
|
||||
switch(key)
|
||||
{
|
||||
case SSNES_FAST_FORWARD_HOLD_KEY:
|
||||
return CTRL_RSTICK_UP(state[0]) && CTRL_R2(~state[0]);
|
||||
case SSNES_LOAD_STATE_KEY:
|
||||
return (CTRL_RSTICK_UP(state[0]) && CTRL_R2(state[0]));
|
||||
case SSNES_SAVE_STATE_KEY:
|
||||
return (CTRL_RSTICK_DOWN(state[0]) && CTRL_R2(state[0]));
|
||||
case SSNES_STATE_SLOT_PLUS:
|
||||
return (CTRL_RSTICK_RIGHT(state[0]) && CTRL_R2(state[0]));
|
||||
case SSNES_STATE_SLOT_MINUS:
|
||||
return (CTRL_RSTICK_LEFT(state[0]) && CTRL_R2(state[0]));
|
||||
case SSNES_REWIND:
|
||||
return CTRL_RSTICK_DOWN(state[0]) && CTRL_R2(~state[0]);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const input_driver_t input_ps3 = {
|
||||
.init = ps3_input_init,
|
||||
.poll = ps3_input_poll,
|
||||
.input_state = ps3_input_state,
|
||||
.key_pressed = ps3_key_pressed,
|
||||
.free = ps3_free_input,
|
||||
.ident = "ps3",
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
+54
-24
@@ -12,16 +12,17 @@ if [ -d /opt/local/lib ]; then
|
||||
add_library_dirs /opt/local/lib
|
||||
fi
|
||||
|
||||
if [ "$OS" = BSD ]; then
|
||||
if [ $OS = BSD ]; then
|
||||
DYLIB=-lc
|
||||
else
|
||||
DYLIB=-ldl
|
||||
fi
|
||||
|
||||
if [ "$HAVE_DYNAMIC" = "yes" ] && [ "$HAVE_CONFIGFILE" = "no" ]; then
|
||||
echo "Cannot have dynamic loading of libsnes and no configfile support."
|
||||
echo "Dynamic loading requires config file support."
|
||||
exit 1
|
||||
if [ -z "$LIBSNES" ]; then
|
||||
LIBSNES="-lsnes"
|
||||
else
|
||||
echo "Explicit libsnes used, disabling dynamic libsnes loading ..."
|
||||
HAVE_DYNAMIC=no
|
||||
fi
|
||||
|
||||
if [ $HAVE_DYNAMIC != yes ]; then
|
||||
@@ -30,21 +31,28 @@ if [ $HAVE_DYNAMIC != yes ]; then
|
||||
add_define_make libsnes $LIBSNES
|
||||
fi
|
||||
|
||||
check_lib THREADS -lpthread pthread_create
|
||||
check_lib DYLIB $DYLIB dlopen
|
||||
check_lib NETPLAY -lc socket
|
||||
check_lib GETOPT_LONG -lc getopt_long
|
||||
|
||||
if [ $HAVE_DYLIB = no ] && [ $HAVE_DYNAMIC = yes ]; then
|
||||
echo "Dynamic loading of libsnes is enabled, but your platform does not appear to have dlopen(), use --disable-dynamic or --with-libsnes=\"-lsnes\"".
|
||||
exit 1
|
||||
fi
|
||||
|
||||
check_pkgconf ALSA alsa
|
||||
check_header OSS sys/soundcard.h
|
||||
check_header OSS_BSD soundcard.h
|
||||
check_lib OSS_LIB -lossaudio
|
||||
|
||||
if [ "$OS" = "Darwin" ]; then
|
||||
if [ $OS = Darwin ]; then
|
||||
check_lib AL "-framework OpenAL" alcOpenDevice
|
||||
else
|
||||
check_lib AL -lopenal alcOpenDevice
|
||||
fi
|
||||
|
||||
if [ "$OS" = "Darwin" ]; then
|
||||
if [ $OS = Darwin ]; then
|
||||
check_lib FBO "-framework OpenGL" glFramebufferTexture2D
|
||||
else
|
||||
check_lib FBO -lGL glFramebufferTexture2D
|
||||
@@ -58,34 +66,56 @@ 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
|
||||
if [ $HAVE_SDL_NEW = yes ]; then
|
||||
echo "SSNES is not compatible with SDL 1.3 API, please use a recent 1.2 release."
|
||||
exit 1
|
||||
fi
|
||||
check_critical SDL "Cannot find SDL library."
|
||||
|
||||
# On some distros, -lCg doesn't link against -lstdc++ it seems ...
|
||||
check_lib_cxx CG -lCg cgCreateContext
|
||||
if [ $HAVE_OPENGL != no ]; then
|
||||
check_lib_cxx CG -lCg cgCreateContext
|
||||
else
|
||||
echo "Ignoring Cg. OpenGL is not enabled."
|
||||
HAVE_CG=no
|
||||
fi
|
||||
|
||||
check_pkgconf XML libxml-2.0
|
||||
check_pkgconf SDL_IMAGE SDL_image
|
||||
|
||||
if [ $HAVE_FFMPEG != no ]; then
|
||||
check_pkgconf AVCODEC libavcodec
|
||||
check_pkgconf AVFORMAT libavformat
|
||||
check_pkgconf AVUTIL libavutil
|
||||
check_pkgconf SWSCALE libswscale
|
||||
if [ $HAVE_THREADS != no ]; then
|
||||
if [ $HAVE_FFMPEG != no ]; then
|
||||
check_pkgconf AVCODEC libavcodec
|
||||
check_pkgconf AVFORMAT libavformat
|
||||
check_pkgconf AVUTIL libavutil
|
||||
check_pkgconf SWSCALE libswscale
|
||||
|
||||
( [ $HAVE_FFMPEG = auto ] && ( [ $HAVE_AVCODEC = no ] || [ $HAVE_AVFORMAT = no ] || [ $HAVE_AVUTIL = no ] || [ $HAVE_SWSCALE = no ] ) && HAVE_FFMPEG=no ) || HAVE_FFMPEG=yes
|
||||
( [ $HAVE_FFMPEG = auto ] && ( [ $HAVE_AVCODEC = no ] || [ $HAVE_AVFORMAT = no ] || [ $HAVE_AVUTIL = no ] || [ $HAVE_SWSCALE = no ] ) && HAVE_FFMPEG=no ) || HAVE_FFMPEG=yes
|
||||
fi
|
||||
|
||||
if [ $HAVE_FFMPEG = yes ]; then
|
||||
check_lib FFMPEG_ALLOC_CONTEXT3 "$AVCODEC_LIBS" avcodec_alloc_context3
|
||||
check_lib FFMPEG_AVCODEC_OPEN2 "$AVCODEC_LIBS" avcodec_open2
|
||||
check_lib FFMPEG_AVIO_OPEN "$AVFORMAT_LIBS" avio_open
|
||||
check_lib FFMPEG_AVFORMAT_WRITE_HEADER "$AVFORMAT_LIBS" avformat_write_header
|
||||
check_lib FFMPEG_AVFORMAT_NEW_STREAM "$AVFORMAT_LIBS" avformat_new_stream
|
||||
fi
|
||||
|
||||
if [ $HAVE_FFMPEG = no ] && [ $HAVE_X264RGB = yes ]; then
|
||||
echo "x264 RGB recording is enabled, but FFmpeg is not. --enable-x264rgb will not have any effect."
|
||||
fi
|
||||
else
|
||||
echo "Not building with threading support. Will skip FFmpeg."
|
||||
HAVE_FFMPEG=no
|
||||
fi
|
||||
|
||||
check_pkgconf SRC samplerate
|
||||
|
||||
check_lib DYNAMIC $DYLIB dlopen
|
||||
|
||||
check_pkgconf FREETYPE freetype2
|
||||
check_pkgconf XVIDEO xv
|
||||
check_pkgconf X11 x11
|
||||
check_pkgconf XEXT xext
|
||||
if [ $HAVE_X11 = yes ] && [ $HAVE_XEXT = yes ]; then
|
||||
check_pkgconf XVIDEO xv
|
||||
else
|
||||
echo "X11 or Xext not present. Skipping XVideo."
|
||||
HAVE_XVIDEO=no
|
||||
fi
|
||||
|
||||
check_lib STRL -lc strlcpy
|
||||
|
||||
@@ -94,7 +124,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 COREAUDIO 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 OPENGL DYLIB GETOPT_LONG THREADS CG XML SDL_IMAGE DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE CONFIGFILE FREETYPE XVIDEO X11 XEXT NETPLAY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM X264RGB"
|
||||
create_config_make config.mk $VARS
|
||||
create_config_header config.h $VARS
|
||||
|
||||
+8
-6
@@ -1,19 +1,21 @@
|
||||
. qb/qb.params.sh
|
||||
|
||||
PACKAGE_NAME=ssnes
|
||||
PACKAGE_VERSION=0.8
|
||||
PACKAGE_VERSION=0.9.3
|
||||
|
||||
# Adds a command line opt to ./configure --help
|
||||
# $1: Variable (HAVE_ALSA, HAVE_OSS, etc)
|
||||
# $2: Comment
|
||||
# $3: Default arg. auto implies that HAVE_ALSA will be set according to library checks later on.
|
||||
add_command_line_enable DYNAMIC "Enable dynamic loading of libsnes library." no
|
||||
add_command_line_string LIBSNES "libsnes library used" "-lsnes"
|
||||
add_command_line_enable FFMPEG "Enable FFmpeg recording support" no
|
||||
add_command_line_enable DYNAMIC "Disable dynamic loading of libsnes library" yes
|
||||
add_command_line_string LIBSNES "libsnes library used" ""
|
||||
add_command_line_enable THREADS "Threading support" auto
|
||||
add_command_line_enable FFMPEG "Enable FFmpeg recording support" auto
|
||||
add_command_line_enable X264RGB "Enable lossless X264 RGB recording" no
|
||||
add_command_line_enable DYLIB "Enable dynamic loading support" auto
|
||||
add_command_line_enable NETPLAY "Enable netplay support" auto
|
||||
add_command_line_enable SRC "Enable libsamplerate support" no
|
||||
add_command_line_enable CONFIGFILE "Disable support for config file" yes
|
||||
add_command_line_enable OPENGL "Disable OpenGL support" yes
|
||||
add_command_line_enable CG "Enable Cg shader support" auto
|
||||
add_command_line_enable XML "Enable bSNES-style XML shader support" auto
|
||||
add_command_line_enable FBO "Enable render-to-texture (FBO) support" auto
|
||||
@@ -28,4 +30,4 @@ 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
|
||||
add_command_line_enable SDL_IMAGE "Enable SDL_image support" auto
|
||||
add_command_line_enable PYTHON "Enable Python 3 support for shaders" no
|
||||
add_command_line_enable PYTHON "Enable Python 3 support for shaders" auto
|
||||
+1
-1
@@ -81,7 +81,7 @@ print_sub_str_opt()
|
||||
|
||||
lowertext="`echo $arg1 | tr '[A-Z]' '[a-z]'`"
|
||||
|
||||
echo "--with-$lowertext: $arg2 (Defaults: $arg3)"
|
||||
echo "--with-$lowertext: $arg2"
|
||||
}
|
||||
|
||||
parse_input()
|
||||
|
||||
+251
-133
@@ -13,11 +13,16 @@
|
||||
#include <stdbool.h>
|
||||
#include "ffemu.h"
|
||||
#include "fifo_buffer.h"
|
||||
#include "SDL_thread.h"
|
||||
#include "thread.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "../config.h"
|
||||
#endif
|
||||
|
||||
struct video_info
|
||||
{
|
||||
AVCodecContext *codec;
|
||||
AVCodec *encoder;
|
||||
|
||||
AVFrame *conv_frame;
|
||||
uint8_t *conv_frame_buf;
|
||||
@@ -27,7 +32,8 @@ struct video_info
|
||||
size_t outbuf_size;
|
||||
|
||||
int fmt;
|
||||
int pix_size;
|
||||
int pix_fmt;
|
||||
size_t pix_size;
|
||||
|
||||
AVFormatContext *format;
|
||||
|
||||
@@ -37,6 +43,7 @@ struct video_info
|
||||
struct audio_info
|
||||
{
|
||||
AVCodecContext *codec;
|
||||
AVCodec *encoder;
|
||||
|
||||
int16_t *buffer;
|
||||
size_t frames_in_buffer;
|
||||
@@ -62,13 +69,14 @@ struct ffemu
|
||||
|
||||
struct ffemu_params params;
|
||||
|
||||
SDL_cond *cond;
|
||||
SDL_mutex *cond_lock;
|
||||
SDL_mutex *lock;
|
||||
scond_t *cond;
|
||||
slock_t *cond_lock;
|
||||
slock_t *lock;
|
||||
fifo_buffer_t *audio_fifo;
|
||||
fifo_buffer_t *video_fifo;
|
||||
fifo_buffer_t *attr_fifo;
|
||||
SDL_Thread *thread;
|
||||
sthread_t *thread;
|
||||
|
||||
volatile bool alive;
|
||||
volatile bool can_sleep;
|
||||
};
|
||||
@@ -79,14 +87,29 @@ static bool init_audio(struct audio_info *audio, struct ffemu_params *param)
|
||||
if (!codec)
|
||||
return false;
|
||||
|
||||
audio->encoder = codec;
|
||||
|
||||
// FFmpeg just loves to deprecate stuff :)
|
||||
#ifdef HAVE_FFMPEG_ALLOC_CONTEXT3
|
||||
audio->codec = avcodec_alloc_context3(codec);
|
||||
#else
|
||||
audio->codec = avcodec_alloc_context();
|
||||
avcodec_get_context_defaults(audio->codec);
|
||||
#endif
|
||||
|
||||
audio->codec->sample_rate = param->samplerate;
|
||||
audio->codec->time_base = (AVRational) { 1, param->samplerate };
|
||||
audio->codec->time_base = av_d2q(1.0 / param->samplerate, 1000000);
|
||||
audio->codec->channels = param->channels;
|
||||
audio->codec->sample_fmt = AV_SAMPLE_FMT_S16;
|
||||
|
||||
#ifdef HAVE_FFMPEG_AVCODEC_OPEN2
|
||||
if (avcodec_open2(audio->codec, codec, NULL) != 0)
|
||||
#else
|
||||
if (avcodec_open(audio->codec, codec) != 0)
|
||||
#endif
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
audio->buffer = av_malloc(audio->codec->frame_size * param->channels * sizeof(int16_t));
|
||||
if (!audio->buffer)
|
||||
@@ -100,12 +123,18 @@ static bool init_audio(struct audio_info *audio, struct ffemu_params *param)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool init_video(struct video_info *video, struct ffemu_params *param)
|
||||
static bool init_video(struct video_info *video, const struct ffemu_params *param)
|
||||
{
|
||||
#ifdef HAVE_X264RGB
|
||||
AVCodec *codec = avcodec_find_encoder(CODEC_ID_H264);
|
||||
#else
|
||||
AVCodec *codec = avcodec_find_encoder(CODEC_ID_FFV1);
|
||||
#endif
|
||||
if (!codec)
|
||||
return false;
|
||||
|
||||
video->encoder = codec;
|
||||
|
||||
#if AV_HAVE_BIGENDIAN
|
||||
video->fmt = PIX_FMT_RGB555BE;
|
||||
#else
|
||||
@@ -118,26 +147,58 @@ static bool init_video(struct video_info *video, struct ffemu_params *param)
|
||||
video->pix_size = sizeof(uint32_t);
|
||||
}
|
||||
|
||||
#ifdef HAVE_X264RGB
|
||||
video->pix_fmt = PIX_FMT_BGR24;
|
||||
#else
|
||||
video->pix_fmt = PIX_FMT_RGB32;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FFMPEG_ALLOC_CONTEXT3
|
||||
video->codec = avcodec_alloc_context3(codec);
|
||||
#else
|
||||
video->codec = avcodec_alloc_context();
|
||||
avcodec_get_context_defaults(video->codec);
|
||||
#endif
|
||||
|
||||
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->time_base = av_d2q(1.0 / param->fps, 1000000); // Arbitrary big number.
|
||||
video->codec->sample_aspect_ratio = av_d2q(param->aspect_ratio * param->out_height / param->out_width, 255);
|
||||
video->codec->thread_count = 2;
|
||||
video->codec->pix_fmt = video->pix_fmt;
|
||||
|
||||
if (avcodec_open2(video->codec, codec, NULL) != 0)
|
||||
#ifdef HAVE_FFMPEG_AVCODEC_OPEN2
|
||||
AVDictionary *opts = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_X264RGB
|
||||
video->codec->thread_count = 3;
|
||||
av_dict_set(&opts, "qp", "0", 0);
|
||||
#else
|
||||
video->codec->thread_count = 2;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FFMPEG_AVCODEC_OPEN2
|
||||
if (avcodec_open2(video->codec, codec, &opts) != 0)
|
||||
#else
|
||||
if (avcodec_open(video->codec, codec) != 0)
|
||||
#endif
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef HAVE_FFMPEG_AVCODEC_OPEN2
|
||||
if (opts)
|
||||
av_dict_free(&opts);
|
||||
#endif
|
||||
|
||||
// Allocate a big buffer :p ffmpeg API doesn't seem to give us some clues how big this buffer should be.
|
||||
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.
|
||||
int size = avpicture_get_size(PIX_FMT_RGB32, param->fb_width, param->fb_height);
|
||||
size_t size = avpicture_get_size(video->pix_fmt, param->out_width, param->out_height);
|
||||
video->conv_frame_buf = av_malloc(size);
|
||||
video->conv_frame = avcodec_alloc_frame();
|
||||
avpicture_fill((AVPicture*)video->conv_frame, video->conv_frame_buf, PIX_FMT_RGB32, param->fb_width, param->fb_height);
|
||||
avpicture_fill((AVPicture*)video->conv_frame, video->conv_frame_buf, video->pix_fmt, param->out_width, param->out_height);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -147,18 +208,34 @@ static bool init_muxer(ffemu_t *handle)
|
||||
AVFormatContext *ctx = avformat_alloc_context();
|
||||
av_strlcpy(ctx->filename, handle->params.filename, sizeof(ctx->filename));
|
||||
ctx->oformat = av_guess_format(NULL, ctx->filename, NULL);
|
||||
#ifdef AVIO_FLAG_WRITE
|
||||
if (avio_open(&ctx->pb, ctx->filename, AVIO_FLAG_WRITE) < 0)
|
||||
|
||||
// FFmpeg sure likes to make things difficult.
|
||||
#if defined(AVIO_FLAG_WRITE)
|
||||
#define FFMPEG_FLAG_RW AVIO_FLAG_WRITE
|
||||
#elif defined(AVIO_WRONLY)
|
||||
#define FFMPEG_FLAG_RW AVIO_WRONLY
|
||||
#elif defined(URL_WRONLY)
|
||||
#define FFMPEG_FLAG_RW URL_WRONLY
|
||||
#else
|
||||
if (url_fopen(&ctx->pb, ctx->filename, URL_WRONLY) < 0)
|
||||
#define FFMPEG_FLAG_RW 2 // Seems to be consistent, but you never know.
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FFMPEG_AVIO_OPEN
|
||||
if (avio_open(&ctx->pb, ctx->filename, FFMPEG_FLAG_RW) < 0)
|
||||
#else
|
||||
if (url_fopen(&ctx->pb, ctx->filename, FFMPEG_FLAG_RW) < 0)
|
||||
#endif
|
||||
{
|
||||
av_free(ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
int stream_cnt = 0;
|
||||
#ifdef HAVE_FFMPEG_AVFORMAT_NEW_STREAM
|
||||
AVStream *stream = avformat_new_stream(ctx, handle->video.encoder);
|
||||
#else
|
||||
unsigned stream_cnt = 0;
|
||||
AVStream *stream = av_new_stream(ctx, stream_cnt++);
|
||||
#endif
|
||||
stream->codec = handle->video.codec;
|
||||
|
||||
if (ctx->oformat->flags & AVFMT_GLOBALHEADER)
|
||||
@@ -166,29 +243,45 @@ static bool init_muxer(ffemu_t *handle)
|
||||
handle->muxer.vstream = stream;
|
||||
handle->muxer.vstream->sample_aspect_ratio = handle->video.codec->sample_aspect_ratio;
|
||||
|
||||
#ifdef HAVE_FFMPEG_AVFORMAT_NEW_STREAM
|
||||
stream = avformat_new_stream(ctx, handle->audio.encoder);
|
||||
#else
|
||||
stream = av_new_stream(ctx, stream_cnt++);
|
||||
#endif
|
||||
stream->codec = handle->audio.codec;
|
||||
|
||||
if (ctx->oformat->flags & AVFMT_GLOBALHEADER)
|
||||
handle->audio.codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
||||
handle->muxer.astream = stream;
|
||||
|
||||
#ifdef HAVE_X264RGB // Avoids a warning at end about non-monotonically increasing DTS values. It seems to be harmless to disable this.
|
||||
ctx->oformat->flags |= AVFMT_TS_NONSTRICT;
|
||||
#endif
|
||||
|
||||
av_dict_set(&ctx->metadata, "title", "SSNES video dump", 0);
|
||||
|
||||
#ifdef HAVE_FFMPEG_AVFORMAT_WRITE_HEADER
|
||||
if (avformat_write_header(ctx, NULL) < 0)
|
||||
#else
|
||||
if (av_write_header(ctx) != 0)
|
||||
#endif
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
handle->muxer.ctx = ctx;
|
||||
return true;
|
||||
}
|
||||
|
||||
#define MAX_FRAMES 64
|
||||
#define MAX_FRAMES 32
|
||||
|
||||
static int SDLCALL ffemu_thread(void *data);
|
||||
static void ffemu_thread(void *data);
|
||||
|
||||
static bool init_thread(ffemu_t *handle)
|
||||
{
|
||||
assert(handle->lock = SDL_CreateMutex());
|
||||
assert(handle->cond_lock = SDL_CreateMutex());
|
||||
assert(handle->cond = SDL_CreateCond());
|
||||
assert(handle->lock = slock_new());
|
||||
assert(handle->cond_lock = slock_new());
|
||||
assert(handle->cond = scond_new());
|
||||
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 *
|
||||
@@ -196,7 +289,7 @@ static bool init_thread(ffemu_t *handle)
|
||||
|
||||
handle->alive = true;
|
||||
handle->can_sleep = true;
|
||||
assert(handle->thread = SDL_CreateThread(ffemu_thread, handle));
|
||||
assert(handle->thread = sthread_create(ffemu_thread, handle));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -205,17 +298,17 @@ static void deinit_thread(ffemu_t *handle)
|
||||
{
|
||||
if (handle->thread)
|
||||
{
|
||||
SDL_mutexP(handle->cond_lock);
|
||||
slock_lock(handle->cond_lock);
|
||||
handle->alive = false;
|
||||
handle->can_sleep = false;
|
||||
SDL_mutexV(handle->cond_lock);
|
||||
slock_unlock(handle->cond_lock);
|
||||
|
||||
SDL_CondSignal(handle->cond);
|
||||
SDL_WaitThread(handle->thread, NULL);
|
||||
scond_signal(handle->cond);
|
||||
sthread_join(handle->thread);
|
||||
|
||||
SDL_DestroyMutex(handle->lock);
|
||||
SDL_DestroyMutex(handle->cond_lock);
|
||||
SDL_DestroyCond(handle->cond);
|
||||
slock_free(handle->lock);
|
||||
slock_free(handle->cond_lock);
|
||||
scond_free(handle->cond);
|
||||
|
||||
handle->thread = NULL;
|
||||
}
|
||||
@@ -306,96 +399,111 @@ void ffemu_free(ffemu_t *handle)
|
||||
}
|
||||
}
|
||||
|
||||
int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data)
|
||||
bool ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
SDL_mutexP(handle->lock);
|
||||
slock_lock(handle->lock);
|
||||
unsigned avail = fifo_write_avail(handle->attr_fifo);
|
||||
SDL_mutexV(handle->lock);
|
||||
slock_unlock(handle->lock);
|
||||
|
||||
if (!handle->alive)
|
||||
return -1;
|
||||
return false;
|
||||
|
||||
if (avail >= sizeof(*data))
|
||||
break;
|
||||
|
||||
SDL_mutexP(handle->cond_lock);
|
||||
slock_lock(handle->cond_lock);
|
||||
if (handle->can_sleep)
|
||||
{
|
||||
handle->can_sleep = false;
|
||||
SDL_CondWait(handle->cond, handle->cond_lock);
|
||||
scond_wait(handle->cond, handle->cond_lock);
|
||||
handle->can_sleep = true;
|
||||
}
|
||||
else
|
||||
SDL_CondSignal(handle->cond);
|
||||
scond_signal(handle->cond);
|
||||
|
||||
SDL_mutexV(handle->cond_lock);
|
||||
slock_unlock(handle->cond_lock);
|
||||
}
|
||||
|
||||
SDL_mutexP(handle->lock);
|
||||
fifo_write(handle->attr_fifo, data, sizeof(*data));
|
||||
fifo_write(handle->video_fifo, data->data, data->pitch * data->height);
|
||||
SDL_mutexV(handle->lock);
|
||||
SDL_CondSignal(handle->cond);
|
||||
slock_lock(handle->lock);
|
||||
|
||||
return 0;
|
||||
// Tightly pack our frame to conserve memory. libsnes tends to use a very large pitch.
|
||||
struct ffemu_video_data attr_data = *data;
|
||||
|
||||
if (attr_data.is_dupe)
|
||||
attr_data.width = attr_data.height = attr_data.pitch = 0;
|
||||
else
|
||||
attr_data.pitch = attr_data.width * handle->video.pix_size;
|
||||
|
||||
fifo_write(handle->attr_fifo, &attr_data, sizeof(attr_data));
|
||||
|
||||
unsigned offset = 0;
|
||||
for (unsigned y = 0; y < attr_data.height; y++, offset += data->pitch)
|
||||
fifo_write(handle->video_fifo, (const uint8_t*)data->data + offset, attr_data.pitch);
|
||||
|
||||
slock_unlock(handle->lock);
|
||||
scond_signal(handle->cond);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
|
||||
bool ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
SDL_mutexP(handle->lock);
|
||||
slock_lock(handle->lock);
|
||||
unsigned avail = fifo_write_avail(handle->audio_fifo);
|
||||
SDL_mutexV(handle->lock);
|
||||
slock_unlock(handle->lock);
|
||||
|
||||
if (!handle->alive)
|
||||
return -1;
|
||||
return false;
|
||||
|
||||
if (avail >= data->frames * handle->params.channels * sizeof(int16_t))
|
||||
break;
|
||||
|
||||
SDL_mutexP(handle->cond_lock);
|
||||
slock_lock(handle->cond_lock);
|
||||
if (handle->can_sleep)
|
||||
{
|
||||
handle->can_sleep = false;
|
||||
SDL_CondWait(handle->cond, handle->cond_lock);
|
||||
scond_wait(handle->cond, handle->cond_lock);
|
||||
handle->can_sleep = true;
|
||||
}
|
||||
else
|
||||
SDL_CondSignal(handle->cond);
|
||||
scond_signal(handle->cond);
|
||||
|
||||
SDL_mutexV(handle->cond_lock);
|
||||
slock_unlock(handle->cond_lock);
|
||||
}
|
||||
|
||||
SDL_mutexP(handle->lock);
|
||||
slock_lock(handle->lock);
|
||||
fifo_write(handle->audio_fifo, data->data, data->frames * handle->params.channels * sizeof(int16_t));
|
||||
SDL_mutexV(handle->lock);
|
||||
SDL_CondSignal(handle->cond);
|
||||
slock_unlock(handle->lock);
|
||||
scond_signal(handle->cond);
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int ffemu_push_video_thread(ffemu_t *handle, const struct ffemu_video_data *data)
|
||||
static bool 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, handle->video.fmt,
|
||||
handle->params.out_width, handle->params.out_height, PIX_FMT_RGB32, SWS_POINT,
|
||||
NULL, NULL, NULL);
|
||||
if (!data->is_dupe)
|
||||
{
|
||||
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, handle->video.pix_fmt, SWS_POINT,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
int linesize = data->pitch;
|
||||
int linesize = data->pitch;
|
||||
|
||||
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);
|
||||
sws_scale(handle->video.sws_ctx, (const uint8_t* const*)&data->data, &linesize, 0,
|
||||
data->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);
|
||||
|
||||
if (outsize < 0)
|
||||
return -1;
|
||||
return false;
|
||||
|
||||
AVPacket pkt;
|
||||
av_init_packet(&pkt);
|
||||
@@ -412,21 +520,21 @@ static int ffemu_push_video_thread(ffemu_t *handle, const struct ffemu_video_dat
|
||||
if (pkt.size > 0)
|
||||
{
|
||||
if (av_interleaved_write_frame(handle->muxer.ctx, &pkt) < 0)
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
handle->video.frame_cnt++;
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int ffemu_push_audio_thread(ffemu_t *handle, const struct ffemu_audio_data *data)
|
||||
static bool ffemu_push_audio_thread(ffemu_t *handle, const struct ffemu_audio_data *data)
|
||||
{
|
||||
size_t written_frames = 0;
|
||||
while (written_frames < data->frames)
|
||||
{
|
||||
size_t can_write = handle->audio.codec->frame_size - handle->audio.frames_in_buffer;
|
||||
size_t write_frames = data->frames > can_write ? can_write : data->frames;
|
||||
size_t write_frames = data->frames - written_frames > can_write ? can_write : data->frames - written_frames;
|
||||
|
||||
memcpy(handle->audio.buffer + handle->audio.frames_in_buffer * handle->params.channels,
|
||||
data->data + written_frames * handle->params.channels,
|
||||
@@ -442,15 +550,15 @@ static int ffemu_push_audio_thread(ffemu_t *handle, const struct ffemu_audio_dat
|
||||
pkt.data = handle->audio.outbuf;
|
||||
pkt.stream_index = handle->muxer.astream->index;
|
||||
|
||||
int out_size = avcodec_encode_audio(handle->audio.codec, handle->audio.outbuf, handle->audio.outbuf_size, handle->audio.buffer);
|
||||
int out_size = avcodec_encode_audio(handle->audio.codec,
|
||||
handle->audio.outbuf, handle->audio.outbuf_size, handle->audio.buffer);
|
||||
if (out_size < 0)
|
||||
return -1;
|
||||
return false;
|
||||
|
||||
pkt.size = out_size;
|
||||
if (handle->audio.codec->coded_frame && handle->audio.codec->coded_frame->pts != AV_NOPTS_VALUE)
|
||||
pkt.pts = av_rescale_q(handle->audio.codec->coded_frame->pts, handle->audio.codec->time_base, handle->muxer.astream->time_base);
|
||||
else
|
||||
pkt.pts = av_rescale_q(handle->audio.frame_cnt, handle->audio.codec->time_base, handle->muxer.astream->time_base);
|
||||
|
||||
pkt.pts = av_rescale_q(handle->audio.codec->coded_frame->pts,
|
||||
handle->audio.codec->time_base, handle->muxer.astream->time_base);
|
||||
|
||||
pkt.flags |= AV_PKT_FLAG_KEY;
|
||||
handle->audio.frames_in_buffer = 0;
|
||||
@@ -459,85 +567,96 @@ static int ffemu_push_audio_thread(ffemu_t *handle, const struct ffemu_audio_dat
|
||||
if (pkt.size > 0)
|
||||
{
|
||||
if (av_interleaved_write_frame(handle->muxer.ctx, &pkt) < 0)
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int ffemu_finalize(ffemu_t *handle)
|
||||
bool ffemu_finalize(ffemu_t *handle)
|
||||
{
|
||||
deinit_thread(handle);
|
||||
|
||||
// Push out frames still stuck in queue.
|
||||
size_t audio_buf_size = 512 * handle->params.channels * sizeof(int16_t);
|
||||
int16_t *audio_buf = av_malloc(audio_buf_size);
|
||||
void *video_buf = av_malloc(2 * handle->params.fb_width * handle->params.fb_height * handle->video.pix_size);
|
||||
if (video_buf)
|
||||
|
||||
// Try pushing data in an interleaving pattern to ease the work of the muxer a bit.
|
||||
bool did_work;
|
||||
do
|
||||
{
|
||||
did_work = false;
|
||||
|
||||
if (fifo_read_avail(handle->audio_fifo) >= audio_buf_size)
|
||||
{
|
||||
fifo_read(handle->audio_fifo, audio_buf, audio_buf_size);
|
||||
|
||||
struct ffemu_audio_data aud = {
|
||||
.frames = 512,
|
||||
.data = audio_buf
|
||||
};
|
||||
|
||||
ffemu_push_audio_thread(handle, &aud);
|
||||
did_work = true;
|
||||
}
|
||||
|
||||
struct ffemu_video_data attr_buf;
|
||||
while (fifo_read_avail(handle->attr_fifo) >= sizeof(attr_buf))
|
||||
if (fifo_read_avail(handle->attr_fifo) >= sizeof(attr_buf))
|
||||
{
|
||||
fifo_read(handle->attr_fifo, &attr_buf, sizeof(attr_buf));
|
||||
fifo_read(handle->video_fifo, video_buf, attr_buf.height * attr_buf.pitch);
|
||||
attr_buf.data = video_buf;
|
||||
ffemu_push_video_thread(handle, &attr_buf);
|
||||
|
||||
did_work = true;
|
||||
}
|
||||
av_free(video_buf);
|
||||
}
|
||||
} while (did_work);
|
||||
|
||||
size_t audio_buf_size = 128 * handle->params.channels * sizeof(int16_t);
|
||||
int16_t *audio_buf = av_malloc(audio_buf_size);
|
||||
if (audio_buf)
|
||||
{
|
||||
while (fifo_read_avail(handle->audio_fifo) >= audio_buf_size)
|
||||
{
|
||||
fifo_read(handle->audio_fifo, audio_buf, sizeof(audio_buf_size));
|
||||
|
||||
struct ffemu_audio_data aud = {
|
||||
.frames = 128,
|
||||
.data = audio_buf
|
||||
};
|
||||
|
||||
ffemu_push_audio_thread(handle, &aud);
|
||||
}
|
||||
av_free(audio_buf);
|
||||
}
|
||||
// Flush out last audio.
|
||||
size_t avail = fifo_read_avail(handle->audio_fifo);
|
||||
fifo_read(handle->audio_fifo, audio_buf, avail);
|
||||
struct ffemu_audio_data aud = {
|
||||
.frames = avail / (sizeof(int16_t) * handle->params.channels),
|
||||
.data = audio_buf
|
||||
};
|
||||
ffemu_push_audio_thread(handle, &aud);
|
||||
|
||||
deinit_thread_buf(handle);
|
||||
av_free(audio_buf);
|
||||
av_free(video_buf);
|
||||
|
||||
// Push out delayed frames. (MPEG codecs)
|
||||
// Flush out last video.
|
||||
AVPacket pkt;
|
||||
av_init_packet(&pkt);
|
||||
pkt.stream_index = handle->muxer.vstream->index;
|
||||
pkt.data = handle->video.outbuf;
|
||||
|
||||
int out_size = 0;
|
||||
do
|
||||
for (;;)
|
||||
{
|
||||
out_size = avcodec_encode_video(handle->video.codec, handle->video.outbuf, handle->video.outbuf_size, NULL);
|
||||
if (out_size <= 0)
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
pkt.size = out_size;
|
||||
|
||||
if (pkt.size > 0)
|
||||
{
|
||||
int err = av_interleaved_write_frame(handle->muxer.ctx, &pkt);
|
||||
if (err < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
} while (out_size > 0);
|
||||
int err = av_interleaved_write_frame(handle->muxer.ctx, &pkt);
|
||||
if (err < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// Write final data.
|
||||
av_write_trailer(handle->muxer.ctx);
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int SDLCALL ffemu_thread(void *data)
|
||||
static void ffemu_thread(void *data)
|
||||
{
|
||||
ffemu_t *ff = data;
|
||||
|
||||
@@ -545,47 +664,47 @@ static int SDLCALL ffemu_thread(void *data)
|
||||
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);
|
||||
size_t audio_buf_size = 512 * ff->params.channels * sizeof(int16_t);
|
||||
int16_t *audio_buf = av_malloc(audio_buf_size);
|
||||
assert(audio_buf);
|
||||
|
||||
struct ffemu_video_data attr_buf;
|
||||
|
||||
while (ff->alive)
|
||||
{
|
||||
struct ffemu_video_data attr_buf;
|
||||
|
||||
bool avail_video = false;
|
||||
bool avail_audio = false;
|
||||
|
||||
SDL_mutexP(ff->lock);
|
||||
slock_lock(ff->lock);
|
||||
if (fifo_read_avail(ff->attr_fifo) >= sizeof(attr_buf))
|
||||
avail_video = true;
|
||||
|
||||
if (fifo_read_avail(ff->audio_fifo) >= audio_buf_size)
|
||||
avail_audio = true;
|
||||
SDL_mutexV(ff->lock);
|
||||
slock_unlock(ff->lock);
|
||||
|
||||
if (!avail_video && !avail_audio)
|
||||
{
|
||||
SDL_mutexP(ff->cond_lock);
|
||||
slock_lock(ff->cond_lock);
|
||||
if (ff->can_sleep)
|
||||
{
|
||||
ff->can_sleep = false;
|
||||
SDL_CondWait(ff->cond, ff->cond_lock);
|
||||
scond_wait(ff->cond, ff->cond_lock);
|
||||
ff->can_sleep = true;
|
||||
}
|
||||
else
|
||||
SDL_CondSignal(ff->cond);
|
||||
scond_signal(ff->cond);
|
||||
|
||||
SDL_mutexV(ff->cond_lock);
|
||||
slock_unlock(ff->cond_lock);
|
||||
}
|
||||
|
||||
if (avail_video)
|
||||
{
|
||||
SDL_mutexP(ff->lock);
|
||||
slock_lock(ff->lock);
|
||||
fifo_read(ff->attr_fifo, &attr_buf, sizeof(attr_buf));
|
||||
fifo_read(ff->video_fifo, video_buf, attr_buf.height * attr_buf.pitch);
|
||||
SDL_mutexV(ff->lock);
|
||||
SDL_CondSignal(ff->cond);
|
||||
slock_unlock(ff->lock);
|
||||
scond_signal(ff->cond);
|
||||
|
||||
attr_buf.data = video_buf;
|
||||
ffemu_push_video_thread(ff, &attr_buf);
|
||||
@@ -593,13 +712,13 @@ static int SDLCALL ffemu_thread(void *data)
|
||||
|
||||
if (avail_audio)
|
||||
{
|
||||
SDL_mutexP(ff->lock);
|
||||
slock_lock(ff->lock);
|
||||
fifo_read(ff->audio_fifo, audio_buf, audio_buf_size);
|
||||
SDL_mutexV(ff->lock);
|
||||
SDL_CondSignal(ff->cond);
|
||||
slock_unlock(ff->lock);
|
||||
scond_signal(ff->cond);
|
||||
|
||||
struct ffemu_audio_data aud = {
|
||||
.frames = 128,
|
||||
.frames = 512,
|
||||
.data = audio_buf
|
||||
};
|
||||
|
||||
@@ -609,6 +728,5 @@ static int SDLCALL ffemu_thread(void *data)
|
||||
|
||||
av_free(video_buf);
|
||||
av_free(audio_buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+12
-34
@@ -8,15 +8,14 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ffemu_rational
|
||||
{
|
||||
unsigned num;
|
||||
unsigned den;
|
||||
};
|
||||
|
||||
// Parameters passed to ffemu_new()
|
||||
struct ffemu_params
|
||||
{
|
||||
// FPS of input video.
|
||||
double fps;
|
||||
// Sample rate of input audio.
|
||||
double samplerate;
|
||||
|
||||
// Desired output resolution.
|
||||
unsigned out_width;
|
||||
unsigned out_height;
|
||||
@@ -24,38 +23,17 @@ struct ffemu_params
|
||||
// Total size of framebuffer used in input.
|
||||
unsigned fb_width;
|
||||
unsigned fb_height;
|
||||
|
||||
// Aspect ratio of input video. Parameters are passed to the muxer,
|
||||
// the video itself is not scaled.
|
||||
float aspect_ratio;
|
||||
|
||||
// FPS of video input.
|
||||
struct ffemu_rational fps;
|
||||
|
||||
// Relative video quality. 0 is lossless (if available), 10 is very low quality.
|
||||
// A value over 10 is codec defined if it will give even worse quality.
|
||||
unsigned videoq;
|
||||
|
||||
// Define some video codec dependent option. (E.g. h264 profiles)
|
||||
uint64_t video_opt;
|
||||
|
||||
// Audio sample rate.
|
||||
unsigned samplerate;
|
||||
|
||||
// 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;
|
||||
|
||||
// Relative audio quality. 0 is lossless (if available), 10 is very low quality.
|
||||
// A value over 10 is codec defined if it will give even worse quality.
|
||||
// Some codecs might ignore this (lossless codecs such as FLAC).
|
||||
unsigned audioq;
|
||||
|
||||
// Define some audio codec dependent option.
|
||||
uint64_t audio_opt;
|
||||
|
||||
// Filename to dump to.
|
||||
const char *filename;
|
||||
};
|
||||
@@ -66,6 +44,7 @@ struct ffemu_video_data
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned pitch;
|
||||
bool is_dupe;
|
||||
};
|
||||
|
||||
struct ffemu_audio_data
|
||||
@@ -79,10 +58,9 @@ typedef struct ffemu ffemu_t;
|
||||
ffemu_t *ffemu_new(const struct ffemu_params *params);
|
||||
void ffemu_free(ffemu_t* handle);
|
||||
|
||||
int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data);
|
||||
int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data);
|
||||
int ffemu_finalize(ffemu_t *handle);
|
||||
|
||||
bool ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data);
|
||||
bool ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data);
|
||||
bool ffemu_finalize(ffemu_t *handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -21,34 +21,66 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
//#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include "general.h"
|
||||
|
||||
struct state_manager
|
||||
{
|
||||
uint64_t *buffer;
|
||||
size_t buf_size;
|
||||
size_t buf_size_mask;
|
||||
uint32_t *tmp_state;
|
||||
uint32_t *scratch_buf;
|
||||
size_t top_ptr;
|
||||
size_t bottom_ptr;
|
||||
size_t state_size;
|
||||
bool first_pop;
|
||||
};
|
||||
|
||||
static inline size_t nearest_pow2_size(size_t v)
|
||||
{
|
||||
size_t orig = v;
|
||||
v--;
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
#if SIZE_MAX >= 0xffffu
|
||||
v |= v >> 8;
|
||||
#endif
|
||||
#if SIZE_MAX >= 0xfffffffflu
|
||||
v |= v >> 16;
|
||||
#endif
|
||||
#if SIZE_MAX >= 0xffffffffffffffffllu
|
||||
v |= v >> 32;
|
||||
#endif
|
||||
v++;
|
||||
|
||||
size_t next = v;
|
||||
size_t prev = v >> 1;
|
||||
|
||||
if ((next - orig) < (orig - prev))
|
||||
return next;
|
||||
else
|
||||
return prev;
|
||||
}
|
||||
|
||||
state_manager_t *state_manager_new(size_t state_size, size_t buffer_size, void *init_buffer)
|
||||
{
|
||||
if (buffer_size <= state_size * 2 + 1) // Need a sufficient buffer size.
|
||||
if (buffer_size <= state_size * 4) // Need a sufficient buffer size.
|
||||
return NULL;
|
||||
|
||||
state_manager_t *state = calloc(1, sizeof(*state));
|
||||
if (!state)
|
||||
return NULL;
|
||||
|
||||
assert(state_size % 4 == 0); // We need 4-byte aligned state_size to avoid having to enforce this with unneeded memcpy's!
|
||||
// We need 4-byte aligned state_size to avoid having to enforce this with unneeded memcpy's!
|
||||
assert(state_size % 4 == 0);
|
||||
state->top_ptr = 1;
|
||||
state->state_size = (state_size + 3) >> 2; // Multiple of 4.
|
||||
state->buf_size = (buffer_size + 7) >> 3; // Multiple of 8.
|
||||
|
||||
state->state_size = state_size / sizeof(uint32_t); // Works in multiple of 4.
|
||||
state->buf_size = nearest_pow2_size(buffer_size) / sizeof(uint64_t); // Works in multiple of 8.
|
||||
state->buf_size_mask = state->buf_size - 1;
|
||||
SSNES_LOG("Readjusted rewind buffer size to %u MiB\n", (unsigned)(sizeof(uint64_t) * (state->buf_size >> 20)));
|
||||
|
||||
if (!(state->buffer = calloc(1, state->buf_size * sizeof(uint64_t))))
|
||||
goto error;
|
||||
if (!(state->tmp_state = calloc(1, state->state_size * sizeof(uint32_t))))
|
||||
@@ -84,14 +116,11 @@ bool state_manager_pop(state_manager_t *state, void **data)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (state->top_ptr == 0)
|
||||
state->top_ptr = state->buf_size - 1;
|
||||
else
|
||||
state->top_ptr--;
|
||||
state->top_ptr = (state->top_ptr - 1) & state->buf_size_mask;
|
||||
|
||||
if (state->top_ptr == state->bottom_ptr) // Our stack is completely empty... :v
|
||||
{
|
||||
state->top_ptr = (state->top_ptr + 1) % state->buf_size;
|
||||
state->top_ptr = (state->top_ptr + 1) & state->buf_size_mask;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -102,15 +131,12 @@ bool state_manager_pop(state_manager_t *state, void **data)
|
||||
uint32_t xor = state->buffer[state->top_ptr] & 0xFFFFFFFFU;
|
||||
state->tmp_state[addr] ^= xor;
|
||||
|
||||
if (state->top_ptr == 0)
|
||||
state->top_ptr = state->buf_size - 1;
|
||||
else
|
||||
state->top_ptr--;
|
||||
state->top_ptr = (state->top_ptr - 1) & state->buf_size_mask;
|
||||
}
|
||||
|
||||
if (state->top_ptr == state->bottom_ptr) // Our stack is completely empty... :v
|
||||
{
|
||||
state->top_ptr = (state->top_ptr + 1) % state->buf_size;
|
||||
state->top_ptr = (state->top_ptr + 1) & state->buf_size_mask;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -119,33 +145,36 @@ bool state_manager_pop(state_manager_t *state, void **data)
|
||||
|
||||
static void reassign_bottom(state_manager_t *state)
|
||||
{
|
||||
state->bottom_ptr = (state->top_ptr + 1) % state->buf_size;
|
||||
state->bottom_ptr = (state->top_ptr + 1) & state->buf_size_mask;
|
||||
while (state->buffer[state->bottom_ptr]) // Skip ahead until we find the first 0 (boundary for state delta).
|
||||
state->bottom_ptr = (state->bottom_ptr + 1) % state->buf_size;
|
||||
state->bottom_ptr = (state->bottom_ptr + 1) & state->buf_size_mask;
|
||||
}
|
||||
|
||||
static void generate_delta(state_manager_t *state, const void *data)
|
||||
{
|
||||
//unsigned patch_size = 0;
|
||||
bool crossed = false;
|
||||
const uint32_t *old_state = state->tmp_state;
|
||||
const uint32_t *new_state = data;
|
||||
|
||||
state->buffer[state->top_ptr++] = 0; // For each separate delta, we have a 0 value sentinel in between.
|
||||
if (state->top_ptr == state->bottom_ptr) // Check if top_ptr and bottom_ptr crossed each other, which means we need to delete old cruft.
|
||||
state->top_ptr &= state->buf_size_mask;
|
||||
|
||||
// Check if top_ptr and bottom_ptr crossed each other, which means we need to delete old cruft.
|
||||
if (state->top_ptr == state->bottom_ptr)
|
||||
crossed = true;
|
||||
|
||||
for (uint64_t i = 0; i < state->state_size; i++)
|
||||
{
|
||||
uint64_t xor = old_state[i] ^ new_state[i];
|
||||
|
||||
// If the data differs (xor != 0), we push that xor on the stack with index and xor. This can be reversed by reapplying the xor.
|
||||
// This, if states don't really differ much, we'll save lots of space :) Hopefully this will work really well with save states.
|
||||
// If the data differs (xor != 0), we push that xor on the stack with index and xor.
|
||||
// This can be reversed by reapplying the xor.
|
||||
// This, if states don't really differ much, we'll save lots of space :)
|
||||
// Hopefully this will work really well with save states.
|
||||
if (xor)
|
||||
{
|
||||
//patch_size++;
|
||||
state->buffer[state->top_ptr] = (i << 32) | xor;
|
||||
state->top_ptr = (state->top_ptr + 1) % state->buf_size;
|
||||
state->top_ptr = (state->top_ptr + 1) & state->buf_size_mask;
|
||||
|
||||
if (state->top_ptr == state->bottom_ptr)
|
||||
crossed = true;
|
||||
@@ -154,8 +183,6 @@ static void generate_delta(state_manager_t *state, const void *data)
|
||||
|
||||
if (crossed)
|
||||
reassign_bottom(state);
|
||||
|
||||
//fprintf(stderr, "DELTA SIZE: %u, ORIG SIZE: %u\n", (unsigned)patch_size << 3, (unsigned)state->state_size << 2);
|
||||
}
|
||||
|
||||
bool state_manager_push(state_manager_t *state, const void *data)
|
||||
|
||||
+6
-6
@@ -69,12 +69,12 @@ static void dump_content(FILE *file, const uint16_t *frame, unsigned width, unsi
|
||||
for (unsigned i = 0; i < width; i++)
|
||||
{
|
||||
uint16_t pixel = *src++;
|
||||
uint8_t b = (pixel & 0x1f) << 3;
|
||||
uint8_t g = (pixel & 0x03e0) >> 2;
|
||||
uint8_t r = (pixel & 0x7c00) >> 7;
|
||||
*dst++ = b;
|
||||
*dst++ = g;
|
||||
*dst++ = r;
|
||||
uint8_t b = (pixel >> 0) & 0x1f;
|
||||
uint8_t g = (pixel >> 5) & 0x1f;
|
||||
uint8_t r = (pixel >> 10) & 0x1f;
|
||||
*dst++ = (b << 3) | (b >> 2);
|
||||
*dst++ = (g << 3) | (g >> 2);
|
||||
*dst++ = (r << 3) | (r >> 2);
|
||||
}
|
||||
|
||||
fwrite(line, 1, sizeof(line), file);
|
||||
|
||||
+181
-272
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "general.h"
|
||||
#include "conf/config_file.h"
|
||||
#include "input/keysym.h"
|
||||
#include <assert.h>
|
||||
#include "strl.h"
|
||||
#include "config.def.h"
|
||||
@@ -61,6 +62,9 @@ static void set_defaults(void)
|
||||
|
||||
switch (AUDIO_DEFAULT_DRIVER)
|
||||
{
|
||||
case AUDIO_PS3:
|
||||
def_audio = "ps3";
|
||||
break;
|
||||
case AUDIO_RSOUND:
|
||||
def_audio = "rsound";
|
||||
break;
|
||||
@@ -100,6 +104,9 @@ static void set_defaults(void)
|
||||
|
||||
switch (INPUT_DEFAULT_DRIVER)
|
||||
{
|
||||
case INPUT_PS3:
|
||||
def_input = "ps3";
|
||||
break;
|
||||
case INPUT_SDL:
|
||||
def_input = "sdl";
|
||||
break;
|
||||
@@ -128,13 +135,18 @@ static void set_defaults(void)
|
||||
g_settings.video.smooth = video_smooth;
|
||||
g_settings.video.force_aspect = force_aspect;
|
||||
g_settings.video.crop_overscan = crop_overscan;
|
||||
g_settings.video.aspect_ratio = SNES_ASPECT_RATIO;
|
||||
g_settings.video.aspect_ratio = -1.0f; // Automatic
|
||||
g_settings.video.shader_type = SSNES_SHADER_AUTO;
|
||||
g_settings.video.font_enable = font_enable;
|
||||
|
||||
#ifdef HAVE_FREETYPE
|
||||
g_settings.video.font_size = font_size;
|
||||
g_settings.video.msg_pos_x = message_pos_offset_x;
|
||||
g_settings.video.msg_pos_y = message_pos_offset_y;
|
||||
|
||||
g_settings.video.msg_color_r = ((message_color >> 16) & 0xff) / 255.0f;
|
||||
g_settings.video.msg_color_g = ((message_color >> 8) & 0xff) / 255.0f;
|
||||
g_settings.video.msg_color_b = ((message_color >> 0) & 0xff) / 255.0f;
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_CG) || defined(HAVE_XML)
|
||||
@@ -144,35 +156,34 @@ static void set_defaults(void)
|
||||
g_settings.video.second_pass_smooth = second_pass_smooth;
|
||||
#endif
|
||||
|
||||
g_settings.video.refresh_rate = refresh_rate;
|
||||
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;
|
||||
g_settings.audio.rate_step = audio_rate_step;
|
||||
if (audio_device)
|
||||
strlcpy(g_settings.audio.device, audio_device, sizeof(g_settings.audio.device));
|
||||
g_settings.audio.latency = out_latency;
|
||||
g_settings.audio.sync = audio_sync;
|
||||
|
||||
#ifdef HAVE_SRC
|
||||
g_settings.audio.src_quality = SAMPLERATE_QUALITY;
|
||||
#endif
|
||||
|
||||
g_settings.rewind_enable = rewind_enable;
|
||||
g_settings.rewind_buffer_size = rewind_buffer_size;
|
||||
g_settings.rewind_granularity = rewind_granularity;
|
||||
g_settings.pause_nonactive = pause_nonactive;
|
||||
g_settings.autosave_interval = autosave_interval;
|
||||
|
||||
g_settings.block_sram_overwrite = block_sram_overwrite;
|
||||
g_settings.savestate_auto_index = savestate_auto_index;
|
||||
|
||||
assert(sizeof(g_settings.input.binds[0]) >= sizeof(snes_keybinds_1));
|
||||
assert(sizeof(g_settings.input.binds[1]) >= sizeof(snes_keybinds_rest));
|
||||
memcpy(g_settings.input.binds[0], snes_keybinds_1, sizeof(snes_keybinds_1));
|
||||
for (unsigned i = 1; i < 5; i++)
|
||||
memcpy(g_settings.input.binds[i], snes_keybinds_rest, sizeof(snes_keybinds_rest));
|
||||
|
||||
g_settings.input.axis_threshold = AXIS_THRESHOLD;
|
||||
g_settings.input.axis_threshold = axis_threshold;
|
||||
g_settings.input.netplay_client_swap_input = netplay_client_swap_input;
|
||||
for (int i = 0; i < MAX_PLAYERS; i++)
|
||||
g_settings.input.joypad_map[i] = i;
|
||||
@@ -221,6 +232,8 @@ static config_file_t *open_default_config_file(void)
|
||||
}
|
||||
if (!conf)
|
||||
conf = config_file_new("/etc/ssnes.cfg");
|
||||
#elif defined(__CELLOS_LV2__)
|
||||
return NULL;
|
||||
#else
|
||||
const char *xdg = getenv("XDG_CONFIG_HOME");
|
||||
if (!xdg)
|
||||
@@ -268,7 +281,7 @@ static void parse_config_file(void)
|
||||
{
|
||||
config_file_t *conf = NULL;
|
||||
|
||||
if (strlen(g_extern.config_path) > 0)
|
||||
if (*g_extern.config_path)
|
||||
{
|
||||
conf = config_file_new(g_extern.config_path);
|
||||
if (!conf)
|
||||
@@ -284,7 +297,11 @@ static void parse_config_file(void)
|
||||
return;
|
||||
|
||||
if (g_extern.verbose)
|
||||
config_file_dump(conf, stderr);
|
||||
{
|
||||
fprintf(stderr, "=== Config ===\n");
|
||||
config_file_dump_all(conf, stderr);
|
||||
fprintf(stderr, "=== Config end ===\n");
|
||||
}
|
||||
|
||||
int tmp_int;
|
||||
double tmp_double;
|
||||
@@ -308,6 +325,7 @@ static void parse_config_file(void)
|
||||
CONFIG_GET_BOOL(video.force_aspect, "video_force_aspect");
|
||||
CONFIG_GET_BOOL(video.crop_overscan, "video_crop_overscan");
|
||||
CONFIG_GET_DOUBLE(video.aspect_ratio, "video_aspect_ratio");
|
||||
CONFIG_GET_DOUBLE(video.refresh_rate, "video_refresh_rate");
|
||||
|
||||
CONFIG_GET_STRING(video.cg_shader_path, "video_cg_shader");
|
||||
CONFIG_GET_STRING(video.bsnes_shader_path, "video_bsnes_shader");
|
||||
@@ -320,8 +338,17 @@ static void parse_config_file(void)
|
||||
#ifdef HAVE_FREETYPE
|
||||
CONFIG_GET_STRING(video.font_path, "video_font_path");
|
||||
CONFIG_GET_INT(video.font_size, "video_font_size");
|
||||
CONFIG_GET_BOOL(video.font_enable, "video_font_enable");
|
||||
CONFIG_GET_DOUBLE(video.msg_pos_x, "video_message_pos_x");
|
||||
CONFIG_GET_DOUBLE(video.msg_pos_y, "video_message_pos_y");
|
||||
|
||||
unsigned msg_color;
|
||||
if (config_get_hex(conf, "video_message_color", &msg_color))
|
||||
{
|
||||
g_settings.video.msg_color_r = ((msg_color >> 16) & 0xff) / 255.0f;
|
||||
g_settings.video.msg_color_g = ((msg_color >> 8) & 0xff) / 255.0f;
|
||||
g_settings.video.msg_color_b = ((msg_color >> 0) & 0xff) / 255.0f;
|
||||
}
|
||||
#endif
|
||||
|
||||
CONFIG_GET_BOOL(video.hires_record, "video_hires_record");
|
||||
@@ -362,28 +389,20 @@ static void parse_config_file(void)
|
||||
// Audio settings.
|
||||
CONFIG_GET_BOOL(audio.enable, "audio_enable");
|
||||
CONFIG_GET_INT(audio.out_rate, "audio_out_rate");
|
||||
CONFIG_GET_DOUBLE(audio.in_rate, "audio_in_rate");
|
||||
CONFIG_GET_DOUBLE(audio.rate_step, "audio_rate_step");
|
||||
CONFIG_GET_STRING(audio.device, "audio_device");
|
||||
CONFIG_GET_INT(audio.latency, "audio_latency");
|
||||
CONFIG_GET_BOOL(audio.sync, "audio_sync");
|
||||
|
||||
#ifdef HAVE_SRC
|
||||
if (config_get_int(conf, "audio_src_quality", &tmp_int))
|
||||
{
|
||||
int quals[] = { SRC_ZERO_ORDER_HOLD, SRC_LINEAR, SRC_SINC_FASTEST,
|
||||
SRC_SINC_MEDIUM_QUALITY, SRC_SINC_BEST_QUALITY };
|
||||
|
||||
if (tmp_int > 0 && tmp_int < 6)
|
||||
g_settings.audio.src_quality = quals[tmp_int];
|
||||
}
|
||||
#endif
|
||||
|
||||
CONFIG_GET_STRING(video.driver, "video_driver");
|
||||
CONFIG_GET_STRING(audio.driver, "audio_driver");
|
||||
CONFIG_GET_STRING(audio.dsp_plugin, "audio_dsp_plugin");
|
||||
CONFIG_GET_STRING(input.driver, "input_driver");
|
||||
CONFIG_GET_STRING(libsnes, "libsnes_path");
|
||||
|
||||
if (!*g_settings.libsnes)
|
||||
{
|
||||
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))
|
||||
@@ -395,7 +414,7 @@ static void parse_config_file(void)
|
||||
CONFIG_GET_BOOL(rewind_enable, "rewind_enable");
|
||||
|
||||
if (config_get_int(conf, "rewind_buffer_size", &tmp_int))
|
||||
g_settings.rewind_buffer_size = tmp_int * 1000000;
|
||||
g_settings.rewind_buffer_size = tmp_int * 1000000LLU;
|
||||
|
||||
CONFIG_GET_INT(rewind_granularity, "rewind_granularity");
|
||||
|
||||
@@ -403,6 +422,10 @@ static void parse_config_file(void)
|
||||
CONFIG_GET_INT(autosave_interval, "autosave_interval");
|
||||
|
||||
CONFIG_GET_STRING(cheat_database, "cheat_database_path");
|
||||
CONFIG_GET_STRING(cheat_settings_path, "cheat_settings_path");
|
||||
|
||||
CONFIG_GET_BOOL(block_sram_overwrite, "block_sram_overwrite");
|
||||
CONFIG_GET_BOOL(savestate_auto_index, "savestate_auto_index");
|
||||
|
||||
if (!g_extern.has_set_save_path && config_get_array(conf, "savefile_directory", tmp_str, sizeof(tmp_str)))
|
||||
{
|
||||
@@ -438,185 +461,70 @@ struct bind_map
|
||||
int snes_key;
|
||||
};
|
||||
|
||||
#define DECLARE_BIND(x, bind) { "input_" #x, "input_" #x "_btn", "input_" #x "_axis", bind }
|
||||
#define DECLARE_BIND_END() { NULL, NULL, NULL, SSNES_BIND_LIST_END }
|
||||
#define DECL_PLAYER(P) \
|
||||
{ \
|
||||
DECLARE_BIND(player##P##_a, SNES_DEVICE_ID_JOYPAD_A), \
|
||||
DECLARE_BIND(player##P##_b, SNES_DEVICE_ID_JOYPAD_B), \
|
||||
DECLARE_BIND(player##P##_y, SNES_DEVICE_ID_JOYPAD_Y), \
|
||||
DECLARE_BIND(player##P##_x, SNES_DEVICE_ID_JOYPAD_X), \
|
||||
DECLARE_BIND(player##P##_start, SNES_DEVICE_ID_JOYPAD_START), \
|
||||
DECLARE_BIND(player##P##_select, SNES_DEVICE_ID_JOYPAD_SELECT), \
|
||||
DECLARE_BIND(player##P##_l, SNES_DEVICE_ID_JOYPAD_L), \
|
||||
DECLARE_BIND(player##P##_r, SNES_DEVICE_ID_JOYPAD_R), \
|
||||
DECLARE_BIND(player##P##_left, SNES_DEVICE_ID_JOYPAD_LEFT), \
|
||||
DECLARE_BIND(player##P##_right, SNES_DEVICE_ID_JOYPAD_RIGHT), \
|
||||
DECLARE_BIND(player##P##_up, SNES_DEVICE_ID_JOYPAD_UP), \
|
||||
DECLARE_BIND(player##P##_down, SNES_DEVICE_ID_JOYPAD_DOWN), \
|
||||
DECLARE_BIND_END(), \
|
||||
}
|
||||
|
||||
#define DECLARE_BIND(x, bind) { "input_" #x, "input_" #x "_btn", "input_" #x "_axis", bind },
|
||||
// Big and nasty bind map... :)
|
||||
static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
|
||||
static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS] = {
|
||||
{
|
||||
DECLARE_BIND(player1_a, SNES_DEVICE_ID_JOYPAD_A)
|
||||
DECLARE_BIND(player1_b, SNES_DEVICE_ID_JOYPAD_B)
|
||||
DECLARE_BIND(player1_y, SNES_DEVICE_ID_JOYPAD_Y)
|
||||
DECLARE_BIND(player1_x, SNES_DEVICE_ID_JOYPAD_X)
|
||||
DECLARE_BIND(player1_start, SNES_DEVICE_ID_JOYPAD_START)
|
||||
DECLARE_BIND(player1_select, SNES_DEVICE_ID_JOYPAD_SELECT)
|
||||
DECLARE_BIND(player1_l, SNES_DEVICE_ID_JOYPAD_L)
|
||||
DECLARE_BIND(player1_r, SNES_DEVICE_ID_JOYPAD_R)
|
||||
DECLARE_BIND(player1_left, SNES_DEVICE_ID_JOYPAD_LEFT)
|
||||
DECLARE_BIND(player1_right, SNES_DEVICE_ID_JOYPAD_RIGHT)
|
||||
DECLARE_BIND(player1_up, SNES_DEVICE_ID_JOYPAD_UP)
|
||||
DECLARE_BIND(player1_down, SNES_DEVICE_ID_JOYPAD_DOWN)
|
||||
DECLARE_BIND(toggle_fast_forward, SSNES_FAST_FORWARD_KEY)
|
||||
DECLARE_BIND(hold_fast_forward, SSNES_FAST_FORWARD_HOLD_KEY)
|
||||
DECLARE_BIND(save_state, SSNES_SAVE_STATE_KEY)
|
||||
DECLARE_BIND(load_state, SSNES_LOAD_STATE_KEY)
|
||||
DECLARE_BIND(state_slot_increase, SSNES_STATE_SLOT_PLUS)
|
||||
DECLARE_BIND(state_slot_decrease, SSNES_STATE_SLOT_MINUS)
|
||||
DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY)
|
||||
DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY)
|
||||
DECLARE_BIND(rate_step_up, SSNES_AUDIO_INPUT_RATE_PLUS)
|
||||
DECLARE_BIND(rate_step_down, SSNES_AUDIO_INPUT_RATE_MINUS)
|
||||
DECLARE_BIND(rewind, SSNES_REWIND)
|
||||
DECLARE_BIND(movie_record_toggle, SSNES_MOVIE_RECORD_TOGGLE)
|
||||
DECLARE_BIND(pause_toggle, SSNES_PAUSE_TOGGLE)
|
||||
DECLARE_BIND(reset, SSNES_RESET)
|
||||
DECLARE_BIND(shader_next, SSNES_SHADER_NEXT)
|
||||
DECLARE_BIND(shader_prev, SSNES_SHADER_PREV)
|
||||
DECLARE_BIND(cheat_index_plus, SSNES_CHEAT_INDEX_PLUS)
|
||||
DECLARE_BIND(cheat_index_minus, SSNES_CHEAT_INDEX_MINUS)
|
||||
DECLARE_BIND(cheat_toggle, SSNES_CHEAT_TOGGLE)
|
||||
DECLARE_BIND(screenshot, SSNES_SCREENSHOT)
|
||||
DECLARE_BIND(dsp_config, SSNES_DSP_CONFIG)
|
||||
},
|
||||
{
|
||||
DECLARE_BIND(player2_a, SNES_DEVICE_ID_JOYPAD_A)
|
||||
DECLARE_BIND(player2_b, SNES_DEVICE_ID_JOYPAD_B)
|
||||
DECLARE_BIND(player2_y, SNES_DEVICE_ID_JOYPAD_Y)
|
||||
DECLARE_BIND(player2_x, SNES_DEVICE_ID_JOYPAD_X)
|
||||
DECLARE_BIND(player2_start, SNES_DEVICE_ID_JOYPAD_START)
|
||||
DECLARE_BIND(player2_select, SNES_DEVICE_ID_JOYPAD_SELECT)
|
||||
DECLARE_BIND(player2_l, SNES_DEVICE_ID_JOYPAD_L)
|
||||
DECLARE_BIND(player2_r, SNES_DEVICE_ID_JOYPAD_R)
|
||||
DECLARE_BIND(player2_left, SNES_DEVICE_ID_JOYPAD_LEFT)
|
||||
DECLARE_BIND(player2_right, SNES_DEVICE_ID_JOYPAD_RIGHT)
|
||||
DECLARE_BIND(player2_up, SNES_DEVICE_ID_JOYPAD_UP)
|
||||
DECLARE_BIND(player2_down, SNES_DEVICE_ID_JOYPAD_DOWN)
|
||||
DECLARE_BIND(toggle_fast_forward, SSNES_FAST_FORWARD_KEY)
|
||||
DECLARE_BIND(hold_fast_forward, SSNES_FAST_FORWARD_HOLD_KEY)
|
||||
DECLARE_BIND(save_state, SSNES_SAVE_STATE_KEY)
|
||||
DECLARE_BIND(load_state, SSNES_LOAD_STATE_KEY)
|
||||
DECLARE_BIND(state_slot_increase, SSNES_STATE_SLOT_PLUS)
|
||||
DECLARE_BIND(state_slot_decrease, SSNES_STATE_SLOT_MINUS)
|
||||
DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY)
|
||||
DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY)
|
||||
DECLARE_BIND(rate_step_up, SSNES_AUDIO_INPUT_RATE_PLUS)
|
||||
DECLARE_BIND(rate_step_down, SSNES_AUDIO_INPUT_RATE_MINUS)
|
||||
DECLARE_BIND(rewind, SSNES_REWIND)
|
||||
DECLARE_BIND(movie_record_toggle, SSNES_MOVIE_RECORD_TOGGLE)
|
||||
DECLARE_BIND(pause_toggle, SSNES_PAUSE_TOGGLE)
|
||||
DECLARE_BIND(reset, SSNES_RESET)
|
||||
DECLARE_BIND(shader_next, SSNES_SHADER_NEXT)
|
||||
DECLARE_BIND(shader_prev, SSNES_SHADER_PREV)
|
||||
DECLARE_BIND(cheat_index_plus, SSNES_CHEAT_INDEX_PLUS)
|
||||
DECLARE_BIND(cheat_index_minus, SSNES_CHEAT_INDEX_MINUS)
|
||||
DECLARE_BIND(cheat_toggle, SSNES_CHEAT_TOGGLE)
|
||||
DECLARE_BIND(screenshot, SSNES_SCREENSHOT)
|
||||
DECLARE_BIND(dsp_config, SSNES_DSP_CONFIG)
|
||||
},
|
||||
{
|
||||
DECLARE_BIND(player3_a, SNES_DEVICE_ID_JOYPAD_A)
|
||||
DECLARE_BIND(player3_b, SNES_DEVICE_ID_JOYPAD_B)
|
||||
DECLARE_BIND(player3_y, SNES_DEVICE_ID_JOYPAD_Y)
|
||||
DECLARE_BIND(player3_x, SNES_DEVICE_ID_JOYPAD_X)
|
||||
DECLARE_BIND(player3_start, SNES_DEVICE_ID_JOYPAD_START)
|
||||
DECLARE_BIND(player3_select, SNES_DEVICE_ID_JOYPAD_SELECT)
|
||||
DECLARE_BIND(player3_l, SNES_DEVICE_ID_JOYPAD_L)
|
||||
DECLARE_BIND(player3_r, SNES_DEVICE_ID_JOYPAD_R)
|
||||
DECLARE_BIND(player3_left, SNES_DEVICE_ID_JOYPAD_LEFT)
|
||||
DECLARE_BIND(player3_right, SNES_DEVICE_ID_JOYPAD_RIGHT)
|
||||
DECLARE_BIND(player3_up, SNES_DEVICE_ID_JOYPAD_UP)
|
||||
DECLARE_BIND(player3_down, SNES_DEVICE_ID_JOYPAD_DOWN)
|
||||
DECLARE_BIND(toggle_fast_forward, SSNES_FAST_FORWARD_KEY)
|
||||
DECLARE_BIND(hold_fast_forward, SSNES_FAST_FORWARD_HOLD_KEY)
|
||||
DECLARE_BIND(save_state, SSNES_SAVE_STATE_KEY)
|
||||
DECLARE_BIND(load_state, SSNES_LOAD_STATE_KEY)
|
||||
DECLARE_BIND(state_slot_increase, SSNES_STATE_SLOT_PLUS)
|
||||
DECLARE_BIND(state_slot_decrease, SSNES_STATE_SLOT_MINUS)
|
||||
DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY)
|
||||
DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY)
|
||||
DECLARE_BIND(rate_step_up, SSNES_AUDIO_INPUT_RATE_PLUS)
|
||||
DECLARE_BIND(rate_step_down, SSNES_AUDIO_INPUT_RATE_MINUS)
|
||||
DECLARE_BIND(rewind, SSNES_REWIND)
|
||||
DECLARE_BIND(movie_record_toggle, SSNES_MOVIE_RECORD_TOGGLE)
|
||||
DECLARE_BIND(pause_toggle, SSNES_PAUSE_TOGGLE)
|
||||
DECLARE_BIND(reset, SSNES_RESET)
|
||||
DECLARE_BIND(shader_next, SSNES_SHADER_NEXT)
|
||||
DECLARE_BIND(shader_prev, SSNES_SHADER_PREV)
|
||||
DECLARE_BIND(cheat_index_plus, SSNES_CHEAT_INDEX_PLUS)
|
||||
DECLARE_BIND(cheat_index_minus, SSNES_CHEAT_INDEX_MINUS)
|
||||
DECLARE_BIND(cheat_toggle, SSNES_CHEAT_TOGGLE)
|
||||
DECLARE_BIND(screenshot, SSNES_SCREENSHOT)
|
||||
DECLARE_BIND(dsp_config, SSNES_DSP_CONFIG)
|
||||
},
|
||||
{
|
||||
DECLARE_BIND(player4_a, SNES_DEVICE_ID_JOYPAD_A)
|
||||
DECLARE_BIND(player4_b, SNES_DEVICE_ID_JOYPAD_B)
|
||||
DECLARE_BIND(player4_y, SNES_DEVICE_ID_JOYPAD_Y)
|
||||
DECLARE_BIND(player4_x, SNES_DEVICE_ID_JOYPAD_X)
|
||||
DECLARE_BIND(player4_start, SNES_DEVICE_ID_JOYPAD_START)
|
||||
DECLARE_BIND(player4_select, SNES_DEVICE_ID_JOYPAD_SELECT)
|
||||
DECLARE_BIND(player4_l, SNES_DEVICE_ID_JOYPAD_L)
|
||||
DECLARE_BIND(player4_r, SNES_DEVICE_ID_JOYPAD_R)
|
||||
DECLARE_BIND(player4_left, SNES_DEVICE_ID_JOYPAD_LEFT)
|
||||
DECLARE_BIND(player4_right, SNES_DEVICE_ID_JOYPAD_RIGHT)
|
||||
DECLARE_BIND(player4_up, SNES_DEVICE_ID_JOYPAD_UP)
|
||||
DECLARE_BIND(player4_down, SNES_DEVICE_ID_JOYPAD_DOWN)
|
||||
DECLARE_BIND(toggle_fast_forward, SSNES_FAST_FORWARD_KEY)
|
||||
DECLARE_BIND(hold_fast_forward, SSNES_FAST_FORWARD_HOLD_KEY)
|
||||
DECLARE_BIND(save_state, SSNES_SAVE_STATE_KEY)
|
||||
DECLARE_BIND(load_state, SSNES_LOAD_STATE_KEY)
|
||||
DECLARE_BIND(state_slot_increase, SSNES_STATE_SLOT_PLUS)
|
||||
DECLARE_BIND(state_slot_decrease, SSNES_STATE_SLOT_MINUS)
|
||||
DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY)
|
||||
DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY)
|
||||
DECLARE_BIND(rate_step_up, SSNES_AUDIO_INPUT_RATE_PLUS)
|
||||
DECLARE_BIND(rate_step_down, SSNES_AUDIO_INPUT_RATE_MINUS)
|
||||
DECLARE_BIND(rewind, SSNES_REWIND)
|
||||
DECLARE_BIND(movie_record_toggle, SSNES_MOVIE_RECORD_TOGGLE)
|
||||
DECLARE_BIND(pause_toggle, SSNES_PAUSE_TOGGLE)
|
||||
DECLARE_BIND(reset, SSNES_RESET)
|
||||
DECLARE_BIND(shader_next, SSNES_SHADER_NEXT)
|
||||
DECLARE_BIND(shader_prev, SSNES_SHADER_PREV)
|
||||
DECLARE_BIND(cheat_index_plus, SSNES_CHEAT_INDEX_PLUS)
|
||||
DECLARE_BIND(cheat_index_minus, SSNES_CHEAT_INDEX_MINUS)
|
||||
DECLARE_BIND(cheat_toggle, SSNES_CHEAT_TOGGLE)
|
||||
DECLARE_BIND(screenshot, SSNES_SCREENSHOT)
|
||||
DECLARE_BIND(dsp_config, SSNES_DSP_CONFIG)
|
||||
},
|
||||
{
|
||||
DECLARE_BIND(player5_a, SNES_DEVICE_ID_JOYPAD_A)
|
||||
DECLARE_BIND(player5_b, SNES_DEVICE_ID_JOYPAD_B)
|
||||
DECLARE_BIND(player5_y, SNES_DEVICE_ID_JOYPAD_Y)
|
||||
DECLARE_BIND(player5_x, SNES_DEVICE_ID_JOYPAD_X)
|
||||
DECLARE_BIND(player5_start, SNES_DEVICE_ID_JOYPAD_START)
|
||||
DECLARE_BIND(player5_select, SNES_DEVICE_ID_JOYPAD_SELECT)
|
||||
DECLARE_BIND(player5_l, SNES_DEVICE_ID_JOYPAD_L)
|
||||
DECLARE_BIND(player5_r, SNES_DEVICE_ID_JOYPAD_R)
|
||||
DECLARE_BIND(player5_left, SNES_DEVICE_ID_JOYPAD_LEFT)
|
||||
DECLARE_BIND(player5_right, SNES_DEVICE_ID_JOYPAD_RIGHT)
|
||||
DECLARE_BIND(player5_up, SNES_DEVICE_ID_JOYPAD_UP)
|
||||
DECLARE_BIND(player5_down, SNES_DEVICE_ID_JOYPAD_DOWN)
|
||||
DECLARE_BIND(toggle_fast_forward, SSNES_FAST_FORWARD_KEY)
|
||||
DECLARE_BIND(hold_fast_forward, SSNES_FAST_FORWARD_HOLD_KEY)
|
||||
DECLARE_BIND(save_state, SSNES_SAVE_STATE_KEY)
|
||||
DECLARE_BIND(load_state, SSNES_LOAD_STATE_KEY)
|
||||
DECLARE_BIND(state_slot_increase, SSNES_STATE_SLOT_PLUS)
|
||||
DECLARE_BIND(state_slot_decrease, SSNES_STATE_SLOT_MINUS)
|
||||
DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY)
|
||||
DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY)
|
||||
DECLARE_BIND(rate_step_up, SSNES_AUDIO_INPUT_RATE_PLUS)
|
||||
DECLARE_BIND(rate_step_down, SSNES_AUDIO_INPUT_RATE_MINUS)
|
||||
DECLARE_BIND(rewind, SSNES_REWIND)
|
||||
DECLARE_BIND(movie_record_toggle, SSNES_MOVIE_RECORD_TOGGLE)
|
||||
DECLARE_BIND(pause_toggle, SSNES_PAUSE_TOGGLE)
|
||||
DECLARE_BIND(reset, SSNES_RESET)
|
||||
DECLARE_BIND(shader_next, SSNES_SHADER_NEXT)
|
||||
DECLARE_BIND(shader_prev, SSNES_SHADER_PREV)
|
||||
DECLARE_BIND(cheat_index_plus, SSNES_CHEAT_INDEX_PLUS)
|
||||
DECLARE_BIND(cheat_index_minus, SSNES_CHEAT_INDEX_MINUS)
|
||||
DECLARE_BIND(cheat_toggle, SSNES_CHEAT_TOGGLE)
|
||||
DECLARE_BIND(screenshot, SSNES_SCREENSHOT)
|
||||
DECLARE_BIND(dsp_config, SSNES_DSP_CONFIG)
|
||||
DECLARE_BIND(player1_a, SNES_DEVICE_ID_JOYPAD_A),
|
||||
DECLARE_BIND(player1_b, SNES_DEVICE_ID_JOYPAD_B),
|
||||
DECLARE_BIND(player1_y, SNES_DEVICE_ID_JOYPAD_Y),
|
||||
DECLARE_BIND(player1_x, SNES_DEVICE_ID_JOYPAD_X),
|
||||
DECLARE_BIND(player1_start, SNES_DEVICE_ID_JOYPAD_START),
|
||||
DECLARE_BIND(player1_select, SNES_DEVICE_ID_JOYPAD_SELECT),
|
||||
DECLARE_BIND(player1_l, SNES_DEVICE_ID_JOYPAD_L),
|
||||
DECLARE_BIND(player1_r, SNES_DEVICE_ID_JOYPAD_R),
|
||||
DECLARE_BIND(player1_left, SNES_DEVICE_ID_JOYPAD_LEFT),
|
||||
DECLARE_BIND(player1_right, SNES_DEVICE_ID_JOYPAD_RIGHT),
|
||||
DECLARE_BIND(player1_up, SNES_DEVICE_ID_JOYPAD_UP),
|
||||
DECLARE_BIND(player1_down, SNES_DEVICE_ID_JOYPAD_DOWN),
|
||||
DECLARE_BIND(toggle_fast_forward, SSNES_FAST_FORWARD_KEY),
|
||||
DECLARE_BIND(hold_fast_forward, SSNES_FAST_FORWARD_HOLD_KEY),
|
||||
DECLARE_BIND(save_state, SSNES_SAVE_STATE_KEY),
|
||||
DECLARE_BIND(load_state, SSNES_LOAD_STATE_KEY),
|
||||
DECLARE_BIND(state_slot_increase, SSNES_STATE_SLOT_PLUS),
|
||||
DECLARE_BIND(state_slot_decrease, SSNES_STATE_SLOT_MINUS),
|
||||
DECLARE_BIND(exit_emulator, SSNES_QUIT_KEY),
|
||||
DECLARE_BIND(toggle_fullscreen, SSNES_FULLSCREEN_TOGGLE_KEY),
|
||||
DECLARE_BIND(rate_step_up, SSNES_AUDIO_INPUT_RATE_PLUS),
|
||||
DECLARE_BIND(rate_step_down, SSNES_AUDIO_INPUT_RATE_MINUS),
|
||||
DECLARE_BIND(rewind, SSNES_REWIND),
|
||||
DECLARE_BIND(movie_record_toggle, SSNES_MOVIE_RECORD_TOGGLE),
|
||||
DECLARE_BIND(pause_toggle, SSNES_PAUSE_TOGGLE),
|
||||
DECLARE_BIND(frame_advance, SSNES_FRAMEADVANCE),
|
||||
DECLARE_BIND(reset, SSNES_RESET),
|
||||
DECLARE_BIND(shader_next, SSNES_SHADER_NEXT),
|
||||
DECLARE_BIND(shader_prev, SSNES_SHADER_PREV),
|
||||
DECLARE_BIND(cheat_index_plus, SSNES_CHEAT_INDEX_PLUS),
|
||||
DECLARE_BIND(cheat_index_minus, SSNES_CHEAT_INDEX_MINUS),
|
||||
DECLARE_BIND(cheat_toggle, SSNES_CHEAT_TOGGLE),
|
||||
DECLARE_BIND(screenshot, SSNES_SCREENSHOT),
|
||||
DECLARE_BIND(dsp_config, SSNES_DSP_CONFIG),
|
||||
DECLARE_BIND(audio_mute, SSNES_MUTE),
|
||||
DECLARE_BIND_END(),
|
||||
},
|
||||
|
||||
DECL_PLAYER(2),
|
||||
DECL_PLAYER(3),
|
||||
DECL_PLAYER(4),
|
||||
DECL_PLAYER(5),
|
||||
};
|
||||
|
||||
struct key_map
|
||||
@@ -627,71 +535,72 @@ struct key_map
|
||||
|
||||
// Edit: Not portable to different input systems atm. Might move this map into the driver itself or something.
|
||||
// However, this should map nicely over to other systems aswell since the definition are mostly the same anyways.
|
||||
static const struct key_map sdlk_map[] = {
|
||||
{ "left", SDLK_LEFT },
|
||||
{ "right", SDLK_RIGHT },
|
||||
{ "up", SDLK_UP },
|
||||
{ "down", SDLK_DOWN },
|
||||
{ "enter", SDLK_RETURN },
|
||||
{ "kp_enter", SDLK_KP_ENTER },
|
||||
{ "tab", SDLK_TAB },
|
||||
{ "insert", SDLK_INSERT },
|
||||
{ "del", SDLK_DELETE },
|
||||
{ "end", SDLK_END },
|
||||
{ "home", SDLK_HOME },
|
||||
{ "rshift", SDLK_RSHIFT },
|
||||
{ "shift", SDLK_LSHIFT },
|
||||
{ "ctrl", SDLK_LCTRL },
|
||||
{ "alt", SDLK_LALT },
|
||||
{ "space", SDLK_SPACE },
|
||||
{ "escape", SDLK_ESCAPE },
|
||||
{ "add", SDLK_KP_PLUS },
|
||||
{ "subtract", SDLK_KP_MINUS },
|
||||
{ "f1", SDLK_F1 },
|
||||
{ "f2", SDLK_F2 },
|
||||
{ "f3", SDLK_F3 },
|
||||
{ "f4", SDLK_F4 },
|
||||
{ "f5", SDLK_F5 },
|
||||
{ "f6", SDLK_F6 },
|
||||
{ "f7", SDLK_F7 },
|
||||
{ "f8", SDLK_F8 },
|
||||
{ "f9", SDLK_F9 },
|
||||
{ "f10", SDLK_F10 },
|
||||
{ "f11", SDLK_F11 },
|
||||
{ "f12", SDLK_F12 },
|
||||
{ "num0", SDLK_0 },
|
||||
{ "num1", SDLK_1 },
|
||||
{ "num2", SDLK_2 },
|
||||
{ "num3", SDLK_3 },
|
||||
{ "num4", SDLK_4 },
|
||||
{ "num5", SDLK_5 },
|
||||
{ "num6", SDLK_6 },
|
||||
{ "num7", SDLK_7 },
|
||||
{ "num8", SDLK_8 },
|
||||
{ "num9", SDLK_9 },
|
||||
{ "pageup", SDLK_PAGEUP },
|
||||
{ "pagedown", SDLK_PAGEDOWN },
|
||||
{ "keypad0", SDLK_KP0 },
|
||||
{ "keypad1", SDLK_KP1 },
|
||||
{ "keypad2", SDLK_KP2 },
|
||||
{ "keypad3", SDLK_KP3 },
|
||||
{ "keypad4", SDLK_KP4 },
|
||||
{ "keypad5", SDLK_KP5 },
|
||||
{ "keypad6", SDLK_KP6 },
|
||||
{ "keypad7", SDLK_KP7 },
|
||||
{ "keypad8", SDLK_KP8 },
|
||||
{ "keypad9", SDLK_KP9 },
|
||||
{ "period", SDLK_PERIOD },
|
||||
{ "capslock", SDLK_CAPSLOCK },
|
||||
{ "numlock", SDLK_NUMLOCK },
|
||||
{ "backspace", SDLK_BACKSPACE },
|
||||
{ "multiply", SDLK_KP_MULTIPLY },
|
||||
{ "divide", SDLK_KP_DIVIDE },
|
||||
{ "print_screen", SDLK_PRINT },
|
||||
{ "scroll_lock", SDLK_SCROLLOCK },
|
||||
{ "tilde", SDLK_BACKQUOTE },
|
||||
{ "backquote", SDLK_BACKQUOTE },
|
||||
{ "nul", SDLK_UNKNOWN },
|
||||
static const struct key_map sk_map[] = {
|
||||
{ "left", SK_LEFT },
|
||||
{ "right", SK_RIGHT },
|
||||
{ "up", SK_UP },
|
||||
{ "down", SK_DOWN },
|
||||
{ "enter", SK_RETURN },
|
||||
{ "kp_enter", SK_KP_ENTER },
|
||||
{ "tab", SK_TAB },
|
||||
{ "insert", SK_INSERT },
|
||||
{ "del", SK_DELETE },
|
||||
{ "end", SK_END },
|
||||
{ "home", SK_HOME },
|
||||
{ "rshift", SK_RSHIFT },
|
||||
{ "shift", SK_LSHIFT },
|
||||
{ "ctrl", SK_LCTRL },
|
||||
{ "alt", SK_LALT },
|
||||
{ "space", SK_SPACE },
|
||||
{ "escape", SK_ESCAPE },
|
||||
{ "add", SK_KP_PLUS },
|
||||
{ "subtract", SK_KP_MINUS },
|
||||
{ "f1", SK_F1 },
|
||||
{ "f2", SK_F2 },
|
||||
{ "f3", SK_F3 },
|
||||
{ "f4", SK_F4 },
|
||||
{ "f5", SK_F5 },
|
||||
{ "f6", SK_F6 },
|
||||
{ "f7", SK_F7 },
|
||||
{ "f8", SK_F8 },
|
||||
{ "f9", SK_F9 },
|
||||
{ "f10", SK_F10 },
|
||||
{ "f11", SK_F11 },
|
||||
{ "f12", SK_F12 },
|
||||
{ "num0", SK_0 },
|
||||
{ "num1", SK_1 },
|
||||
{ "num2", SK_2 },
|
||||
{ "num3", SK_3 },
|
||||
{ "num4", SK_4 },
|
||||
{ "num5", SK_5 },
|
||||
{ "num6", SK_6 },
|
||||
{ "num7", SK_7 },
|
||||
{ "num8", SK_8 },
|
||||
{ "num9", SK_9 },
|
||||
{ "pageup", SK_PAGEUP },
|
||||
{ "pagedown", SK_PAGEDOWN },
|
||||
{ "keypad0", SK_KP0 },
|
||||
{ "keypad1", SK_KP1 },
|
||||
{ "keypad2", SK_KP2 },
|
||||
{ "keypad3", SK_KP3 },
|
||||
{ "keypad4", SK_KP4 },
|
||||
{ "keypad5", SK_KP5 },
|
||||
{ "keypad6", SK_KP6 },
|
||||
{ "keypad7", SK_KP7 },
|
||||
{ "keypad8", SK_KP8 },
|
||||
{ "keypad9", SK_KP9 },
|
||||
{ "period", SK_PERIOD },
|
||||
{ "capslock", SK_CAPSLOCK },
|
||||
{ "numlock", SK_NUMLOCK },
|
||||
{ "backspace", SK_BACKSPACE },
|
||||
{ "multiply", SK_KP_MULTIPLY },
|
||||
{ "divide", SK_KP_DIVIDE },
|
||||
{ "print_screen", SK_PRINT },
|
||||
{ "scroll_lock", SK_SCROLLOCK },
|
||||
{ "tilde", SK_BACKQUOTE },
|
||||
{ "backquote", SK_BACKQUOTE },
|
||||
{ "pause", SK_PAUSE },
|
||||
{ "nul", SK_UNKNOWN },
|
||||
};
|
||||
|
||||
static struct snes_keybind *find_snes_bind(unsigned port, int id)
|
||||
@@ -706,23 +615,23 @@ static struct snes_keybind *find_snes_bind(unsigned port, int id)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int find_sdlk_bind(const char *str)
|
||||
static int find_sk_bind(const char *str)
|
||||
{
|
||||
for (int i = 0; i < sizeof(sdlk_map)/sizeof(struct key_map); i++)
|
||||
for (int i = 0; i < sizeof(sk_map) / sizeof(struct key_map); i++)
|
||||
{
|
||||
if (strcasecmp(sdlk_map[i].str, str) == 0)
|
||||
return sdlk_map[i].key;
|
||||
if (strcasecmp(sk_map[i].str, str) == 0)
|
||||
return sk_map[i].key;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int find_sdlk_key(const char *str)
|
||||
static int find_sk_key(const char *str)
|
||||
{
|
||||
// If the bind is a normal key-press ...
|
||||
if (strlen(str) == 1 && isalpha(*str))
|
||||
return (int)SDLK_a + (tolower(*str) - (int)'a');
|
||||
return (int)SK_a + (tolower(*str) - (int)'a');
|
||||
else // Check if we have a special mapping for it.
|
||||
return find_sdlk_bind(str);
|
||||
return find_sk_bind(str);
|
||||
}
|
||||
|
||||
// Yes, this function needs a good refactor :)
|
||||
@@ -734,7 +643,7 @@ static void read_keybinds(config_file_t *conf)
|
||||
|
||||
for (int j = 0; j < MAX_PLAYERS; j++)
|
||||
{
|
||||
for (int i = 0; i < sizeof(bind_maps[0])/sizeof(struct bind_map); i++)
|
||||
for (int i = 0; bind_maps[j][i].snes_key != SSNES_BIND_LIST_END; i++)
|
||||
{
|
||||
struct snes_keybind *bind = find_snes_bind(j, bind_maps[j][i].snes_key);
|
||||
if (!bind)
|
||||
@@ -743,7 +652,7 @@ static void read_keybinds(config_file_t *conf)
|
||||
// Check keybind
|
||||
if (bind_maps[j][i].key && config_get_string(conf, bind_maps[j][i].key, &tmp_key))
|
||||
{
|
||||
int key = find_sdlk_key(tmp_key);
|
||||
int key = find_sk_key(tmp_key);
|
||||
|
||||
if (key >= 0)
|
||||
bind->key = key;
|
||||
|
||||
@@ -68,8 +68,8 @@ do_phoenix_build()
|
||||
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 ..."
|
||||
make -f Makefile.win clean || die "Failed to clean ..."
|
||||
make -f Makefile.win 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 ..."
|
||||
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
# Path to external video driver using the SSNES driver API.
|
||||
# video_external_driver =
|
||||
|
||||
# Windowed xscale and yscale (Real x res: 224 * xscale * aspect_ratio, real y scale: 224 * xscale)
|
||||
# Windowed xscale and yscale
|
||||
# (Real x res: base_size * xscale * aspect_ratio, real y res: base_size * yscale)
|
||||
# video_xscale = 3.0
|
||||
# video_yscale = 3.0
|
||||
|
||||
@@ -88,10 +89,21 @@
|
||||
# Size of the TTF font rendered.
|
||||
# video_font_size = 48
|
||||
|
||||
# Enable usage of OSD messages.
|
||||
# video_font_enable = true
|
||||
|
||||
# Offset for where messages will be placed on screen. Values are in range 0.0 to 1.0 for both x and y values.
|
||||
# [0.0, 0.0] maps to the lower left corner of the screen.
|
||||
# video_message_pos_x = 0.05
|
||||
# video_message_pox_y = 0.05
|
||||
# video_message_pos_y = 0.05
|
||||
|
||||
# Color for message. The value is treated as a hexadecimal value.
|
||||
# It is a regular RGB hex number, i.e. red is "ff0000".
|
||||
# video_message_color = ffffff
|
||||
|
||||
# Video refresh rate of your monitor.
|
||||
# Used to calculate a suitable audio input rate.
|
||||
# video_refresh_rate = 59.95
|
||||
|
||||
#### Audio
|
||||
|
||||
@@ -101,12 +113,6 @@
|
||||
# Audio output samplerate.
|
||||
# audio_out_rate = 48000
|
||||
|
||||
# Audio input samplerate from libsnes.
|
||||
# Lower this (slightly) if you are experiencing frequent audio dropouts while vsync is enabled.
|
||||
# Conversely, increase this slightly if you are experiencing good audio,
|
||||
# but lots of dropped frames. Reasonable values for this is 32000 +/- 100 Hz.
|
||||
# audio_in_rate = 31980
|
||||
|
||||
# When altering audio_in_rate on-the-fly, define by how much each time.
|
||||
# audio_rate_step = 0.25
|
||||
|
||||
@@ -128,8 +134,6 @@
|
||||
# Desired audio latency in milliseconds. Might not be honored if driver can't provide given latency.
|
||||
# audio_latency = 64
|
||||
|
||||
# libsamplerate quality. Valid values are from 1 to 5. These values map to zero_order_hold, linear, sinc_fastest, sinc_medium and sinc_best respectively.
|
||||
# audio_src_quality =
|
||||
|
||||
### Input
|
||||
|
||||
@@ -271,6 +275,9 @@
|
||||
# Toggle between paused and non-paused state
|
||||
# input_pause_toggle = p
|
||||
|
||||
# Frame advance when game is paused
|
||||
# input_frame_advance = k
|
||||
|
||||
# Reset the emulated SNES.
|
||||
# input_reset = h
|
||||
|
||||
@@ -291,11 +298,19 @@
|
||||
# Path to XML cheat database (as used by bSNES).
|
||||
# cheat_database_path =
|
||||
|
||||
# Path to XML cheat config, a file which keeps track of which
|
||||
# cheat settings are used for individual games.
|
||||
# If the file does not exist, it will be created.
|
||||
# cheat_settings_path =
|
||||
|
||||
# Cheats.
|
||||
# input_cheat_index_plus = y
|
||||
# input_cheat_index_minus = t
|
||||
# input_cheat_toggle = u
|
||||
|
||||
# Mute/unmute audio
|
||||
# input_audio_mute = f9
|
||||
|
||||
# Take screenshot
|
||||
# input_screenshot = print_screen
|
||||
|
||||
@@ -307,3 +322,14 @@
|
||||
|
||||
# Records video after CPU video filter.
|
||||
# video_post_filter_record = false
|
||||
|
||||
# Block SRAM from being overwritten when loading save states.
|
||||
# Might potentially lead to buggy games.
|
||||
# block_sram_overwrite = false
|
||||
|
||||
# When saving a savestate, save state index is automatically increased before
|
||||
# it is saved.
|
||||
# Also, when loading a ROM, the index will be set to the highest existing index.
|
||||
# There is no upper bound on the index.
|
||||
# savestate_auto_index = false
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef HAVE_CONFIG
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
|
||||
Loaded 100 of 103 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user