Compare commits

...
278 Commits
Author SHA1 Message Date
Themaister bce679df78 Only use hack on X when in windowed mode. 2011-08-16 10:26:36 +02:00
Themaister 22e8c3d895 Should fix breakage of OGL on OSX. 2011-08-15 22:21:50 +02:00
Themaister d2b06a92c5 0.7 baby! 2011-08-14 23:17:02 +02:00
Themaister 3901934d97 Add an ugly hack to try and remedy things ... 2011-08-14 23:03:48 +02:00
Themaister a76b25aff9 Fix ugly Jack crash with race condition. 2011-08-14 16:29:31 +02:00
Themaister 343852af26 Allocate before activation. 2011-08-14 16:24:42 +02:00
Themaister 4395f124ca Slight fixups. 2011-08-14 04:50:16 +02:00
Themaister b16ff3024c Add new DSP plugin API, events. 2011-08-13 04:09:08 +02:00
Themaister bc23b421e1 New resize checking code. 2011-08-12 19:02:12 +02:00
Themaister d9cb85a8d5 Add new possible workaround for tiling WMs ... 2011-08-11 15:19:30 +02:00
Themaister 7d1cc23d16 Fix build. 2011-08-11 06:02:05 +02:00
Themaister 4496ed3c13 Should make sizes right after startup ... 2011-08-11 05:59:32 +02:00
Themaister 3f5b6a450e Should fix behavior for tiled WMs. 2011-08-11 05:32:03 +02:00
Themaister 9e24474047 Can record hi-res and filtered output. 2011-08-11 05:25:31 +02:00
Themaister 7c17ede1ef Updates for 0.7beta1. 2011-08-09 23:04:13 +02:00
Themaister 5d9805c3e5 Small fix. 2011-08-09 22:07:11 +02:00
Themaister b17617bed5 Updated copyrights. 2011-08-09 12:58:13 +02:00
Themaister a405708f1c Huzzah, ordering matters. 2011-08-09 00:11:09 +02:00
Themaister 594e66704d Allow disabling composition in SDL as well. 2011-08-08 23:38:11 +02:00
Themaister 50579aaeea Floating point. 2011-08-08 17:45:49 +02:00
Themaister 38eb855b48 Works! :D 2011-08-08 17:41:55 +02:00
Themaister 12cc85c8a5 CoreAudio builds. 2011-08-08 17:31:03 +02:00
Themaister 5b12595c85 Merge branch 'master' of git://github.com/Themaister/SSNES 2011-08-08 17:29:06 +02:00
Themaister 3753f0bfca Merge branch 'master' of github.com:Themaister/SSNES 2011-08-08 17:28:32 +02:00
Themaister 14ae5e397a Add CoreAudio driver. Untested. 2011-08-08 17:27:52 +02:00
Themaister 657b4ce8c7 Merge branch 'master' of git://github.com/Themaister/SSNES 2011-08-08 14:47:47 +02:00
Themaister 777e6a5334 Better shader path semantics. 2011-08-07 23:35:54 +02:00
Themaister a2ec78320b Improve behavior a bit. 2011-08-07 22:31:59 +02:00
Themaister 1fb53627ac Make sure to free lib. 2011-08-07 21:18:51 +02:00
Themaister fc2a39cd58 More appropriate renaming. 2011-08-07 21:15:50 +02:00
Themaister 7425e1f879 Allow disabling composition in Win Vista/7Allow disabling composition in Win Vista/7. 2011-08-07 15:00:34 +02:00
Themaister 6a260a57dd Reset frame counter properly. 2011-08-06 03:28:07 +02:00
Themaister dadd794ece Remove useless define. 2011-08-05 19:18:43 +02:00
Themaister 706a3738b5 Abandon POT buffer sizes only for dsound.
Allows more fine tuned latencies.
2011-08-05 19:17:33 +02:00
Themaister 9aef33aabd More fixups in DSound. 2011-08-05 14:01:58 +02:00
Themaister ac4219bcde Small clarification. 2011-08-05 13:39:27 +02:00
Themaister b337856f80 More extensive error checking in dsound. 2011-08-05 13:35:02 +02:00
Themaister 079a0100c0 Remove unneeded call. 2011-08-05 03:06:48 +02:00
Themaister 014c3c90e0 Fix possible race condition. 2011-08-05 02:57:30 +02:00
Themaister 4759e3411f Fix DSound up some more in Windows ... :D 2011-08-05 02:05:32 +02:00
Themaister 4a05e50636 Minor progress. But no audio, hrm. 2011-08-04 19:11:14 +02:00
Themaister 72c070f338 Alter default ordering of audio drivers. 2011-08-04 18:52:00 +02:00
Themaister 776291a137 Start working on DirectSound driver. Untested so far. 2011-08-04 18:45:40 +02:00
Themaister f3cdb95b04 Fix endian issues in OSS driver. 2011-07-26 00:53:24 +02:00
Themaister 033860ceb9 Add -f/--fullscreen command line option. 2011-07-09 08:37:08 +02:00
Themaister ab601ee3dc v0.6.2 2011-07-06 16:54:54 +02:00
Themaister 783ab7acd5 Improve error handling. 2011-07-04 13:33:09 +02:00
Themaister 19a46b1f8b Seems to work in Cg as well. 2011-07-03 15:53:56 +02:00
Themaister 508cfdbda7 Start implementing access to previous texture. 2011-07-03 15:39:35 +02:00
Themaister d81b9ee0a7 Print full compile error in Cg. 2011-07-03 13:41:24 +02:00
Themaister 36c6c21194 Use RSOUND_CFLAGS, etc. 2011-06-29 04:54:30 +02:00
Themaister 1631e940f6 Fix up VSync calls for OSX. 2011-06-29 03:45:52 +02:00
Themaister 2eeb1fa6d1 Fix up VSync calls for OSX. 2011-06-29 03:45:17 +02:00
Themaister 26ac00ad76 Fix incompatible type. 2011-06-29 03:26:21 +02:00
Themaister bafc4f596f Add nonused VSync code for OSX. 2011-06-28 03:20:36 +02:00
Themaister eee89dddb1 Check for SDL 1.3 API and report it as an error. 2011-06-28 02:31:41 +02:00
Themaister 089b4fbba0 Updates for 0.6.1. 2011-06-27 02:28:35 +02:00
Themaister 80727cef5a Nitpick. 2011-06-26 18:47:54 +02:00
Themaister 346a9f2b4e Remove debug messages. 2011-06-26 17:06:05 +02:00
Themaister 363d8ca5b4 More proper string reading function. 2011-06-26 17:05:29 +02:00
Themaister 06a574225d Merge branch 'master' of github.com:Themaister/SSNES 2011-06-26 16:04:13 +02:00
Themaister fa777b8356 Should fix netplay for bSNES as well.
Do not rely on when polling takes place in the emu core.
We earlier assumed that this occured always at the start of the main
loop. We now do not care about when the poll callback is called, and we
manually poll the network ourselves in pre_frame.
2011-06-26 16:02:18 +02:00
Themaister 7cf01318cb Should fix netplay for bSNES as well. 2011-06-26 15:32:24 +02:00
Themaister 7c538274b4 Do not check twice for OS. 2011-06-25 17:22:47 +02:00
Themaister 72fb694e97 Use pkg-config more where appropriate. 2011-06-25 17:15:58 +02:00
Themaister 59c92dddc3 Slight QB cleanup. 2011-06-25 17:11:18 +02:00
Themaister 3f60fdd106 Fix build for XVideo. 2011-06-25 18:11:04 +02:00
Themaister d221c480e6 Fix aliasing issue. 2011-06-25 16:49:34 +02:00
Themaister 59d9ff0d29 Add check_code_c/cxx. 2011-06-25 14:44:45 +02:00
Themaister d279ea79e8 Fix some standard compliance. 2011-06-25 14:17:31 +02:00
Themaister 77cf9423cc Abolish use of echo -n as it isn't perfectly standard. 2011-06-25 14:10:40 +02:00
Themaister 60b94bbe43 Add some additional checks in qb. 2011-06-25 13:57:46 +02:00
Themaister b6272f9653 Small bug. 2011-06-24 13:31:21 +02:00
Themaister 25fce1a6c6 Allow XML mapping. 2011-06-24 13:29:30 +02:00
Themaister 59229f99c5 Swap client by default. 2011-06-22 21:56:04 +02:00
Themaister db3e1ae053 Updates for 0.6. 2011-06-21 22:37:48 +02:00
Themaister ec1401e657 Fix build error on Win32. 2011-06-21 02:33:59 +02:00
Themaister f85fd50fa1 Add --nodevice. 2011-06-19 11:11:04 +02:00
Themaister c39b0ab09b Merge branch 'master' of github.com:Themaister/SSNES
Conflicts:
	Makefile
	qb/config.libs.sh
2011-06-16 23:54:40 +02:00
Themaister 4b5ed0fa41 Do not force -lossaudio. 2011-06-16 23:50:33 +02:00
Themaister 75d1155a23 Fix OSS build for BSD. 2011-06-16 23:20:12 +02:00
Themaister 753d2057bb Fix BSD build. Convert strcpy()/strcat() to strl* variants. 2011-06-16 22:47:37 +02:00
Themaister b06d814d9c Fix OSS build for BSD. 2011-06-16 21:20:59 +02:00
Themaister eb085a8068 Fix BSD build. Convert strcpy()/strcat() to strl* variants. 2011-06-16 20:59:48 +02:00
Themaister 45904ccd73 Fix issue when input_slot is 2. 2011-06-16 15:00:04 +02:00
Themaister 3b0606bbd6 Ifdef for AVIO. 2011-06-15 12:20:51 +02:00
Themaister 0f1a76f932 POSIX stuff for Quickbuild. 2011-06-15 04:34:28 +02:00
Themaister d3e0140f01 Holy crap, now that was painful. 2011-06-15 04:16:53 +02:00
Themaister ea7dded915 Fix up alignment issues. 2011-06-15 03:04:20 +02:00
Themaister f187f3db29 Small regex change. 2011-06-15 00:42:09 +02:00
Themaister 3099a79bac Should fix grep error. 2011-06-15 00:26:44 +02:00
Themaister 7cdea39c95 Can build with FFmpeg if desired :) 2011-06-14 23:59:00 +02:00
Themaister 95f6a5116a Just to be sure ... ;) 2011-06-14 22:59:47 +02:00
Themaister da94877155 Less magic numbers ... ;) 2011-06-14 22:44:54 +02:00
Themaister b1feed9d2b Allocate from heap. 2011-06-14 22:20:09 +02:00
Themaister a3816bffa1 Force rewind granularity to 1 when we're using movies. 2011-06-14 22:09:16 +02:00
Themaister 612270ec9b Aww yeah :3 2011-06-14 21:52:31 +02:00
Themaister 54bc0a8d96 Heyhey :D 2011-06-14 21:35:31 +02:00
Themaister f761811189 Move buffer to top-level as it's not audio only. 2011-06-14 19:58:12 +02:00
Themaister 0e964c5cc2 More generic naming for FIFO buffer. 2011-06-14 19:55:08 +02:00
Themaister c34a944fcf Fix deprecation, yay. :) 2011-06-14 18:52:02 +02:00
Themaister 3feade6b6e Update FFemu a bit. Convert to lossless HuffYUV/FLAC. 2011-06-14 18:45:30 +02:00
Themaister f79eafe449 Should perhaps continue if one joypad failed. 2011-06-12 10:42:42 +02:00
Themaister 111b159373 Should fix dinput. 2011-06-12 10:37:04 +02:00
Themaister d744167865 Small cleanup. 2011-06-11 22:46:51 +02:00
Themaister a2fea86c92 Fixes 2-5 player. (Damn o.O) 2011-06-11 22:25:55 +02:00
Themaister 5385806e74 Hrm ... 2011-06-11 22:02:05 +02:00
Themaister 99e85684db Input capturing semantic. 2011-06-11 20:02:17 +02:00
Themaister 45727bf9f5 Fix mask value. (32-bit). 2011-06-11 19:01:34 +02:00
Themaister 2a0abb0618 Unsigned mask. 2011-06-11 18:57:16 +02:00
Themaister 192e3303a1 Update dist and version. 2011-06-11 18:19:00 +02:00
Themaister b6051add6a SDL_image for Win32 2011-06-11 18:13:12 +02:00
Themaister 24d0cda5fd SDL_image seems to work :) 2011-06-11 18:05:52 +02:00
Themaister d0ba59f8b5 Start moving over to SDL_image rather than imlib2.
Imlib2 takes more space, and was a pure bitch to compile for Win32,
so I gave up ... ;)
2011-06-11 16:55:53 +02:00
Themaister 99a1784aaf Move window logic away from SDL input.
We weren't using the event loop anyways ...
2011-06-11 12:54:14 +02:00
Themaister b6ff98cf18 Should fix DInput crash. 2011-06-10 18:01:44 +02:00
Themaister 2dcccc7cda Fix some includes and stuff. 2011-06-10 17:55:40 +02:00
Themaister d03855c655 Add some sanity logging. 2011-06-10 17:47:16 +02:00
Themaister 69227bc1ca Alter design. Use DInput as piggyback only. 2011-06-10 17:34:04 +02:00
Themaister 851debf1e4 Start working on DInput. 2011-06-10 16:55:05 +02:00
Themaister 8c1db1c407 Fix shenanigans. 2011-06-09 23:17:10 +02:00
Themaister 576894e0e8 Build Python in Windows. 2011-06-09 13:56:29 +02:00
Themaister 631c3ca393 Fix link for Clang. 2011-06-08 19:52:03 +02:00
Themaister fd70a1c875 Make sure we only call semantics once per frame. 2011-06-08 17:59:29 +02:00
Themaister 30cb2e4c07 Slight cleanup. 2011-06-08 17:55:32 +02:00
Themaister 7d87cc2d24 Add input hook API to python. 2011-06-08 17:53:15 +02:00
Themaister 0342dcb169 Fix transition_prev. 2011-06-07 21:07:00 +02:00
Themaister f339560550 Put all scripts in <script> 2011-06-07 16:28:58 +02:00
Themaister 11b58d8c69 Clean up error handling a bit. 2011-06-07 15:58:30 +02:00
Themaister 3717f7442c Inline XML python. 2011-06-07 15:33:29 +02:00
Themaister e02d9f41e8 Transition count semantic. 2011-06-06 21:32:10 +02:00
Themaister f62120861a Show if python is compiled in or not. 2011-06-06 20:21:26 +02:00
Themaister 884e2881a0 Add script support to Cg. 2011-06-06 20:18:31 +02:00
Themaister a605628dff :v 2011-06-06 18:50:59 +02:00
Themaister 4d96fbba6a Python works :P 2011-06-06 18:50:36 +02:00
Themaister 9bfeb88e24 Start implementing Python support for game-aware shaders. 2011-06-06 17:44:05 +02:00
Themaister 712f463022 Disable audio sync properly in external driver. 2011-06-06 02:01:21 +02:00
Themaister 713e8624a0 Fixed typo. 2011-06-05 22:23:50 +02:00
Themaister 5c8a549125 Cleanups. 2011-06-05 02:21:35 +02:00
Themaister c3e7a35b62 Fixes case where backdrops are used in second or later pass. 2011-06-05 02:01:44 +02:00
Themaister 5bc9ca216e Clean up shader compilation slightly. 2011-06-02 13:49:22 +02:00
Themaister 838a85a719 Could make coordinate flipping consistent ... Finally. 2011-06-01 12:19:48 +02:00
Themaister b880039541 Clarify more ... 2011-05-31 23:08:02 +02:00
Themaister c646166fe1 Make an "empty" buffer for performance. Also attempt to fall back if
vsync fails on start.
2011-05-31 23:03:01 +02:00
Themaister 44c52ca4f8 Add -pedantic for shits 'n giggles.
Fix portability issues with void* -> void (*)(void) casts.
Use a more ... bizarre technique ;)
2011-05-31 15:03:59 +02:00
Themaister 9c9eee5d01 Should fix zip loading of [!] files... 2011-05-31 14:29:32 +02:00
Themaister 7bbaa6ee31 Add pixel size uniform. 2011-05-30 14:32:31 +02:00
Themaister f24aed9358 (addr) is redundant... 2011-05-30 00:04:26 +02:00
Themaister 02344f9048 Bitmasking. 2011-05-29 23:58:04 +02:00
Themaister 200d2b598b Slight cleanup. 2011-05-28 15:06:46 +02:00
Themaister 3213bb13ea Fix color conversion for XVideo. 2011-05-28 14:37:16 +02:00
Themaister 53fd0b3751 Fix up color conversion for SDL. 2011-05-28 14:33:23 +02:00
Themaister 9b6e6a1215 Goddamnit, it was ARGB after all o.O weird. 2011-05-28 14:11:37 +02:00
Themaister e5a9d78b70 Fixed typo. 2011-05-28 11:29:40 +02:00
Themaister f22e964cc0 Err ... 32-bit color was RGBA, not ARGB. 2011-05-27 20:38:42 +02:00
Themaister 9fb827d238 Stuff seems to work. 2011-05-27 03:48:59 +02:00
Themaister 24f802023a Start adding tracker stuff to Cg as well. 2011-05-27 02:25:26 +02:00
Themaister ef2b755e17 Merge branch 'master' of github.com:Themaister/SSNES 2011-05-26 23:56:28 +02:00
Themaister 7cc8c01a1e Fix slight bug. Also add missing scale_type_x/y. 2011-05-26 23:55:46 +02:00
Themaister ca509c5b9f Fix slight bug. 2011-05-26 23:55:27 +02:00
Themaister 12a39e160b Should fix weird viewport bug with some FBO shaders. 2011-05-26 22:25:30 +02:00
Themaister 7c5f36be61 Fix build when config file support isn't compiled in. 2011-05-26 20:44:12 +02:00
Themaister aec44b093e Add new transition_previous. 2011-05-26 16:23:11 +02:00
Themaister ee83c31183 Reorder set-param and texture upload. 2011-05-26 12:53:24 +02:00
Themaister f2ad94f1c3 Finally figure it all out ... Damn. 2011-05-26 12:34:14 +02:00
Themaister 3aed471d57 This bug is driving me nuts. It cannot possibly be my bug ... 2011-05-26 02:03:38 +02:00
Themaister 3ee19ec75f Found something ... unsettling. 2011-05-26 00:23:36 +02:00
Themaister b0d1a98cd1 Fix when too many variables are used. 2011-05-25 20:55:38 +02:00
Themaister 94ff67ee15 Stuff seems to work :) 2011-05-25 17:31:07 +02:00
Themaister 8d2a7e4593 More work on state monitor ... compiles. 2011-05-25 15:58:12 +02:00
Themaister 4e7a003497 Start adding snes state tracker uniforms. 2011-05-25 15:15:20 +02:00
Themaister 380384654c Add hold bind to joyconfig. 2011-05-24 11:56:55 +02:00
Themaister 2144ef84fd Add fast-forward (hold) mode.
Also purge default player 2-5 binds ...
2011-05-24 02:31:21 +02:00
Themaister 83d913b6d2 Add possibility to build Imlib on win32/64. 2011-05-24 01:28:00 +02:00
Themaister 2782c9da91 Ifdef CG debugging. 2011-05-23 21:43:08 +02:00
Themaister 2f539cd326 Seems to work in Cg as well ... :) 2011-05-23 21:04:25 +02:00
Themaister f4af518fb4 Yay ... <_< 2011-05-23 20:08:16 +02:00
Themaister eebe0ae5d0 Getting there ... But passN textures aren't set properly ;x 2011-05-23 19:57:52 +02:00
Themaister f9c5c7c8a5 Path to check initial texture with GLSL. 2011-05-23 17:43:29 +02:00
Themaister 8bc3d3ac5c Add possibility for audio driver to override sample rate. 2011-05-23 14:04:31 +02:00
Themaister 6a3386ad1a Should fix stupid SDL message rendering bug when no font is set. 2011-05-23 11:45:44 +02:00
Themaister 5dced40c98 LUT textures work :) 2011-05-22 18:48:43 +02:00
Themaister 3f58932c2b Seems to work :) Yay! 2011-05-22 17:36:18 +02:00
Themaister 373b9dcc37 Some fixups. 2011-05-22 17:07:47 +02:00
Themaister 993bdfb75f Start working on Cg preset spec. 2011-05-22 17:02:09 +02:00
Themaister 70000607a2 Some EQ-ish stuff for lulz. 2011-05-21 20:09:43 +02:00
Themaister 1b64e4b902 Fix macro for input plugin. 2011-05-20 19:58:11 +02:00
Themaister 2166db91e2 Fix awkward compiler warning. 2011-05-19 22:51:23 +02:00
Themaister 4f50b6a749 Clarify default path in $HOME. 2011-05-19 15:51:30 +02:00
Themaister 676f659306 Fix Makefile to work when mkdir -p is being stupid. 2011-05-19 15:40:39 +02:00
Themaister 2f858b7948 Clean up ugly inline stuff. :\ 2011-05-18 22:40:42 +02:00
Themaister 36d9adbc0f *Shrug* windows. Works at least ... :D 2011-05-18 22:11:34 +02:00
Themaister 9473b392df TGA loader (nolib). 2011-05-18 21:49:23 +02:00
Themaister 3bf050d5aa Use MultiTexCoord1 for image coords. 2011-05-18 20:38:04 +02:00
Themaister 8a0ccc99df Make sure we don't load too many pictures. 2011-05-18 20:27:27 +02:00
Themaister c938f3d8de Add lookup texture support. :D yay. 2011-05-18 20:22:27 +02:00
Themaister d1c7b774f0 Remove default buttons. 2011-05-18 14:07:31 +02:00
Themaister 6b6877afc1 DSP config option to ssnes-joyconfig. 2011-05-17 19:41:57 +02:00
Themaister c370cba5b5 Update config. 2011-05-17 19:37:05 +02:00
Themaister 05d62a93c9 Update gitignore. 2011-05-17 19:34:57 +02:00
Themaister 24cb2e6844 Update filter a bit. Add a Makefile. 2011-05-17 19:34:24 +02:00
Themaister 7f1cd62c8a Update DSP API for config callback.
Clean up API a bit to have separate
SSNES_*_API_VERSION defines.
2011-05-17 19:20:41 +02:00
Themaister 5f5013871a Should fix tilde/grave key. 2011-05-17 18:52:00 +02:00
Themaister aebe6bcd23 Add example SSE3-optimized IIR filter. 2011-05-17 02:45:10 +02:00
Themaister ac21fb81e2 Prepare for 0.5 release. 2011-05-15 22:18:36 +02:00
Themaister 74afe7f95b Win32 doesn't like %g in strftime ... 2011-05-15 22:01:24 +02:00
Themaister c8161a31d6 Everything implemented. 2011-05-15 21:57:16 +02:00
Themaister 628217c6ae Fix up screenshot formatting. 2011-05-15 17:30:00 +02:00
Themaister efed98d5b6 Yay, works :) 2011-05-15 17:16:29 +02:00
Themaister 29addd605e Start adding screenshot support. 2011-05-15 16:54:43 +02:00
Themaister 0c121571b3 Update TODO. 2011-05-15 16:17:57 +02:00
Themaister 6244671b04 Small API cleanup. 2011-05-15 15:57:47 +02:00
Themaister 21b046cdd1 Add more binds to X11 input. 2011-05-15 15:54:46 +02:00
Themaister b32224d406 Check for API mismatch. 2011-05-15 01:49:37 +02:00
Themaister 5d5865cfd8 Add external API for audio driver. 2011-05-15 01:46:11 +02:00
Themaister 4a78f2fef1 Add a clarifying comment. 2011-05-14 20:53:38 +02:00
Themaister b521becd3c Fix up external video lib a bit. 2011-05-14 20:52:35 +02:00
Themaister f10ed12b74 Fix doc bug. 2011-05-13 22:05:16 +02:00
Themaister 8d051ab413 Fix up case where plugin resamples. 2011-05-13 21:43:32 +02:00
Themaister 4cc17a74f7 Clean up error handling a bit. 2011-05-13 21:22:37 +02:00
Themaister 5ae4235545 Some fixes. 2011-05-13 21:19:05 +02:00
Themaister 8ceb8225ad DSP plugin interface, weeee :D 2011-05-13 21:05:28 +02:00
Themaister 20a3087d1c Make input_scale more clear. 2011-05-13 19:42:37 +02:00
Themaister 1f45dc512b 80-cols comments. 2011-05-12 01:30:30 +02:00
Themaister 13dee59fc8 Fix some more ... :v 2011-05-12 00:35:29 +02:00
Themaister 5f78063449 Fix up call conventions, etc. 2011-05-12 00:34:08 +02:00
Themaister ea6bd8c5c5 Fix up Win32 build a bit, and a crash. :D 2011-05-11 22:53:54 +02:00
Themaister bdc85f2def Fix up Win32 build. 2011-05-11 21:37:50 +02:00
Themaister efa731fc6d Fix inconsistency with ext input. 2011-05-11 21:12:14 +02:00
Themaister 80d50b046d Fix crash. 2011-05-11 18:47:36 +02:00
Themaister 1855a7ea0d Add driver stuff. 2011-05-11 18:40:03 +02:00
Themaister f5c11071be Start hooking up input driver. 2011-05-11 18:26:00 +02:00
Themaister 4b9e46ff06 Update config. 2011-05-11 17:57:31 +02:00
Themaister 75d618140b Some more work on external API. 2011-05-11 17:52:16 +02:00
Themaister 59c6de69b0 Frame counter for shaders. Not in spec currently. 2011-05-11 13:24:59 +02:00
Themaister dc20c58189 Update path. 2011-05-11 12:19:33 +02:00
Themaister c6eecf706a Add Win64 Makefile. 2011-05-11 12:13:55 +02:00
Themaister f2ee22d259 Start working on video interface. 2011-05-05 20:23:08 +02:00
Themaister b48322222a Add overscan cropping. 2011-05-05 14:13:12 +02:00
Themaister bbf5a1ee72 Should fix aspect ratio calculation. 2011-05-05 11:38:57 +02:00
Themaister c0851877bf Update TODO. 2011-05-05 00:13:51 +02:00
Themaister 51d96676af Update TODO list. 2011-05-04 22:26:37 +02:00
Themaister 9f3b86e893 Update joyconfig. 2011-05-04 22:25:27 +02:00
Themaister ac85037fc1 And some more ... 2011-05-04 22:16:25 +02:00
Themaister fa8e948e3e Some additional keybinds. 2011-05-04 22:10:17 +02:00
Themaister 252cce07a6 Update TODO again. 2011-05-01 23:41:39 +02:00
Themaister 17581106ba Update TODO. 2011-05-01 17:10:26 +02:00
Themaister 1bd8424fb4 A TODO list to keep track of things. 2011-05-01 17:05:49 +02:00
Themaister 23db525296 Fix nasty cheat handling bug. 2011-04-29 12:36:28 +02:00
Themaister d679f8b24f v0.4.1 updates. 2011-04-26 19:00:50 +02:00
Themaister d2f2ab6a9d Windowed width with xscale depends on aspect ratio. 2011-04-24 02:44:02 +02:00
Themaister 3e1e08ca9a Variadic shifts. Optimizes the path when format matches. 2011-04-23 19:15:15 +02:00
Themaister bb93f20be9 Hopefully make conversions more portable ... 2011-04-23 18:33:56 +02:00
Themaister 54dc289cde SDL fixes. 2011-04-23 18:06:48 +02:00
Themaister fcebad2589 FreeConsole() to avoid ugly console. 2011-04-23 14:47:50 +02:00
Themaister 4b60f98603 Might fix some odd warnings. 2011-04-22 04:27:53 +02:00
Themaister d304960c21 0.4.1-beta I suppose. 2011-04-22 03:17:31 +02:00
Themaister 8da13b716a Simple font rendering with SDL. 2011-04-22 03:13:09 +02:00
Themaister a6c08a24d7 Fix 32-bit 2011-04-21 14:30:54 +02:00
Themaister 6713272809 Hrm... Seems to work, except for 32-bit -> 32-bit <_< 2011-04-21 13:12:45 +02:00
Themaister 8d7cf23b40 Meh, SDL performs like shit :p 2011-04-21 03:39:03 +02:00
Themaister d317a9773f Start on SDL video driver. 2011-04-21 03:23:44 +02:00
Themaister e8806c48c9 Default keybinds and stuff. 2011-04-17 16:55:31 +02:00
Themaister abd7bab43a Cheat support... Somewhat. 2011-04-17 16:53:19 +02:00
Themaister 66464c2cce Parsing seems to work at least. 2011-04-17 15:58:18 +02:00
Themaister 092fa9d531 Get initial cheat support down. 2011-04-17 13:30:59 +02:00
Themaister 1005d7fa9b SHA256 support. Now onto cheats ... 2011-04-17 12:29:58 +02:00
Themaister 079549cbd2 Fix an edge case with strlcat(). 2011-04-11 21:28:03 +02:00
Themaister 0f0a4d0a5e Fix fullscreen state flickering with XVideo. 2011-04-09 13:15:14 +02:00
Themaister f0bbeb901d Fix some error handling for XVideo. 2011-04-08 20:53:11 +02:00
Themaister 8027a25016 Fix include issue in windows. 2011-04-07 21:31:58 +02:00
Themaister a1426c96f8 Update for Win32. 2011-04-07 21:26:02 +02:00
Themaister a4c901df57 Bit more logging when saving SRAM to be sure. 0.4! :D 2011-04-07 21:20:11 +02:00
Themaister f2cc19d1a7 Some fixups for SGB etc. 2011-04-07 21:06:31 +02:00
Themaister ad55829890 Convert to strlcpy/strlcat. More sane. 2011-04-03 22:29:36 +02:00
Themaister 6b8eea6363 Start with strlcpy/strlcat ... 2011-04-03 22:16:59 +02:00
Themaister b2d214ee79 Should fix path issues with UPS patching.
When filename had two dots ('.') in it,
using fill_pathname on g_extern.basename was wrong.
Added fill_pathname_noext to countermeasure this.
2011-04-03 21:43:25 +02:00
Themaister f5fb113563 Should fix case where we have 1 pass and scale attributes. 2011-03-30 23:09:29 +02:00
83 changed files with 8479 additions and 1041 deletions

No files matched your search

+2
View File
@@ -1,4 +1,6 @@
*.o
*.so
*.dll
ssnes
/config.h
/config.mk
+3
View File
@@ -3,3 +3,6 @@ Hans-Kristian Arntzen - <[email protected]>
Devin J. Pohly - <[email protected]>
- Joypad axis support
Chris Moeller - <[email protected]>
- CoreAudio audio driver
+63 -26
View File
@@ -2,20 +2,27 @@ 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
OBJ = ssnes.o file.o driver.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o strl.o screenshot.o
JOYCONFIG_OBJ = tools/ssnes-joyconfig.o conf/config_file.o
HEADERS = $(wildcard */*.h) $(wildcard *.h)
LIBS = -lm
DEFINES = -DHAVE_CONFIG_H
ifneq ($(findstring Darwin,$(shell uname -a)),)
ifneq ($(findstring Darwin,$(OS)),)
OSX := 1
LIBS += -framework AppKit
else
OSX := 0
endif
BSD_LOCAL_INC =
DYLIB_LIB = -ldl
ifneq ($(findstring BSD,$(OS)),)
BSD_LOCAL_INC = -I/usr/local/include
DYLIB_LIB = -lc
endif
ifeq ($(HAVE_SRC), 1)
LIBS += $(SRC_LIBS)
DEFINES += $(SRC_CFLAGS)
@@ -33,19 +40,29 @@ endif
ifeq ($(HAVE_RSOUND), 1)
OBJ += audio/rsound.o
LIBS += -lrsound
LIBS += $(RSOUND_LIBS)
DEFINES += $(RSOUND_CFLAGS)
endif
ifeq ($(HAVE_OSS), 1)
OBJ += audio/oss.o
endif
ifeq ($(HAVE_OSS_BSD), 1)
OBJ += audio/oss.o
endif
ifeq ($(HAVE_OSS_LIB), 1)
LIBS += -lossaudio
endif
ifeq ($(HAVE_ALSA), 1)
OBJ += audio/alsa.o
LIBS += -lasound
LIBS += $(ALSA_LIBS)
DEFINES += $(ALSA_CFLAGS)
endif
ifeq ($(HAVE_ROAR), 1)
OBJ += audio/roar.o
LIBS += -lroar
LIBS += $(ROAR_LIBS)
DEFINES += $(ROAR_CFLAGS)
endif
ifeq ($(HAVE_AL), 1)
OBJ += audio/openal.o
@@ -66,9 +83,14 @@ ifeq ($(HAVE_PULSE), 1)
DEFINES += $(PULSE_CFLAGS)
endif
ifeq ($(HAVE_COREAUDIO), 1)
OBJ += audio/coreaudio.o
LIBS += -framework CoreServices -framework CoreAudio -framework AudioUnit
endif
ifeq ($(HAVE_SDL), 1)
OBJ += gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o
DEFINES += $(SDL_CFLAGS)
OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o fifo_buffer.o
DEFINES += $(SDL_CFLAGS) $(BSD_LOCAL_INC)
LIBS += $(SDL_LIBS)
ifeq ($(OSX),1)
LIBS += -framework OpenGL
@@ -77,13 +99,10 @@ else
endif
endif
ifeq ($(HAVE_FBO), 1)
DEFINES += -DHAVE_FBO
endif
ifeq ($(HAVE_XVIDEO), 1)
OBJ += gfx/xvideo.o input/x11_input.o
LIBS += -lXv -lX11
LIBS += $(XVIDEO_LIBS)
DEFINES += $(XVIDEO_CFLAGS)
endif
ifeq ($(HAVE_CG), 1)
@@ -92,13 +111,14 @@ ifeq ($(HAVE_CG), 1)
endif
ifeq ($(HAVE_XML), 1)
OBJ += gfx/shader_glsl.o
OBJ += gfx/shader_glsl.o gfx/image.o gfx/snes_state.o sha256.o cheats.o
LIBS += $(XML_LIBS)
DEFINES += $(XML_CFLAGS)
endif
ifeq ($(HAVE_FILTER), 1)
LIBS += -ldl
ifeq ($(HAVE_DYLIB), 1)
OBJ += gfx/ext.o audio/ext.o
LIBS += $(DYLIB_LIB)
endif
ifeq ($(HAVE_FREETYPE), 1)
@@ -107,23 +127,38 @@ ifeq ($(HAVE_FREETYPE), 1)
DEFINES += $(FREETYPE_CFLAGS)
endif
ifeq ($(HAVE_SDL_IMAGE), 1)
LIBS += $(SDL_IMAGE_LIBS)
DEFINES += $(SDL_IMAGE_CFLAGS)
endif
ifeq ($(HAVE_FFMPEG), 1)
OBJ += record/ffemu.o
LIBS += $(AVCODEC_LIBS) $(AVCORE_LIBS) $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS)
DEFINES += $(AVCODEC_CFLAGS) $(AVCORE_CFLAGS) $(AVFORMAT_CFLAGS) $(AVUTIL_CFLAGS) $(SWSCALE_CFLAGS)
LIBS += $(AVCODEC_LIBS) $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS)
DEFINES += $(AVCODEC_CFLAGS) $(AVFORMAT_CFLAGS) $(AVUTIL_CFLAGS) $(SWSCALE_CFLAGS)
endif
ifeq ($(HAVE_DYNAMIC), 1)
LIBS += -ldl
LIBS += $(DYLIB_LIB)
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)
OBJ += gfx/py_state/py_state.o
endif
ifneq ($(V),1)
Q := @
endif
CFLAGS += -Wall -O3 -g -I.
CFLAGS += -Wall -O3 -g -I. -pedantic
ifneq ($(findstring icc,$(CC)),)
CFLAGS += -std=c99
else
@@ -149,9 +184,9 @@ tools/ssnes-joyconfig: $(JOYCONFIG_OBJ)
@$(if $(Q), $(shell echo echo CC $<),)
install: $(TARGET)
mkdir -p $(DESTDIR)$(PREFIX)/bin
mkdir -p $(DESTDIR)/etc
mkdir -p $(DESTDIR)$(PREFIX)/share/man/man1
mkdir -p $(DESTDIR)$(PREFIX)/bin 2>/dev/null || /bin/true
mkdir -p $(DESTDIR)/etc 2>/dev/null || /bin/true
mkdir -p $(DESTDIR)$(PREFIX)/share/man/man1 2>/dev/null || /bin/true
install -m755 $(TARGET) $(DESTDIR)$(PREFIX)/bin
install -m644 ssnes.cfg $(DESTDIR)/etc/ssnes.cfg
install -m644 docs/ssnes.1 $(DESTDIR)$(PREFIX)/share/man/man1
@@ -159,18 +194,20 @@ install: $(TARGET)
install -m755 ssnes-zip $(DESTDIR)$(PREFIX)/bin
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/{ssnes,ssnes-joyconfig,ssnes-zip}
rm -f $(DESTDIR)$(PREFIX)/bin/ssnes
rm -f $(DESTDIR)$(PREFIX)/bin/ssnes-joyconfig
rm -f $(DESTDIR)$(PREFIX)/bin/ssnes-zip
rm -f $(DESTDIR)/etc/ssnes.cfg
rm -f $(DESTDIR)$(PREFIX)/share/man/man1/{ssnes,ssnes-joyconfig}.1
rm -f $(DESTDIR)$(PREFIX)/share/man/man1/ssnes.1
rm -f $(DESTDIR)$(PREFIX)/share/man/man1/ssnes-joyconfig.1
clean:
rm -f *.o
rm -f audio/*.o
rm -f conf/*.o
rm -f gfx/*.o
rm -f gfx/py_state/*.o
rm -f record/*.o
rm -f hqflt/*.o
rm -f hqflt/snes_ntsc/*.o
rm -f input/*.o
rm -f tools/*.o
rm -f $(TARGET)
+53 -7
View File
@@ -1,20 +1,26 @@
TARGET = ssnes.exe
JTARGET = ssnes-joyconfig.exe
OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o
OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o strl.o screenshot.o
JOBJ = conf/config_file.o tools/main-stub.o tools/ssnes-joyconfig.o
CC = gcc
CXX = g++
HAVE_DINPUT = 1
HAVE_SRC = 0
HAVE_SDL = 1
HAVE_SDL_IMAGE = 1
HAVE_XML = 1
HAVE_FREETYPE = 1
HAVE_XAUDIO = 1
HAVE_DSOUND = 1
HAVE_RSOUND = 1
HAVE_FILTER = 1
HAVE_DYLIB = 1
HAVE_NETPLAY = 1
HAVE_FBO = 1
HAVE_CG = 1
HAVE_PYTHON = 1
HAVE_FFMPEG = 0
libsnes ?= -lsnes
LIBS = -lm
@@ -33,16 +39,33 @@ else
endif
ifeq ($(HAVE_SDL), 1)
OBJ += gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o
OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o fifo_buffer.o
LIBS += $(SDL_LIBS) -lopengl32
DEFINES += $(SDL_CFLAGS) -DHAVE_SDL
endif
ifeq ($(HAVE_SDL_IMAGE), 1)
LIBS += -lSDL_image
DEFINES += -DHAVE_SDL_IMAGE
endif
ifeq ($(HAVE_CG), 1)
OBJ += gfx/shader_cg.o
LIBS += -lcg -lcgGL
DEFINES += -DHAVE_CG
endif
ifeq ($(HAVE_XAUDIO), 1)
OBJ += audio/xaudio.o
DEFINES += -DHAVE_XAUDIO
endif
ifeq ($(HAVE_DSOUND), 1)
OBJ += audio/dsound.o
DEFINES += -DHAVE_DSOUND
LIBS += -ldxguid -ldsound
endif
ifeq ($(HAVE_RSOUND), 1)
OBJ += audio/rsound.o
DEFINES += -DHAVE_RSOUND
@@ -50,13 +73,13 @@ ifeq ($(HAVE_RSOUND), 1)
endif
ifeq ($(HAVE_XML), 1)
OBJ += gfx/shader_glsl.o
OBJ += gfx/shader_glsl.o gfx/image.o gfx/snes_state.o sha256.o cheats.o
DEFINES += $(XML_CFLAGS) -DHAVE_XML
LIBS += -lxml2
endif
ifeq ($(HAVE_FILTER), 1)
DEFINES += -DHAVE_FILTER
ifeq ($(HAVE_DYLIB), 1)
DEFINES += -DHAVE_DYLIB
endif
ifeq ($(HAVE_NETPLAY), 1)
@@ -81,6 +104,28 @@ ifeq ($(HAVE_FBO), 1)
DEFINES += -DHAVE_FBO
endif
ifeq ($(HAVE_DYLIB), 1)
OBJ += gfx/ext.o audio/ext.o
endif
ifeq ($(HAVE_PYTHON), 1)
LIBS += -lpython32
DEFINES += -DHAVE_PYTHON -Ipython
OBJ += gfx/py_state/py_state.o
endif
ifeq ($(HAVE_DINPUT), 1)
LIBS += -ldinput8 -ldxguid -lole32
DEFINES += -DHAVE_DINPUT -Iffmpeg
OBJ += input/dinput.o
endif
ifeq ($(HAVE_FFMPEG), 1)
LIBS += -lavformat -lavcodec -lavutil -lswscale -lws2_32 -lzlib1
DEFINES += -DHAVE_FFMPEG
OBJ += record/ffemu.o
endif
ifneq ($(V),1)
Q := @
endif
@@ -107,6 +152,7 @@ clean:
rm -f audio/*.o
rm -f conf/*.o
rm -f gfx/*.o
rm -f gfx/py_state/*.o
rm -f record/*.o
rm -f hqflt/*.o
rm -f input/*.o
@@ -116,7 +162,7 @@ clean:
rm -f tools/*.o
dist: all
zip -r ssnes-win32-0.4-beta.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll freetype6.dll xaudio-c.dll rsound.dll pthreadGC2.dll README.win32.txt $(JTARGET)
zip -r ssnes-win32-0.7.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll freetype6.dll xaudio-c.dll rsound.dll pthreadGC2.dll cg.dll cgGL.dll libjpeg-8.dll libpng15-15.dll python32.dll SDL_image.dll $(JTARGET)
libs:
wget https://github.com/downloads/Themaister/SSNES/SSNES-win32-libs.zip --no-check-certificate
+158
View File
@@ -0,0 +1,158 @@
TARGET = ssnes.exe
JTARGET = ssnes-joyconfig.exe
OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o strl.o screenshot.o
JOBJ = conf/config_file.o tools/main-stub.o tools/ssnes-joyconfig.o
CC = gcc
CXX = g++
HAVE_DINPUT = 1
HAVE_SRC = 0
HAVE_SDL = 1
HAVE_XML = 1
HAVE_FREETYPE = 1
HAVE_XAUDIO = 1
HAVE_DSOUND = 1
HAVE_RSOUND = 0
HAVE_DYLIB = 1
HAVE_NETPLAY = 1
HAVE_FBO = 1
HAVE_CG = 0
HAVE_PYTHON = 0
libsnes ?= -lsnes
LIBS = -lm
DEFINES = -I. -DHAVE_CONFIGFILE
LDFLAGS = -L. -static-libgcc -s
SRC_LIBS = -lsamplerate-0
SDL_LIBS = -lmingw32 -lSDL -lSDLmain -lwinmm -mwindows -lm -luser32 -lgdi32 -lwinmm -ldxguid
SDL_CFLAGS = -ISDL
ifeq ($(HAVE_SRC), 1)
LIBS += $(SRC_LIBS)
DEFINES += $(SRC_CFLAGS) -DHAVE_SRC
else
OBJ += audio/hermite.o
endif
ifeq ($(HAVE_SDL), 1)
OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o fifo_buffer.o
LIBS += $(SDL_LIBS) -lopengl32
DEFINES += $(SDL_CFLAGS) -DHAVE_SDL
endif
ifeq ($(HAVE_CG), 1)
OBJ += gfx/shader_cg.o
LIBS += -lcg -lcgGL
DEFINES += -DHAVE_CG
endif
ifeq ($(HAVE_XAUDIO), 1)
OBJ += audio/xaudio.o
DEFINES += -DHAVE_XAUDIO
endif
ifeq ($(HAVE_DSOUND), 1)
OBJ += audio/dsound.o
DEFINES += -DHAVE_DSOUND
LIBS += -ldxguid -ldsound
endif
ifeq ($(HAVE_RSOUND), 1)
OBJ += audio/rsound.o
DEFINES += -DHAVE_RSOUND
LIBS += -lrsound
endif
ifeq ($(HAVE_XML), 1)
OBJ += gfx/shader_glsl.o gfx/image.o gfx/snes_state.o sha256.o cheats.o
DEFINES += $(XML_CFLAGS) -DHAVE_XML -DLIBXML_STATIC
LIBS += -lxml2 -lz -lws2_32
endif
ifeq ($(HAVE_DYLIB), 1)
DEFINES += -DHAVE_DYLIB
endif
ifeq ($(HAVE_NETPLAY), 1)
DEFINES += -DHAVE_NETPLAY
OBJ += netplay.o
LIBS += -lws2_32
endif
ifeq ($(HAVE_FREETYPE), 1)
OBJ += gfx/fonts.o
DEFINES += -DHAVE_FREETYPE -Ifreetype2
LIBS += -lz -lfreetype6
endif
ifeq ($(DYNAMIC), 1)
DEFINES += -DHAVE_DYNAMIC
else
LIBS += $(libsnes)
endif
ifeq ($(HAVE_FBO), 1)
DEFINES += -DHAVE_FBO
endif
ifeq ($(HAVE_DYLIB), 1)
OBJ += gfx/ext.o audio/ext.o
endif
ifeq ($(HAVE_PYTHON), 1)
LIBS += -lpython32
DEFINES += -DHAVE_PYTHON -Ipython
OBJ += gfx/py_state/py_state.o
endif
ifeq ($(HAVE_DINPUT), 1)
LIBS += -ldinput8 -ldxguid -lole32
DEFINES += -DHAVE_DINPUT
OBJ += input/dinput.o
endif
ifneq ($(V),1)
Q := @
endif
CFLAGS = -Wall -O3 -std=gnu99 -I.
all: $(TARGET) $(JTARGET)
$(TARGET): $(OBJ)
$(Q)$(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS)
@$(if $(Q), $(shell echo echo LD $@),)
%.o: %.c
$(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
@$(if $(Q), $(shell echo echo CC $<),)
$(JTARGET): $(JOBJ)
$(Q)$(CC) -o $@ $(JOBJ) $(SDL_LIBS) $(LDFLAGS)
@$(if $(Q), $(shell echo echo LD $@),)
clean:
rm -f *.o
rm -f audio/*.o
rm -f conf/*.o
rm -f gfx/*.o
rm -f record/*.o
rm -f hqflt/*.o
rm -f input/*.o
rm -f hqflt/snes_ntsc/*.o
rm -f $(TARGET)
rm -f ssnes-joyconfig.exe
rm -f tools/*.o
dist: all
zip -r ssnes-win64-0.7.zip $(TARGET) ssnes.cfg snes.dll xaudio-c.dll README.win32.txt $(JTARGET)
libs:
wget https://github.com/downloads/Themaister/SSNES/ssnes-win64-libs.zip --no-check-certificate
unzip ssnes-win64-libs.zip
.PHONY: all install uninstall clean dist libs
+2 -2
View File
@@ -18,7 +18,7 @@
#include "driver.h"
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <asoundlib.h>
#include "general.h"
#define TRY_ALSA(x) if ( x < 0 ) { \
@@ -49,7 +49,7 @@ static bool find_float_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
return false;
}
static void* __alsa_init(const char* device, int rate, int latency)
static void* __alsa_init(const char* device, unsigned rate, unsigned latency)
{
alsa_t *alsa = calloc(1, sizeof(alsa_t));
if ( alsa == NULL )
+281
View File
@@ -0,0 +1,281 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
* Copyright (C) 2011 - Chris Moeller
*
* Some code herein may be based on code found in BSNES.
*
* SSNES is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "driver.h"
#include "general.h"
#include "fifo_buffer.h"
#include <stdlib.h>
#include <stdbool.h>
#include <pthread.h>
#include <CoreAudio/CoreAudio.h>
#include <CoreServices/CoreServices.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioUnit/AUComponent.h>
typedef struct coreaudio
{
pthread_mutex_t lock;
pthread_cond_t cond;
ComponentInstance dev;
bool dev_alive;
fifo_buffer_t *buffer;
bool nonblock;
} coreaudio_t;
static void coreaudio_free(void *data)
{
coreaudio_t *dev = data;
if (!dev)
return;
if (dev->dev_alive)
{
AudioOutputUnitStop(dev->dev);
CloseComponent(dev->dev);
}
if (dev->buffer)
fifo_free(dev->buffer);
pthread_mutex_destroy(&dev->lock);
pthread_cond_destroy(&dev->cond);
free(dev);
}
static OSStatus audio_cb(void *userdata, AudioUnitRenderActionFlags *action_flags,
const AudioTimeStamp *time_stamp, UInt32 bus_number,
UInt32 number_frames, AudioBufferList *io_data)
{
coreaudio_t *dev = userdata;
(void)time_stamp;
(void)bus_number;
(void)number_frames;
if (!io_data)
return noErr;
if (io_data->mNumberBuffers != 1)
return noErr;
unsigned write_avail = io_data->mBuffers[0].mDataByteSize;
void *outbuf = io_data->mBuffers[0].mData;
pthread_mutex_lock(&dev->lock);
if (fifo_read_avail(dev->buffer) < write_avail)
{
*action_flags = kAudioUnitRenderAction_OutputIsSilence;
memset(outbuf, 0, write_avail); // Seems to be needed.
pthread_mutex_unlock(&dev->lock);
pthread_cond_signal(&dev->cond); // Technically possible to deadlock without.
return noErr;
}
fifo_read(dev->buffer, outbuf, write_avail);
pthread_mutex_unlock(&dev->lock);
pthread_cond_signal(&dev->cond);
return noErr;
}
static void* coreaudio_init(const char* device, unsigned rate, unsigned latency)
{
(void)device;
coreaudio_t *dev = calloc(1, sizeof(*dev));
if (!dev)
return NULL;
CFRunLoopRef run_loop = NULL;
AudioObjectPropertyAddress addr = {
kAudioHardwarePropertyRunLoop,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
pthread_mutex_init(&dev->lock, NULL);
pthread_cond_init(&dev->cond, NULL);
AudioObjectSetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL,
sizeof(CFRunLoopRef), &run_loop);
ComponentDescription desc = {
.componentType = kAudioUnitType_Output,
.componentSubType = kAudioUnitSubType_HALOutput,
.componentManufacturer = kAudioUnitManufacturer_Apple,
};
Component comp = FindNextComponent(NULL, &desc);
if (comp == NULL)
goto error;
OSStatus res = OpenAComponent(comp, &dev->dev);
if (res != noErr)
goto error;
dev->dev_alive = true;
AudioStreamBasicDescription stream_desc = {
.mSampleRate = rate,
.mBitsPerChannel = sizeof(float) * CHAR_BIT,
.mChannelsPerFrame = 2,
.mBytesPerPacket = 2 * sizeof(float),
.mBytesPerFrame = 2 * sizeof(float),
.mFramesPerPacket = 1,
.mFormatID = kAudioFormatLinearPCM,
.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | (is_little_endian() ? 0 : kAudioFormatFlagIsBigEndian),
};
res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0, &stream_desc, sizeof(stream_desc));
if (res != noErr)
goto error;
AudioStreamBasicDescription real_desc;
UInt32 i_size = sizeof(real_desc);
res = AudioUnitGetProperty(dev->dev, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &real_desc, &i_size);
if (res != noErr)
goto error;
SSNES_LOG("[CoreAudio]: Using output sample rate of %.1f Hz\n", (float)real_desc.mSampleRate);
g_settings.audio.out_rate = real_desc.mSampleRate;
if (real_desc.mChannelsPerFrame != stream_desc.mChannelsPerFrame)
goto error;
if (real_desc.mBitsPerChannel != stream_desc.mBitsPerChannel)
goto error;
if (real_desc.mFormatFlags != stream_desc.mFormatFlags)
goto error;
if (real_desc.mFormatID != stream_desc.mFormatID)
goto error;
AudioChannelLayout layout = {
.mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelBitmap,
.mChannelBitmap = (1 << 2) - 1,
};
res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_AudioChannelLayout,
kAudioUnitScope_Input, 0, &layout, sizeof(layout));
if (res != noErr)
goto error;
AURenderCallbackStruct cb = {
.inputProc = audio_cb,
.inputProcRefCon = dev,
};
res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, 0, &cb, sizeof(cb));
if (res != noErr)
goto error;
res = AudioUnitInitialize(dev->dev);
if (res != noErr)
goto error;
size_t fifo_size = (latency * g_settings.audio.out_rate) / 1000;
fifo_size *= 2 * sizeof(float);
dev->buffer = fifo_new(fifo_size);
if (!dev->buffer)
goto error;
SSNES_LOG("[CoreAudio]: Using buffer size of %u bytes: (latency = %u ms)\n", (unsigned)fifo_size, latency);
res = AudioOutputUnitStart(dev->dev);
if (res != noErr)
goto error;
return dev;
error:
SSNES_ERR("[CoreAudio]: Failed to initialize driver ...\n");
coreaudio_free(dev);
return NULL;
}
static ssize_t coreaudio_write(void* data, const void* buf_, size_t size)
{
coreaudio_t *dev = data;
const uint8_t *buf = buf_;
size_t written = 0;
while (size > 0)
{
pthread_mutex_lock(&dev->lock);
size_t write_avail = fifo_write_avail(dev->buffer);
if (write_avail > size)
write_avail = size;
fifo_write(dev->buffer, buf, write_avail);
buf += write_avail;
written += write_avail;
size -= write_avail;
if (dev->nonblock)
{
pthread_mutex_unlock(&dev->lock);
break;
}
if (write_avail == 0)
pthread_cond_wait(&dev->cond, &dev->lock);
pthread_mutex_unlock(&dev->lock);
}
return written;
}
static bool coreaudio_stop(void *data)
{
coreaudio_t *dev = data;
return AudioOutputUnitStop(dev->dev) == noErr;
}
static void coreaudio_set_nonblock_state(void *data, bool state)
{
coreaudio_t *dev = data;
dev->nonblock = state;
}
static bool coreaudio_start(void *data)
{
coreaudio_t *dev = data;
return AudioOutputUnitStart(dev->dev) == noErr;
}
static bool coreaudio_use_float(void *data)
{
(void)data;
return true;
}
const audio_driver_t audio_coreaudio = {
.init = coreaudio_init,
.write = coreaudio_write,
.stop = coreaudio_stop,
.start = coreaudio_start,
.set_nonblock_state = coreaudio_set_nonblock_state,
.free = coreaudio_free,
.use_float = coreaudio_use_float,
.ident = "coreaudio"
};
+387
View File
@@ -0,0 +1,387 @@
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
*
* Some code herein may be based on code found in BSNES.
*
* SSNES is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with SSNES.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "driver.h"
#include <stdlib.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <dsound.h>
#include "fifo_buffer.h"
#include "general.h"
typedef struct dsound
{
LPDIRECTSOUND ds;
LPDIRECTSOUNDBUFFER dsb;
HANDLE event;
bool nonblock;
fifo_buffer_t *buffer;
CRITICAL_SECTION crit;
volatile bool thread_alive;
HANDLE thread;
unsigned buffer_size;
} dsound_t;
static inline unsigned write_avail(unsigned read_ptr, unsigned write_ptr, unsigned buffer_size)
{
return (read_ptr + buffer_size - write_ptr) % buffer_size;
}
static inline void get_positions(dsound_t *ds, DWORD *read_ptr, DWORD *write_ptr)
{
IDirectSoundBuffer_GetCurrentPosition(ds->dsb, read_ptr, write_ptr);
}
#define CHUNK_SIZE 256
struct audio_lock
{
void *chunk1;
DWORD size1;
void *chunk2;
DWORD size2;
};
static inline bool grab_region(dsound_t *ds, DWORD write_ptr, struct audio_lock *region)
{
HRESULT res = IDirectSoundBuffer_Lock(ds->dsb, write_ptr, CHUNK_SIZE, &region->chunk1, &region->size1, &region->chunk2, &region->size2, 0);
if (res == DSERR_BUFFERLOST)
{
res = IDirectSoundBuffer_Restore(ds->dsb);
if (res != DS_OK)
return false;
res = IDirectSoundBuffer_Lock(ds->dsb, write_ptr, CHUNK_SIZE, &region->chunk1, &region->size1, &region->chunk2, &region->size2, 0);
if (res != DS_OK)
return false;
}
const char *err;
switch (res)
{
case DSERR_BUFFERLOST:
err = "DSERR_BUFFERLOST";
break;
case DSERR_INVALIDCALL:
err = "DSERR_INVALIDCALL";
break;
case DSERR_INVALIDPARAM:
err = "DSERR_INVALIDPARAM";
break;
case DSERR_PRIOLEVELNEEDED:
err = "DSERR_PRIOLEVELNEEDED";
break;
default:
err = NULL;
}
if (err)
{
SSNES_WARN("[DirectSound error]: %s\n", err);
return false;
}
return true;
}
static inline void release_region(dsound_t *ds, const struct audio_lock *region)
{
IDirectSoundBuffer_Unlock(ds->dsb, region->chunk1, region->size1, region->chunk2, region->size2);
}
static DWORD CALLBACK dsound_thread(PVOID data)
{
dsound_t *ds = data;
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
DWORD write_ptr;
get_positions(ds, NULL, &write_ptr);
write_ptr = (write_ptr + ds->buffer_size / 2) % ds->buffer_size;
while (ds->thread_alive)
{
DWORD read_ptr;
get_positions(ds, &read_ptr, NULL);
DWORD avail = write_avail(read_ptr, write_ptr, ds->buffer_size);
EnterCriticalSection(&ds->crit);
DWORD fifo_avail = fifo_read_avail(ds->buffer);
LeaveCriticalSection(&ds->crit);
// No space to write, or we don't have data in our fifo, but we can wait some time before it underruns ...
if (avail < CHUNK_SIZE || ((fifo_avail < CHUNK_SIZE) && (avail < ds->buffer_size / 2)))
{
Sleep(1);
// We could opt for using the notification interface,
// but it is not guaranteed to work, so use high priority sleeping patterns. :(
}
else if (fifo_avail < CHUNK_SIZE) // Got space to write, but nothing in FIFO (underrun), fill block with silence.
{
struct audio_lock region;
if (!grab_region(ds, write_ptr, &region))
{
ds->thread_alive = false;
SetEvent(ds->event);
break;
}
memset(region.chunk1, 0, region.size1);
memset(region.chunk2, 0, region.size2);
release_region(ds, &region);
write_ptr = (write_ptr + region.size1 + region.size2) % ds->buffer_size;
}
else // All is good. Pull from it and notify FIFO :D
{
struct audio_lock region;
if (!grab_region(ds, write_ptr, &region))
{
ds->thread_alive = false;
SetEvent(ds->event);
break;
}
EnterCriticalSection(&ds->crit);
if (region.chunk1)
fifo_read(ds->buffer, region.chunk1, region.size1);
if (region.chunk2)
fifo_read(ds->buffer, region.chunk2, region.size2);
LeaveCriticalSection(&ds->crit);
release_region(ds, &region);
write_ptr = (write_ptr + region.size1 + region.size2) % ds->buffer_size;
SetEvent(ds->event);
}
}
ExitThread(0);
}
static void dsound_stop_thread(dsound_t *ds)
{
if (ds->thread)
{
ds->thread_alive = false;
WaitForSingleObject(ds->thread, INFINITE);
CloseHandle(ds->thread);
ds->thread = NULL;
}
}
static bool dsound_start_thread(dsound_t *ds)
{
if (!ds->thread)
{
ds->thread_alive = true;
ds->thread = CreateThread(NULL, 0, dsound_thread, ds, 0, NULL);
if (ds->thread == NULL)
return false;
}
return true;
}
static void dsound_clear_buffer(dsound_t *ds)
{
IDirectSoundBuffer_SetCurrentPosition(ds->dsb, 0);
void *ptr;
DWORD size;
if (IDirectSoundBuffer_Lock(ds->dsb, 0, 0, &ptr, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER) == DS_OK)
{
memset(ptr, 0, size);
IDirectSoundBuffer_Unlock(ds->dsb, ptr, size, NULL, 0);
}
}
static void dsound_free(void *data)
{
dsound_t *ds = data;
if (ds)
{
if (ds->thread)
{
ds->thread_alive = false;
WaitForSingleObject(ds->thread, INFINITE);
CloseHandle(ds->thread);
}
DeleteCriticalSection(&ds->crit);
if (ds->dsb)
{
IDirectSoundBuffer_Stop(ds->dsb);
IDirectSoundBuffer_Release(ds->dsb);
}
if (ds)
IDirectSound_Release(ds->ds);
if (ds->event)
CloseHandle(ds->event);
if (ds->buffer)
fifo_free(ds->buffer);
free(ds);
}
}
static void* dsound_init(const char *device, unsigned rate, unsigned latency)
{
dsound_t *ds = calloc(1, sizeof(*ds));
if (!ds)
goto error;
InitializeCriticalSection(&ds->crit);
if (DirectSoundCreate(NULL, &ds->ds, NULL) != DS_OK)
goto error;
if (IDirectSound_SetCooperativeLevel(ds->ds, GetDesktopWindow(), DSSCL_PRIORITY) != DS_OK)
goto error;
WAVEFORMATEX wfx = {
.wFormatTag = WAVE_FORMAT_PCM,
.nChannels = 2,
.nSamplesPerSec = rate,
.wBitsPerSample = 16,
.nBlockAlign = 2 * sizeof(int16_t),
.nAvgBytesPerSec = rate * 2 * sizeof(int16_t),
};
ds->buffer_size = (latency * wfx.nAvgBytesPerSec) / 1000;
ds->buffer_size /= CHUNK_SIZE;
ds->buffer_size *= CHUNK_SIZE;
if (ds->buffer_size < 4 * CHUNK_SIZE)
ds->buffer_size = 4 * CHUNK_SIZE;
SSNES_LOG("[DirectSound]: Setting buffer size of %u bytes\n", ds->buffer_size);
SSNES_LOG("[DirectSound]: Latency = %u ms\n", (unsigned)((1000 * ds->buffer_size) / wfx.nAvgBytesPerSec));
DSBUFFERDESC bufdesc = {
.dwSize = sizeof(DSBUFFERDESC),
.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS,
.dwBufferBytes = ds->buffer_size,
.lpwfxFormat = &wfx,
};
ds->event = CreateEvent(NULL, false, false, NULL);
if (!ds->event)
goto error;
ds->buffer = fifo_new(4 * 1024);
if (!ds->buffer)
goto error;
if (IDirectSound_CreateSoundBuffer(ds->ds, &bufdesc, &ds->dsb, 0) != DS_OK)
goto error;
IDirectSoundBuffer_SetCurrentPosition(ds->dsb, 0);
dsound_clear_buffer(ds);
if (IDirectSoundBuffer_Play(ds->dsb, 0, 0, DSBPLAY_LOOPING) != DS_OK)
goto error;
if (!dsound_start_thread(ds))
goto error;
return ds;
error:
SSNES_ERR("[DirectSound] Error occured in init!\n");
dsound_free(ds);
return NULL;
}
static bool dsound_stop(void *data)
{
dsound_t *ds = data;
dsound_stop_thread(ds);
return IDirectSoundBuffer_Stop(ds->dsb) == DS_OK;
}
static bool dsound_start(void *data)
{
dsound_t *ds = data;
dsound_clear_buffer(ds);
if (!dsound_start_thread(ds))
return false;
return IDirectSoundBuffer_Play(ds->dsb, 0, 0, DSBPLAY_LOOPING) == DS_OK;
}
static void dsound_set_nonblock_state(void *data, bool state)
{
dsound_t *ds = data;
ds->nonblock = state;
}
static ssize_t dsound_write(void *data, const void *buf_, size_t size)
{
dsound_t *ds = data;
const uint8_t *buf = buf_;
if (!ds->thread_alive)
return -1;
size_t written = 0;
while (size > 0)
{
EnterCriticalSection(&ds->crit);
size_t avail = fifo_write_avail(ds->buffer);
if (avail > size)
avail = size;
fifo_write(ds->buffer, buf, avail);
LeaveCriticalSection(&ds->crit);
buf += avail;
size -= avail;
written += avail;
if (ds->nonblock || !ds->thread_alive)
break;
if (avail == 0)
WaitForSingleObject(ds->event, INFINITE);
}
return written;
}
const audio_driver_t audio_dsound = {
.init = dsound_init,
.write = dsound_write,
.stop = dsound_stop,
.start = dsound_start,
.set_nonblock_state = dsound_set_nonblock_state,
.free = dsound_free,
.ident = "dsound"
};
+163
View File
@@ -0,0 +1,163 @@
/* 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 "ext/ssnes_audio.h"
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include "driver.h"
#include "dynamic.h"
#include "general.h"
#include <sys/types.h>
typedef struct audio_ext
{
dylib_t lib;
const ssnes_audio_driver_t *driver;
void *handle;
bool is_float;
} audio_ext_t;
static void audio_ext_free(void *data)
{
audio_ext_t *ext = data;
if (ext)
{
if (ext->driver && ext->handle)
ext->driver->free(ext->handle);
if (ext->lib)
dylib_close(ext->lib);
free(ext);
}
}
static void* audio_ext_init(const char *device, unsigned rate, unsigned latency)
{
if (!(*g_settings.audio.external_driver))
{
SSNES_ERR("Please define an external audio driver.\n");
return NULL;
}
audio_ext_t *ext = calloc(1, sizeof(*ext));
if (!ext)
return NULL;
ext->lib = dylib_load(g_settings.audio.external_driver);
if (!ext->lib)
{
SSNES_ERR("Failed to load external library \"%s\"\n", g_settings.audio.external_driver);
goto error;
}
const ssnes_audio_driver_t* (*plugin_load)(void) = (const ssnes_audio_driver_t* (*)(void))dylib_proc(ext->lib, "ssnes_audio_driver_init");
if (!plugin_load)
{
SSNES_ERR("Failed to find symbol \"ssnes_audio_driver_init\" in plugin.\n");
goto error;
}
ext->driver = plugin_load();
if (!ext->driver)
{
SSNES_ERR("Received invalid driver from plugin.\n");
goto error;
}
SSNES_LOG("Loaded external audio driver: \"%s\"\n", ext->driver->ident ? ext->driver->ident : "Unknown");
if (ext->driver->api_version != SSNES_AUDIO_API_VERSION)
{
SSNES_ERR("API mismatch in external audio plugin. SSNES: %d, Plugin: %d ...\n", SSNES_AUDIO_API_VERSION, ext->driver->api_version);
goto error;
}
ssnes_audio_driver_info_t info = {
.device = device,
.sample_rate = rate,
.latency = latency
};
ext->handle = ext->driver->init(&info);
if (!ext->handle)
{
SSNES_ERR("Failed to init audio driver.\n");
goto error;
}
if (ext->driver->sample_rate)
g_settings.audio.out_rate = ext->driver->sample_rate(ext->handle);
if (!g_settings.audio.sync)
ext->driver->set_nonblock_state(ext->handle, SSNES_TRUE);
return ext;
error:
audio_ext_free(ext);
return NULL;
}
static ssize_t audio_ext_write(void *data, const void *buf, size_t size)
{
audio_ext_t *ext = data;
unsigned frame_size = ext->is_float ? (2 * sizeof(float)) : (2 * sizeof(int16_t));
size /= frame_size;
int ret = ext->driver->write(ext->handle, buf, size);
if (ret < 0)
return -1;
return ret * frame_size;
}
static bool audio_ext_start(void *data)
{
audio_ext_t *ext = data;
return ext->driver->start(ext->handle);
}
static bool audio_ext_stop(void *data)
{
audio_ext_t *ext = data;
return ext->driver->stop(ext->handle);
}
static void audio_ext_set_nonblock_state(void *data, bool toggle)
{
audio_ext_t *ext = data;
ext->driver->set_nonblock_state(ext->handle, toggle);
}
static bool audio_ext_use_float(void *data)
{
audio_ext_t *ext = data;
ext->is_float = ext->driver->use_float(ext->handle);
return ext->is_float;
}
const audio_driver_t audio_ext = {
.init = audio_ext_init,
.write = audio_ext_write,
.stop = audio_ext_stop,
.start = audio_ext_start,
.set_nonblock_state = audio_ext_set_nonblock_state,
.use_float = audio_ext_use_float,
.free = audio_ext_free,
.ident = "ext"
};
+16
View File
@@ -0,0 +1,16 @@
TARGET = ssnes_iir_filter.so
OBJ = ssnes_iir_filter.o
all: $(TARGET)
$(TARGET): $(OBJ)
$(CC) -o $@ -shared $(OBJ) -lm
%.o: %.c
$(CC) -c -o $@ $< -g -std=c99 -fPIC -O3 -march=native $(CFLAGS)
clean:
rm -f $(TARGET)
rm -f $(OBJ)
.PHONY: clean
+16
View File
@@ -0,0 +1,16 @@
TARGET = iir_filter
OBJ = ssnes_iir_filter.o
all: $(TARGET)
$(TARGET): $(OBJ)
$(CC) -o $@ $(OBJ) -lm
%.o: %.c
$(CC) -c -o $@ $< -g -std=c99 -O3 -march=native $(CFLAGS) -DTEST -DLOG
clean:
rm -f $(TARGET)
rm -f $(OBJ)
.PHONY: clean
+128
View File
@@ -0,0 +1,128 @@
/////
// API header for external SSNES audio driver plugins.
//
//
#ifndef __SSNES_AUDIO_DRIVER_PLUGIN_H
#define __SSNES_AUDIO_DRIVER_PLUGIN_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _WIN32
#ifdef SSNES_DLL_IMPORT
#define SSNES_API_EXPORT __declspec(dllimport)
#else
#define SSNES_API_EXPORT __declspec(dllexport)
#endif
#define SSNES_API_CALLTYPE __cdecl
#else
#define SSNES_API_EXPORT
#define SSNES_API_CALLTYPE
#endif
#ifndef SSNES_TRUE
#define SSNES_TRUE 1
#endif
#ifndef SSNES_FALSE
#define SSNES_FALSE 0
#endif
#ifndef SSNES_OK
#define SSNES_OK 1
#endif
#ifndef SSNES_ERROR
#define SSNES_ERROR 0
#endif
#define SSNES_AUDIO_API_VERSION 2
typedef struct ssnes_audio_driver_info
{
// A hint for a subdevice of the audio driver.
// This is driver independent, and not relevant for all
// audio drivers. I.e. ALSA driver might use "hw:0",
// OSS "/dev/audio", etc.
const char *device;
// Audio sample rate.
unsigned sample_rate;
// Maximum audio latency requested for output,
// measured in milliseconds.
// If driver is not able to provide this latency, it can
// be disregarded.
unsigned latency;
} ssnes_audio_driver_info_t;
typedef struct ssnes_audio_driver
{
// Initializes the device.
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
// channels. (I.e. 44.1kHz, 16-bit stereo has 88.2k samples/s, and
// 44.1k frames/s.)
//
// Samples are interleaved in format LRLRLRLRLR ...
// If the driver returns true in use_float(), a floating point
// format will be used, with range [-1.0, 1.0].
// If not, signed 16-bit samples in native byte ordering will be used.
//
// This function returns the number of frames successfully written.
// If an error occurs, -1 should be returned.
// Note that non-blocking behavior that cannot write at this time
// should return 0 as returning -1 will terminate the driver.
//
// Unless said otherwise with set_nonblock_state(), all writes
// are blocking, and it should block till it has written all frames.
int (*write)(void *data, const void *buffer, unsigned frames);
// Temporarily pauses the audio driver.
int (*stop)(void *data);
// Resumes audio driver from the paused state.
int (*start)(void *data);
// If state is true, nonblocking operation is assumed.
// This is typically used for fast-forwarding. If driver cannot
// implement nonblocking writes, this can be disregarded, but should
// log a message to stderr.
void (*set_nonblock_state)(void *data, int state);
// Stops and frees the audio driver.
void (*free)(void *data);
// If true is returned, the audio driver is capable of using
// floating point data. This will likely increase performance as the
// resampler unit uses floating point. The sample range is
// [-1.0, 1.0].
int (*use_float)(void *data);
// The driver might be forced to use a certain output frequency
// (i.e. Jack), and thus to avoid double resampling, the driver
// can request SSNES to resample to a different sample rate.
// This function can be set to NULL if the driver does not
// desire to override the sample rate.
unsigned (*sample_rate)(void *data);
// Human readable identification string for the driver.
const char *ident;
// Must be set to SSNES_AUDIO_API_VERSION.
// Used for detecting API mismatch.
int api_version;
} ssnes_audio_driver_t;
SSNES_API_EXPORT const ssnes_audio_driver_t* SSNES_API_CALLTYPE
ssnes_audio_driver_init(void);
#ifdef __cplusplus
}
#endif
#endif
+129
View File
@@ -0,0 +1,129 @@
/////
// API header for external SSNES DSP plugins.
//
//
#ifndef __SSNES_DSP_PLUGIN_H
#define __SSNES_DSP_PLUGIN_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _WIN32
#ifdef SSNES_DLL_IMPORT
#define SSNES_API_EXPORT __declspec(dllimport)
#else
#define SSNES_API_EXPORT __declspec(dllexport)
#endif
#define SSNES_API_CALLTYPE __cdecl
#else
#define SSNES_API_EXPORT
#define SSNES_API_CALLTYPE
#endif
#ifndef SSNES_FALSE
#define SSNES_FALSE 0
#endif
#ifndef SSNES_TRUE
#define SSNES_TRUE 1
#endif
#define SSNES_DSP_API_VERSION 3
typedef struct ssnes_dsp_info
{
// Input sample rate that the DSP plugin receives. This is generally ~32kHz.
// Some small variance is allowed due to syncing behavior.
float input_rate;
// SSNES requests that the DSP plugin resamples the
// input to a certain frequency.
//
// However, the plugin might ignore this
// using the resample field in ssnes_dsp_output_t (see below).
float output_rate;
} ssnes_dsp_info_t;
typedef struct ssnes_dsp_output
{
// The DSP plugin has to provide the buffering for the output samples.
// This is for performance reasons to avoid redundant copying of data.
// The samples are laid out in interleaving order: LRLRLRLR
// The range of the samples are [-1.0, 1.0].
// This range cannot be exceeded without horrible audio glitches.
const float *samples;
// Frames which the DSP plugin outputted for the current process.
// One frame is here defined as a combined sample of
// left and right channels.
// (I.e. 44.1kHz, 16bit stereo will have
// 88.2k samples/sec and 44.1k frames/sec.)
unsigned frames;
// If true, the DSP plugin did not resample the input audio,
// and requests resampling to the proper frequency to be
// performed outside the plugin.
// If false,
// it is assumed that the output has the same sample rate as given
// in output_rate.
int should_resample;
} ssnes_dsp_output_t;
typedef struct ssnes_dsp_input
{
// Input data for the DSP. The samples are interleaved in order: LRLRLRLR
const float *samples;
// Number of frames for input data.
// One frame is here defined as a combined sample of
// left and right channels.
// (I.e. 44.1kHz, 16bit stereo will have
// 88.2k samples/sec and 44.1k frames/sec.)
unsigned frames;
} ssnes_dsp_input_t;
typedef struct ssnes_dsp_plugin
{
// Creates a handle of the plugin. Returns NULL if failed.
void* (*init)(const ssnes_dsp_info_t *info);
// Processes input data.
// The plugin is allowed to return variable sizes for output data.
void (*process)(void *data, ssnes_dsp_output_t *output,
const ssnes_dsp_input_t *input);
// Frees the handle.
void (*free)(void *data);
// API version used to compile the plugin.
// Used to detect mismatches in API.
// Must be set to SSNES_DSP_API_VERSION on compile.
int api_version;
// Signal plugin that it may open a configuring window or
// something similiar. The behavior of this function
// is thus plugin dependent. Implementing this is optional,
// and can be set to NULL.
void (*config)(void *data);
// Human readable identification string.
const char *ident;
// Called every frame, allows creating a GUI main loop in the main thread.
// GUI events can be processed here in a non-blocking fashion.
// Can be set to NULL to ignore it.
void (*events)(void *data);
} ssnes_dsp_plugin_t;
// Called by SSNES at startup to get the callback struct.
// This is NOT dynamically allocated!
SSNES_API_EXPORT const ssnes_dsp_plugin_t* SSNES_API_CALLTYPE
ssnes_dsp_plugin_init(void);
#ifdef __cplusplus
}
#endif
#endif
+418
View File
@@ -0,0 +1,418 @@
#include "ssnes_dsp.h"
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <math.h>
#include <complex.h>
// Simple IIR/EQ filter implementation, optimized for SSE3.
#ifdef __SSE3__ // Build with -march=native or -msse3 to let this be detected! D:
#define USE_SSE3
#endif
#ifdef USE_SSE3
#include <pmmintrin.h>
#endif
// Make a test build with standalone main()
//#define TEST
#ifdef TEST
#include <stdio.h>
#include <assert.h>
#endif
// Log filter coeffs for debugging
//#define LOG
#define min(x, y) ((x) < (y) ? (x) : (y))
#ifndef M_PI
#define M_PI 3.14159265
#endif
// Taps needs to be power-of-two.
#define TAPS (2048)
static const float lp_freq = 5000.0;
static const float hp_freq = 500.0;
static const float bp_freq = 300.0;
static const unsigned bp_q = 500;
#ifdef USE_SSE3
typedef union
{
__m128 vec[TAPS / 4 + 1]; // Wraparound for unaligned reads.
float f[TAPS + 4];
} vec_filt;
#else
typedef float vec_filt[TAPS + 4];
#endif
typedef struct
{
vec_filt buffer[2];
vec_filt iir_buffer[2];
vec_filt fir_coeff;
vec_filt iir_coeff;
float out_buffer[4096];
unsigned buf_ptr;
} dsp_state;
// Convolve polynoms.
static void conv(float * restrict a, const float * restrict b, unsigned a_size, unsigned b_size)
{
float output[a_size + b_size - 1];
// Lead-in
for (int i = 0; i < b_size - 1; i++)
{
output[i] = 0.0;
for (int j = 0; j < b_size; j++)
{
if ((i - j) >= 0)
output[i] += a[i - j] * b[j];
}
}
// Mid-section
for (unsigned i = b_size - 1; i < a_size; i++)
{
output[i] = 0.0;
for (unsigned j = 0; j < b_size; j++)
output[i] += a[i - j] * b[j];
}
// Lead-out
for (unsigned i = a_size; i < a_size + b_size - 1; i++)
{
output[i] = 0.0;
for (unsigned j = 0; j < b_size; j++)
{
if ((i - j) < a_size)
output[i] += a[i - j] * b[j];
}
}
memcpy(a, output, sizeof(output));
}
#ifdef LOG
static void print_coeff(const char *key, const float *coeff, unsigned elem)
{
for (unsigned i = 0; i < elem; i++)
fprintf(stderr, "%4s [%3u] = %9.5f\n", key, i, coeff[i]);
}
static void print_filter_coeffs(const float *fir, const float *iir, unsigned elem)
{
print_coeff("FIR", fir, elem);
print_coeff("IIR", iir, elem);
}
#endif
// Simulate plain 1-pole RC circuit.
// dVc/dt = (Vin - Vc) / RC =>
// y[n] = y[n - 1] * (1 - Ts/RC) + x[n] * Ts/RC
// RCw = 1 => RC = 1/(2*pi*fc)
static void generate_low_pass(float fir_out[1], float iir_out[2], float freq, float input_rate)
{
float ts = 1.0 / input_rate;
float t = 1.0 / (2 * M_PI * freq);
fir_out[0] = ts / t;
iir_out[0] = 1.0;
iir_out[1] = -(1.0 - ts / t);
#ifdef LOG
print_coeff("FLOW", fir_out, 1);
print_coeff("ILOW", iir_out, 2);
#endif
}
// 1-pole RC circuit (Vo taken over R).
static void generate_high_pass(float fir_out[2], float iir_out[2], float freq, float input_rate)
{
float ts = 1.0 / input_rate;
float t = 1.0 / (2 * M_PI * freq);
fir_out[0] = 1.0;
fir_out[1] = -1.0;
iir_out[0] = 1.0;
iir_out[1] = -(1.0 - ts / t);
#ifdef LOG
print_coeff("FHI", fir_out, 2);
print_coeff("IHI", iir_out, 2);
#endif
}
static void generate_band_pass(float *fir_out, unsigned num_fir, float freq, float input_rate)
{
float w_norm = 2.0 * M_PI * freq / input_rate;
for (unsigned i = 0; i < num_fir; i++)
fir_out[i] = cosf(i * w_norm);
complex float amp = 0.0;
for (unsigned i = 0; i < num_fir; i++)
amp += cosf(i * w_norm) * cexp(-I * w_norm * i);
float abs = cabs(amp);
for (unsigned i = 0; i < num_fir; i++)
fir_out[i] /= abs;
}
// Generic low-pass. Differential equation.
static void generate_filter_coeffs(float * restrict fir_out, float * restrict iir_out, float input_rate)
{
fir_out[0] = 1.0;
iir_out[0] = 1.0;
unsigned fir_length = 1;
unsigned iir_length = 1;
/*
// Low pass
float lp_fir[1];
float lp_iir[2];
generate_low_pass(lp_fir, lp_iir, lp_freq, input_rate);
conv(fir_out, lp_fir, fir_length, 1);
conv(iir_out, lp_iir, iir_length, 2);
fir_length += 0;
iir_length += 1;
// High pass
float hp_fir[2];
float hp_iir[2];
generate_high_pass(hp_fir, hp_iir, hp_freq, input_rate);
conv(fir_out, hp_fir, fir_length, 2);
conv(iir_out, hp_iir, iir_length, 2);
fir_length += 1;
iir_length += 1;
*/
// Band pass
float bp_fir[bp_q];
generate_band_pass(bp_fir, bp_q, bp_freq, input_rate);
conv(fir_out, bp_fir, fir_length, bp_q);
fir_length += bp_q - 1;
iir_length += 0;
// Adjust from Z rational polynomial model to discrete domain.
// We don't care about first param as it's implicitly 1.
for (unsigned i = 0; i < TAPS; i++)
iir_out[i] = -1.0 * iir_out[i + 1];
}
static void* dsp_init(const ssnes_dsp_info_t *info)
{
(void)info;
dsp_state *state = calloc(1, sizeof(*state));
if (!state)
return NULL;
fprintf(stderr, "Input rate = %.2f\n", (float)info->input_rate);
#ifdef USE_SSE3
generate_filter_coeffs(state->fir_coeff.f, state->iir_coeff.f, info->input_rate);
#ifdef LOG
print_filter_coeffs(state->fir_coeff.f, state->iir_coeff.f, 3);
#endif
#else
generate_filter_coeffs(state->fir_coeff, state->iir_coeff, info->input_rate);
#ifdef LOG
print_filter_coeffs(state->fir_coeff, state->iir_coeff, 3);
#endif
#endif
#ifdef USE_SSE3
memcpy(state->fir_coeff.f + TAPS, state->fir_coeff.f, 4 * sizeof(float));
memcpy(state->iir_coeff.f + TAPS, state->iir_coeff.f, 4 * sizeof(float));
#endif
return state;
}
#ifdef USE_SSE3
static void calculate_iir(float *out, dsp_state *dsp)
{
unsigned buf_ptr = (dsp->buf_ptr - 1) & (TAPS - 1);
unsigned iir_ptr = (TAPS - dsp->buf_ptr) & (TAPS - 1);
const float * restrict samples_left = dsp->buffer[0].f;
const float * restrict samples_right = dsp->buffer[1].f;
const float * restrict iir_left = dsp->iir_buffer[0].f;
const float * restrict iir_right = dsp->iir_buffer[1].f;
__m128 sum_left = _mm_setzero_ps();
__m128 sum_right = _mm_setzero_ps();
for (unsigned i = 0; i < TAPS; i += 4)
{
__m128 left = _mm_load_ps(samples_left + i);
__m128 right = _mm_load_ps(samples_right + i);
const __m128 ileft = _mm_load_ps(iir_left + i);
const __m128 iright = _mm_load_ps(iir_right + i);
// Need unaligned reads here.
const __m128 fir = _mm_loadu_ps(dsp->fir_coeff.f + iir_ptr);
const __m128 iir = _mm_loadu_ps(dsp->iir_coeff.f + iir_ptr);
const __m128 fir_res_left = _mm_mul_ps(fir, left);
const __m128 fir_res_right = _mm_mul_ps(fir, right);
const __m128 iir_res_left = _mm_mul_ps(iir, ileft);
const __m128 iir_res_right = _mm_mul_ps(iir, iright);
left = _mm_add_ps(fir_res_left, iir_res_left);
right = _mm_add_ps(fir_res_right, iir_res_right);
sum_left = _mm_add_ps(sum_left, left);
sum_right = _mm_add_ps(sum_right, right);
iir_ptr = (iir_ptr + 4) & (TAPS - 1);
}
__m128 res = _mm_hadd_ps(sum_left, sum_right);
res = _mm_hadd_ps(res, res);
union
{
__m128 vec;
float f[4];
} u;
u.vec = res;
out[0] = u.f[0];
out[1] = u.f[1];
dsp->iir_buffer[0].f[buf_ptr] = u.f[0];
dsp->iir_buffer[1].f[buf_ptr] = u.f[1];
}
#else
static void calculate_iir(float *out, dsp_state *dsp)
{
const float * restrict samples_left = dsp->buffer[0];
const float * restrict samples_right = dsp->buffer[1];
const float * restrict iir_left = dsp->iir_buffer[0];
const float * restrict iir_right = dsp->iir_buffer[1];
const float * restrict fir = dsp->fir_coeff;
const float * restrict iir = dsp->iir_coeff;
unsigned iir_ptr = 0;
unsigned sample_ptr = dsp->buf_ptr;
float sum[2] = { 0.0f, 0.0f };
for (unsigned i = 0; i < TAPS; i++)
{
sum[0] += fir[iir_ptr] * samples_left[sample_ptr] + iir[iir_ptr] * iir_left[sample_ptr];
sum[1] += fir[iir_ptr] * samples_right[sample_ptr] + iir[iir_ptr] * iir_right[sample_ptr];
iir_ptr++;
sample_ptr = (sample_ptr + 1) & (TAPS - 1);
}
// Stick our output value in the IIR buffer.
for (unsigned i = 0; i < 2; i++)
{
out[i] = sum[i];
dsp->iir_buffer[i][(dsp->buf_ptr - 1) & (TAPS - 1)] = sum[i];
}
}
#endif
static void dsp_process(void *data, ssnes_dsp_output_t *output, const ssnes_dsp_input_t *input)
{
dsp_state *dsp = data;
output->should_resample = SSNES_TRUE;
output->frames = input->frames;
output->samples = dsp->out_buffer;
for (unsigned i = 0; i < input->frames; i++)
{
#ifdef USE_SSE3
dsp->buffer[0].f[dsp->buf_ptr] = input->samples[(i << 1) + 0];
dsp->buffer[1].f[dsp->buf_ptr] = input->samples[(i << 1) + 1];
#else
dsp->buffer[0][dsp->buf_ptr] = input->samples[(i << 1) + 0];
dsp->buffer[1][dsp->buf_ptr] = input->samples[(i << 1) + 1];
#endif
calculate_iir(&dsp->out_buffer[i << 1], dsp);
dsp->buf_ptr = (dsp->buf_ptr - 1) & (TAPS - 1);
}
}
static void dsp_config(void *data)
{
(void)data;
// Normally we unhide a GUI window or something,
// but we're just going to print to the log instead.
fprintf(stderr, "DSP_CONFIG\n");
}
static void dsp_free(void *data)
{
free(data);
}
const ssnes_dsp_plugin_t dsp_plug = {
.init = dsp_init,
.process = dsp_process,
.free = dsp_free,
.config = dsp_config,
.api_version = SSNES_DSP_API_VERSION,
.ident = "IIR filter"
};
SSNES_API_EXPORT const ssnes_dsp_plugin_t*
SSNES_API_CALLTYPE ssnes_dsp_plugin_init(void)
{
return &dsp_plug;
}
#ifdef TEST
int main(void)
{
const ssnes_dsp_plugin_t *plug = ssnes_dsp_plugin_init();
assert(plug);
ssnes_dsp_info_t info = {
.input_rate = 44100,
.output_rate = 44100
};
void *handle = plug->init(&info); // Info isn't used.
assert(handle);
int16_t buf[64];
float fbuf[64];
for (;;)
{
size_t rd = fread(buf, sizeof(int16_t), 64, stdin);
for (unsigned i = 0; i < rd; i++)
fbuf[i] = (float)buf[i];
ssnes_dsp_input_t input = {
.samples = fbuf,
.frames = rd >> 1
};
ssnes_dsp_output_t output;
plug->process(handle, &output, &input);
for (unsigned i = 0; i < output.frames << 1; i++)
{
int32_t sample = (int32_t)output.samples[i];
buf[i] = (sample > 0x7fff) ? 0x7fff : ((sample < -0x8000) ? -0x8000 : (int16_t)sample);
}
fwrite(buf, sizeof(int16_t), output.frames << 1, stdout);
if (rd < 64)
break;
}
plug->free(handle);
}
#endif
+18 -19
View File
@@ -124,12 +124,15 @@ 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, int rate, int latency)
static void* __jack_init(const char* device, unsigned rate, unsigned latency)
{
jack_t *jd = calloc(1, sizeof(jack_t));
if ( jd == NULL )
if (!jd)
return NULL;
pthread_cond_init(&jd->cond, NULL);
pthread_mutex_init(&jd->cond_lock, NULL);
const char **jports = NULL;
jd->client = jack_client_open("SSNES", JackNullOption, NULL);
@@ -148,7 +151,6 @@ static void* __jack_init(const char* device, int rate, int latency)
SSNES_ERR("Failed to register ports.\n");
goto error;
}
char *dest_ports[2];
jports = jack_get_ports(jd->client, NULL, NULL, JackPortIsPhysical | JackPortIsInput);
@@ -158,6 +160,18 @@ static void* __jack_init(const char* device, int rate, int latency)
goto error;
}
size_t bufsize = find_buffersize(jd, latency);
SSNES_LOG("JACK: Internal buffer size: %d frames.\n", (int)(bufsize / sizeof(jack_default_audio_sample_t)));
for (int i = 0; i < 2; i++)
{
jd->buffer[i] = jack_ringbuffer_create(bufsize);
if (jd->buffer[i] == NULL)
{
SSNES_ERR("Failed to create buffers.\n");
goto error;
}
}
int parsed = parse_ports(dest_ports, jports);
if (jack_activate(jd->client) < 0)
@@ -178,22 +192,7 @@ static void* __jack_init(const char* device, int rate, int latency)
for (int i = 0; i < parsed; i++)
free(dest_ports[i]);
size_t bufsize = find_buffersize(jd, latency);
SSNES_LOG("JACK: Internal buffer size: %d frames.\n", (int)(bufsize / sizeof(jack_default_audio_sample_t)));
for (int i = 0; i < 2; i++)
{
jd->buffer[i] = jack_ringbuffer_create(bufsize);
if (jd->buffer[i] == NULL)
{
SSNES_ERR("Failed to create buffers.\n");
goto error;
}
}
pthread_cond_init(&jd->cond, NULL);
pthread_mutex_init(&jd->cond_lock, NULL);
jack_free(jports);
return jd;
+1 -1
View File
@@ -56,7 +56,7 @@ typedef struct al
} al_t;
static void* __al_init(const char* device, int rate, int latency)
static void* __al_init(const char* device, unsigned rate, unsigned latency)
{
(void)device;
al_t *al = calloc(1, sizeof(al_t));
+30 -18
View File
@@ -15,28 +15,44 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "driver.h"
#include "general.h"
#include <stdlib.h>
#ifdef HAVE_OSS_BSD
#include <soundcard.h>
#else
#include <sys/soundcard.h>
#endif
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
static void* __oss_init(const char* device, int rate, int latency)
#ifdef HAVE_OSS_BSD
#define DEFAULT_OSS_DEV "/dev/audio"
#else
#define DEFAULT_OSS_DEV "/dev/dsp"
#endif
static void* __oss_init(const char* device, unsigned rate, unsigned latency)
{
int *fd = calloc(1, sizeof(int));
if ( fd == NULL )
if (fd == NULL)
return NULL;
const char *oss_device = "/dev/dsp";
const char *oss_device = DEFAULT_OSS_DEV;
if ( device != NULL )
if (device != NULL)
oss_device = device;
if ( (*fd = open(oss_device, O_WRONLY)) < 0 )
if ((*fd = open(oss_device, O_WRONLY)) < 0)
{
free(fd);
return NULL;
@@ -45,7 +61,7 @@ static void* __oss_init(const char* device, int rate, int latency)
int frags = (latency * rate * 4)/(1000 * (1 << 9));
int frag = (frags << 16) | 9;
if ( ioctl(*fd, SNDCTL_DSP_SETFRAGMENT, &frag) < 0 )
if (ioctl(*fd, SNDCTL_DSP_SETFRAGMENT, &frag) < 0)
{
close(*fd);
free(fd);
@@ -53,23 +69,24 @@ static void* __oss_init(const char* device, int rate, int latency)
}
int channels = 2;
int format = AFMT_S16_LE;
int format = is_little_endian() ?
AFMT_S16_LE : AFMT_S16_BE;
if ( ioctl(*fd, SNDCTL_DSP_CHANNELS, &channels) < 0 )
if (ioctl(*fd, SNDCTL_DSP_CHANNELS, &channels) < 0)
{
close(*fd);
free(fd);
return NULL;
}
if ( ioctl(*fd, SNDCTL_DSP_SETFMT, &format) < 0 )
if (ioctl(*fd, SNDCTL_DSP_SETFMT, &format) < 0)
{
close(*fd);
free(fd);
return NULL;
}
if ( ioctl(*fd, SNDCTL_DSP_SPEED, &rate) < 0 )
if (ioctl(*fd, SNDCTL_DSP_SPEED, &rate) < 0)
{
close(*fd);
free(fd);
@@ -83,13 +100,13 @@ static ssize_t __oss_write(void* data, const void* buf, size_t size)
{
int *fd = data;
if ( size == 0 )
if (size == 0)
return 0;
ssize_t ret;
if ( (ret = write(*fd, buf, size)) <= 0 )
if ((ret = write(*fd, buf, size)) <= 0)
{
if ( (fcntl(*fd, F_GETFL) & O_NONBLOCK) && errno == EAGAIN )
if ((fcntl(*fd, F_GETFL) & O_NONBLOCK) && errno == EAGAIN)
return 0;
return -1;
}
@@ -139,9 +156,4 @@ const audio_driver_t audio_oss = {
.free = __oss_free,
.ident = "oss"
};
+1 -13
View File
@@ -59,18 +59,6 @@ static void __pulse_free(void *data)
}
}
static inline uint8_t is_little_endian(void)
{
union
{
uint16_t x;
uint8_t y[2];
} u;
u.x = 1;
return u.y[0];
}
static void context_state_cb(pa_context *c, void *data)
{
pa_t *pa = data;
@@ -115,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, int rate, int latency)
static void* __pulse_init(const char* device, unsigned rate, unsigned latency)
{
pa_t *pa = calloc(1, sizeof(*pa));
if (!pa)
+1 -1
View File
@@ -30,7 +30,7 @@ typedef struct
bool nonblocking;
} roar_t;
static void* __roar_init(const char* device, int rate, int latency)
static void* __roar_init(const char* device, unsigned rate, unsigned latency)
{
int err;
roar_t *roar = calloc(1, sizeof(roar_t));
+11 -11
View File
@@ -19,7 +19,7 @@
#include "driver.h"
#include <stdlib.h>
#include <rsound.h>
#include "buffer.h"
#include "fifo_buffer.h"
#include <stdbool.h>
#include "SDL.h"
@@ -29,7 +29,7 @@ typedef struct rsd
bool nonblock;
volatile bool has_error;
rsound_fifo_buffer_t *buffer;
fifo_buffer_t *buffer;
SDL_mutex *cond_lock;
SDL_cond *cond;
@@ -39,9 +39,9 @@ static ssize_t audio_cb(void *data, size_t bytes, void *userdata)
{
rsd_t *rsd = userdata;
size_t avail = rsnd_fifo_read_avail(rsd->buffer);
size_t avail = fifo_read_avail(rsd->buffer);
size_t write_size = bytes > avail ? avail : bytes;
rsnd_fifo_read(rsd->buffer, data, write_size);
fifo_read(rsd->buffer, data, write_size);
SDL_CondSignal(rsd->cond);
return write_size;
@@ -54,7 +54,7 @@ static void err_cb(void *userdata)
SDL_CondSignal(rsd->cond);
}
static void* __rsd_init(const char* device, int rate, int latency)
static void* __rsd_init(const char* device, unsigned rate, unsigned latency)
{
rsd_t *rsd = calloc(1, sizeof(rsd_t));
if ( rsd == NULL )
@@ -71,7 +71,7 @@ static void* __rsd_init(const char* device, int rate, int latency)
rsd->cond_lock = SDL_CreateMutex();
rsd->cond = SDL_CreateCond();
rsd->buffer = rsnd_fifo_new(1024 * 4);
rsd->buffer = fifo_new(1024 * 4);
int channels = 2;
int format = RSD_S16_NE;
@@ -108,9 +108,9 @@ static ssize_t __rsd_write(void* data, const void* buf, size_t size)
if (rsd->nonblock)
{
rsd_callback_lock(rsd->rd);
size_t avail = rsnd_fifo_write_avail(rsd->buffer);
size_t avail = fifo_write_avail(rsd->buffer);
size_t write_amt = avail > size ? size : avail;
rsnd_fifo_write(rsd->buffer, buf, write_amt);
fifo_write(rsd->buffer, buf, write_amt);
rsd_callback_unlock(rsd->rd);
return write_amt;
}
@@ -120,7 +120,7 @@ static ssize_t __rsd_write(void* data, const void* buf, size_t size)
while (written < size && !rsd->has_error)
{
rsd_callback_lock(rsd->rd);
size_t avail = rsnd_fifo_write_avail(rsd->buffer);
size_t avail = fifo_write_avail(rsd->buffer);
if (avail == 0)
{
@@ -135,7 +135,7 @@ static ssize_t __rsd_write(void* data, const void* buf, size_t size)
else
{
size_t write_amt = size - written > avail ? avail : size - written;
rsnd_fifo_write(rsd->buffer, (const char*)buf + written, write_amt);
fifo_write(rsd->buffer, (const char*)buf + written, write_amt);
rsd_callback_unlock(rsd->rd);
written += write_amt;
}
@@ -174,7 +174,7 @@ static void __rsd_free(void *data)
rsd_stop(rsd->rd);
rsd_free(rsd->rd);
rsnd_fifo_free(rsd->buffer);
fifo_free(rsd->buffer);
SDL_DestroyMutex(rsd->cond_lock);
SDL_DestroyCond(rsd->cond);
+12 -17
View File
@@ -27,7 +27,7 @@
#include "SDL_audio.h"
#include "SDL_thread.h"
#include "general.h"
#include "buffer.h"
#include "fifo_buffer.h"
typedef struct sdl_audio
{
@@ -35,16 +35,16 @@ typedef struct sdl_audio
SDL_mutex *lock;
SDL_cond *cond;
rsound_fifo_buffer_t *buffer;
fifo_buffer_t *buffer;
} sdl_audio_t;
static void sdl_audio_cb(void *data, Uint8 *stream, int len)
{
sdl_audio_t *sdl = data;
size_t avail = rsnd_fifo_read_avail(sdl->buffer);
size_t avail = fifo_read_avail(sdl->buffer);
size_t write_size = len > avail ? avail : len;
rsnd_fifo_read(sdl->buffer, stream, write_size);
fifo_read(sdl->buffer, stream, write_size);
SDL_CondSignal(sdl->cond);
// If underrun, fill rest with silence.
@@ -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, int rate, int latency)
static void* sdl_audio_init(const char* device, unsigned rate, unsigned latency)
{
(void)device;
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
@@ -98,10 +98,10 @@ static void* sdl_audio_init(const char* device, int rate, int latency)
// Create a buffer twice as big as needed and prefill the buffer.
size_t bufsize = out.samples * 4 * sizeof(int16_t);
void *tmp = calloc(1, bufsize);
sdl->buffer = rsnd_fifo_new(bufsize);
sdl->buffer = fifo_new(bufsize);
if (tmp)
{
rsnd_fifo_write(sdl->buffer, tmp, bufsize);
fifo_write(sdl->buffer, tmp, bufsize);
free(tmp);
}
@@ -117,9 +117,9 @@ static ssize_t sdl_audio_write(void* data, const void* buf, size_t size)
if (sdl->nonblock)
{
SDL_LockAudio();
size_t avail = rsnd_fifo_write_avail(sdl->buffer);
size_t avail = fifo_write_avail(sdl->buffer);
size_t write_amt = avail > size ? size : avail;
rsnd_fifo_write(sdl->buffer, buf, write_amt);
fifo_write(sdl->buffer, buf, write_amt);
SDL_UnlockAudio();
ret = write_amt;
}
@@ -129,7 +129,7 @@ static ssize_t sdl_audio_write(void* data, const void* buf, size_t size)
while (written < size)
{
SDL_LockAudio();
size_t avail = rsnd_fifo_write_avail(sdl->buffer);
size_t avail = fifo_write_avail(sdl->buffer);
if (avail == 0)
{
@@ -141,7 +141,7 @@ static ssize_t sdl_audio_write(void* data, const void* buf, size_t size)
else
{
size_t write_amt = size - written > avail ? avail : size - written;
rsnd_fifo_write(sdl->buffer, (const char*)buf + written, write_amt);
fifo_write(sdl->buffer, (const char*)buf + written, write_amt);
SDL_UnlockAudio();
written += write_amt;
}
@@ -180,7 +180,7 @@ static void sdl_audio_free(void *data)
sdl_audio_t *sdl = data;
if (sdl)
{
rsnd_fifo_free(sdl->buffer);
fifo_free(sdl->buffer);
SDL_DestroyMutex(sdl->lock);
SDL_DestroyCond(sdl->cond);
}
@@ -196,9 +196,4 @@ const audio_driver_t audio_sdl = {
.free = sdl_audio_free,
.ident = "sdl"
};
+4 -1
View File
@@ -67,11 +67,14 @@ static bool init_lib(void)
return true;
}
static void* __xa_init(const char* device, int rate, int latency)
static void* __xa_init(const char* device, unsigned rate, unsigned latency)
{
if (!init_lib())
return NULL;
if (latency < 8)
latency = 8; // Do not allow shenanigans.
xa_t *xa = calloc(1, sizeof(xa_t));
if (xa == NULL)
return NULL;
+261
View File
@@ -0,0 +1,261 @@
/* 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 "cheats.h"
#include "sha256.h"
#include "dynamic.h"
#include "general.h"
#include "strl.h"
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <assert.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
struct cheat
{
char *desc;
bool state;
char *code;
};
struct cheat_manager
{
struct cheat *cheats;
unsigned ptr;
unsigned size;
unsigned buf_size;
};
static char* strcat_alloc(char *dest, const char *input)
{
size_t dest_len = dest ? strlen(dest) : 0;
size_t input_len = strlen(input);
size_t required_len = dest_len + input_len + 1;
char *output = realloc(dest, required_len);
assert(output);
if (dest)
strlcat(output, input, required_len);
else
strlcpy(output, input, required_len);
return output;
}
static void xml_grab_cheat(struct cheat *cht, xmlNodePtr ptr)
{
memset(cht, 0, sizeof(struct cheat));
bool first = true;
for (xmlNodePtr node = ptr; node; node = node->next)
{
if (strcmp((const char*)node->name, "description") == 0)
{
cht->desc = (char*)xmlNodeGetContent(node);
}
else if (strcmp((const char*)node->name, "code") == 0)
{
if (!first)
cht->code = strcat_alloc(cht->code, "+");
xmlChar *code = xmlNodeGetContent(node);
assert(code);
cht->code = strcat_alloc(cht->code, (const char*)code);
xmlFree(code);
first = false;
}
}
}
static void xml_grab_cheats(cheat_manager_t *handle, xmlNodePtr ptr)
{
xmlNodePtr node = NULL;
for (node = ptr; node; node = node->next)
{
if (strcmp((const char*)node->name, "name") == 0)
{
xmlChar *name = xmlNodeGetContent(node);
if (name)
{
SSNES_LOG("Found cheat for game: \"%s\"\n", name);
xmlFree(name);
}
}
else if (strcmp((const char*)node->name, "cheat") == 0)
{
if (handle->size == handle->buf_size)
{
handle->buf_size *= 2;
handle->cheats = realloc(handle->cheats, handle->buf_size * sizeof(struct cheat));
assert(handle->cheats);
}
xml_grab_cheat(&handle->cheats[handle->size++], node->children);
}
}
}
cheat_manager_t* cheat_manager_new(const char *path)
{
psnes_cheat_reset();
xmlParserCtxtPtr ctx = NULL;
xmlDocPtr doc = NULL;
cheat_manager_t *handle = calloc(1, sizeof(struct cheat_manager));
if (!handle)
return NULL;
handle->buf_size = 32;
handle->cheats = malloc(handle->buf_size * sizeof(struct cheat));
if (!handle->cheats)
{
handle->buf_size = 0;
goto error;
}
ctx = xmlNewParserCtxt();
if (!ctx)
goto error;
doc = xmlCtxtReadFile(ctx, path, NULL, 0);
if (!doc)
{
SSNES_ERR("Failed to parse XML file: %s\n", path);
goto error;
}
if (ctx->valid == 0)
{
SSNES_ERR("Cannot validate XML file: %s\n", path);
goto error;
}
xmlNodePtr head = xmlDocGetRootElement(doc);
xmlNodePtr cur = NULL;
for (cur = head; cur; cur = cur->next)
{
if (cur->type == XML_ELEMENT_NODE && strcmp((const char*)cur->name, "database") == 0)
break;
}
if (!cur)
goto error;
for (cur = cur->children; cur; cur = cur->next)
{
if (cur->type != XML_ELEMENT_NODE)
continue;
if (strcmp((const char*)cur->name, "cartridge") == 0)
{
xmlChar *sha256 = xmlGetProp(cur, (const xmlChar*)"sha256");
if (!sha256)
continue;
if (memcmp(sha256, g_extern.sha256, 64) == 0)
{
xmlFree(sha256);
break;
}
xmlFree(sha256);
}
}
if (!cur)
goto error;
xml_grab_cheats(handle, cur->children);
xmlFreeDoc(doc);
xmlFreeParserCtxt(ctx);
return handle;
error:
cheat_manager_free(handle);
if (doc)
xmlFreeDoc(doc);
if (ctx)
xmlFreeParserCtxt(ctx);
return NULL;
}
void cheat_manager_free(cheat_manager_t *handle)
{
if (!handle)
return;
if (handle->cheats)
{
for (unsigned i = 0; i < handle->size; i++)
{
xmlFree(handle->cheats[i].desc);
free(handle->cheats[i].code);
}
free(handle->cheats);
}
free(handle);
}
static void cheat_manager_update(cheat_manager_t *handle)
{
msg_queue_clear(g_extern.msg_queue);
char msg[256];
snprintf(msg, sizeof(msg), "Cheat: #%u [%s]: %s", handle->ptr, handle->cheats[handle->ptr].state ? "ON" : "OFF", handle->cheats[handle->ptr].desc);
msg_queue_push(g_extern.msg_queue, msg, 1, 180);
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_update(handle);
}
void cheat_manager_index_next(cheat_manager_t *handle)
{
handle->ptr = (handle->ptr + 1) % handle->size;
cheat_manager_update(handle);
}
void cheat_manager_index_prev(cheat_manager_t *handle)
{
if (handle->ptr == 0)
handle->ptr = handle->size - 1;
else
handle->ptr--;
cheat_manager_update(handle);
}
+30
View File
@@ -0,0 +1,30 @@
/* 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_CHEATS_H
#define __SSNES_CHEATS_H
typedef struct cheat_manager cheat_manager_t;
cheat_manager_t* cheat_manager_new(const char *path);
void cheat_manager_free(cheat_manager_t *handle);
void cheat_manager_index_next(cheat_manager_t *handle);
void cheat_manager_index_prev(cheat_manager_t *handle);
void cheat_manager_toggle(cheat_manager_t *handle);
#endif
+16
View File
@@ -223,6 +223,22 @@ bool config_get_int(config_file_t *conf, const char *key, int *in)
return false;
}
bool config_get_hex(config_file_t *conf, const char *key, unsigned *in)
{
struct entry_list *list = conf->entries;
while (list != NULL)
{
if (strcmp(key, list->key) == 0)
{
*in = strtol(list->value, NULL, 16);
return true;
}
list = list->next;
}
return false;
}
bool config_get_char(config_file_t *conf, const char *key, char *in)
{
struct entry_list *list = conf->entries;
+2
View File
@@ -42,6 +42,8 @@ void config_file_free(config_file_t *conf);
bool config_get_double(config_file_t *conf, const char *entry, double *in);
// Extracts an int from config file.
bool config_get_int(config_file_t *conf, const char *entry, int *in);
// Extracts an int from config file. (Hexadecimal)
bool config_get_hex(config_file_t *conf, const char *entry, unsigned *in);
// Extracts a single char. If value consists of several chars, this is an error.
bool config_get_char(config_file_t *conf, const char *entry, char *in);
// Extracts an allocated string in *in. This must be free()-d if this function succeeds.
+70 -85
View File
@@ -33,7 +33,7 @@
#ifdef HAVE_SDL
#include "SDL.h"
#else
#error HAVE_SDL is not defined!
#error "HAVE_SDL is not defined!"
#endif
#ifdef HAVE_SRC
@@ -44,6 +44,8 @@
///////////////// Drivers
#define VIDEO_GL 0
#define VIDEO_XVIDEO 11
#define VIDEO_SDL 13
#define VIDEO_EXT 14
////////////////////////
#define AUDIO_RSOUND 1
#define AUDIO_OSS 2
@@ -54,6 +56,9 @@
#define AUDIO_SDL 8
#define AUDIO_XAUDIO 9
#define AUDIO_PULSE 10
#define AUDIO_EXT 15
#define AUDIO_DSOUND 16
#define AUDIO_COREAUDIO 17
////////////////////////
#define INPUT_SDL 7
#define INPUT_X 12
@@ -63,6 +68,12 @@
#define VIDEO_DEFAULT_DRIVER VIDEO_GL
#elif defined(HAVE_XVIDEO)
#define VIDEO_DEFAULT_DRIVER VIDEO_XVIDEO
#elif defined(HAVE_SDL)
#define VIDEO_DEFAULT_DRIVER VIDEO_SDL
#elif defined(HAVE_DYLIB)
#define VIDEO_DEFAULT_DRIVER VIDEO_EXT
#else
#error "Need at least one video driver!"
#endif
#if defined(HAVE_ALSA)
@@ -73,8 +84,12 @@
#define AUDIO_DEFAULT_DRIVER AUDIO_OSS
#elif defined(HAVE_JACK)
#define AUDIO_DEFAULT_DRIVER AUDIO_JACK
#elif defined(HAVE_COREAUDIO)
#define AUDIO_DEFAULT_DRIVER AUDIO_COREAUDIO
#elif defined(HAVE_AL)
#define AUDIO_DEFAULT_DRIVER AUDIO_AL
#elif defined(HAVE_DSOUND)
#define AUDIO_DEFAULT_DRIVER AUDIO_DSOUND
#elif defined(HAVE_SDL)
#define AUDIO_DEFAULT_DRIVER AUDIO_SDL
#elif defined(HAVE_XAUDIO)
@@ -83,14 +98,18 @@
#define AUDIO_DEFAULT_DRIVER AUDIO_RSOUND
#elif defined(HAVE_ROAR)
#define AUDIO_DEFAULT_DRIVER AUDIO_ROAR
#elif defined(HAVE_DYLIB)
#define AUDIO_DEFAULT_DRIVER AUDIO_EXT
#else
#error Need at least one audio driver!
#error "Need at least one audio driver!"
#endif
#if defined(HAVE_SDL)
#define INPUT_DEFAULT_DRIVER INPUT_SDL
#elif defined(HAVE_XVIDEO)
#define INPUT_DEFAULT_DRIVER INPUT_X
#else
#error "Need at least one input driver!"
#endif
@@ -107,6 +126,12 @@ static const bool fullscreen = false; // To start in Fullscreen or not
static const unsigned fullscreen_x = 0; // Fullscreen resolution. A value of 0 uses the desktop resolution.
static const unsigned fullscreen_y = 0;
// Force 16-bit colors.
static const bool force_16bit = false;
// Forcibly disable composition. Only valid on Windows Vista/7 for now.
static const bool disable_composition = false;
// Video VSYNC (recommended)
static const bool vsync = true;
@@ -116,6 +141,9 @@ static const bool video_smooth = true;
// On resize and fullscreen, rendering area will stay 4:3
static const bool force_aspect = true;
// Crop overscanned frames (7/8 or 15/15 for interlaced frames).
static const bool crop_overscan = false;
// Font size for on-screen messages.
static const unsigned font_size = 48;
@@ -129,6 +157,12 @@ static const float fbo_scale_x = 2.0;
static const float fbo_scale_y = 2.0;
static const bool second_pass_smooth = true;
// Record video assuming game runs hi-res.
static const bool hires_record = false;
// Record post-filtered (CPU filter) video rather than raw SNES output.
static const bool post_filter_record = false;
#define SNES_ASPECT_RATIO (4.0/3)
////////////////
@@ -179,7 +213,7 @@ static const bool pause_nonactive = true;
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 = false;
static const bool netplay_client_swap_input = true;
////////////////////
@@ -196,19 +230,20 @@ static const bool netplay_client_swap_input = false;
// 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, 1, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_z, 0, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_s, 3, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_a, 2, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_q, 4, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_w, 5, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_LEFT, 11, AXIS_NEG(0) },
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_RIGHT, 12, AXIS_POS(0) },
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_UP, 13, AXIS_NEG(1) },
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_DOWN, 14, AXIS_POS(1) },
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_RETURN, 7, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_RSHIFT, 6, AXIS_NONE },
{ SSNES_FAST_FORWARD_KEY, SDLK_SPACE, 10, AXIS_NONE },
{ 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 },
@@ -223,78 +258,28 @@ static const struct snes_keybind snes_keybinds_1[] = {
{ 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 },
{ -1 }
};
// Player 2
static const struct snes_keybind snes_keybinds_2[] = {
// SNES button | keyboard key | js btn | js axis |
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_b, 1, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_v, 0, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_g, 3, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_f, 2, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_r, 4, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_t, 5, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_j, 11, AXIS_NEG(0) },
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_l, 12, AXIS_POS(0) },
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_i, 13, AXIS_NEG(1) },
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, 14, AXIS_POS(1) },
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_p, 6, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_o, 7, AXIS_NONE },
{ -1 }
};
// Player 3
static const struct snes_keybind snes_keybinds_3[] = {
// SNES button | keyboard key | js btn | js axis |
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_b, 1, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_v, 0, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_g, 3, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_f, 2, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_r, 4, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_t, 5, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_j, 11, AXIS_NEG(0) },
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_l, 12, AXIS_POS(0) },
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_i, 13, AXIS_NEG(1) },
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, 14, AXIS_POS(1) },
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_p, 6, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_o, 7, AXIS_NONE },
{ -1 }
};
// Player 4
static const struct snes_keybind snes_keybinds_4[] = {
// SNES button | keyboard key | js btn | js axis |
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_b, 1, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_v, 0, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_g, 3, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_f, 2, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_r, 4, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_t, 5, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_j, 11, AXIS_NEG(0) },
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_l, 12, AXIS_POS(0) },
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_i, 13, AXIS_NEG(1) },
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, 14, AXIS_POS(1) },
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_p, 6, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_o, 7, AXIS_NONE },
{ -1 }
};
// Player 5
static const struct snes_keybind snes_keybinds_5[] = {
// SNES button | keyboard key | js btn | js axis |
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_b, 1, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_v, 0, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_g, 3, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_f, 2, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_r, 4, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_t, 5, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_j, 11, AXIS_NEG(0) },
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_l, 12, AXIS_POS(0) },
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_i, 13, AXIS_NEG(1) },
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, 14, AXIS_POS(1) },
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_p, 6, AXIS_NONE },
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_o, 7, AXIS_NONE },
// 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 },
{ -1 }
};
+22 -4
View File
@@ -14,13 +14,19 @@ static const bool _sdl_supp = true;
static const bool _sdl_supp = false;
#endif
#ifdef HAVE_XVIDEO
static const bool _xvideo_supp = true;
#else
static const bool _xvideo_supp = false;
#endif
#ifdef HAVE_ALSA
static const bool _alsa_supp = true;
#else
static const bool _alsa_supp = false;
#endif
#ifdef HAVE_OSS
#if defined(HAVE_OSS) || defined(HAVE_OSS_BSD)
static const bool _oss_supp = true;
#else
static const bool _oss_supp = false;
@@ -62,10 +68,10 @@ static const bool _xaudio_supp = true;
static const bool _xaudio_supp = false;
#endif
#ifdef HAVE_FILTER
static const bool _filter_supp = true;
#ifdef HAVE_DYLIB
static const bool _dylib_supp = true;
#else
static const bool _filter_supp = false;
static const bool _dylib_supp = false;
#endif
#ifdef HAVE_CG
@@ -80,6 +86,12 @@ static const bool _xml_supp = true;
static const bool _xml_supp = false;
#endif
#ifdef HAVE_SDL_IMAGE
static const bool _sdl_image_supp = true;
#else
static const bool _sdl_image_supp = false;
#endif
#ifdef HAVE_FBO
static const bool _fbo_supp = true;
#else
@@ -122,4 +134,10 @@ static const bool _netplay_supp = true;
static const bool _netplay_supp = false;
#endif
#ifdef HAVE_PYTHON
static const bool _python_supp = true;
#else
static const bool _python_supp = false;
#endif
#endif
+16
View File
@@ -40,6 +40,10 @@ Without this flag, the save state path will be inferred from the rom path name,
When rom is loaded from \fBstdin\fR, this flag is mandatory to define as no path can be inferred.
Do note that save states are bound to the libsnes implementation being used. Using a different libsnes could invalidate the save state file.
.TP
\fB--fullscreen, -f\fR
Always starts SSNES in fullscreen. Disregards settings in configuration file.
.TP
\fB--config PATH, -c PATH\fR
Sets the configuration file path. \fBssnes\fR will use this path to load the configuration file.
@@ -80,6 +84,9 @@ 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.
\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.
.TP
\fB--scope, -p\fR
Connects a Super Scope into port 2 of the SNES. It is controlled with your mouse.
@@ -131,5 +138,14 @@ Network port used for netplay. This defaults to 55435. This option affects both
Attempts to apply an UPS patch to the current ROM image. No files are altered.
If this flag is not specified, SSNES will look for a .ups file with same basename as ROM specified.
.TP
\fB--xml MAP, -X MAP\fR
Specifies path to XML memory map for the given ROM.
If this flag is not specified, SSNES will look for a .xml file with same basename as ROM specified.
.TP
\fB-D, --detach\fR
Detach from the current console. This is currently only relevant for Microsoft Windows.
.SH "SEE ALSO"
\fBssnes-joyconfig\fR(1)
+124 -17
View File
@@ -32,12 +32,15 @@ static const audio_driver_t *audio_drivers[] = {
#ifdef HAVE_ALSA
&audio_alsa,
#endif
#ifdef HAVE_OSS
#if defined(HAVE_OSS) || defined(HAVE_OSS_BSD)
&audio_oss,
#endif
#ifdef HAVE_RSOUND
&audio_rsound,
#endif
#ifdef HAVE_COREAUDIO
&audio_coreaudio,
#endif
#ifdef HAVE_AL
&audio_openal,
#endif
@@ -53,27 +56,37 @@ static const audio_driver_t *audio_drivers[] = {
#ifdef HAVE_XAUDIO
&audio_xa,
#endif
#ifdef HAVE_DSOUND
&audio_dsound,
#endif
#ifdef HAVE_PULSE
&audio_pulse,
#endif
#ifdef HAVE_DYLIB
&audio_ext,
#endif
};
static const video_driver_t *video_drivers[] = {
#ifdef HAVE_SDL
&video_gl,
&video_sdl,
#endif
#ifdef HAVE_XVIDEO
&video_xvideo,
#endif
#ifdef HAVE_DYLIB
&video_ext,
#endif
};
static const input_driver_t *input_drivers[] = {
#ifdef HAVE_SDL
&input_sdl,
#endif
//#ifdef HAVE_XVIDEO
// &input_x,
//#endif
#ifdef HAVE_XVIDEO
&input_x,
#endif
};
static void find_audio_driver(void)
@@ -125,7 +138,7 @@ static void find_input_driver(void)
SSNES_ERR("Couldn't find any input driver named \"%s\"\n", g_settings.input.driver);
fprintf(stderr, "Available input drivers are:\n");
for (int i = 0; i < sizeof(input_drivers) / sizeof(input_driver_t*); i++)
fprintf(stderr, "\t%s\n", video_drivers[i]->ident);
fprintf(stderr, "\t%s\n", input_drivers[i]->ident);
exit(1);
}
@@ -142,6 +155,71 @@ void uninit_drivers(void)
uninit_audio();
}
static void init_dsp_plugin(void)
{
if (!(*g_settings.audio.dsp_plugin))
return;
g_extern.audio_data.dsp_lib = dylib_load(g_settings.audio.dsp_plugin);
if (!g_extern.audio_data.dsp_lib)
{
SSNES_ERR("Failed to open DSP plugin: \"%s\" ...\n", g_settings.audio.dsp_plugin);
return;
}
const ssnes_dsp_plugin_t* (*SSNES_API_CALLTYPE plugin_init)(void) =
(const ssnes_dsp_plugin_t *(SSNES_API_CALLTYPE*)(void))dylib_proc(g_extern.audio_data.dsp_lib, "ssnes_dsp_plugin_init");
if (!plugin_init)
{
SSNES_ERR("Failed to find symbol \"ssnes_dsp_plugin_init\" in DSP plugin.\n");
goto error;
}
g_extern.audio_data.dsp_plugin = plugin_init();
if (!g_extern.audio_data.dsp_plugin)
{
SSNES_ERR("Failed to get a valid DSP plugin.\n");
goto error;
}
if (g_extern.audio_data.dsp_plugin->api_version != SSNES_DSP_API_VERSION)
{
SSNES_ERR("DSP plugin API mismatch! SSNES: %d, Plugin: %d\n", SSNES_DSP_API_VERSION, g_extern.audio_data.dsp_plugin->api_version);
goto error;
}
SSNES_LOG("Loaded DSP plugin: \"%s\"\n", g_extern.audio_data.dsp_plugin->ident ? g_extern.audio_data.dsp_plugin->ident : "Unknown");
const ssnes_dsp_info_t info = {
.input_rate = g_settings.audio.in_rate,
.output_rate = g_settings.audio.out_rate
};
g_extern.audio_data.dsp_handle = g_extern.audio_data.dsp_plugin->init(&info);
if (!g_extern.audio_data.dsp_handle)
{
SSNES_ERR("Failed to init DSP plugin.\n");
goto error;
}
return;
error:
if (g_extern.audio_data.dsp_lib)
dylib_close(g_extern.audio_data.dsp_lib);
g_extern.audio_data.dsp_plugin = NULL;
g_extern.audio_data.dsp_lib = NULL;
}
static void deinit_dsp_plugin(void)
{
if (g_extern.audio_data.dsp_lib && g_extern.audio_data.dsp_plugin)
{
g_extern.audio_data.dsp_plugin->free(g_extern.audio_data.dsp_handle);
dylib_close(g_extern.audio_data.dsp_lib);
}
}
#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
@@ -192,6 +270,8 @@ void init_audio(void)
g_extern.audio_data.data_ptr = 0;
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)));
init_dsp_plugin();
}
void uninit_audio(void)
@@ -217,9 +297,11 @@ void uninit_audio(void)
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;
deinit_dsp_plugin();
}
#ifdef HAVE_FILTER
#ifdef HAVE_DYLIB
static void init_filter(void)
{
if (g_extern.filter.active)
@@ -235,8 +317,12 @@ static void init_filter(void)
return;
}
g_extern.filter.psize = dylib_proc(g_extern.filter.lib, "filter_size");
g_extern.filter.prender = dylib_proc(g_extern.filter.lib, "filter_render");
g_extern.filter.psize =
(void (*)(unsigned*, unsigned*))dylib_proc(g_extern.filter.lib, "filter_size");
g_extern.filter.prender =
(void (*)(uint32_t*, uint32_t*,
unsigned, const uint16_t*,
unsigned, unsigned, unsigned))dylib_proc(g_extern.filter.lib, "filter_render");
if (!g_extern.filter.psize || !g_extern.filter.prender)
{
SSNES_ERR("Failed to find functions in filter...\n");
@@ -263,7 +349,7 @@ static void init_filter(void)
g_extern.filter.colormap = malloc(32768 * sizeof(uint32_t));
assert(g_extern.filter.colormap);
// Set up conversion map from 16-bit XBGR1555 to 32-bit RGBA.
// Set up conversion map from 16-bit XRGB1555 to 32-bit ARGB.
for (int i = 0; i < 32768; i++)
{
unsigned r = (i >> 10) & 31;
@@ -273,7 +359,7 @@ static void init_filter(void)
r = (r << 3) | (r >> 2);
g = (g << 3) | (g >> 2);
b = (b << 3) | (b >> 2);
g_extern.filter.colormap[i] = (r << 24) | (g << 16) | (b << 8);
g_extern.filter.colormap[i] = (r << 16) | (g << 8) | (b << 0);
}
}
#endif
@@ -318,7 +404,7 @@ static void deinit_shader_dir(void)
void init_video_input(void)
{
#ifdef HAVE_FILTER
#ifdef HAVE_DYLIB
init_filter();
#endif
@@ -335,9 +421,30 @@ void init_video_input(void)
if (g_extern.filter.active)
scale = g_extern.filter.scale;
unsigned width;
unsigned height;
if (g_settings.video.fullscreen)
{
width = g_settings.video.fullscreen_x;
height = g_settings.video.fullscreen_y;
}
else
{
if (g_settings.video.force_aspect)
{
width = 224 * g_settings.video.xscale * g_settings.video.aspect_ratio;
height = 224 * g_settings.video.yscale;
}
else
{
width = 256 * g_settings.video.xscale;
height = 224 * g_settings.video.yscale;
}
}
video_info_t video = {
.width = (g_settings.video.fullscreen) ? g_settings.video.fullscreen_x : (296 * g_settings.video.xscale),
.height = (g_settings.video.fullscreen) ? g_settings.video.fullscreen_y : (224 * g_settings.video.yscale),
.width = width,
.height = height,
.fullscreen = g_settings.video.fullscreen,
.vsync = g_settings.video.vsync,
.force_aspect = g_settings.video.force_aspect,
@@ -351,7 +458,7 @@ void init_video_input(void)
if ( driver.video_data == NULL )
{
SSNES_ERR("Cannot open video driver... Exiting ...\n");
SSNES_ERR("Cannot open video driver ... Exiting ...\n");
exit(1);
}
@@ -378,12 +485,12 @@ void init_video_input(void)
void uninit_video_input(void)
{
if ( driver.video_data && driver.video )
driver.video->free(driver.video_data);
if ( driver.input_data != driver.video_data && driver.input )
driver.input->free(driver.input_data);
if ( driver.video_data && driver.video )
driver.video->free(driver.video_data);
deinit_filter();
#ifdef HAVE_XML
+15 -4
View File
@@ -28,6 +28,7 @@
enum
{
SSNES_FAST_FORWARD_KEY = 0x666, // Hurr, durr
SSNES_FAST_FORWARD_HOLD_KEY,
SSNES_LOAD_STATE_KEY,
SSNES_SAVE_STATE_KEY,
SSNES_FULLSCREEN_TOGGLE_KEY,
@@ -42,6 +43,11 @@ enum
SSNES_RESET,
SSNES_SHADER_NEXT,
SSNES_SHADER_PREV,
SSNES_CHEAT_INDEX_PLUS,
SSNES_CHEAT_INDEX_MINUS,
SSNES_CHEAT_TOGGLE,
SSNES_SCREENSHOT,
SSNES_DSP_CONFIG
};
@@ -55,8 +61,8 @@ struct snes_keybind
typedef struct video_info
{
int width;
int height;
unsigned width;
unsigned height;
bool fullscreen;
bool vsync;
bool force_aspect;
@@ -67,7 +73,7 @@ typedef struct video_info
typedef struct audio_driver
{
void* (*init)(const char* device, int rate, int latency);
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);
@@ -108,7 +114,7 @@ typedef struct input_driver
typedef struct video_driver
{
void* (*init)(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.
@@ -151,8 +157,13 @@ extern const audio_driver_t audio_jack;
extern const audio_driver_t audio_sdl;
extern const audio_driver_t audio_xa;
extern const audio_driver_t audio_pulse;
extern const audio_driver_t audio_ext;
extern const audio_driver_t audio_dsound;
extern const audio_driver_t audio_coreaudio;
extern const video_driver_t video_gl;
extern const video_driver_t video_xvideo;
extern const video_driver_t video_sdl;
extern const video_driver_t video_ext;
extern const input_driver_t input_sdl;
extern const input_driver_t input_x;
////////////////////////////////////////////////
+51 -32
View File
@@ -18,6 +18,7 @@
#include "dynamic.h"
#include "general.h"
#include <string.h>
#include <assert.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -34,13 +35,13 @@
#ifdef HAVE_DYNAMIC
#define DLSYM(lib, x) dylib_proc(lib, #x)
#define SYM(x) do { \
p##x = DLSYM(lib_handle, 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)
#define OPT_SYM(x) do { \
p##x = DLSYM(lib_handle, x); \
#define OPT_SYM(type, x) do { \
p##x = (type)DLSYM(lib_handle, x); \
} while(0)
static dylib_t lib_handle = NULL;
@@ -57,6 +58,9 @@ void (*psnes_set_input_state)(snes_input_state_t);
void (*psnes_reset)(void);
void (*psnes_run)(void);
void (*psnes_cheat_reset)(void);
void (*psnes_cheat_set)(unsigned, bool, const char*);
const char *(*psnes_library_id)(void) = NULL;
unsigned (*psnes_library_revision_minor)(void);
unsigned (*psnes_library_revision_major)(void);
@@ -103,31 +107,38 @@ static void load_dynamic(void)
exit(1);
}
SYM(snes_init);
SYM(snes_set_video_refresh);
SYM(snes_set_audio_sample);
SYM(snes_set_input_poll);
SYM(snes_set_input_state);
OPT_SYM(snes_library_id);
SYM(snes_library_revision_minor);
SYM(snes_library_revision_major);
SYM(snes_reset);
SYM(snes_run);
SYM(snes_get_region);
SYM(snes_load_cartridge_normal);
SYM(snes_load_cartridge_super_game_boy);
SYM(snes_load_cartridge_bsx);
SYM(snes_load_cartridge_bsx_slotted);
SYM(snes_load_cartridge_sufami_turbo);
SYM(snes_set_controller_port_device);
SYM(snes_serialize_size);
SYM(snes_serialize);
SYM(snes_unserialize);
SYM(snes_set_cartridge_basename);
SYM(snes_get_memory_data);
SYM(snes_get_memory_size);
SYM(snes_unload_cartridge);
SYM(snes_term);
SYM(void (*)(void), snes_init);
SYM(void (*)(snes_video_refresh_t), snes_set_video_refresh);
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(unsigned (*)(void), snes_library_revision_minor);
SYM(unsigned (*)(void), snes_library_revision_major);
SYM(void (*)(void), snes_cheat_reset);
SYM(void (*)(unsigned, bool, const char*), snes_cheat_set);
SYM(void (*)(void), snes_reset);
SYM(void (*)(void), snes_run);
SYM(bool (*)(void), snes_get_region);
SYM(bool (*)(const char*, const uint8_t*, unsigned), snes_load_cartridge_normal);
SYM(bool (*)(const char*, const uint8_t*, unsigned,
const char*, const uint8_t*, unsigned), snes_load_cartridge_super_game_boy);
SYM(bool (*)(const char*, const uint8_t*, unsigned,
const char*, const uint8_t*, unsigned), snes_load_cartridge_bsx);
SYM(bool (*)(const char*, const uint8_t*, unsigned,
const char*, const uint8_t*, unsigned), snes_load_cartridge_bsx_slotted);
SYM(bool (*)(const char*, const uint8_t*, unsigned,
const char*, const uint8_t*, unsigned,
const char*, const uint8_t*, unsigned), snes_load_cartridge_sufami_turbo);
SYM(void (*)(bool, unsigned), snes_set_controller_port_device);
SYM(unsigned (*)(void), snes_serialize_size);
SYM(bool (*)(uint8_t*, unsigned), snes_serialize);
SYM(bool (*)(const uint8_t*, unsigned), snes_unserialize);
SYM(void (*)(const char*), snes_set_cartridge_basename);
SYM(uint8_t *(*)(unsigned), snes_get_memory_data);
SYM(unsigned (*)(unsigned), snes_get_memory_size);
SYM(void (*)(void), snes_unload_cartridge);
SYM(void (*)(void), snes_term);
}
#endif
@@ -145,6 +156,8 @@ static void set_statics(void)
SSYM(snes_set_input_state);
SSYM(snes_library_revision_minor);
SSYM(snes_library_revision_major);
SSYM(snes_cheat_reset);
SSYM(snes_cheat_set);
SSYM(snes_reset);
SSYM(snes_run);
SSYM(snes_get_region);
@@ -167,6 +180,9 @@ static void set_statics(void)
void init_dlsym(void)
{
// 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
if (strlen(g_settings.libsnes) > 0)
load_dynamic();
@@ -198,12 +214,15 @@ dylib_t dylib_load(const char *path)
#endif
}
void* dylib_proc(dylib_t lib, const char *proc)
function_t dylib_proc(dylib_t lib, const char *proc)
{
#ifdef _WIN32
void *sym = (void*)GetProcAddress(lib, proc);
function_t sym = (function_t)GetProcAddress(lib, proc);
#else
void *sym = dlsym(lib, proc);
// Dirty hack to workaround the non-legality of void* -> fn-pointer casts.
void *ptr_sym = dlsym(lib, proc);
function_t sym;
memcpy(&sym, &ptr_sym, sizeof(void*));
#endif
if (!sym)
+5 -1
View File
@@ -25,10 +25,11 @@ void init_dlsym(void);
void uninit_dlsym(void);
typedef void *dylib_t;
typedef void (*function_t)(void);
dylib_t dylib_load(const char *path);
void dylib_close(dylib_t lib);
void* dylib_proc(dylib_t lib, const char *proc);
function_t dylib_proc(dylib_t lib, const char *proc);
extern void (*psnes_init)(void);
@@ -37,6 +38,9 @@ extern void (*psnes_set_audio_sample)(snes_audio_sample_t);
extern void (*psnes_set_input_poll)(snes_input_poll_t);
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 unsigned (*psnes_library_revision_minor)(void);
extern unsigned (*psnes_library_revision_major)(void);
+11 -11
View File
@@ -13,9 +13,9 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "buffer.h"
#include "fifo_buffer.h"
struct rsound_fifo_buffer
struct fifo_buffer
{
char *buffer;
size_t bufsize;
@@ -23,9 +23,9 @@ struct rsound_fifo_buffer
size_t end;
};
rsound_fifo_buffer_t* rsnd_fifo_new(size_t size)
fifo_buffer_t* fifo_new(size_t size)
{
rsound_fifo_buffer_t *buf = calloc(1, sizeof(*buf));
fifo_buffer_t *buf = calloc(1, sizeof(*buf));
if (buf == NULL)
return NULL;
@@ -40,7 +40,7 @@ rsound_fifo_buffer_t* rsnd_fifo_new(size_t size)
return buf;
}
void rsnd_fifo_free(rsound_fifo_buffer_t* buffer)
void fifo_free(fifo_buffer_t* buffer)
{
assert(buffer);
assert(buffer->buffer);
@@ -49,7 +49,7 @@ void rsnd_fifo_free(rsound_fifo_buffer_t* buffer)
free(buffer);
}
size_t rsnd_fifo_read_avail(rsound_fifo_buffer_t* buffer)
size_t fifo_read_avail(fifo_buffer_t* buffer)
{
assert(buffer);
assert(buffer->buffer);
@@ -61,7 +61,7 @@ size_t rsnd_fifo_read_avail(rsound_fifo_buffer_t* buffer)
return end - first;
}
size_t rsnd_fifo_write_avail(rsound_fifo_buffer_t* buffer)
size_t fifo_write_avail(fifo_buffer_t* buffer)
{
assert(buffer);
assert(buffer->buffer);
@@ -74,12 +74,12 @@ size_t rsnd_fifo_write_avail(rsound_fifo_buffer_t* buffer)
return (buffer->bufsize - 1) - (end - first);
}
void rsnd_fifo_write(rsound_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(rsnd_fifo_write_avail(buffer) >= size);
assert(fifo_write_avail(buffer) >= size);
size_t first_write = size;
size_t rest_write = 0;
@@ -97,12 +97,12 @@ void rsnd_fifo_write(rsound_fifo_buffer_t* buffer, const void* in_buf, size_t si
}
void rsnd_fifo_read(rsound_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(rsnd_fifo_read_avail(buffer) >= size);
assert(fifo_read_avail(buffer) >= size);
size_t first_read = size;
size_t rest_read = 0;
+9 -9
View File
@@ -21,16 +21,16 @@
#include <stdio.h>
#include <string.h>
#ifndef RSD_FIFO_BUF_TYPEDEF
#define RSD_FIFO_BUF_TYPEDEF
typedef struct rsound_fifo_buffer rsound_fifo_buffer_t;
#ifndef FIFO_BUF_TYPEDEF
#define FIFO_BUF_TYPEDEF
typedef struct fifo_buffer fifo_buffer_t;
#endif
rsound_fifo_buffer_t* rsnd_fifo_new(size_t size);
void rsnd_fifo_write(rsound_fifo_buffer_t* buffer, const void* in_buf, size_t size);
void rsnd_fifo_read(rsound_fifo_buffer_t* buffer, void* in_buf, size_t size);
void rsnd_fifo_free(rsound_fifo_buffer_t* buffer);
size_t rsnd_fifo_read_avail(rsound_fifo_buffer_t* buffer);
size_t rsnd_fifo_write_avail(rsound_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
+104 -42
View File
@@ -24,6 +24,11 @@
#include "dynamic.h"
#include "movie.h"
#include "ups.h"
#include "strl.h"
#ifdef HAVE_XML
#include "sha256.h"
#endif
#ifdef _WIN32
#include <io.h>
@@ -35,7 +40,7 @@
#endif
// Generic file loader.
static ssize_t read_file(const char *path, void **buf)
ssize_t read_file(const char *path, void **buf)
{
void *rom_buf = NULL;
FILE *file = fopen(path, "rb");
@@ -46,7 +51,7 @@ static ssize_t read_file(const char *path, void **buf)
long len = ftell(file);
ssize_t rc = 0;
rewind(file);
rom_buf = malloc(len);
rom_buf = malloc(len + 1);
if (!rom_buf)
{
SSNES_ERR("Couldn't allocate memory!\n");
@@ -57,6 +62,9 @@ static ssize_t read_file(const char *path, void **buf)
SSNES_WARN("Didn't read whole file.\n");
*buf = rom_buf;
// Allow for easy reading of strings to be safe.
// Will only work with sane character formatting (Unix).
((char*)rom_buf)[len] = '\0';
fclose(file);
return rc;
@@ -68,6 +76,46 @@ error:
return -1;
}
// Reads file content as one string.
bool read_file_string(const char *path, char **buf)
{
*buf = NULL;
FILE *file = fopen(path, "r");
if (!file)
goto error;
fseek(file, 0, SEEK_END);
long len = ftell(file) + 2; // Takes account of being able to read in EOF and '\0' at end.
rewind(file);
*buf = calloc(len, sizeof(char));
if (!*buf)
goto error;
char *ptr = *buf;
while (ptr && !feof(file))
{
size_t bufsize = (size_t)(((ptrdiff_t)*buf + (ptrdiff_t)len) - (ptrdiff_t)ptr);
fgets(ptr, bufsize, file);
ptr = strchr(ptr, '\0');
}
ptr = strchr(ptr, EOF);
if (ptr) *ptr = '\0';
fclose(file);
return true;
error:
if (file)
fclose(file);
if (*buf)
free(*buf);
return false;
}
// Load SNES rom only. Applies a hack for headered ROMs.
static ssize_t read_rom_file(FILE* file, void** buf)
{
@@ -164,9 +212,9 @@ static ssize_t read_rom_file(FILE* file, void** buf)
}
}
else if (*g_extern.ups_name)
SSNES_LOG("Could not find UPS patch.\n");
SSNES_LOG("Could not find UPS patch in: \"%s\".\n", g_extern.ups_name);
// Remove SMC header if present.
// Remove copier header if present (512 first bytes).
if ((ret & 0x7fff) == 512)
{
memmove(ret_buf, ret_buf + 512, ret - 512);
@@ -174,6 +222,10 @@ static ssize_t read_rom_file(FILE* file, void** buf)
}
g_extern.cart_crc = crc32_calculate(ret_buf, ret);
#ifdef HAVE_XML
sha256_hash(g_extern.sha256, ret_buf, ret);
SSNES_LOG("SHA256 sum: %s\n", g_extern.sha256);
#endif
*buf = ret_buf;
return ret;
}
@@ -257,10 +309,24 @@ void save_ram_file(const char* path, int type)
size_t size = psnes_get_memory_size(type);
uint8_t *data = psnes_get_memory_data(type);
if ( data && size > 0 )
if (data && size > 0)
dump_to_file(path, data, size);
}
static char* load_xml_map(const char *path)
{
char *xml_buf = NULL;
if (*path)
{
if (!read_file_string(path, &xml_buf))
SSNES_LOG("Did not find XML memory map in \"%s\"\n", path);
else
SSNES_LOG("Found XML memory map in \"%s\"\n", path);
}
return xml_buf;
}
static bool load_sgb_rom(void)
{
void *rom_buf = NULL;
@@ -276,27 +342,25 @@ static bool load_sgb_rom(void)
goto error;
}
extra_rom = fopen(g_extern.gb_rom_path, "rb");
if (!extra_rom)
{
SSNES_ERR("Couldn't open GameBoy ROM!\n");
goto error;
}
if ((extra_rom_len = read_rom_file(extra_rom, &extra_rom_buf)) == -1)
if ((extra_rom_len = read_file(g_extern.gb_rom_path, &extra_rom_buf)) == -1)
{
SSNES_ERR("Cannot read GameBoy rom.\n");
goto error;
}
char *xml_buf = load_xml_map(g_extern.xml_name);
if (!psnes_load_cartridge_super_game_boy(
NULL, rom_buf, rom_len,
xml_buf, rom_buf, rom_len,
NULL, extra_rom_buf, extra_rom_len))
{
SSNES_ERR("Cannot load SGB/GameBoy rom.\n");
goto error;
}
if (xml_buf)
free(xml_buf);
if (g_extern.rom_file)
fclose(g_extern.rom_file);
if (extra_rom)
@@ -330,23 +394,18 @@ static bool load_bsx_rom(bool slotted)
goto error;
}
extra_rom = fopen(g_extern.bsx_rom_path, "rb");
if (!extra_rom)
{
SSNES_ERR("Couldn't open BSX game rom!\n");
goto error;
}
if ((extra_rom_len = read_rom_file(extra_rom, &extra_rom_buf)) == -1)
if ((extra_rom_len = read_file(g_extern.bsx_rom_path, &extra_rom_buf)) == -1)
{
SSNES_ERR("Cannot read BSX game rom.\n");
goto error;
}
char *xml_buf = load_xml_map(g_extern.xml_name);
if (slotted)
{
if (!psnes_load_cartridge_bsx_slotted(
NULL, rom_buf, rom_len,
xml_buf, rom_buf, rom_len,
NULL, extra_rom_buf, extra_rom_len))
{
SSNES_ERR("Cannot load BSX slotted rom.\n");
@@ -365,6 +424,9 @@ static bool load_bsx_rom(bool slotted)
}
}
if (xml_buf)
free(xml_buf);
if (g_extern.rom_file)
fclose(g_extern.rom_file);
if (extra_rom)
@@ -404,23 +466,18 @@ static bool load_sufami_rom(void)
{
if (strlen(roms[i]) > 0)
{
extra_rom[i] = fopen(roms[i], "rb");
if (!extra_rom[i])
if ((extra_rom_len[i] = read_file(roms[i], &extra_rom_buf[i])) == -1)
{
SSNES_ERR("Couldn't open BSX game rom!\n");
goto error;
}
if ((extra_rom_len[i] = read_rom_file(extra_rom[i], &extra_rom_buf[i])) == -1)
{
SSNES_ERR("Cannot read BSX game rom.\n");
SSNES_ERR("Cannot read Sufami game rom.\n");
goto error;
}
}
}
char *xml_buf = load_xml_map(g_extern.xml_name);
if (!psnes_load_cartridge_sufami_turbo(
NULL, rom_buf, rom_len,
xml_buf, rom_buf, rom_len,
NULL, extra_rom_buf[0], extra_rom_len[0],
NULL, extra_rom_buf[1], extra_rom_len[1]))
{
@@ -428,6 +485,8 @@ static bool load_sufami_rom(void)
goto error;
}
if (xml_buf)
free(xml_buf);
if (g_extern.rom_file)
fclose(g_extern.rom_file);
@@ -469,13 +528,18 @@ static bool load_normal_rom(void)
SSNES_LOG("ROM size: %d bytes\n", (int)rom_len);
if (!psnes_load_cartridge_normal(NULL, rom_buf, rom_len))
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);
return false;
}
if (xml_buf)
free(xml_buf);
free(rom_buf);
return true;
}
@@ -537,8 +601,8 @@ char** dir_list_new(const char *dir, const char *ext)
wchar_t wchar_buf[MAX_PATH + 1];
char utf8_buf[MAX_PATH + 3];
strncpy(utf8_buf, dir, MAX_PATH);
strcat(utf8_buf, "/*");
strlcpy(utf8_buf, dir, sizeof(utf8_buf));
strlcat(utf8_buf, "/*", sizeof(utf8_buf));
utf8_buf[MAX_PATH + 2] = '\0';
int ret = MultiByteToWideChar(CP_UTF8, 0, utf8_buf, strlen(utf8_buf), wchar_buf, MAX_PATH);
@@ -584,15 +648,13 @@ char** dir_list_new(const char *dir, const char *ext)
if (!dir_list[cur_ptr])
goto error;
strcpy(dir_list[cur_ptr], dir);
strlcpy(dir_list[cur_ptr], dir, final_off);
strlcat(dir_list[cur_ptr], "/", final_off);
#ifdef _WIN32
dir_list[cur_ptr][path_len] = '\\';
strcpy(&dir_list[cur_ptr][path_len + 1], utf8_buf);
strlcat(dir_list[cur_ptr], utf8_buf, final_off);
#else
dir_list[cur_ptr][path_len] = '/';
strcpy(&dir_list[cur_ptr][path_len + 1], entry->d_name);
strlcat(dir_list[cur_ptr], entry->d_name, final_off);
#endif
dir_list[cur_ptr][final_off - 1] = '\0';
cur_ptr++;
if (cur_ptr + 1 == cur_size) // Need to reserve for NULL.
+2
View File
@@ -26,6 +26,8 @@
#include <sys/types.h>
#include "general.h"
ssize_t read_file(const char *path, void **buf);
bool load_state(const char* path);
bool save_state(const char* path);
+50 -7
View File
@@ -29,6 +29,8 @@
#include "autosave.h"
#include "netplay.h"
#include "dynamic.h"
#include "cheats.h"
#include "audio/ext/ssnes_dsp.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -42,7 +44,7 @@
#define MAX_PLAYERS 5
#define MAX_BINDS 28 // Needs to be increased every time there are new binds added.
#define MAX_BINDS 34 // Needs to be increased every time there are new binds added.
#define SSNES_NO_JOYPAD 0xFFFF
enum ssnes_shader_type
@@ -67,6 +69,7 @@ struct settings
bool vsync;
bool smooth;
bool force_aspect;
bool crop_overscan;
float aspect_ratio;
char cg_shader_path[256];
char bsnes_shader_path[256];
@@ -85,6 +88,13 @@ struct settings
float msg_pos_x;
float msg_pos_y;
bool force_16bit;
bool disable_composition;
bool hires_record;
bool post_filter_record;
char external_driver[256];
} video;
struct
@@ -98,6 +108,9 @@ struct settings
unsigned latency;
bool sync;
int src_quality;
char dsp_plugin[256];
char external_driver[256];
} audio;
struct
@@ -110,6 +123,9 @@ struct settings
} input;
char libsnes[256];
char cheat_database[256];
char screenshot_directory[256];
bool rewind_enable;
unsigned rewind_buffer_size;
@@ -135,12 +151,14 @@ struct global
bool verbose;
bool audio_active;
bool video_active;
bool force_fullscreen;
bool has_mouse[2];
bool has_scope[2];
bool has_justifier;
bool has_justifiers;
bool has_multitap;
bool disconnect_device[2];
FILE *rom_file;
enum ssnes_game_type game_type;
@@ -164,6 +182,7 @@ struct global
char savefile_name_bsrm[512];
char savestate_name[256];
char ups_name[512];
char xml_name[512];
unsigned state_slot;
@@ -185,6 +204,10 @@ struct global
float *outsamples;
int16_t *conv_outsamples;
dylib_t dsp_lib;
const ssnes_dsp_plugin_t *dsp_plugin;
void *dsp_handle;
} audio_data;
struct
@@ -243,6 +266,14 @@ struct global
size_t size;
size_t ptr;
} shader_dir;
char sha256[64 + 1];
bool do_screenshot;
#ifdef HAVE_XML
cheat_manager_t *cheat;
#endif
};
void parse_config(void);
@@ -250,19 +281,19 @@ void parse_config(void);
extern struct settings g_settings;
extern struct global g_extern;
#define SSNES_LOG(msg, args...) do { \
#define SSNES_LOG(...) do { \
if (g_extern.verbose) \
fprintf(stderr, "SSNES: " msg, ##args); \
fprintf(stderr, "SSNES: " __VA_ARGS__); \
fflush(stderr); \
} while(0)
#define SSNES_ERR(msg, args...) do { \
fprintf(stderr, "SSNES [ERROR] :: " msg, ##args); \
#define SSNES_ERR(...) do { \
fprintf(stderr, "SSNES [ERROR] :: " __VA_ARGS__); \
fflush(stderr); \
} while(0)
#define SSNES_WARN(msg, args...) do { \
fprintf(stderr, "SSNES [WARN] :: " msg, ##args); \
#define SSNES_WARN(...) do { \
fprintf(stderr, "SSNES [WARN] :: " __VA_ARGS__); \
fflush(stderr); \
} while(0)
@@ -278,6 +309,18 @@ static inline uint32_t next_pow2(uint32_t v)
return v;
}
static inline uint8_t is_little_endian(void)
{
union
{
uint16_t x;
uint8_t y[2];
} u;
u.x = 1;
return u.y[0];
}
#endif
+323
View File
@@ -0,0 +1,323 @@
/* 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/>.
*/
// Loader for external API plugins.
#define SSNES_DLL_IMPORT
#include "ext/ssnes_video.h"
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include "dynamic.h"
#include "general.h"
static bool g_input_dead = true;
static bool g_video_dead = true;
static dylib_t g_lib = NULL;
/////////// Input hook
typedef struct
{
const ssnes_input_driver_t *driver;
void *handle;
} input_ext_t;
static void* input_ext_init(void)
{
g_input_dead = false;
return calloc(1, sizeof(input_ext_t));
}
static void input_ext_poll(void *data)
{
input_ext_t *ext = data;
ext->driver->poll(ext->handle);
}
static int16_t input_ext_input_state(void *data, const struct snes_keybind **snes_keybinds, bool port, unsigned device, unsigned index, unsigned id)
{
input_ext_t *ext = data;
unsigned player = 0;
if (device == SNES_DEVICE_MULTITAP)
player = (port == SNES_PORT_1) ? 1 : index + 2;
else
player = (port == SNES_PORT_1) ? 1 : 2;
const struct snes_keybind *ssnes_bind = NULL;
for (unsigned i = 0; g_settings.input.binds[player - 1][i].id != -1; i++)
{
if (g_settings.input.binds[player - 1][i].id == id)
{
ssnes_bind = &g_settings.input.binds[player - 1][i];
break;
}
}
if (ssnes_bind)
{
struct ssnes_keybind bind = {
.key = ssnes_bind->key,
.joykey = ssnes_bind->joykey,
.joyaxis = ssnes_bind->joyaxis
};
return ext->driver->input_state(ext->handle, &bind, player);
}
else
return 0;
}
static bool input_ext_key_pressed(void *data, int key)
{
input_ext_t *ext = data;
const struct snes_keybind *ssnes_bind = NULL;
for (unsigned i = 0; g_settings.input.binds[0][i].id != -1; i++)
{
if (g_settings.input.binds[0][i].id == key)
{
ssnes_bind = &g_settings.input.binds[0][i];
break;
}
}
if (ssnes_bind)
{
struct ssnes_keybind bind = {
.key = ssnes_bind->key,
.joykey = ssnes_bind->joykey,
.joyaxis = ssnes_bind->joyaxis
};
return ext->driver->input_state(ext->handle, &bind, 1);
}
else
return false;
}
static void input_ext_free(void *data)
{
input_ext_t *ext = data;
if (ext)
{
if (ext->driver && ext->handle)
ext->driver->free(ext->handle);
if (g_video_dead)
{
dylib_close(g_lib);
g_lib = NULL;
}
g_input_dead = true;
free(ext);
}
}
static const input_driver_t input_ext = {
.init = input_ext_init,
.poll = input_ext_poll,
.input_state = input_ext_input_state,
.key_pressed = input_ext_key_pressed,
.free = input_ext_free,
.ident = "ext"
};
//////////// Video hook
typedef struct
{
const ssnes_video_driver_t *driver;
void *handle;
} ext_t;
static void video_ext_free(void *data)
{
ext_t *ext = data;
if (ext)
{
if (ext->driver && ext->handle)
ext->driver->free(ext->handle);
if (g_input_dead)
{
dylib_close(g_lib);
g_lib = NULL;
}
g_video_dead = true;
free(ext);
}
}
static bool video_ext_focus(void *data)
{
ext_t *ext = data;
return ext->driver->focus(ext->handle);
}
static bool video_ext_alive(void *data)
{
ext_t *ext = data;
return ext->driver->alive(ext->handle);
}
static void video_ext_set_nonblock_state(void *data, bool state)
{
ext_t *ext = data;
ext->driver->set_nonblock_state(ext->handle, state);
}
static bool video_ext_frame(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
{
ext_t *ext = data;
return ext->driver->frame(ext->handle, frame, width, height, pitch, msg);
}
static void* setup_input(ext_t *ext, const ssnes_input_driver_t *driver)
{
int joypad_index[5];
for (unsigned i = 0; i < 5; i++)
joypad_index[i] = g_settings.input.joypad_map[i] == SSNES_NO_JOYPAD ? -1 : g_settings.input.joypad_map[i];
void *handle = driver->init(joypad_index, g_settings.input.axis_threshold);
if (!handle)
return NULL;
input_ext_t *wrap_handle = input_ext.init();
if (!wrap_handle)
return NULL;
wrap_handle->handle = handle;
wrap_handle->driver = driver;
return wrap_handle;
}
static bool setup_video(ext_t *ext, const video_info_t *video, const input_driver_t **input, void **input_data)
{
SSNES_LOG("Loaded driver: \"%s\"\n", ext->driver->ident ? ext->driver->ident : "Unknown");
if (SSNES_GRAPHICS_API_VERSION != ext->driver->api_version)
{
SSNES_ERR("API version mismatch detected!\n");
SSNES_ERR("Required API version: %d, Library version: %d\n", SSNES_GRAPHICS_API_VERSION, ext->driver->api_version);
return false;
}
const char *cg_shader = NULL;
const char *xml_shader = NULL;
enum ssnes_shader_type type = g_settings.video.shader_type;
if ((type == SSNES_SHADER_CG || type == SSNES_SHADER_AUTO) && *g_settings.video.cg_shader_path)
cg_shader = g_settings.video.cg_shader_path;
else if ((type == SSNES_SHADER_BSNES || type == SSNES_SHADER_AUTO) && *g_settings.video.bsnes_shader_path)
xml_shader = g_settings.video.bsnes_shader_path;
ssnes_video_info_t info = {
.width = video->width,
.height = video->height,
.fullscreen = video->fullscreen,
.vsync = video->vsync,
.force_aspect = video->force_aspect,
.aspect_ratio = g_settings.video.aspect_ratio,
.smooth = video->smooth,
.input_scale = video->input_scale,
.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
};
const ssnes_input_driver_t *input_driver = NULL;
ext->handle = ext->driver->init(&info, &input_driver);
if (!ext->handle)
return false;
*input = input_driver ? &input_ext : NULL;
if (input_driver)
*input_data = setup_input(ext, input_driver);
else
*input_data = NULL;
return true;
}
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)
return NULL;
if (!(*g_settings.video.external_driver))
{
SSNES_ERR("External driver needs video_external_driver path to be set!\n");
goto error;
}
g_lib = dylib_load(g_settings.video.external_driver);
if (!g_lib)
{
SSNES_ERR("Failed to open library: \"%s\"\n", g_settings.video.external_driver);
goto error;
}
const ssnes_video_driver_t* (*video_init)(void) =
(const ssnes_video_driver_t *(*)(void))dylib_proc(g_lib, "ssnes_video_init");
if (!video_init)
{
SSNES_ERR("Couldn't find function ssnes_video_init in library ...\n");
goto error;
}
ext->driver = video_init();
if (!ext->driver)
{
SSNES_ERR("External driver returned invalid driver handle.\n");
goto error;
}
if (!setup_video(ext, video, input, input_data))
{
SSNES_ERR("Failed to start driver.\n");
goto error;
}
g_video_dead = false;
return ext;
error:
video_ext_free(ext);
return NULL;
}
const video_driver_t video_ext = {
.init = video_ext_init,
.frame = video_ext_frame,
.alive = video_ext_alive,
.set_nonblock_state = video_ext_set_nonblock_state,
.focus = video_ext_focus,
.free = video_ext_free,
.ident = "ext"
};
+251
View File
@@ -0,0 +1,251 @@
/////
// API header for external SSNES video and input plugins.
//
//
#ifndef __SSNES_VIDEO_DRIVER_H
#define __SSNES_VIDEO_DRIVER_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _WIN32
#ifdef SSNES_DLL_IMPORT
#define SSNES_API_EXPORT __declspec(dllimport)
#else
#define SSNES_API_EXPORT __declspec(dllexport)
#endif
#define SSNES_API_CALLTYPE __cdecl
#else
#define SSNES_API_EXPORT
#define SSNES_API_CALLTYPE
#endif
#define SSNES_GRAPHICS_API_VERSION 1
// Since we don't want to rely on C++ or C99 for a proper boolean type,
// and make sure return semantics are perfectly clear ... ;)
#ifndef SSNES_OK
#define SSNES_OK 1
#endif
#ifndef SSNES_ERROR
#define SSNES_ERROR 0
#endif
#ifndef SSNES_TRUE
#define SSNES_TRUE 1
#endif
#ifndef SSNES_FALSE
#define SSNES_FALSE 0
#endif
#define SSNES_COLOR_FORMAT_XRGB1555 0
#define SSNES_COLOR_FORMAT_ARGB8888 1
typedef struct ssnes_video_info
{
// Width of window.
// If fullscreen mode is requested,
// a width of 0 means the resolution of the desktop should be used.
unsigned width;
// Height of window.
// If fullscreen mode is requested,
// a height of 0 means the resolutiof the desktop should be used.
unsigned height;
// If true, start the window in fullscreen mode.
int fullscreen;
// If true, VSync should be enabled.
int vsync;
// If true, the output image should have the aspect ratio
// as set in aspect_ratio.
int force_aspect;
// Aspect ratio. Only takes effect if force_aspect is enabled.
float aspect_ratio;
// Requests that the image is smoothed,
// using bilinear filtering or otherwise.
// If this cannot be implemented efficiently, this can be disregarded.
// If smooth is false, nearest-neighbor scaling is requested.
int smooth;
// 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,
// 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.
// XRGB1555 format is 16-bit and has byte ordering: 0RRRRRGGGGGBBBBB,
// in native endian.
// ARGB8888 is AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB, native endian.
// Alpha channel should be disregarded.
int color_format;
// If non-NULL, requests the use of an XML shader.
// Can be disregarded.
const char *xml_shader;
// If non-NULL, requests the use of a Cg shader.
// Can be disregarded.
// If both are non-NULL,
// Cg or XML could be used at the discretion of the plugin.
const char *cg_shader;
// Requestes that a certain
// TTF font is used for rendering messages to the screen.
// Can be disregarded.
const char *ttf_font;
unsigned ttf_font_size;
} ssnes_video_info_t;
// Some convenience macros.
// Extract which axes to test for in negative or positive direction.
// May be equal to SSNES_NO_AXIS, which means testing should not occur.
#define SSNES_AXIS_NEG_GET(x) (((unsigned)(x) >> 16) & 0xFFFFU)
#define SSNES_AXIS_POS_GET(x) ((unsigned)(x) & 0xFFFFU)
// 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_BTN ((unsigned short)0xFFFFU)
// Masks to test on joykey which hat direction is to be tested for.
#define SSNES_HAT_UP_MASK (1 << 15)
#define SSNES_HAT_DOWN_MASK (1 << 14)
#define SSNES_HAT_LEFT_MASK (1 << 13)
#define SSNES_HAT_RIGHT_MASK (1 << 12)
#define SSNES_HAT_MAP(x, hat) ((x & ((1 << 12) - 1)) | hat)
#define SSNES_HAT_MASK (SSNES_HAT_UP_MASK | SSNES_HAT_DOWN_MASK | \
SSNES_HAT_LEFT_MASK | SSNES_HAT_RIGHT_MASK)
// Test this on the joykey. If true, we want to test for a joypad hat
// rather than a button.
#define SSNES_GET_HAT_DIR(x) (x & SSNES_HAT_MASK)
// Gets the joypad hat to be tested for.
// Only valid when SSNES_GET_HAT_DIR() returns true.
#define SSNES_GET_HAT(x) (x & (~SSNES_HAT_MASK))
// key, joykey and joyaxis are all checked at the same time.
// If any one of these are pressed, return 1 in state callback.
struct ssnes_keybind
{
// If analog_x is true, we request an analog device to be polled
// rather than normal keys.
// The returned value should be the delta of
// last frame and current frame in the X-axis.
int analog_x;
// If analog_y is true, we request an analog device to be polled
// rather than normal keys.
// The returned value should be the delta of
// last frame and current frame in the Y-axis.
int analog_y;
// Keyboard key. The key values use the SDL keysyms,
// which probably need to be transformed to the native format.
unsigned short key;
// Joypad key. Joypad POV (hats) are embedded into this key as well.
unsigned short joykey;
// Joypad axis. Negative and positive axes are embedded into this variable.
unsigned joyaxis;
};
typedef struct ssnes_input_driver
{
// Inits input driver.
// Joypad index denotes which joypads are desired for the various players.
// Should an entry be negative,
// do not open joypad for that player.
// Threshold states the minimum offset that a joypad axis
// has to be held for it to be registered.
// 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);
// Polls input. Called once every frame.
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,
unsigned player);
// Frees the input struct.
void (*free)(void* data);
// Human readable indentification string.
const char *ident;
} ssnes_input_driver_t;
typedef struct ssnes_video_driver
{
// Inits the video driver. Returns an opaque handle pointer to the driver.
// Returns NULL on error.
//
// 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,
const ssnes_input_driver_t **input);
// Updates frame on the screen.
// Frame can be either XRGB1555 or ARGB32 format
// depending on rgb32 setting in ssnes_video_info_t.
// Pitch is the distance in bytes between two scanlines in memory.
//
// When msg is non-NULL,
// it's a message that should be displayed to the user.
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);
// This must return false when the user exits the emulator.
int (*alive)(void *data);
// Does the window have focus?
int (*focus)(void *data);
// Frees the video driver.
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.
int api_version;
} ssnes_video_driver_t;
// Called by SSNES on startup to get a driver handle.
// This is NOT dynamically allocated.
SSNES_API_EXPORT const ssnes_video_driver_t* SSNES_API_CALLTYPE
ssnes_video_init(void);
#ifdef __cplusplus
}
#endif
#endif
+83 -7
View File
@@ -25,21 +25,26 @@ static float tv_to_fps(const struct timeval *tv, const struct timeval *new_tv, i
return frames/time;
}
static unsigned gl_frames = 0;
void gfx_window_title_reset(void)
{
gl_frames = 0;
}
bool gfx_window_title(char *buf, size_t size)
{
static int frames = 0;
static struct timeval tv;
struct timeval new_tv;
bool ret = false;
if (frames == 0)
if (gl_frames == 0)
{
gettimeofday(&tv, NULL);
snprintf(buf, size, g_extern.title_buf);
snprintf(buf, size, "%s", g_extern.title_buf);
ret = true;
}
if ((frames % 180) == 0 && frames > 0)
else if ((gl_frames % 180) == 0)
{
gettimeofday(&new_tv, NULL);
struct timeval tmp_tv = tv;
@@ -47,12 +52,83 @@ bool gfx_window_title(char *buf, size_t size)
float fps = tv_to_fps(&tmp_tv, &new_tv, 180);
snprintf(buf, size, "%s || FPS: %6.1f || Frames: %d", g_extern.title_buf, fps, frames);
snprintf(buf, size, "%s || FPS: %6.1f || Frames: %d", g_extern.title_buf, fps, gl_frames);
ret = true;
}
frames++;
gl_frames++;
return ret;
}
#ifdef _WIN32
#include <windows.h>
#include "dynamic.h"
// We only load this library once, so we let it be unloaded at application shutdown,
// since unloading it early seems to cause issues on some systems.
static dylib_t dwmlib = NULL;
static void gfx_dwm_shutdown(void)
{
if (dwmlib)
dylib_close(dwmlib);
}
void gfx_set_dwm(void)
{
static bool inited = false;
if (inited)
return;
inited = true;
dwmlib = dylib_load("dwmapi.dll");
if (!dwmlib)
{
SSNES_LOG("Did not find dwmapi.dll");
return;
}
atexit(gfx_dwm_shutdown);
HRESULT (WINAPI *mmcss)(BOOL) = (HRESULT (WINAPI*)(BOOL))dylib_proc(dwmlib, "DwmEnableMMCSS");
if (mmcss)
{
SSNES_LOG("Setting multimedia scheduling for DWM.\n");
mmcss(TRUE);
}
if (!g_settings.video.disable_composition)
return;
HRESULT (WINAPI *composition_enable)(UINT) = (HRESULT (WINAPI*)(UINT))dylib_proc(dwmlib, "DwmEnableComposition");
if (!composition_enable)
{
SSNES_ERR("Did not find DwmEnableComposition ...\n");
return;
}
HRESULT ret = composition_enable(0);
if (FAILED(ret))
SSNES_ERR("Failed to set composition state ...\n");
}
#endif
#include "SDL_syswm.h"
#include "SDL.h"
#if !defined(__APPLE__) && !defined(_WIN32)
void gfx_get_window_size(unsigned *width, unsigned *height)
{
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
SDL_GetWMInfo(&info);
XWindowAttributes target;
info.info.x11.lock_func();
XGetWindowAttributes(info.info.x11.display, info.info.x11.window,
&target);
info.info.x11.unlock_func();
*width = target.width;
*height = target.height;
}
#endif
+9
View File
@@ -22,5 +22,14 @@
#include <stdbool.h>
bool gfx_window_title(char *buf, size_t size);
void gfx_window_title_reset(void);
#ifdef _WIN32
void gfx_set_dwm(void);
#endif
#if !defined(__APPLE__) && !defined(_WIN32)
void gfx_get_window_size(unsigned *width, unsigned *height);
#endif
#endif
+316 -102
View File
@@ -52,13 +52,23 @@
#include "fonts.h"
#endif
static const GLfloat vertexes[] = {
// Used for the last pass when rendering to the back buffer.
static const GLfloat vertexes_flipped[] = {
0, 0,
0, 1,
1, 1,
1, 0
};
// Used when rendering to an FBO.
// Texture coords have to be aligned with vertex coordinates.
static const GLfloat vertexes[] = {
0, 1,
0, 0,
1, 0,
1, 1
};
static const GLfloat tex_coords[] = {
0, 1,
0, 0,
@@ -66,12 +76,6 @@ static const GLfloat tex_coords[] = {
1, 1
};
static const GLfloat fbo_tex_coords[] = {
0, 0,
0, 1,
1, 1,
1, 0
};
#ifdef HAVE_FBO
#ifdef _WIN32
@@ -81,7 +85,7 @@ static PFNGLFRAMEBUFFERTEXTURE2DPROC pglFramebufferTexture2D = NULL;
static PFNGLCHECKFRAMEBUFFERSTATUSPROC pglCheckFramebufferStatus = NULL;
static PFNGLDELETEFRAMEBUFFERSPROC pglDeleteFramebuffers = NULL;
#define LOAD_SYM(sym) if (!p##sym) p##sym = ((void*)SDL_GL_GetProcAddress(#sym))
#define LOAD_SYM(sym) if (!p##sym) { SDL_SYM_WRAP(p##sym, #sym) }
static bool load_fbo_proc(void)
{
LOAD_SYM(glGenFramebuffers);
@@ -103,14 +107,33 @@ static bool load_fbo_proc(void) { return true; }
#endif
#endif
#if (defined(HAVE_XML) || defined(HAVE_CG)) && defined(_WIN32)
PFNGLCLIENTACTIVETEXTUREPROC pglClientActiveTexture = NULL;
PFNGLACTIVETEXTUREPROC pglActiveTexture = NULL;
static inline bool load_gl_proc(void)
{
LOAD_SYM(glClientActiveTexture);
LOAD_SYM(glActiveTexture);
return pglClientActiveTexture && pglActiveTexture;
}
#else
static inline bool load_gl_proc(void) { return true; }
#endif
#define MAX_SHADERS 16
typedef struct gl
{
bool vsync;
GLuint texture;
GLuint texture[2];
unsigned tex_index; // For use with PREV.
struct gl_tex_info prev_info;
GLuint tex_filter;
void *empty_buf;
unsigned frame_count;
#ifdef HAVE_FBO
// Render-to-texture, multipass shaders
GLuint fbo[MAX_SHADERS];
@@ -124,6 +147,7 @@ typedef struct gl
bool should_resize;
bool quitting;
bool fullscreen;
bool keep_aspect;
unsigned full_x, full_y;
@@ -132,15 +156,15 @@ typedef struct gl
unsigned win_height;
unsigned vp_width, vp_out_width;
unsigned vp_height, vp_out_height;
unsigned last_width;
unsigned last_height;
unsigned last_width[2];
unsigned last_height[2];
unsigned tex_w, tex_h;
GLfloat tex_coords[8];
#ifdef HAVE_FBO
GLfloat fbo_tex_coords[8];
#endif
GLenum texture_type; // XBGR1555 or RGBA
GLenum texture_type; // XBGR1555 or ARGB
GLenum texture_fmt;
unsigned base_size; // 2 or 4
@@ -231,14 +255,24 @@ static void gl_shader_set_proj_matrix(void)
static void gl_shader_set_params(unsigned width, unsigned height,
unsigned tex_width, unsigned tex_height,
unsigned out_width, unsigned out_height)
unsigned out_width, unsigned out_height,
unsigned frame_count,
const struct gl_tex_info *info,
const struct gl_tex_info *prev_info,
const struct gl_tex_info *fbo_info, unsigned fbo_info_cnt)
{
#ifdef HAVE_CG
gl_cg_set_params(width, height, tex_width, tex_height, out_width, out_height);
gl_cg_set_params(width, height,
tex_width, tex_height,
out_width, out_height,
frame_count, info, prev_info, fbo_info, fbo_info_cnt);
#endif
#ifdef HAVE_XML
gl_glsl_set_params(width, height, tex_width, tex_height, out_width, out_height);
gl_glsl_set_params(width, height,
tex_width, tex_height,
out_width, out_height,
frame_count, info, prev_info, fbo_info, fbo_info_cnt);
#endif
}
@@ -308,7 +342,7 @@ static inline void gl_init_font(gl_t *gl, const char *font_path, unsigned font_s
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, gl->texture);
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
}
else
SSNES_WARN("Couldn't init font renderer with font \"%s\"...\n", font_path);
@@ -320,7 +354,15 @@ static inline void gl_init_font(gl_t *gl, const char *font_path, unsigned font_s
static void gl_init_fbo(gl_t *gl, unsigned width, unsigned height)
{
#ifdef HAVE_FBO
if (!g_settings.video.render_to_texture && gl_shader_num() <= 1)
if (!g_settings.video.render_to_texture && gl_shader_num() == 0)
return;
struct gl_fbo_scale scale, scale_last;
gl_shader_scale(1, &scale);
gl_shader_scale(gl_shader_num(), &scale_last);
// No need to use FBOs.
if (gl_shader_num() == 1 && !scale.valid && !g_settings.video.render_to_texture)
return;
if (!load_fbo_proc())
@@ -330,13 +372,12 @@ static void gl_init_fbo(gl_t *gl, unsigned width, unsigned height)
}
gl->fbo_pass = gl_shader_num() - 1;
struct gl_fbo_scale scale;
gl_shader_scale(gl_shader_num(), &scale);
if (scale.valid)
if (scale_last.valid)
gl->fbo_pass++;
gl_shader_scale(1, &scale);
if (gl->fbo_pass <= 0)
gl->fbo_pass = 1;
if (!scale.valid)
{
scale.scale_x = g_settings.video.fbo_scale_x;
@@ -385,9 +426,6 @@ static void gl_init_fbo(gl_t *gl, unsigned width, unsigned height)
SSNES_LOG("Creating FBO 0 @ %ux%u\n", gl->fbo_rect[0].width, gl->fbo_rect[0].height);
if (gl->fbo_pass <= 0)
gl->fbo_pass = 1;
for (int i = 1; i < gl->fbo_pass; i++)
{
gl_shader_scale(i + 1, &gl->fbo_scale[i]);
@@ -618,17 +656,85 @@ static void gl_render_msg(gl_t *gl, const char *msg)
// Go back to old rendering path.
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->tex_coords);
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes);
glBindTexture(GL_TEXTURE_2D, gl->texture);
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes_flipped);
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
glDisable(GL_BLEND);
#endif
}
static inline void set_lut_texture_coords(const GLfloat *coords)
{
#if defined(HAVE_XML) || defined(HAVE_CG)
// For texture images.
pglClientActiveTexture(GL_TEXTURE1);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), coords);
pglClientActiveTexture(GL_TEXTURE0);
#else
(void)coords;
#endif
}
static inline void set_texture_coords(GLfloat *coords, GLfloat xamt, GLfloat yamt)
{
coords[1] = yamt;
coords[4] = xamt;
coords[6] = xamt;
coords[7] = yamt;
}
static void check_window(gl_t *gl)
{
SDL_Event event;
// Search for events we care about ...
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
gl->quitting = true;
break;
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
}
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++;
#if defined(HAVE_XML) || defined(HAVE_CG)
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
#endif
#ifdef HAVE_FBO
// Render to texture in first pass.
@@ -685,7 +791,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
last_max_height = gl->fbo_rect[i].max_img_height;
}
glBindTexture(GL_TEXTURE_2D, gl->texture);
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
pglBindFramebuffer(GL_FRAMEBUFFER, gl->fbo[0]);
gl->render_to_tex = true;
set_viewport(gl, gl->fbo_rect[0].img_width, gl->fbo_rect[0].img_height, true);
@@ -732,7 +838,7 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
}
// Go back to what we're supposed to do, render to FBO #0 :D
glBindTexture(GL_TEXTURE_2D, gl->texture);
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
pglBindFramebuffer(GL_FRAMEBUFFER, gl->fbo[0]);
set_viewport(gl, gl->fbo_rect[0].img_width, gl->fbo_rect[0].img_height, true);
}
@@ -741,39 +847,56 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
#endif
}
glClear(GL_COLOR_BUFFER_BIT);
gl_shader_set_params(width, height, gl->tex_w, gl->tex_h, gl->vp_width, gl->vp_height);
if (width != gl->last_width || height != gl->last_height) // Res change. need to clear out texture.
if ((width != gl->last_width[gl->tex_index] || height != gl->last_height[gl->tex_index]) && gl->empty_buf) // Res change. need to clear out texture.
{
gl->last_width = width;
gl->last_height = height;
gl->last_width[gl->tex_index] = width;
gl->last_height[gl->tex_index] = height;
glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(pitch));
glPixelStorei(GL_UNPACK_ROW_LENGTH, gl->tex_w);
// Can we pass NULL here, hmm?
void *tmp = calloc(1, gl->tex_w * gl->tex_h * gl->base_size);
glTexSubImage2D(GL_TEXTURE_2D,
0, 0, 0, gl->tex_w, gl->tex_h, gl->texture_type,
gl->texture_fmt, tmp);
free(tmp);
gl->texture_fmt, gl->empty_buf);
GLfloat x = (GLfloat)width / gl->tex_w;
GLfloat y = (GLfloat)height / gl->tex_h;
GLfloat xamt = (GLfloat)width / gl->tex_w;
GLfloat yamt = (GLfloat)height / gl->tex_h;
gl->tex_coords[1] = y;
gl->tex_coords[4] = x;
gl->tex_coords[6] = x;
gl->tex_coords[7] = y;
//SSNES_LOG("Setting last rect: %ux%u\n", width, height);
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])
{
GLfloat xamt = (GLfloat)width / gl->tex_w;
GLfloat yamt = (GLfloat)height / gl->tex_h;
set_texture_coords(gl->tex_coords, xamt, yamt);
}
#endif
#ifdef HAVE_FBO
// Need to preserve the "flipped" state when in FBO too to have
// consistent texture coordinates.
if (gl->render_to_tex)
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes);
#endif
glPixelStorei(GL_UNPACK_ROW_LENGTH, pitch / gl->base_size);
glTexSubImage2D(GL_TEXTURE_2D,
0, 0, 0, width, height, gl->texture_type,
gl->texture_fmt, frame);
struct gl_tex_info tex_info = {
.tex = gl->texture[gl->tex_index],
.input_size = {width, height},
.tex_size = {gl->tex_w, gl->tex_h}
};
struct gl_tex_info fbo_tex_info[MAX_SHADERS];
unsigned fbo_tex_info_cnt = 0;
memcpy(tex_info.coord, gl->tex_coords, sizeof(gl->tex_coords));
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);
glDrawArrays(GL_QUADS, 0, 4);
#ifdef HAVE_FBO
@@ -785,20 +908,26 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
// It's kinda handy ... :)
const struct gl_fbo_rect *prev_rect;
const struct gl_fbo_rect *rect;
struct gl_tex_info *fbo_info;
// Calculate viewports, texture coordinates etc, and render all passes from FBOs, to another FBO.
for (int i = 1; i < gl->fbo_pass; i++)
{
prev_rect = &gl->fbo_rect[i - 1];
rect = &gl->fbo_rect[i];
fbo_info = &fbo_tex_info[i - 1];
GLfloat xamt = (GLfloat)prev_rect->img_width / prev_rect->width;
GLfloat yamt = (GLfloat)prev_rect->img_height / prev_rect->height;
gl->fbo_tex_coords[3] = yamt;
gl->fbo_tex_coords[4] = xamt;
gl->fbo_tex_coords[5] = yamt;
gl->fbo_tex_coords[6] = xamt;
set_texture_coords(gl->fbo_tex_coords, xamt, yamt);
fbo_info->tex = gl->fbo_texture[i - 1];
fbo_info->input_size[0] = prev_rect->img_width;
fbo_info->input_size[1] = prev_rect->img_height;
fbo_info->tex_size[0] = prev_rect->width;
fbo_info->tex_size[1] = prev_rect->height;
memcpy(fbo_info->coord, gl->fbo_tex_coords, sizeof(gl->fbo_tex_coords));
pglBindFramebuffer(GL_FRAMEBUFFER, gl->fbo[i]);
gl_shader_use(i + 1);
@@ -808,8 +937,14 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
// Render to FBO with certain size.
set_viewport(gl, rect->img_width, rect->img_height, true);
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_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);
glDrawArrays(GL_QUADS, 0, 4);
fbo_tex_info_cnt++;
}
// Render our last FBO texture directly to screen.
@@ -817,26 +952,34 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei
GLfloat xamt = (GLfloat)prev_rect->img_width / prev_rect->width;
GLfloat yamt = (GLfloat)prev_rect->img_height / prev_rect->height;
gl->fbo_tex_coords[3] = yamt;
gl->fbo_tex_coords[4] = xamt;
gl->fbo_tex_coords[5] = yamt;
gl->fbo_tex_coords[6] = xamt;
set_texture_coords(gl->fbo_tex_coords, xamt, yamt);
// Render our FBO texture to back buffer.
pglBindFramebuffer(GL_FRAMEBUFFER, 0);
gl_shader_use(gl->fbo_pass + 1);
glBindTexture(GL_TEXTURE_2D, gl->fbo_texture[gl->fbo_pass - 1]);
glClear(GL_COLOR_BUFFER_BIT);
gl->render_to_tex = false;
set_viewport(gl, gl->win_width, gl->win_height, false);
gl_shader_set_params(prev_rect->img_width, prev_rect->img_height, prev_rect->width, prev_rect->height, gl->vp_width, gl->vp_height);
glBindTexture(GL_TEXTURE_2D, gl->fbo_texture[gl->fbo_pass - 1]);
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);
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes_flipped);
glDrawArrays(GL_QUADS, 0, 4);
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), 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;
#endif
if (msg)
gl_render_msg(gl, msg);
@@ -856,7 +999,7 @@ static void gl_free(void *data)
gl_shader_deinit();
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDeleteTextures(1, &gl->texture);
glDeleteTextures(2, gl->texture);
#ifdef HAVE_FBO
if (gl->fbo_inited)
@@ -867,30 +1010,53 @@ static void gl_free(void *data)
#endif
SDL_QuitSubSystem(SDL_INIT_VIDEO);
if (gl->empty_buf)
free(gl->empty_buf);
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");
#ifdef _WIN32
#if defined(_WIN32)
static BOOL (APIENTRY *wgl_swap_interval)(int) = NULL;
if (!wgl_swap_interval) wgl_swap_interval = (BOOL (APIENTRY*)(int)) SDL_GL_GetProcAddress("wglSwapIntervalEXT");
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) glx_swap_interval = (int (*)(int))SDL_GL_GetProcAddress("glXSwapIntervalSGI");
if (!glx_swap_interval) glx_swap_interval = (int (*)(int))SDL_GL_GetProcAddress("glXSwapIntervalMESA");
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
}
}
static void* gl_init(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)
return NULL;
@@ -903,27 +1069,49 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, video->vsync ? 1 : 0);
if (!SDL_SetVideoMode(video->width, video->height, 0, SDL_OPENGL | SDL_RESIZABLE | (video->fullscreen ? SDL_FULLSCREEN : 0)))
if (!SDL_SetVideoMode(video->width, video->height, g_settings.video.force_16bit ? 16 : 0, SDL_OPENGL | SDL_RESIZABLE | (video->fullscreen ? SDL_FULLSCREEN : 0)))
return NULL;
gfx_window_title_reset();
char buf[128];
if (gfx_window_title(buf, sizeof(buf)))
SDL_WM_SetCaption(buf, NULL);
// Remove that ugly mouse :D
SDL_ShowCursor(SDL_DISABLE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#if (defined(HAVE_XML) || defined(HAVE_CG)) && defined(_WIN32)
// Win32 GL lib doesn't have some functions needed for XML shaders.
// Need to load dynamically :(
if (!load_gl_proc())
{
SDL_QuitSubSystem(SDL_INIT_VIDEO);
return NULL;
}
#endif
gl_t *gl = calloc(1, sizeof(gl_t));
if (!gl)
{
SDL_QuitSubSystem(SDL_INIT_VIDEO);
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("GL VSync has not been enabled!\n");
{
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_t *gl = calloc(1, sizeof(gl_t));
if (!gl)
return NULL;
gl->full_x = full_x;
gl->full_y = full_y;
@@ -948,15 +1136,16 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i
return NULL;
}
SSNES_LOG("GL: Loaded %u programs(s).\n", gl_shader_num());
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->vsync = video->vsync;
gl->keep_aspect = video->force_aspect;
// Apparently need to set viewport for passes when we aren't using FBOs.
gl_shader_use(0);
set_viewport(gl, gl->win_width, gl->win_height, false);
gl_shader_use(1);
set_viewport(gl, gl->win_width, gl->win_height, false);
@@ -966,8 +1155,8 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i
else
gl->tex_filter = video->smooth ? GL_LINEAR : GL_NEAREST;
gl->texture_type = video->rgb32 ? GL_RGBA : GL_BGRA;
gl->texture_fmt = video->rgb32 ? GL_UNSIGNED_INT_8_8_8_8 : GL_UNSIGNED_SHORT_1_5_5_5_REV;
gl->texture_type = GL_BGRA;
gl->texture_fmt = video->rgb32 ? GL_UNSIGNED_INT_8_8_8_8_REV : GL_UNSIGNED_SHORT_1_5_5_5_REV;
gl->base_size = video->rgb32 ? sizeof(uint32_t) : sizeof(uint16_t);
glEnable(GL_TEXTURE_2D);
@@ -976,48 +1165,63 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i
glColor4f(1, 1, 1, 1);
glClearColor(0, 0, 0, 1);
char buf[128];
if (gfx_window_title(buf, sizeof(buf)))
SDL_WM_SetCaption(buf, NULL);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glGenTextures(1, &gl->texture);
glGenTextures(2, gl->texture);
glBindTexture(GL_TEXTURE_2D, gl->texture);
for (unsigned i = 0; i < 2; i++)
{
glBindTexture(GL_TEXTURE_2D, gl->texture[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl->tex_filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl->tex_filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl->tex_filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl->tex_filter);
}
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes);
glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), vertexes_flipped);
memcpy(gl->tex_coords, tex_coords, sizeof(tex_coords));
glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), gl->tex_coords);
set_lut_texture_coords(tex_coords);
gl->tex_w = 256 * video->input_scale;
gl->tex_h = 256 * video->input_scale;
glTexImage2D(GL_TEXTURE_2D,
0, GL_RGBA, gl->tex_w, gl->tex_h, 0, gl->texture_type,
gl->texture_fmt, NULL);
// Empty buffer that we use to clear out the texture with on res change.
gl->empty_buf = calloc(gl->base_size, gl->tex_w * gl->tex_h);
gl->last_width = gl->tex_w;
gl->last_height = gl->tex_h;
for (unsigned i = 0; i < 2; i++)
{
glBindTexture(GL_TEXTURE_2D, gl->texture[i]);
glTexImage2D(GL_TEXTURE_2D,
0, GL_RGBA, gl->tex_w, gl->tex_h, 0, gl->texture_type,
gl->texture_fmt, gl->empty_buf ? gl->empty_buf : NULL);
}
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
for (unsigned i = 0; i < 2; 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));
// Hook up SDL input driver to get SDL_QUIT events and RESIZE.
sdl_input_t *sdl_input = input_sdl.init();
if (sdl_input)
{
sdl_input->quitting = &gl->quitting;
sdl_input->should_resize = &gl->should_resize;
sdl_input->new_width = &gl->win_width;
sdl_input->new_height = &gl->win_height;
*input = &input_sdl;
*input_data = sdl_input;
}
@@ -1025,7 +1229,7 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i
*input = NULL;
gl_init_font(gl, g_settings.video.font_path, g_settings.video.font_size);
if (!gl_check_error())
{
SDL_QuitSubSystem(SDL_INIT_VIDEO);
@@ -1052,7 +1256,13 @@ static bool gl_focus(void *data)
static bool gl_xml_shader(void *data, const char *path)
{
gl_t *gl = data;
gl_shader_deinit();
//if (!gl_check_error())
// SSNES_WARN("Error happened before deinit!\n");
//if (!gl_check_error())
// SSNES_WARN("Error happened in deinit!\n");
#ifdef HAVE_FBO
if (gl->fbo_inited)
@@ -1065,13 +1275,15 @@ static bool gl_xml_shader(void *data, const char *path)
gl->render_to_tex = false;
gl->fbo_pass = 0;
if (!gl_check_error())
SSNES_WARN("Failed to deinit FBO properly!\n");
glBindTexture(GL_TEXTURE_2D, gl->texture);
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
}
#endif
gl_shader_deinit();
//if (!gl_check_error())
// SSNES_WARN("Failed to deinit rendering path properly!\n");
if (!gl_glsl_init(path))
return false;
@@ -1079,6 +1291,8 @@ static bool gl_xml_shader(void *data, const char *path)
gl_init_fbo(gl, gl->tex_w, gl->tex_h);
// Apparently need to set viewport for passes when we aren't using FBOs.
gl_shader_use(0);
set_viewport(gl, gl->win_width, gl->win_height, false);
gl_shader_use(1);
set_viewport(gl, gl->win_width, gl->win_height, false);
+29
View File
@@ -19,6 +19,11 @@
#define __GL_COMMON_H
#include "general.h"
#include <assert.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef __APPLE__
#include <OpenGL/gl.h>
@@ -93,4 +98,28 @@ struct gl_fbo_scale
bool valid;
};
struct gl_tex_info
{
GLuint tex;
GLfloat input_size[2];
GLfloat tex_size[2];
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;
extern PFNGLACTIVETEXTUREPROC pglActiveTexture;
#else
#define pglClientActiveTexture glClientActiveTexture
#define pglActiveTexture glActiveTexture
#endif
#endif
+183
View File
@@ -0,0 +1,183 @@
/* 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/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "image.h"
#include "file.h"
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include "general.h"
#ifdef HAVE_SDL_IMAGE
#include "SDL_image.h"
bool texture_image_load(const char *path, struct texture_image *out_img)
{
SDL_Surface *img = IMG_Load(path);
if (!img)
return false;
out_img->width = img->w;
out_img->height = img->h;
size_t size = out_img->width * out_img->height * sizeof(uint32_t);
out_img->pixels = malloc(size);
if (!out_img->pixels)
{
SDL_FreeSurface(img);
return false;
}
const SDL_PixelFormat *fmt = img->format;
SSNES_LOG("SDL_image: %dx%d @ %d bpp\n", img->w, img->h, img->format->BitsPerPixel);
if (img->format->BitsPerPixel == 32)
{
for (unsigned y = 0; y < img->h; y++)
{
uint32_t *dst = out_img->pixels + y * img->w;
const uint32_t *src = (const uint32_t*)img->pixels + y * img->pitch / sizeof(uint32_t);
for (unsigned x = 0; x < img->w; x++)
{
uint32_t r = (src[x] & fmt->Rmask) >> fmt->Rshift;
uint32_t g = (src[x] & fmt->Gmask) >> fmt->Gshift;
uint32_t b = (src[x] & fmt->Bmask) >> fmt->Bshift;
uint32_t a = (src[x] & fmt->Amask) >> fmt->Ashift;
dst[x] = (r << 24) | (g << 16) | (b << 8) | a;
}
}
}
else if (img->format->BitsPerPixel == 24)
{
for (unsigned y = 0; y < img->h; y++)
{
uint32_t *dst = out_img->pixels + y * img->w;
const uint8_t *src = (const uint8_t*)img->pixels + y * img->pitch;
for (unsigned x = 0; x < img->w; x++)
{
// Correct?
uint32_t color = 0;
color |= src[3 * x + 0] << 0;
color |= src[3 * x + 1] << 8;
color |= src[3 * x + 2] << 16;
uint32_t r = (color & fmt->Rmask) >> fmt->Rshift;
uint32_t g = (color & fmt->Gmask) >> fmt->Gshift;
uint32_t b = (color & fmt->Bmask) >> fmt->Bshift;
dst[x] = (r << 24) | (g << 16) | (b << 8) | 0xff;
}
}
}
else
{
SSNES_ERR("8-bit and 16-bit image support are not implemented.\n");
SDL_FreeSurface(img);
return false;
}
SDL_FreeSurface(img);
return true;
}
#else
bool texture_image_load(const char *path, struct texture_image *out_img)
{
// TODO: Check more gracefully.
if (!strstr(path, ".tga"))
return false;
void *raw_buf = NULL;
ssize_t len = read_file(path, &raw_buf);
if (len < 0)
return false;
uint8_t *buf = raw_buf;
if (buf[2] != 2) // Uncompressed RGB
{
free(buf);
return false;
}
unsigned width = 0;
unsigned height = 0;
uint8_t info[6];
memcpy(info, buf + 12, 6);
width = info[0] + ((unsigned)info[1] * 256);
height = info[2] + ((unsigned)info[3] * 256);
unsigned bits = info[4];
SSNES_LOG("Loaded TGA: (%ux%u @ %u bpp)\n", width, height, bits);
unsigned size = width * height * sizeof(uint32_t);
out_img->pixels = malloc(size);
out_img->width = width;
out_img->height = height;
if (!out_img->pixels)
{
free(buf);
return false;
}
const uint8_t *tmp = buf + 18;
if (bits == 32)
{
for (unsigned i = 0; i < width * height; i++)
{
uint32_t r = tmp[i * 4 + 2];
uint32_t g = tmp[i * 4 + 1];
uint32_t b = tmp[i * 4 + 0];
uint32_t a = tmp[i * 4 + 3];
out_img->pixels[i] = (r << 24) | (g << 16) | (b << 8) | a;
}
}
else if (bits == 24)
{
for (unsigned i = 0; i < width * height; i++)
{
uint32_t r = tmp[i * 3 + 2];
uint32_t g = tmp[i * 3 + 1];
uint32_t b = tmp[i * 3 + 0];
uint32_t a = 0xff;
out_img->pixels[i] = (r << 24) | (g << 16) | (b << 8) | a;
}
}
else
{
free(buf);
free(out_img->pixels);
out_img->pixels = NULL;
return false;
}
free(buf);
return true;
}
#endif
+34
View File
@@ -0,0 +1,34 @@
/* 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_IMAGE_H
#define __SSNES_IMAGE_H
#include <stdint.h>
#include <stdbool.h>
struct texture_image
{
unsigned width;
unsigned height;
uint32_t *pixels;
};
bool texture_image_load(const char *path, struct texture_image* img);
#endif
+288
View File
@@ -0,0 +1,288 @@
/* 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 <Python.h>
#include <stdbool.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "dynamic.h"
#include "libsnes.hpp"
#include "py_state.h"
#include "general.h"
#include "strl.h"
#define PY_READ_FUNC_DECL(RAMTYPE) py_read_##RAMTYPE
#define PY_READ_FUNC(RAMTYPE) \
static PyObject* PY_READ_FUNC_DECL(RAMTYPE) (PyObject *self, PyObject *args) \
{ \
(void)self; \
\
const uint8_t *data = psnes_get_memory_data(SNES_MEMORY_##RAMTYPE); \
if (!data) \
{ \
Py_INCREF(Py_None); \
return Py_None; \
} \
unsigned max = psnes_get_memory_size(SNES_MEMORY_##RAMTYPE); \
\
unsigned addr; \
if (!PyArg_ParseTuple(args, "I", &addr)) \
return NULL; \
\
if (addr >= max || addr < 0) \
{ \
Py_INCREF(Py_None); \
return Py_None; \
} \
\
return PyLong_FromLong((long)data[addr]); \
}
PY_READ_FUNC(WRAM)
PY_READ_FUNC(VRAM)
PY_READ_FUNC(APURAM)
PY_READ_FUNC(CGRAM)
PY_READ_FUNC(OAM)
static PyObject* py_read_input(PyObject *self, PyObject *args)
{
(void)self;
if (!driver.input_data)
return PyBool_FromLong(0);
unsigned player;
unsigned key;
if (!PyArg_ParseTuple(args, "II", &player, &key))
return NULL;
if (player > MAX_PLAYERS || player < 1)
return NULL;
const struct snes_keybind *binds[MAX_PLAYERS];
for (int i = 0; i < MAX_PLAYERS; i++)
binds[i] = g_settings.input.binds[i];
int16_t res = driver.input->input_state(driver.input_data,
binds, player > 1,
player > 2 ? SNES_DEVICE_MULTITAP : SNES_DEVICE_JOYPAD,
player > 2 ? player - 2 : 0,
key);
return PyBool_FromLong((long)res);
}
static PyMethodDef SNESMethods[] = {
{ "read_wram", PY_READ_FUNC_DECL(WRAM), METH_VARARGS, "Read WRAM from SNES." },
{ "read_vram", PY_READ_FUNC_DECL(VRAM), METH_VARARGS, "Read VRAM from SNES." },
{ "read_apuram", PY_READ_FUNC_DECL(APURAM), METH_VARARGS, "Read APURAM from SNES." },
{ "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." },
{ NULL, NULL, 0, NULL }
};
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));
}
static PyModuleDef SNESModule = {
PyModuleDef_HEAD_INIT, "snes", NULL, -1, SNESMethods,
NULL, NULL, NULL, NULL
};
static PyObject* PyInit_SNES(void)
{
PyObject *mod = PyModule_Create(&SNESModule);
if (!mod)
return NULL;
py_set_attrs(mod);
return mod;
}
struct py_state
{
PyObject *main;
PyObject *dict;
PyObject *inst;
bool warned_ret;
bool warned_type;
};
static char *dupe_newline(const char *str)
{
if (!str)
return NULL;
unsigned size = strlen(str) + 2;
char *ret = malloc(size);
if (!ret)
return NULL;
strlcpy(ret, str, size);
ret[size - 2] = '\n';
ret[size - 1] = '\0';
return ret;
}
// Need to make sure that first-line indentation is 0. :(
static char* align_program(const char *program)
{
char *prog = strdup(program);
if (!prog)
return NULL;
size_t prog_size = strlen(program) + 1;
char *new_prog = calloc(1, prog_size);
if (!new_prog)
return NULL;
char *line = dupe_newline(strtok(prog, "\n"));
if (!line)
{
free(prog);
return NULL;
}
unsigned skip_chars = 0;
while (isblank(line[skip_chars]) && line[skip_chars])
skip_chars++;
while (line)
{
unsigned length = strlen(line);
unsigned skip_len = skip_chars > length ? length : skip_chars;
strlcat(new_prog, line + skip_len, prog_size);
free(line);
line = dupe_newline(strtok(NULL, "\n"));
}
free(prog);
return new_prog;
}
py_state_t *py_state_new(const char *script, bool is_file, const char *pyclass)
{
PyImport_AppendInittab("snes", &PyInit_SNES);
Py_Initialize();
py_state_t *handle = calloc(1, sizeof(*handle));
handle->main = PyImport_AddModule("__main__");
if (!handle->main)
goto error;
if (is_file)
{
FILE *file = fopen(script, "r");
if (!file)
goto error;
PyRun_SimpleFile(file, script);
fclose(file);
}
else
{
char *script_ = align_program(script);
if (script_)
{
PyRun_SimpleString(script_);
free(script_);
}
}
handle->dict = PyModule_GetDict(handle->main);
if (!handle->dict)
goto error;
PyObject *hook = PyDict_GetItemString(handle->dict, pyclass);
if (!hook)
goto error;
handle->inst = PyObject_CallFunction(hook, NULL);
if (!handle->inst)
goto error;
return handle;
error:
PyErr_Print();
py_state_free(handle);
return NULL;
}
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);
free(handle);
}
Py_Finalize();
}
int 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);
if (!ret)
{
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;
}
Py_DECREF(ret);
return retval;
}
+32
View File
@@ -0,0 +1,32 @@
/* 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_PY_STATE_H
#define __SSNES_PY_STATE_H
#include <stdint.h>
#include <stdbool.h>
typedef struct py_state py_state_t;
py_state_t *py_state_new(const char *program, bool is_file, const char *pyclass);
void py_state_free(py_state_t *handle);
int py_state_get(py_state_t *handle,
const char *id, unsigned frame_count);
#endif
+524
View File
@@ -0,0 +1,524 @@
/* 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 "SDL.h"
#include "driver.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "general.h"
#include "input/ssnes_sdl_input.h"
#include "gfx_common.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_FREETYPE
#include "fonts.h"
#endif
static void convert_15bit_15bit_direct(uint16_t *out, unsigned outpitch, const uint16_t *input, unsigned width, unsigned height, unsigned pitch, const SDL_PixelFormat *fmt);
static void convert_32bit_32bit_direct(uint32_t *out, unsigned outpitch, const uint32_t *input, unsigned width, unsigned height, unsigned pitch, const SDL_PixelFormat *fmt);
static void convert_15bit_15bit_shift(uint16_t *out, unsigned outpitch, const uint16_t *input, unsigned width, unsigned height, unsigned pitch, const SDL_PixelFormat *fmt);
static void convert_32bit_32bit_shift(uint32_t *out, unsigned outpitch, const uint32_t *input, unsigned width, unsigned height, unsigned pitch, const SDL_PixelFormat *fmt);
typedef struct sdl_video sdl_video_t;
struct sdl_video
{
SDL_Surface *screen, *buffer;
bool quitting;
bool rgb32;
bool upsample;
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;
#endif
};
static void sdl_gfx_free(void *data)
{
sdl_video_t *vid = data;
if (!vid)
return;
if (vid->buffer)
SDL_FreeSurface(vid->buffer);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
#ifdef HAVE_FREETYPE
if (vid->font)
font_renderer_free(vid->font);
#endif
free(vid);
}
static void sdl_init_font(sdl_video_t *vid, const char *font_path, unsigned font_size)
{
#ifdef HAVE_FREETYPE
if (*font_path)
{
vid->font = font_renderer_new(font_path, font_size);
if (!vid->font)
SSNES_WARN("Failed to init font.\n");
}
#else
(void)vid;
(void)font_path;
(void)font_size;
#endif
}
// Not very optimized, but hey :D
static void sdl_render_msg_15(sdl_video_t *vid, SDL_Surface *buffer, const char *msg, unsigned width, unsigned height, const SDL_PixelFormat *fmt)
{
#ifdef HAVE_FREETYPE
if (!vid->font)
return;
struct font_output_list out;
font_renderer_msg(vid->font, msg, &out);
struct font_output *head = out.head;
int base_x = g_settings.video.msg_pos_x * width;
int base_y = (1.0 - g_settings.video.msg_pos_y) * height;
unsigned rshift = fmt->Rshift;
unsigned gshift = fmt->Gshift;
unsigned bshift = fmt->Bshift;
while (head)
{
int rbase_x = base_x + head->off_x;
int rbase_y = base_y - head->off_y;
if (rbase_y >= 0)
{
for (int y = 0; y < head->height && (y + rbase_y) < height; y++)
{
if (rbase_x < 0)
continue;
const uint8_t *a = head->output + head->pitch * y;
uint16_t *out = (uint16_t*)buffer->pixels + (rbase_y - head->height + y) * (buffer->pitch >> 1) + rbase_x;
for (int x = 0; x < head->width && (x + rbase_x) < width; x++)
{
unsigned blend = a[x];
unsigned out_pix = out[x];
unsigned r = (out_pix >> rshift) & 0x1f;
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;
out[x] = (out_r << rshift) | (out_g << gshift) | (out_b << bshift);
}
}
}
head = head->next;
}
font_renderer_free_output(&out);
#else
(void)vid;
(void)buffer;
(void)msg;
(void)width;
(void)height;
#endif
}
static void sdl_render_msg_32(sdl_video_t *vid, SDL_Surface *buffer, const char *msg, unsigned width, unsigned height, const SDL_PixelFormat *fmt)
{
#ifdef HAVE_FREETYPE
if (!vid->font)
return;
struct font_output_list out;
font_renderer_msg(vid->font, msg, &out);
struct font_output *head = out.head;
int base_x = g_settings.video.msg_pos_x * width;
int base_y = (1.0 - g_settings.video.msg_pos_y) * height;
unsigned rshift = fmt->Rshift;
unsigned gshift = fmt->Gshift;
unsigned bshift = fmt->Bshift;
while (head)
{
int rbase_x = base_x + head->off_x;
int rbase_y = base_y - head->off_y;
if (rbase_y >= 0)
{
for (int y = 0; y < head->height && (y + rbase_y) < height; y++)
{
if (rbase_x < 0)
continue;
const uint8_t *a = head->output + head->pitch * y;
uint32_t *out = (uint32_t*)buffer->pixels + (rbase_y - head->height + y) * (buffer->pitch >> 2) + rbase_x;
for (int x = 0; x < head->width && (x + rbase_x) < width; x++)
{
unsigned blend = a[x];
unsigned out_pix = out[x];
unsigned r = (out_pix >> rshift) & 0xff;
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;
out[x] = (out_r << rshift) | (out_g << gshift) | (out_b << bshift);
}
}
}
head = head->next;
}
font_renderer_free_output(&out);
#else
(void)vid;
(void)buffer;
(void)msg;
(void)width;
(void)height;
#endif
}
static void* sdl_gfx_init(const video_info_t *video, const input_driver_t **input, void **input_data)
{
#ifdef _WIN32
gfx_set_dwm();
#endif
SDL_InitSubSystem(SDL_INIT_VIDEO);
sdl_video_t *vid = calloc(1, sizeof(*vid));
if (!vid)
return NULL;
const SDL_VideoInfo *video_info = SDL_GetVideoInfo();
assert(video_info);
unsigned full_x = video_info->current_w;
unsigned full_y = video_info->current_h;
SSNES_LOG("Detecting desktop resolution %ux%u.\n", full_x, full_y);
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));
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");
}
if (!vid->screen)
{
SSNES_ERR("Failed to init SDL surface: %s\n", SDL_GetError());
goto error;
}
SDL_ShowCursor(SDL_DISABLE);
const SDL_PixelFormat *fmt = vid->screen->format;
if (!g_settings.video.force_16bit && (video->rgb32 || vid->upsample))
{
SSNES_LOG("SDL: Creating 32-bit buffer.\n");
vid->buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, 256 * video->input_scale, 256 * 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,
fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
}
SSNES_LOG("[Debug]: SDL Pixel format: Rshift = %u, Gshift = %u, Bshift = %u\n",
(unsigned)fmt->Rshift, (unsigned)fmt->Gshift, (unsigned)fmt->Bshift);
if (!vid->buffer)
{
SSNES_ERR("SDL_CreateRGBSurface failed: %s\n", SDL_GetError());
goto error;
}
sdl_input_t *sdl_input = input_sdl.init();
if (sdl_input)
{
*input = &input_sdl;
*input_data = sdl_input;
}
else
*input = NULL;
vid->rgb32 = video->rgb32;
sdl_init_font(vid, g_settings.video.font_path, g_settings.video.font_size);
if (fmt->Rshift == 10 && fmt->Gshift == 5 && fmt->Bshift == 0)
{
SSNES_LOG("SDL: 15-bit format matches. Fast blit.\n");
vid->convert_15_func = convert_15bit_15bit_direct;
}
else
{
SSNES_LOG("SDL: 15-bit format does not match. Needs conversion.\n");
vid->convert_15_func = convert_15bit_15bit_shift;
}
if (fmt->Rshift == 16 && fmt->Gshift == 8 && fmt->Bshift == 0)
{
SSNES_LOG("SDL: 32-bit format matches. Fast blit.\n");
vid->convert_32_func = convert_32bit_32bit_direct;
}
else
{
SSNES_LOG("SDL: 32-bit format does not match. Needs conversion.\n");
vid->convert_32_func = convert_32bit_32bit_shift;
}
return vid;
error:
sdl_gfx_free(vid);
return NULL;
}
static inline uint16_t conv_pixel_32_15(uint32_t pix, const SDL_PixelFormat *fmt)
{
uint16_t r = ((pix & 0x00f80000) >> 19) << fmt->Rshift;
uint16_t g = ((pix & 0x0000f800) >> 11) << fmt->Gshift;
uint16_t b = ((pix & 0x000000f8) >> 3) << fmt->Bshift;
return r | g | b;
}
static inline uint32_t conv_pixel_15_32(uint16_t pix, const SDL_PixelFormat *fmt)
{
uint32_t r = ((pix >> 10) & 0x1f) << (fmt->Rshift + 3);
uint32_t g = ((pix >> 5) & 0x1f) << (fmt->Gshift + 3);
uint32_t b = ((pix >> 0) & 0x1f) << (fmt->Bshift + 3);
return r | g | b;
}
static void convert_32bit_15bit(uint16_t *out, unsigned outpitch, const uint32_t *input, unsigned width, unsigned height, unsigned pitch, const SDL_PixelFormat *fmt)
{
for (unsigned y = 0; y < height; y++)
{
for (unsigned x = 0; x < width; x++)
out[x] = conv_pixel_32_15(input[x], fmt);
out += outpitch >> 1;
input += pitch >> 2;
}
}
static void convert_15bit_32bit(uint32_t *out, unsigned outpitch, const uint16_t *input, unsigned width, unsigned height, unsigned pitch, const SDL_PixelFormat *fmt)
{
for (unsigned y = 0; y < height; y++)
{
for (unsigned x = 0; x < width; x++)
out[x] = conv_pixel_15_32(input[x], fmt);
out += outpitch >> 2;
input += pitch >> 1;
}
}
static void convert_15bit_15bit_direct(uint16_t *out, unsigned outpitch, const uint16_t *input, unsigned width, unsigned height, unsigned pitch, const SDL_PixelFormat *fmt)
{
for (unsigned y = 0; y < height; y++)
{
uint16_t *dest = out + ((y * outpitch) >> 1);
const uint16_t *src = input + ((y * pitch) >> 1);
memcpy(dest, src, width * sizeof(uint16_t));
}
(void)fmt;
}
static void convert_32bit_32bit_direct(uint32_t *out, unsigned outpitch, const uint32_t *input, unsigned width, unsigned height, unsigned pitch, const SDL_PixelFormat *fmt)
{
for (unsigned y = 0; y < height; y++)
{
uint32_t *dest = out + ((y * outpitch) >> 2);
const uint32_t *src = input + ((y * pitch) >> 2);
memcpy(dest, src, width * sizeof(uint32_t));
}
(void)fmt;
}
static void convert_15bit_15bit_shift(uint16_t *out, unsigned outpitch, const uint16_t *input, unsigned width, unsigned height, unsigned pitch, const SDL_PixelFormat *fmt)
{
for (unsigned y = 0; y < height; y++)
{
uint16_t *dest = out + ((y * outpitch) >> 1);
const uint16_t *src = input + ((y * pitch) >> 1);
for (unsigned x = 0; x < width; x++)
{
uint16_t color = src[x];
uint16_t r = ((color >> 10) & 0x1f) << fmt->Rshift;
uint16_t g = ((color >> 5) & 0x1f) << fmt->Gshift;
uint16_t b = ((color >> 0) & 0x1f) << fmt->Bshift;
dest[x] = r | g | b;
}
}
}
static void convert_32bit_32bit_shift(uint32_t *out, unsigned outpitch, const uint32_t *input, unsigned width, unsigned height, unsigned pitch, const SDL_PixelFormat *fmt)
{
for (unsigned y = 0; y < height; y++)
{
uint32_t *dest = out + ((y * outpitch) >> 2);
const uint32_t *src = input + ((y * pitch) >> 2);
for (unsigned x = 0; x < width; x++)
{
uint32_t color = src[x];
uint32_t r = ((color >> 16) & 0xff) << fmt->Rshift;
uint32_t g = ((color >> 8) & 0xff) << fmt->Gshift;
uint32_t b = ((color >> 0) & 0xff) << fmt->Bshift;
dest[x] = r | g | b;
}
}
}
static void check_window(sdl_video_t *vid)
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
vid->quitting = true;
break;
default:
break;
}
}
}
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);
// :(
// 15-bit -> 32-bit (Sometimes 15-bit won't work on "modern" OSes :\)
if (vid->upsample)
{
convert_15bit_32bit(vid->buffer->pixels, vid->buffer->pitch, frame, width, height, pitch, vid->screen->format);
}
// 15-bit -> 15-bit
else if (!vid->rgb32)
{
vid->convert_15_func(vid->buffer->pixels, vid->buffer->pitch, frame, width, height, pitch, vid->screen->format);
}
// 32-bit -> 15-bit
else if (vid->rgb32 && g_settings.video.force_16bit)
{
convert_32bit_15bit(vid->buffer->pixels, vid->buffer->pitch, frame, width, height, pitch, vid->screen->format);
}
// 32-bit -> 32-bit
else
{
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);
SDL_Rect src = {
.x = 0,
.y = 0,
.w = width,
.h = height
};
SDL_Rect dest = {
.x = 0,
.y = 0,
.w = vid->screen->w,
.h = vid->screen->h
};
SDL_SoftStretch(vid->buffer, &src, vid->screen, &dest);
char buf[128];
if (gfx_window_title(buf, sizeof(buf)))
SDL_WM_SetCaption(buf, NULL);
SDL_Flip(vid->screen);
return true;
}
static void sdl_gfx_set_nonblock_state(void *data, bool state)
{
(void)data; // Can SDL even do this?
(void)state;
}
static bool sdl_gfx_alive(void *data)
{
sdl_video_t *vid = data;
return !vid->quitting;
}
static bool sdl_gfx_focus(void *data)
{
(void)data;
return (SDL_GetAppState() & (SDL_APPINPUTFOCUS | SDL_APPACTIVE)) == (SDL_APPINPUTFOCUS | SDL_APPACTIVE);
}
const video_driver_t video_sdl = {
.init = sdl_gfx_init,
.frame = sdl_gfx_frame,
.alive = sdl_gfx_alive,
.set_nonblock_state = sdl_gfx_set_nonblock_state,
.focus = sdl_gfx_focus,
.free = sdl_gfx_free,
.ident = "sdl"
};
+873 -72
View File
File diff suppressed because it is too large. Load diff
+5 -1
View File
@@ -30,7 +30,11 @@ void gl_cg_set_proj_matrix(void);
void gl_cg_set_params(unsigned width, unsigned height,
unsigned tex_width, unsigned tex_height,
unsigned out_width, unsigned out_height);
unsigned out_width, unsigned out_height,
unsigned frame_count,
const struct gl_tex_info *info,
const struct gl_tex_info *prev_info,
const struct gl_tex_info *fbo_info, unsigned fbo_info_cnt);
void gl_cg_use(unsigned index);
+575 -37
View File
@@ -19,6 +19,9 @@
#include <string.h>
#include "general.h"
#include "shader_glsl.h"
#include "strl.h"
#include "snes_state.h"
#include "dynamic.h"
#ifdef __APPLE__
#include <OpenGL/gl.h>
@@ -37,6 +40,8 @@
#include <libxml/tree.h>
#include "gl_common.h"
#include "image.h"
#ifdef __APPLE__
#define pglCreateProgram glCreateProgram
@@ -58,6 +63,9 @@
#define pglGetProgramInfoLog glGetProgramInfoLog
#define pglDeleteProgram glDeleteProgram
#define pglGetAttachedShaders glGetAttachedShaders
#define pglGetAttribLocation glGetAttribLocation
#define pglEnableVertexAttribArray glEnableVertexAttribArray
#define pglVertexAttribPointer glVertexAttribPointer
#else
static PFNGLCREATEPROGRAMPROC pglCreateProgram = NULL;
static PFNGLUSEPROGRAMPROC pglUseProgram = NULL;
@@ -78,9 +86,14 @@ static PFNGLGETPROGRAMIVPROC pglGetProgramiv = NULL;
static PFNGLGETPROGRAMINFOLOGPROC pglGetProgramInfoLog = NULL;
static PFNGLDELETEPROGRAMPROC pglDeleteProgram = NULL;
static PFNGLGETATTACHEDSHADERSPROC pglGetAttachedShaders = NULL;
static PFNGLGETATTRIBLOCATIONPROC pglGetAttribLocation = NULL;
static PFNGLENABLEVERTEXATTRIBARRAYPROC pglEnableVertexAttribArray = NULL;
static PFNGLVERTEXATTRIBPOINTERPROC pglVertexAttribPointer = NULL;
#endif
#define MAX_PROGRAMS 16
#define MAX_TEXTURES 8
#define MAX_VARIABLES 256
enum filter_type
{
@@ -96,6 +109,17 @@ static struct gl_fbo_scale gl_scale[MAX_PROGRAMS];
static unsigned gl_num_programs = 0;
static unsigned active_index = 0;
static GLuint gl_teximage[MAX_TEXTURES];
static unsigned gl_teximage_cnt = 0;
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_class[64];
static xmlChar *gl_script_program = NULL;
struct shader_program
{
char *vertex;
@@ -273,6 +297,292 @@ static bool get_xml_attrs(struct shader_program *prog, xmlNodePtr ptr)
return true;
}
static bool get_texture_image(const char *shader_path, xmlNodePtr ptr)
{
if (gl_teximage_cnt >= MAX_TEXTURES)
{
SSNES_WARN("Too many texture images! Ignoring ...\n");
return true;
}
bool linear = true;
xmlChar *filename = xmlGetProp(ptr, (const xmlChar*)"file");
xmlChar *filter = xmlGetProp(ptr, (const xmlChar*)"filter");
xmlChar *id = xmlGetProp(ptr, (const xmlChar*)"id");
if (!id)
{
SSNES_ERR("Could not find ID in texture.\n");
goto error;
}
if (!filename)
{
SSNES_ERR("Could not find filename in texture.\n");
goto error;
}
if (filter && strcmp((const char*)filter, "nearest") == 0)
linear = false;
char tex_path[256];
strlcpy(tex_path, shader_path, sizeof(tex_path));
char *last = strrchr(tex_path, '/');
if (!last) last = strrchr(tex_path, '\\');
if (last) last[1] = '\0';
strlcat(tex_path, (const char*)filename, sizeof(tex_path));
struct texture_image img;
SSNES_LOG("Loading texture image from: \"%s\" ...\n", tex_path);
if (!texture_image_load(tex_path, &img))
{
SSNES_ERR("Failed to load texture image from: \"%s\"\n", tex_path);
goto error;
}
strlcpy(gl_teximage_uniforms[gl_teximage_cnt], (const char*)id, sizeof(gl_teximage_uniforms[0]));
glGenTextures(1, &gl_teximage[gl_teximage_cnt]);
pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 1);
glBindTexture(GL_TEXTURE_2D, gl_teximage[gl_teximage_cnt]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelStorei(GL_UNPACK_ROW_LENGTH, img.width);
glTexImage2D(GL_TEXTURE_2D,
0, GL_RGBA, img.width, img.height, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, img.pixels);
pglActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
free(img.pixels);
xmlFree(filename);
xmlFree(id);
if (filter)
xmlFree(filter);
gl_teximage_cnt++;
return true;
error:
if (filename)
xmlFree(filename);
if (filter)
xmlFree(filter);
if (filter)
xmlFree(id);
return false;
}
#ifdef HAVE_PYTHON
static bool get_script(const char *path, xmlNodePtr ptr)
{
if (*gl_tracker_script || gl_script_program)
{
SSNES_ERR("Script already imported.\n");
return false;
}
xmlChar *script_class = xmlGetProp(ptr, (const xmlChar*)"class");
if (script_class)
{
strlcpy(gl_tracker_script_class, (const char*)script_class, sizeof(gl_tracker_script_class));
xmlFree(script_class);
}
xmlChar *language = xmlGetProp(ptr, (const xmlChar*)"language");
if (!language || strcmp((const char*)language, "python") != 0)
{
SSNES_ERR("Script language is not Python!\n");
if (language)
xmlFree(language);
return false;
}
if (language)
xmlFree(language);
xmlChar *src = xmlGetProp(ptr, (const xmlChar*)"src");
if (src)
{
strlcpy(gl_tracker_script, path, sizeof(gl_tracker_script));
char *dir_ptr = strrchr(gl_tracker_script, '/');
if (!dir_ptr) dir_ptr = strrchr(gl_tracker_script, '\\');
if (dir_ptr) dir_ptr[1] = '\0';
strlcat(gl_tracker_script, (const char*)src, sizeof(gl_tracker_script));
xmlFree(src);
}
else
{
xmlChar *script = xmlNodeGetContent(ptr);
if (!script)
{
SSNES_ERR("No content in script!\n");
return false;
}
gl_script_program = script;
}
return true;
}
#endif
static bool get_import_value(xmlNodePtr ptr)
{
if (gl_tracker_info_cnt >= MAX_VARIABLES)
{
SSNES_ERR("Too many import variables ...\n");
return false;
}
xmlChar *id = xmlGetProp(ptr, (const xmlChar*)"id");
xmlChar *semantic = xmlGetProp(ptr, (const xmlChar*)"semantic");
xmlChar *wram = xmlGetProp(ptr, (const xmlChar*)"wram");
xmlChar *input = xmlGetProp(ptr, (const xmlChar*)"input_slot");
xmlChar *apuram = xmlGetProp(ptr, (const xmlChar*)"apuram");
xmlChar *vram = xmlGetProp(ptr, (const xmlChar*)"vram");
xmlChar *oam = xmlGetProp(ptr, (const xmlChar*)"oam");
xmlChar *cgram = xmlGetProp(ptr, (const xmlChar*)"cgram");
xmlChar *bitmask = xmlGetProp(ptr, (const xmlChar*)"mask");
if (!semantic || !id)
{
SSNES_ERR("No semantic or ID for import value!\n");
goto error;
}
enum snes_tracker_type tracker_type;
if (strcmp((const char*)semantic, "capture") == 0)
tracker_type = SSNES_STATE_CAPTURE;
else if (strcmp((const char*)semantic, "capture_previous") == 0)
tracker_type = SSNES_STATE_CAPTURE_PREV;
else if (strcmp((const char*)semantic, "transition") == 0)
tracker_type = SSNES_STATE_TRANSITION;
else if (strcmp((const char*)semantic, "transition_count") == 0)
tracker_type = SSNES_STATE_TRANSITION_COUNT;
else if (strcmp((const char*)semantic, "transition_previous") == 0)
tracker_type = SSNES_STATE_TRANSITION_PREV;
#ifdef HAVE_PYTHON
else if (strcmp((const char*)semantic, "python") == 0)
tracker_type = SSNES_STATE_PYTHON;
#endif
else
{
SSNES_ERR("Invalid semantic for import value.\n");
goto error;
}
enum snes_ram_type ram_type = SSNES_STATE_WRAM;
uint32_t addr = 0;
#ifdef HAVE_PYTHON
if (tracker_type != SSNES_STATE_PYTHON)
{
#endif
if (input)
{
unsigned slot = strtoul((const char*)input, NULL, 0);
switch (slot)
{
case 1:
ram_type = SSNES_STATE_INPUT_SLOT1;
break;
case 2:
ram_type = SSNES_STATE_INPUT_SLOT2;
break;
default:
SSNES_ERR("Invalid input slot for import!\n");
goto error;
}
}
else if (wram) { addr = strtoul((const char*)wram, NULL, 16); ram_type = SSNES_STATE_WRAM; }
else if (apuram) { addr = strtoul((const char*)apuram, NULL, 16); ram_type = SSNES_STATE_APURAM; }
else if (vram) { addr = strtoul((const char*)vram, NULL, 16); ram_type = SSNES_STATE_VRAM; }
else if (oam) { addr = strtoul((const char*)oam, NULL, 16); ram_type = SSNES_STATE_OAM; }
else if (cgram) { addr = strtoul((const char*)cgram, NULL, 16); ram_type = SSNES_STATE_CGRAM; }
else
{
SSNES_ERR("No RAM address specificed for import value.\n");
goto error;
}
#ifdef HAVE_PYTHON
}
#endif
int memtype = 0;
switch (ram_type)
{
case SSNES_STATE_WRAM:
memtype = SNES_MEMORY_WRAM;
break;
case SSNES_STATE_APURAM:
memtype = SNES_MEMORY_APURAM;
break;
case SSNES_STATE_VRAM:
memtype = SNES_MEMORY_VRAM;
break;
case SSNES_STATE_OAM:
memtype = SNES_MEMORY_OAM;
break;
case SSNES_STATE_CGRAM:
memtype = SNES_MEMORY_CGRAM;
break;
default:
memtype = SNES_MEMORY_WRAM;
}
if (addr >= psnes_get_memory_size(memtype))
{
SSNES_ERR("Address out of bounds.\n");
goto error;
}
unsigned mask_value = 0;
if (bitmask)
mask_value = strtoul((const char*)bitmask, NULL, 16);
strlcpy(gl_tracker_info[gl_tracker_info_cnt].id, (const char*)id, sizeof(gl_tracker_info[0].id));
gl_tracker_info[gl_tracker_info_cnt].addr = addr;
gl_tracker_info[gl_tracker_info_cnt].type = tracker_type;
gl_tracker_info[gl_tracker_info_cnt].ram_type = ram_type;
gl_tracker_info[gl_tracker_info_cnt].mask = mask_value;
gl_tracker_info_cnt++;
if (id) xmlFree(id);
if (semantic) xmlFree(semantic);
if (wram) xmlFree(wram);
if (input) xmlFree(input);
if (apuram) xmlFree(apuram);
if (vram) xmlFree(vram);
if (oam) xmlFree(oam);
if (cgram) xmlFree(cgram);
if (bitmask) xmlFree(bitmask);
return true;
error:
if (id) xmlFree(id);
if (semantic) xmlFree(semantic);
if (wram) xmlFree(wram);
if (input) xmlFree(input);
if (apuram) xmlFree(apuram);
if (vram) xmlFree(vram);
if (oam) xmlFree(oam);
if (cgram) xmlFree(cgram);
if (bitmask) xmlFree(bitmask);
return false;
}
static unsigned get_xml_shaders(const char *path, struct shader_program *prog, size_t size)
{
LIBXML_TEST_VERSION;
@@ -354,6 +664,32 @@ static unsigned get_xml_shaders(const char *path, struct shader_program *prog, s
}
num++;
}
else if (strcmp((const char*)cur->name, "texture") == 0)
{
if (!get_texture_image(path, cur))
{
SSNES_ERR("Texture image failed to load.\n");
goto error;
}
}
else if (strcmp((const char*)cur->name, "import") == 0)
{
if (!get_import_value(cur))
{
SSNES_ERR("Import value is invalid.\n");
goto error;
}
}
#ifdef HAVE_PYTHON
else if (strcmp((const char*)cur->name, "script") == 0)
{
if (!get_script(path, cur))
{
SSNES_ERR("Script is invalid.\n");
goto error;
}
}
#endif
}
if (num == 0)
@@ -376,8 +712,8 @@ error:
static void print_shader_log(GLuint obj)
{
int info_len = 0;
int max_len;
GLint info_len = 0;
GLint max_len;
pglGetShaderiv(obj, GL_INFO_LOG_LENGTH, &max_len);
@@ -390,8 +726,8 @@ static void print_shader_log(GLuint obj)
static void print_linker_log(GLuint obj)
{
int info_len = 0;
int max_len;
GLint info_len = 0;
GLint max_len;
pglGetProgramiv(obj, GL_INFO_LOG_LENGTH, &max_len);
@@ -402,13 +738,42 @@ static void print_linker_log(GLuint obj)
SSNES_LOG("Linker log: %s\n", info_log);
}
static bool compile_shader(GLuint shader, const char *program)
{
pglShaderSource(shader, 1, &program, 0);
pglCompileShader(shader);
GLint status;
pglGetShaderiv(shader, GL_COMPILE_STATUS, &status);
print_shader_log(shader);
return status == GL_TRUE;
}
static bool link_program(GLuint prog)
{
pglLinkProgram(prog);
GLint status;
pglGetProgramiv(prog, GL_LINK_STATUS, &status);
print_linker_log(prog);
if (status == GL_TRUE)
{
pglUseProgram(prog);
return true;
}
else
return false;
}
static bool compile_programs(GLuint *gl_prog, struct shader_program *progs, size_t num)
{
for (unsigned i = 0; i < num; i++)
{
gl_prog[i] = pglCreateProgram();
if (!gl_check_error() || gl_prog[i] == 0)
if (gl_prog[i] == 0)
{
SSNES_ERR("Failed to create GL program #%u.\n", i);
return false;
@@ -418,61 +783,49 @@ static bool compile_programs(GLuint *gl_prog, struct shader_program *progs, size
{
SSNES_LOG("Found GLSL vertex shader.\n");
GLuint shader = pglCreateShader(GL_VERTEX_SHADER);
pglShaderSource(shader, 1, (const char**)&progs[i].vertex, 0);
pglCompileShader(shader);
print_shader_log(shader);
if (!compile_shader(shader, progs[i].vertex))
{
SSNES_ERR("Failed to compile vertex shader #%u\n", i);
return false;
}
pglAttachShader(gl_prog[i], shader);
free(progs[i].vertex);
}
if (!gl_check_error())
{
SSNES_ERR("Failed to compile vertex shader #%u\n", i);
return false;
}
if (progs[i].fragment)
{
SSNES_LOG("Found GLSL fragment shader.\n");
GLuint shader = pglCreateShader(GL_FRAGMENT_SHADER);
pglShaderSource(shader, 1, (const char**)&progs[i].fragment, 0);
pglCompileShader(shader);
print_shader_log(shader);
if (!compile_shader(shader, progs[i].fragment))
{
SSNES_ERR("Failed to compile fragment shader #%u\n", i);
return false;
}
pglAttachShader(gl_prog[i], shader);
free(progs[i].fragment);
}
if (!gl_check_error())
{
SSNES_ERR("Failed to compile fragment shader #%u\n", i);
return false;
}
if (progs[i].vertex || progs[i].fragment)
{
SSNES_LOG("Linking GLSL program.\n");
pglLinkProgram(gl_prog[i]);
pglUseProgram(gl_prog[i]);
print_linker_log(gl_prog[i]);
if (!link_program(gl_prog[i]))
{
SSNES_ERR("Failed to link program #%u\n", i);
return false;
}
GLint location = pglGetUniformLocation(gl_prog[i], "rubyTexture");
pglUniform1i(location, 0);
pglUseProgram(0);
}
if (!gl_check_error())
{
SSNES_ERR("Failed to link program #%u\n", i);
return false;
}
}
return true;
}
#define LOAD_GL_SYM(SYM) if (!(pgl##SYM)) pgl##SYM = SDL_GL_GetProcAddress("gl" #SYM)
#define LOAD_GL_SYM(SYM) if (!pgl##SYM) { SDL_SYM_WRAP(pgl##SYM, "gl" #SYM) }
bool gl_glsl_init(const char *path)
{
@@ -497,6 +850,9 @@ bool gl_glsl_init(const char *path)
LOAD_GL_SYM(GetProgramInfoLog);
LOAD_GL_SYM(DeleteProgram);
LOAD_GL_SYM(GetAttachedShaders);
LOAD_GL_SYM(GetAttribLocation);
LOAD_GL_SYM(EnableVertexAttribArray);
LOAD_GL_SYM(VertexAttribPointer);
#endif
SSNES_LOG("Checking GLSL shader support ...\n");
@@ -508,7 +864,9 @@ bool gl_glsl_init(const char *path)
&& pglDetachShader && pglLinkProgram && pglGetUniformLocation
&& pglUniform1i && pglUniform2fv && pglUniform4fv
&& pglGetShaderiv && pglGetShaderInfoLog && pglGetProgramiv && pglGetProgramInfoLog
&& pglDeleteProgram && pglGetAttachedShaders;
&& pglDeleteProgram && pglGetAttachedShaders
&& pglGetAttribLocation && pglEnableVertexAttribArray
&& pglVertexAttribPointer;
#endif
if (!shader_support)
@@ -557,8 +915,37 @@ bool gl_glsl_init(const char *path)
}
}
if (!gl_check_error())
return false;
//if (!gl_check_error())
// SSNES_WARN("Detected GL error!\n");
if (gl_tracker_info_cnt > 0)
{
struct snes_tracker_info info = {
.wram = psnes_get_memory_data(SNES_MEMORY_WRAM),
.vram = psnes_get_memory_data(SNES_MEMORY_VRAM),
.cgram = psnes_get_memory_data(SNES_MEMORY_CGRAM),
.apuram = psnes_get_memory_data(SNES_MEMORY_APURAM),
.oam = psnes_get_memory_data(SNES_MEMORY_OAM),
.info = gl_tracker_info,
.info_elem = gl_tracker_info_cnt,
};
#ifdef HAVE_PYTHON
if (*gl_tracker_script)
info.script = gl_tracker_script;
else if (gl_script_program)
info.script = (const char*)gl_script_program;
else
info.script = NULL;
info.script_class = *gl_tracker_script_class ? gl_tracker_script_class : NULL;
info.script_is_file = *gl_tracker_script;
#endif
gl_snes_tracker = snes_tracker_init(&info);
if (!gl_snes_tracker)
SSNES_WARN("Failed to init SNES tracker!\n");
}
glsl_enable = true;
gl_num_programs = num_progs;
@@ -587,17 +974,47 @@ void gl_glsl_deinit(void)
pglDeleteProgram(gl_program[i]);
}
glDeleteTextures(gl_teximage_cnt, gl_teximage);
gl_teximage_cnt = 0;
memset(gl_teximage_uniforms, 0, sizeof(gl_teximage_uniforms));
}
memset(gl_program, 0, sizeof(gl_program));
glsl_enable = false;
active_index = 0;
gl_tracker_info_cnt = 0;
memset(gl_tracker_info, 0, sizeof(gl_tracker_info));
memset(gl_tracker_script, 0, sizeof(gl_tracker_script));
memset(gl_tracker_script_class, 0, sizeof(gl_tracker_script_class));
if (gl_script_program)
{
xmlFree(gl_script_program);
gl_script_program = NULL;
}
if (gl_snes_tracker)
{
snes_tracker_free(gl_snes_tracker);
gl_snes_tracker = NULL;
}
}
void gl_glsl_set_params(unsigned width, unsigned height,
unsigned tex_width, unsigned tex_height,
unsigned out_width, unsigned out_height)
unsigned out_width, unsigned out_height,
unsigned frame_count,
const struct gl_tex_info *info,
const struct gl_tex_info *prev_info,
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.
if (glsl_enable && gl_program[active_index] > 0)
{
GLint location;
@@ -613,6 +1030,127 @@ void gl_glsl_set_params(unsigned width, unsigned height,
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);
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);
// Pass texture coordinates.
location = pglGetAttribLocation(gl_program[active_index], "rubyPrevTexCoord");
if (location >= 0)
{
pglEnableVertexAttribArray(location);
pglVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, prev_info->coord);
}
// Set original texture unless we're in first pass (pointless).
if (active_index > 1)
{
// Bind original texture.
pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 2);
glBindTexture(GL_TEXTURE_2D, info->tex);
location = pglGetUniformLocation(gl_program[active_index], "rubyOrigTexture");
pglUniform1i(location, gl_teximage_cnt + 2);
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], "rubyOrigTexCoord");
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);
}
}
}
else
{
// First pass, so unbind everything to avoid collitions.
// Unbind ORIG.
pglActiveTexture(GL_TEXTURE0 + gl_teximage_cnt + 2);
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);
}
}
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);
pglUniform1i(location, info[i].value);
}
}
}
}
+5 -1
View File
@@ -30,7 +30,11 @@ void gl_glsl_set_proj_matrix(void);
void gl_glsl_set_params(unsigned width, unsigned height,
unsigned tex_width, unsigned tex_height,
unsigned out_width, unsigned out_height);
unsigned out_width, unsigned out_height,
unsigned frame_counter,
const struct gl_tex_info *info,
const struct gl_tex_info *prev_info,
const struct gl_tex_info *fbo_info, unsigned fbo_info_cnt);
void gl_glsl_use(unsigned index);
+260
View File
@@ -0,0 +1,260 @@
/* 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 "snes_state.h"
#include <stdlib.h>
#include <assert.h>
#include "strl.h"
#include "general.h"
#include <libsnes.hpp>
#ifdef HAVE_PYTHON
#include "py_state/py_state.h"
#endif
struct snes_tracker_internal
{
char id[64];
bool is_input;
const uint16_t *input_ptr;
const uint8_t *ptr;
#ifdef HAVE_PYTHON
py_state_t *py;
#endif
uint32_t addr;
unsigned mask;
enum snes_tracker_type type;
uint32_t prev[2];
int frame_count;
int frame_count_prev;
uint32_t old_value;
int transition_count;
};
struct snes_tracker
{
struct snes_tracker_internal *info;
unsigned info_elem;
uint16_t input_state[2];
#ifdef HAVE_PYTHON
py_state_t *py;
#endif
};
snes_tracker_t* snes_tracker_init(const struct snes_tracker_info *info)
{
snes_tracker_t *tracker = calloc(1, sizeof(*tracker));
if (!tracker)
return NULL;
#ifdef HAVE_PYTHON
if (info->script)
{
tracker->py = py_state_new(info->script, info->script_is_file, info->script_class ? info->script_class : "GameAware");
if (!tracker->py)
{
free(tracker);
SSNES_ERR("Failed to init Python script.\n");
return NULL;
}
}
#endif
tracker->info = calloc(info->info_elem, sizeof(struct snes_tracker_internal));
tracker->info_elem = info->info_elem;
for (unsigned i = 0; i < info->info_elem; i++)
{
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;
#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);
switch (info->info[i].ram_type)
{
case SSNES_STATE_WRAM:
tracker->info[i].ptr = info->wram;
break;
case SSNES_STATE_APURAM:
tracker->info[i].ptr = info->apuram;
break;
case SSNES_STATE_OAM:
tracker->info[i].ptr = info->oam;
break;
case SSNES_STATE_CGRAM:
tracker->info[i].ptr = info->cgram;
break;
case SSNES_STATE_VRAM:
tracker->info[i].ptr = info->vram;
break;
case SSNES_STATE_INPUT_SLOT1:
tracker->info[i].input_ptr = &tracker->input_state[0];
tracker->info[i].is_input = true;
break;
case SSNES_STATE_INPUT_SLOT2:
tracker->info[i].input_ptr = &tracker->input_state[1];
tracker->info[i].is_input = true;
break;
default:
tracker->info[i].ptr = NULL;
}
}
return tracker;
}
void snes_tracker_free(snes_tracker_t *tracker)
{
free(tracker->info);
#ifdef HAVE_PYTHON
py_state_free(tracker->py);
#endif
free(tracker);
}
#define fetch() ((info->is_input ? *info->input_ptr : info->ptr[info->addr]) & info->mask)
static void update_element(
struct snes_tracker_uniform *uniform,
struct snes_tracker_internal *info,
unsigned frame_count)
{
uniform->id = info->id;
switch (info->type)
{
case SSNES_STATE_CAPTURE:
uniform->value = fetch();
break;
case SSNES_STATE_CAPTURE_PREV:
if (info->prev[0] != fetch())
{
info->prev[1] = info->prev[0];
info->prev[0] = fetch();
}
uniform->value = info->prev[1];
break;
case SSNES_STATE_TRANSITION:
if (info->old_value != fetch())
{
info->old_value = fetch();
info->frame_count = frame_count;
}
uniform->value = info->frame_count;
break;
case SSNES_STATE_TRANSITION_COUNT:
if (info->old_value != fetch())
{
info->old_value = fetch();
info->transition_count++;
}
uniform->value = info->transition_count;
break;
case SSNES_STATE_TRANSITION_PREV:
if (info->old_value != fetch())
{
info->old_value = fetch();
info->frame_count_prev = info->frame_count;
info->frame_count = frame_count;
}
uniform->value = info->frame_count_prev;
break;
#ifdef HAVE_PYTHON
case SSNES_STATE_PYTHON:
uniform->value = py_state_get(info->py, info->id, frame_count);
break;
#endif
default:
break;
}
}
#undef fetch
// Updates 16-bit input in same format as SNES itself.
static void update_input(snes_tracker_t *tracker)
{
if (!driver.input_data)
return;
static const unsigned buttons[] = {
SNES_DEVICE_ID_JOYPAD_R,
SNES_DEVICE_ID_JOYPAD_L,
SNES_DEVICE_ID_JOYPAD_X,
SNES_DEVICE_ID_JOYPAD_A,
SNES_DEVICE_ID_JOYPAD_RIGHT,
SNES_DEVICE_ID_JOYPAD_LEFT,
SNES_DEVICE_ID_JOYPAD_DOWN,
SNES_DEVICE_ID_JOYPAD_UP,
SNES_DEVICE_ID_JOYPAD_START,
SNES_DEVICE_ID_JOYPAD_SELECT,
SNES_DEVICE_ID_JOYPAD_Y,
SNES_DEVICE_ID_JOYPAD_B
};
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]
};
uint16_t state[2] = {0};
for (unsigned i = 4; i < 16; i++)
{
state[0] |= (driver.input->input_state(driver.input_data, binds, SNES_PORT_1, SNES_DEVICE_JOYPAD, 0, buttons[i - 4]) ? 1 : 0) << i;
state[1] |= (driver.input->input_state(driver.input_data, binds, SNES_PORT_2, SNES_DEVICE_JOYPAD, 0, buttons[i - 4]) ? 1 : 0) << i;
}
for (unsigned i = 0; i < 2; i++)
tracker->input_state[i] = state[i];
}
unsigned snes_get_uniform(snes_tracker_t *tracker, struct snes_tracker_uniform *uniforms, unsigned elem, unsigned frame_count)
{
unsigned elems = tracker->info_elem < elem ? tracker->info_elem : elem;
update_input(tracker);
for (unsigned i = 0; i < elems; i++)
update_element(&uniforms[i], &tracker->info[i], frame_count);
return elems;
}
+91
View File
@@ -0,0 +1,91 @@
/* 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_SNES_STATE_H
#define __SSNES_SNES_STATE_H
#include <stdint.h>
#include <stdbool.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
enum snes_tracker_type
{
SSNES_STATE_CAPTURE,
SSNES_STATE_CAPTURE_PREV,
SSNES_STATE_TRANSITION,
SSNES_STATE_TRANSITION_COUNT,
SSNES_STATE_TRANSITION_PREV,
#ifdef HAVE_PYTHON
SSNES_STATE_PYTHON
#endif
};
enum snes_ram_type
{
SSNES_STATE_WRAM,
SSNES_STATE_APURAM,
SSNES_STATE_OAM,
SSNES_STATE_CGRAM,
SSNES_STATE_VRAM,
SSNES_STATE_INPUT_SLOT1,
SSNES_STATE_INPUT_SLOT2
};
struct snes_tracker_uniform_info
{
char id[64];
uint32_t addr;
enum snes_tracker_type type;
enum snes_ram_type ram_type;
unsigned mask;
};
struct snes_tracker_info
{
const uint8_t *wram;
const uint8_t *vram;
const uint8_t *cgram;
const uint8_t *oam;
const uint8_t *apuram;
const struct snes_tracker_uniform_info *info;
unsigned info_elem;
#ifdef HAVE_PYTHON
const char *script;
const char *script_class;
bool script_is_file;
#endif
};
struct snes_tracker_uniform
{
const char *id;
int value;
};
typedef struct snes_tracker snes_tracker_t;
snes_tracker_t* snes_tracker_init(const struct snes_tracker_info *info);
void snes_tracker_free(snes_tracker_t *tracker);
unsigned snes_get_uniform(snes_tracker_t *tracker, struct snes_tracker_uniform *uniforms, unsigned elem, unsigned frame_count);
#endif
+7 -7
View File
@@ -77,7 +77,7 @@ static void xv_set_nonblock_state(void *data, bool state)
{
xv_t *xv = data;
Atom atom = XInternAtom(xv->display, "XV_SYNC_TO_VBLANK", true);
if (atom != None && xv->port >= 0)
if (atom != None && xv->port)
XvSetPortAttribute(xv->display, xv->port, atom, !state);
else
SSNES_WARN("Failed to set SYNC_TO_VBLANK attribute.\n");
@@ -177,7 +177,7 @@ static void xv_init_font(xv_t *xv, const char *font_path, unsigned font_size)
{
xv->font = font_renderer_new(font_path, font_size);
if (!xv->font)
SSNES_WARN("Failed to init font\n");
SSNES_WARN("Failed to init font.\n");
}
#endif
}
@@ -249,7 +249,7 @@ static void render32_yuy2(xv_t *xv, const void *input_, unsigned width, unsigned
for (unsigned x = 0; x < width; x++)
{
uint32_t p = *input++;
p = ((p >> 17) & 0x7c00) | ((p >> 14) & 0x03e0) | ((p >> 11) & 0x1f); // RGBA -> RGB15
p = ((p >> 9) & 0x7c00) | ((p >> 6) & 0x03e0) | ((p >> 3) & 0x1f); // ARGB -> RGB15
uint8_t y0 = xv->ytable[p];
uint8_t u = xv->utable[p];
@@ -278,7 +278,7 @@ static void render32_uyvy(xv_t *xv, const void *input_, unsigned width, unsigned
for (unsigned x = 0; x < width; x++)
{
uint32_t p = *input++;
p = ((p >> 17) & 0x7c00) | ((p >> 14) & 0x03e0) | ((p >> 11) & 0x1f); // RGBA -> RGB15
p = ((p >> 9) & 0x7c00) | ((p >> 6) & 0x03e0) | ((p >> 3) & 0x1f); // ARGB -> RGB15
uint8_t y0 = xv->ytable[p];
uint8_t u = xv->utable[p];
@@ -298,7 +298,7 @@ static void render32_uyvy(xv_t *xv, const void *input_, unsigned width, unsigned
}
static void* xv_init(video_info_t *video, const input_driver_t **input, void **input_data)
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)
@@ -315,7 +315,7 @@ static void* xv_init(video_info_t *video, const input_driver_t **input, void **i
xv->keep_aspect = video->force_aspect;
// Find an appropriate Xv port.
xv->port = -1;
xv->port = 0;
XvAdaptorInfo *adaptor_info;
unsigned adaptor_count = 0;
XvQueryAdaptors(xv->display, DefaultRootWindow(xv->display), &adaptor_count, &adaptor_info);
@@ -333,7 +333,7 @@ static void* xv_init(video_info_t *video, const input_driver_t **input, void **i
}
XvFreeAdaptorInfo(adaptor_info);
if (xv->port < 0)
if (xv->port == 0)
{
SSNES_ERR("XVideo: Failed to find valid XvPort.\n");
goto error;
+249
View File
@@ -0,0 +1,249 @@
/* 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 "SDL.h"
#include "SDL_syswm.h"
#include <stdbool.h>
#include "ssnes_dinput.h"
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
void sdl_dinput_free(sdl_dinput_t *di)
{
if (di)
{
for (unsigned i = 0; i < MAX_PLAYERS; i++)
{
if (di->joypad[i])
{
IDirectInputDevice8_Unacquire(di->joypad[i]);
IDirectInputDevice8_Release(di->joypad[i]);
}
}
if (di->ctx)
IDirectInput8_Release(di->ctx);
free(di);
}
}
static BOOL CALLBACK enum_axes_cb(const DIDEVICEOBJECTINSTANCE *inst, void *p)
{
IDirectInputDevice8 *joypad = p;
DIPROPRANGE range;
memset(&range, 0, sizeof(range));
range.diph.dwSize = sizeof(DIPROPRANGE);
range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
range.diph.dwHow = DIPH_BYID;
range.diph.dwObj = inst->dwType;
range.lMin = -32768;
range.lMax = 32767;
IDirectInputDevice8_SetProperty(joypad, DIPROP_RANGE, &range.diph);
return DIENUM_CONTINUE;
}
static BOOL CALLBACK enum_joypad_cb(const DIDEVICEINSTANCE *inst, void *p)
{
sdl_dinput_t *di = p;
unsigned active = 0;
unsigned n;
for (n = 0; n < MAX_PLAYERS; n++)
{
if (!di->joypad[n] && g_settings.input.joypad_map[n] == di->joypad_cnt)
break;
if (di->joypad[n])
active++;
}
if (active == MAX_PLAYERS) return DIENUM_STOP;
di->joypad_cnt++;
if (n == MAX_PLAYERS)
return DIENUM_CONTINUE;
if (FAILED(IDirectInput8_CreateDevice(di->ctx, &inst->guidInstance, &di->joypad[n], NULL)))
return DIENUM_CONTINUE;
IDirectInputDevice8_SetDataFormat(di->joypad[n], &c_dfDIJoystick2);
IDirectInputDevice8_SetCooperativeLevel(di->joypad[n], di->hWnd,
DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
IDirectInputDevice8_EnumObjects(di->joypad[n], enum_axes_cb,
di->joypad[n], DIDFT_ABSAXIS);
return DIENUM_CONTINUE;
}
sdl_dinput_t* sdl_dinput_init(void)
{
sdl_dinput_t *di = calloc(1, sizeof(*di));
if (!di)
return NULL;
CoInitialize(NULL);
SDL_SysWMinfo info;
if (!SDL_GetWMInfo(&info))
{
SSNES_ERR("Failed to get SysWM info.\n");
goto error;
}
di->hWnd = info.window;
if (FAILED(DirectInput8Create(
GetModuleHandle(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8,
(void**)&di->ctx, NULL)))
{
SSNES_ERR("Failed to init DirectInput.\n");
goto error;
}
SSNES_LOG("Enumerating DInput devices ...\n");
IDirectInput8_EnumDevices(di->ctx, DI8DEVCLASS_GAMECTRL, enum_joypad_cb, di, DIEDFL_ATTACHEDONLY);
SSNES_LOG("Done enumerating DInput devices ...\n");
return di;
error:
sdl_dinput_free(di);
return NULL;
}
static bool dinput_joykey_pressed(sdl_dinput_t *di, unsigned port_num, uint16_t joykey)
{
if (joykey == NO_BTN)
return false;
// Check hat.
if (GET_HAT_DIR(joykey))
{
unsigned hat = GET_HAT(joykey);
unsigned elems = sizeof(di->joy_state[0].rgdwPOV) / sizeof(di->joy_state[0].rgdwPOV[0]);
if (hat >= elems)
return false;
unsigned pov = di->joy_state[port_num].rgdwPOV[hat];
// Magic numbers I'm not sure where originate from.
if (pov < 36000)
{
switch (GET_HAT_DIR(joykey))
{
case HAT_UP_MASK:
return (pov >= 31500) || (pov <= 4500);
case HAT_RIGHT_MASK:
return (pov >= 4500) && (pov <= 13500);
case HAT_DOWN_MASK:
return (pov >= 13500) && (pov <= 22500);
case HAT_LEFT_MASK:
return (pov >= 22500) && (pov <= 31500);
}
}
return false;
}
else
{
unsigned elems = sizeof(di->joy_state[0].rgbButtons) / sizeof(di->joy_state[0].rgbButtons[0]);
if (joykey < elems)
return di->joy_state[port_num].rgbButtons[joykey];
}
return false;
}
static bool dinput_joyaxis_pressed(sdl_dinput_t *di, unsigned port_num, uint32_t joyaxis)
{
if (joyaxis == AXIS_NONE)
return false;
int min = -32678 * g_settings.input.axis_threshold;
int max = 32677 * g_settings.input.axis_threshold;
switch (AXIS_NEG_GET(joyaxis))
{
case 0:
return di->joy_state[port_num].lX <= min;
case 1:
return di->joy_state[port_num].lY <= min;
case 2:
return di->joy_state[port_num].lZ <= min;
case 3:
return di->joy_state[port_num].lRx <= min;
case 4:
return di->joy_state[port_num].lRy <= min;
case 5:
return di->joy_state[port_num].lRz <= min;
}
switch (AXIS_POS_GET(joyaxis))
{
case 0:
return di->joy_state[port_num].lX >= max;
case 1:
return di->joy_state[port_num].lY >= max;
case 2:
return di->joy_state[port_num].lZ >= max;
case 3:
return di->joy_state[port_num].lRx >= max;
case 4:
return di->joy_state[port_num].lRy >= max;
case 5:
return di->joy_state[port_num].lRz >= max;
}
return false;
}
bool sdl_dinput_pressed(sdl_dinput_t *di, unsigned port_num, const struct snes_keybind *key)
{
if (di->joypad[port_num] == NULL)
return false;
if (dinput_joykey_pressed(di, port_num, key->joykey))
return true;
if (dinput_joyaxis_pressed(di, port_num, key->joyaxis))
return true;
return false;
}
void sdl_dinput_poll(sdl_dinput_t *di)
{
memset(di->joy_state, 0, sizeof(di->joy_state));
for (unsigned i = 0; i < MAX_PLAYERS; i++)
{
if (di->joypad[i])
{
if (FAILED(IDirectInputDevice8_Poll(di->joypad[i])))
{
IDirectInputDevice8_Acquire(di->joypad[i]);
continue;
}
IDirectInputDevice8_GetDeviceState(di->joypad[i], sizeof(DIJOYSTATE2), &di->joy_state[i]);
}
}
}
+59 -58
View File
@@ -25,12 +25,22 @@
#include <libsnes.hpp>
#include "ssnes_sdl_input.h"
static void* sdl_input_init(void)
{
sdl_input_t *sdl = calloc(1, sizeof(*sdl));
if (!sdl)
return NULL;
#ifdef HAVE_DINPUT
sdl->di = sdl_dinput_init();
if (!sdl->di)
{
free(sdl);
SSNES_ERR("Failed to init SDL/DInput.\n");
return NULL;
}
#else
if (SDL_Init(SDL_INIT_JOYSTICK) < 0)
return NULL;
@@ -60,9 +70,9 @@ static void* sdl_input_init(void)
sdl->num_hats[i] = SDL_JoystickNumHats(sdl->joysticks[i]);
}
}
#endif
sdl->use_keyboard = true;
return sdl;
}
@@ -78,8 +88,12 @@ static bool sdl_key_pressed(int key)
return keymap[key];
}
#ifndef HAVE_DINPUT
static bool sdl_joykey_pressed(sdl_input_t *sdl, int port_num, uint16_t joykey)
{
if (joykey == NO_BTN)
return false;
// Check hat.
if (GET_HAT_DIR(joykey))
{
@@ -120,37 +134,43 @@ static bool sdl_joykey_pressed(sdl_input_t *sdl, int port_num, uint16_t joykey)
static bool sdl_axis_pressed(sdl_input_t *sdl, int port_num, uint32_t joyaxis)
{
if (joyaxis != AXIS_NONE)
if (joyaxis == AXIS_NONE)
return false;
if (AXIS_NEG_GET(joyaxis) < sdl->num_axes[port_num])
{
if (AXIS_NEG_GET(joyaxis) < sdl->num_axes[port_num])
{
Sint16 val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_NEG_GET(joyaxis));
float scaled = (float)val / 0x8000;
if (scaled < -g_settings.input.axis_threshold)
return true;
}
if (AXIS_POS_GET(joyaxis) < sdl->num_axes[port_num])
{
Sint16 val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_POS_GET(joyaxis));
float scaled = (float)val / 0x8000;
if (scaled > g_settings.input.axis_threshold)
return true;
}
Sint16 val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_NEG_GET(joyaxis));
float scaled = (float)val / 0x8000;
if (scaled < -g_settings.input.axis_threshold)
return true;
}
if (AXIS_POS_GET(joyaxis) < sdl->num_axes[port_num])
{
Sint16 val = SDL_JoystickGetAxis(sdl->joysticks[port_num], AXIS_POS_GET(joyaxis));
float scaled = (float)val / 0x8000;
if (scaled > g_settings.input.axis_threshold)
return true;
}
return false;
}
#endif
static bool sdl_is_pressed(sdl_input_t *sdl, int port_num, const struct snes_keybind *key)
static bool sdl_is_pressed(sdl_input_t *sdl, unsigned port_num, const struct snes_keybind *key)
{
if (sdl->use_keyboard && sdl_key_pressed(key->key))
return true;
#ifdef HAVE_DINPUT
return sdl_dinput_pressed(sdl->di, port_num, key);
#else
if (sdl->joysticks[port_num] == NULL)
return false;
if (sdl_joykey_pressed(sdl, port_num, key->joykey))
return true;
if (sdl_axis_pressed(sdl, port_num, key->joyaxis))
return true;
#endif
return false;
}
@@ -159,7 +179,7 @@ static bool sdl_bind_button_pressed(void *data, int key)
{
// Only let player 1 use special binds called from main loop.
const struct snes_keybind *binds = g_settings.input.binds[0];
for (int i = 0; binds[i].id != -1; i++)
for (unsigned i = 0; binds[i].id != -1; i++)
{
if (binds[i].id == key)
return sdl_is_pressed(data, 0, &binds[i]);
@@ -168,23 +188,21 @@ static bool sdl_bind_button_pressed(void *data, int key)
}
static int16_t sdl_joypad_device_state(sdl_input_t *sdl, const struct snes_keybind **binds,
int port_num, unsigned device, unsigned index, unsigned id)
unsigned port_num, int id)
{
const struct snes_keybind *snes_keybinds = binds[port_num];
for (int i = 0; snes_keybinds[i].id != -1; i++)
for (unsigned i = 0; snes_keybinds[i].id != -1; i++)
{
if (snes_keybinds[i].id == (int)id)
return sdl_is_pressed(sdl, port_num, &snes_keybinds[i]);
if (snes_keybinds[i].id == id)
return sdl_is_pressed(sdl, port_num, &snes_keybinds[i]) ? 1 : 0;
}
return false;
return 0;
}
static int16_t sdl_mouse_device_state(sdl_input_t *sdl, bool port, unsigned id)
static int16_t sdl_mouse_device_state(sdl_input_t *sdl, unsigned id)
{
// Might implement support for joypad mapping later.
(void)port;
switch (id)
{
case SNES_DEVICE_ID_MOUSE_LEFT:
@@ -248,11 +266,11 @@ static int16_t sdl_input_state(void *data, const struct snes_keybind **binds, bo
switch (device)
{
case SNES_DEVICE_JOYPAD:
return sdl_joypad_device_state(data, binds, (port == SNES_PORT_1) ? 0 : 1, device, index, id);
return sdl_joypad_device_state(data, binds, (port == SNES_PORT_1) ? 0 : 1, id);
case SNES_DEVICE_MULTITAP:
return sdl_joypad_device_state(data, binds, (port == SNES_PORT_2) ? 1 + index : 0, device, index, id);
return sdl_joypad_device_state(data, binds, (port == SNES_PORT_2) ? 1 + index : 0, id);
case SNES_DEVICE_MOUSE:
return sdl_mouse_device_state(data, port, id);
return sdl_mouse_device_state(data, id);
case SNES_DEVICE_SUPER_SCOPE:
return sdl_scope_device_state(data, id);
case SNES_DEVICE_JUSTIFIER:
@@ -273,14 +291,19 @@ static void sdl_input_free(void *data)
while (SDL_PollEvent(&event));
sdl_input_t *sdl = data;
#ifdef HAVE_DINPUT
sdl_dinput_free(sdl->di);
#else
for (int i = 0; i < MAX_PLAYERS; i++)
{
if (sdl->joysticks[i])
SDL_JoystickClose(sdl->joysticks[i]);
}
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
#endif
free(data);
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
}
}
@@ -298,37 +321,15 @@ static void sdl_poll_mouse(sdl_input_t *sdl)
static void sdl_input_poll(void *data)
{
SDL_PumpEvents();
SDL_Event event;
SDL_JoystickUpdate();
sdl_poll_mouse(data);
sdl_input_t *sdl = data;
// Search for events...
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
if (sdl->quitting)
{
*sdl->quitting = true;
return;
}
break;
case SDL_VIDEORESIZE:
if (sdl->should_resize)
{
*sdl->new_width = event.resize.w;
*sdl->new_height = event.resize.h;
*sdl->should_resize = true;
}
break;
#ifdef HAVE_DINPUT
sdl_dinput_poll(sdl->di);
#else
SDL_JoystickUpdate();
#endif
default:
break;
}
}
sdl_poll_mouse(sdl);
}
const input_driver_t input_sdl = {
+48
View File
@@ -0,0 +1,48 @@
/* 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_DINPUT_H
#define __SSNES_DINPUT_H
#undef DIRECTINPUT_VERSION
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#include <stdbool.h>
#include "general.h"
// Piggyback joypad driver for SDL.
typedef struct sdl_dinput
{
HWND hWnd;
IDirectInput8 *ctx;
IDirectInputDevice8 *joypad[MAX_PLAYERS];
unsigned joypad_cnt;
DIJOYSTATE2 joy_state[MAX_PLAYERS];
} sdl_dinput_t;
sdl_dinput_t *sdl_dinput_init(void);
void sdl_dinput_free(sdl_dinput_t *di);
bool sdl_dinput_pressed(sdl_dinput_t *di, unsigned port_num,
const struct snes_keybind *key);
void sdl_dinput_poll(sdl_dinput_t *di);
#endif
+10 -5
View File
@@ -20,20 +20,25 @@
#include "SDL.h"
#include "general.h"
#ifdef HAVE_DINPUT
#include "ssnes_dinput.h"
#endif
typedef struct sdl_input
{
#ifdef HAVE_DINPUT
sdl_dinput_t *di;
#else
SDL_Joystick *joysticks[MAX_PLAYERS];
unsigned num_axes[MAX_PLAYERS];
unsigned num_buttons[MAX_PLAYERS];
unsigned num_hats[MAX_PLAYERS];
unsigned num_joysticks;
#endif
bool use_keyboard;
// A video driver could pre-init with the SDL driver and have it handle resizing events...
bool *quitting;
bool *should_resize;
unsigned *new_width;
unsigned *new_height;
int16_t mouse_x, mouse_y;
int16_t mouse_l, mouse_r, mouse_m;
} sdl_input_t;
+27
View File
@@ -59,6 +59,33 @@ static const struct key_bind lut_binds[] = {
{ 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 },
-12
View File
@@ -107,18 +107,6 @@ struct bsv_movie
#define CRC_INDEX 2
#define STATE_SIZE_INDEX 3
static inline uint8_t is_little_endian(void)
{
union
{
uint16_t u16;
uint8_t u8[2];
} u;
u.u16 = 1;
return u.u8[0];
}
// Convert to big-endian if needed
static inline uint32_t swap_if_big32(uint32_t val)
{
+4 -5
View File
@@ -96,10 +96,7 @@ struct netplay
void input_poll_net(void)
{
if (!netplay_should_skip(g_extern.netplay) && netplay_can_poll(g_extern.netplay))
{
netplay_callbacks(g_extern.netplay)->poll_cb();
netplay_poll(g_extern.netplay);
}
}
void video_frame_net(const uint16_t *data, unsigned width, unsigned height)
@@ -243,7 +240,7 @@ static bool init_socket(netplay_t *handle, const char *server, uint16_t port)
{
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
WSACleanup();
return false;
@@ -470,7 +467,7 @@ static bool get_self_input_state(netplay_t *handle)
if (handle->frame_count > 0) // First frame we always give zero input since relying on input from first frame screws up when we use -F 0.
{
snes_input_state_t cb = handle->cbs.state_cb;
for (int i = 0; i <= 11; i++)
for (unsigned i = 0; i <= 11; i++)
{
int16_t tmp = cb(g_settings.input.netplay_client_swap_input ? 0 : !handle->port, SNES_DEVICE_JOYPAD, 0, i);
state |= tmp ? 1 << i : 0;
@@ -659,6 +656,8 @@ void netplay_pre_frame(netplay_t *handle)
{
psnes_serialize(handle->buffer[handle->self_ptr].state, handle->state_size);
handle->can_poll = true;
input_poll_net();
}
// Here we check if we have new input and replay from recorded input.
-1
View File
@@ -34,7 +34,6 @@ struct snes_callbacks
{
snes_video_refresh_t frame_cb;
snes_audio_sample_t sample_cb;
snes_input_poll_t poll_cb;
snes_input_state_t state_cb;
};
File renamed without changes.
+31 -10
View File
@@ -3,6 +3,12 @@
check_switch_c C99 -std=gnu99
check_critical C99 "Cannot find C99 compatible compiler."
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."
@@ -10,16 +16,18 @@ if [ "$HAVE_DYNAMIC" = "yes" ] && [ "$HAVE_CONFIGFILE" = "no" ]; then
fi
if [ $HAVE_DYNAMIC != yes ]; then
check_lib_cxx SNES $LIBSNES snes_init -ldl
check_lib_cxx SNES $LIBSNES snes_init $DYLIB
check_critical SNES "Cannot find libsnes."
add_define_make libsnes $LIBSNES
fi
check_lib FILTER -ldl dlopen
check_lib DYLIB $DYLIB dlopen
check_lib NETPLAY -lc socket
check_lib ALSA -lasound snd_pcm_open
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
check_lib AL "-framework OpenAL" alcOpenDevice
@@ -34,35 +42,48 @@ else
fi
check_pkgconf RSOUND rsound 1.1
check_lib ROAR -lroar roar_vs_new
check_pkgconf ROAR libroar
check_pkgconf JACK jack 0.120.1
check_pkgconf PULSE libpulse
check_lib COREAUDIO "-framework AudioUnit" AudioUnitInitialize
check_pkgconf SDL sdl 1.2.10
check_critical SDL "Cannot find SDL library."
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_lib CG -lCg cgCreateContext
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 AVCORE libavcore
check_pkgconf AVUTIL libavutil
check_pkgconf SWSCALE libswscale
( [ $HAVE_FFMPEG = auto ] && ( [ $HAVE_AVCODEC = no ] || [ $HAVE_AVFORMAT = no ] || [ $HAVE_AVCORE = 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
check_pkgconf SRC samplerate
check_lib DYNAMIC -ldl dlopen
check_lib DYNAMIC $DYLIB dlopen
check_pkgconf FREETYPE freetype2
check_lib XVIDEO -lXv XvShmCreateImage
check_pkgconf XVIDEO xv
check_lib STRL -lc strlcpy
check_pkgconf PYTHON python3
add_define_make OS $OS
# Creates config.mk and config.h.
VARS="ALSA OSS AL RSOUND ROAR JACK PULSE SDL FILTER CG XML DYNAMIC FFMPEG AVCODEC AVFORMAT AVCORE AVUTIL SWSCALE SRC CONFIGFILE FREETYPE XVIDEO NETPLAY FBO"
VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL DYLIB CG XML SDL_IMAGE DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE SRC CONFIGFILE FREETYPE XVIDEO NETPLAY FBO STRL PYTHON"
create_config_make config.mk $VARS
create_config_header config.h $VARS
+5 -2
View File
@@ -1,7 +1,7 @@
. qb/qb.params.sh
PACKAGE_NAME=ssnes
PACKAGE_VERSION=0.4-beta
PACKAGE_VERSION=0.7
# Adds a command line opt to ./configure --help
# $1: Variable (HAVE_ALSA, HAVE_OSS, etc)
@@ -10,7 +10,7 @@ PACKAGE_VERSION=0.4-beta
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 FILTER "Enable CPU filter support" auto
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
@@ -23,6 +23,9 @@ add_command_line_enable RSOUND "Enable RSound support" auto
add_command_line_enable ROAR "Enable RoarAudio support" auto
add_command_line_enable AL "Enable OpenAL support" auto
add_command_line_enable JACK "Enable JACK support" auto
add_command_line_enable COREAUDIO "Enable CoreAudio support" auto
add_command_line_enable PULSE "Enable PulseAudio support" auto
add_command_line_enable FREETYPE "Enable FreeType support" auto
add_command_line_enable XVIDEO "Enable XVideo support" auto
add_command_line_enable SDL_IMAGE "Enable SDL_image support" auto
add_command_line_enable PYTHON "Enable Python 3 support for shaders" no
+13 -12
View File
@@ -1,24 +1,25 @@
. qb/conf.comp.sh
. qb/config.comp.sh
TEMP_C=.tmp.c
TEMP_CXX=.tmp.cxx
TEMP_EXE=.tmp
echo -n "Checking operating system ... "
ECHOBUF="Checking operating system ... "
OS="Win32" # whatever ;D
unamestr="`uname -a`"
if [ ! -z "`echo $unamestr | grep -i Linux`" ]; then
if [ ! -z "`echo "$unamestr" | grep -i Linux`" ]; then
OS="Linux"
elif [ ! -z "`echo $unamestr | grep -i Darwin`" ]; then
elif [ ! -z "`echo "$unamestr" | grep -i Darwin`" ]; then
OS="Darwin"
elif [ ! -z "`echo $unamestr | grep -i BSD`" ]; then
elif [ ! -z "`echo "$unamestr" | grep -i BSD`" ]; then
OS="BSD"
elif [ ! -z "`echo $unamestr | grep -i NT`" ]; then
elif [ ! -z "`echo "$unamestr" | grep -i MINGW32`" ]; then
OS="MinGW"
elif [ ! -z "`echo "$unamestr" | grep -i NT`" ]; then
OS="Cygwin"
fi
echo $OS
echo $ECHOBUF $OS
# Checking for working C compiler
if [ "$USE_LANG_C" = yes ]; then
@@ -31,12 +32,12 @@ if [ "$USE_LANG_C" = yes ]; then
exit 1
fi
echo -n "Checking if $CC is a suitable compiler ... "
ECHOBUF="Checking if $CC is a suitable compiler ..."
answer=no
echo "#include <stdio.h>" > $TEMP_C
echo "int main(void) { puts(\"Hai world!\"); return 0; }" >> $TEMP_C
$CC -o $TEMP_EXE $TEMP_C 2>/dev/null >/dev/null && answer=yes
echo $answer
echo $ECHOBUF $answer
rm -rf $TEMP_C $TEMP_EXE
@@ -54,12 +55,12 @@ if [ "$USE_LANG_CXX" = "yes" ]; then
exit 1
fi
echo -n "Checking if $CXX is a suitable compiler ... "
ECHOBUF="Checking if $CXX is a suitable compiler ..."
answer=no
echo "#include <iostream>" > $TEMP_CXX
echo "int main() { std::cout << \"Hai guise\" << std::endl; return 0; }" >> $TEMP_CXX
$CXX -o $TEMP_EXE $TEMP_CXX 2>/dev/null >/dev/null && answer=yes
echo $answer
echo $ECHOBUF $answer
rm -rf $TEMP_CXX $TEMP_EXE
+61 -23
View File
@@ -41,9 +41,13 @@ check_lib()
eval tmpval=\$$tmpval
[ "$tmpval" = "no" ] && return 0
echo -n "Checking function $3 in $2 ... "
echo "void $3(void); int main(void) { $3(); return 0; }" > $TEMP_C
if [ -z "$3" ]; then
ECHOBUF="Checking existence of $2 ..."
echo "int main(void) { return 0; }" > $TEMP_C
else
ECHOBUF="Checking function $3 in $2 ..."
echo "void $3(void); int main(void) { $3(); return 0; }" > $TEMP_C
fi
eval HAVE_$1=no
answer=no
@@ -52,9 +56,9 @@ check_lib()
$CC -o $TEMP_EXE $TEMP_C $INCLUDE_DIRS $LIBRARY_DIRS $extralibs $CFLAGS $LDFLAGS $2 2>/dev/null >/dev/null && answer=yes && eval HAVE_$1=yes
echo $answer
echo $ECHOBUF $answer
rm -rf $TEMP_C $TEMP_EXE
rm -f $TEMP_C $TEMP_EXE
if [ "$tmpval" = "yes" ] && [ "$answer" = "no" ]; then
echo "Forced to build with library $2, but cannot locate. Exiting ..."
exit 1
@@ -67,8 +71,13 @@ check_lib_cxx()
eval tmpval=\$$tmpval
[ "$tmpval" = "no" ] && return 0
echo -n "Checking function $3 in $2 ... "
echo "extern \"C\" { void $3(void); } int main() { $3(); }" > $TEMP_CXX
if [ -z "$3" ]; then
ECHOBUF="Checking existence of $2 ..."
echo "int main() { return 0; }" > $TEMP_CXX
else
ECHOBUF="Checking function $3 in $2 ..."
echo "extern \"C\" { void $3(void); } int main() { $3(); }" > $TEMP_CXX
fi
eval HAVE_$1=no
answer=no
@@ -77,25 +86,55 @@ check_lib_cxx()
$CXX -o $TEMP_EXE $TEMP_CXX $INCLUDE_DIRS $LIBRARY_DIRS $extralibs $CFLAGS $LDFLAGS $2 2>/dev/null >/dev/null && answer=yes && eval HAVE_$1=yes
echo $answer
echo $ECHOBUF $answer
rm -rf $TEMP_CXX $TEMP_EXE
rm -f $TEMP_CXX $TEMP_EXE
if [ "$tmpval" = "yes" ] && [ "$answer" = "no" ]; then
echo "Forced to build with library $2, but cannot locate. Exiting ..."
exit 1
fi
}
check_code_c()
{
tmpval="HAVE_$1"
eval tmpval=\$$tmpval
[ "$tmpval" = "no" ] && return 0
ECHOBUF="Checking C code snippet \"$3\" ..."
eval HAVE_$1=no
answer=no
$CC -o $TEMP_EXE $TEMP_C $INCLUDE_DIRS $LIBRARY_DIRS $2 $CFLAGS $LDFLAGS 2>/dev/null >/dev/null && answer=yes && eval HAVE_$1=yes
echo $ECHOBUF $answer
rm -f $TEMP_C $TEMP_EXE
}
check_code_cxx()
{
tmpval="HAVE_$1"
eval tmpval=\$$tmpval
[ "$tmpval" = "no" ] && return 0
ECHOBUF="Checking C++ code snippet \"$3\" ..."
eval HAVE_$1=no
answer=no
$CXX -o $TEMP_EXE $TEMP_CXX $INCLUDE_DIRS $LIBRARY_DIRS $2 $CXXFLAGS $LDFLAGS 2>/dev/null >/dev/null && answer=yes && eval HAVE_$1=yes
echo $ECHOBUF $answer
rm -f $TEMP_CXX $TEMP_EXE
}
locate_pkg_conf()
{
echo -n "Checking for pkg-config ... "
ECHOBUF="Checking for pkg-config ... "
PKG_CONF_PATH="`which pkg-config | grep ^/ | head -n1`"
if [ -z $PKG_CONF_PATH ]; then
echo "not found"
echo "Cannot locate pkg-config. Exiting ..."
exit 1
fi
echo "$PKG_CONF_PATH"
echo $ECHOBUF $PKG_CONF_PATH
}
check_pkgconf()
@@ -106,16 +145,16 @@ check_pkgconf()
eval tmpval=\$$tmpval
[ "$tmpval" = "no" ] && return 0
echo -n "Checking presence of package $2"
ECHOBUF="Checking presence of package $2"
eval HAVE_$1=no
eval $1_CFLAGS=""
eval $1_LIBS=""
answer=no
minver=0.0
[ ! -z $3 ] && minver=$3 && echo -n " with minimum version $minver"
echo -n " ... "
[ ! -z $3 ] && minver=$3 && ECHOBUF="$ECHOBUF with minimum version $minver"
ECHOBUF="$ECHOBUF ... "
pkg-config --atleast-version=$minver "$2" && eval HAVE_$1=yes && eval $1_CFLAGS='"`pkg-config $2 --cflags`"' && eval $1_LIBS='"`pkg-config $2 --libs`"' && answer=yes
echo $answer
echo $ECHOBUF $answer
PKG_CONF_USED="$PKG_CONF_USED $1"
@@ -131,7 +170,7 @@ check_header()
eval tmpval=\$$tmpval
[ "$tmpval" = "no" ] && return 0
echo -n "Checking presence of header file $2 ... "
ECHOBUF="Checking presence of header file $2 ..."
echo "#include<$2>" > $TEMP_C
echo "int main(void) { return 0; }" >> $TEMP_C
eval HAVE_$1=no
@@ -139,7 +178,7 @@ check_header()
$CC -o $TEMP_EXE $TEMP_C $INCLUDE_DIRS 2>/dev/null >/dev/null && answer=yes && eval HAVE_$1=yes
echo $answer
echo $ECHOBUF $answer
rm -rf $TEMP_C $TEMP_EXE
if [ "$tmpval" = "yes" ] && [ "$answer" = "no" ]; then
@@ -150,7 +189,7 @@ check_header()
check_switch_c()
{
echo -n "Checking for availability of switch $2 in $CC ... "
ECHOBUF="Checking for availability of switch $2 in $CC ..."
if [ -z "$CC" ]; then
echo "No C compiler, cannot check ..."
exit 1
@@ -160,14 +199,14 @@ check_switch_c()
answer=no
$CC -o $TEMP_EXE $TEMP_C $2 2>/dev/null >/dev/null && answer=yes && eval HAVE_$1=yes
echo $answer
echo $ECHOBUF $answer
rm -rf $TEMP_C $TEMP_EXE
}
check_switch_cxx()
{
echo -n "Checking for availability of switch $2 in $CXX ... "
ECHOBUF="Checking for availability of switch $2 in $CXX ... "
if [ -z "$CXX" ]; then
echo "No C++ compiler, cannot check ..."
exit 1
@@ -177,7 +216,7 @@ check_switch_cxx()
answer=no
$CXX -o $TEMP_EXE $TEMP_CXX $2 2>/dev/null >/dev/null && answer=yes && eval HAVE_$1=yes
echo $answer
echo $ECHOBUF $answer
rm -rf $TEMP_CXX $TEMP_EXE
}
@@ -207,7 +246,7 @@ create_config_header()
echo "Creating config header: $outfile"
name="`echo __$outfile | sed 's|[\./]|_|g' | tr '[a-z]' '[A-Z]'`"
name="`echo QB_${outfile}__ | sed 's|[\./]|_|g' | tr '[a-z]' '[A-Z]'`"
echo "#ifndef $name" > "$outfile"
echo "#define $name" >> "$outfile"
echo "" >> "$outfile"
@@ -251,7 +290,6 @@ output_define_make()
create_config_make()
{
outfile="$1"
shift
+12 -9
View File
@@ -64,8 +64,7 @@ print_sub_opt()
lowertext="`echo $arg1 | tr '[A-Z]' '[a-z]'`"
if [ "$arg3" = "auto" ]; then
echo -n "--enable-$lowertext: "
echo $arg2
echo "--enable-$lowertext: $arg2"
echo "--disable-$lowertext"
elif [ "$arg3" = "yes" ]; then
echo "--disable-$lowertext: $arg2"
@@ -103,8 +102,9 @@ parse_input()
;;
--enable-*)
enable=`echo $1 | sed 's|^--enable-||'`
if [ -z "`echo $COMMAND_LINE_OPTS_ENABLE | grep -i $enable`" ]; then
tmp="$1"
enable="${tmp#--enable-}"
if [ -z "`echo $COMMAND_LINE_OPTS_ENABLE | grep -i -- $enable`" ]; then
print_help
exit 1
fi
@@ -112,8 +112,9 @@ parse_input()
;;
--disable-*)
disable=`echo $1 | sed 's|^--disable-||'`
if [ -z "`echo $COMMAND_LINE_OPTS_ENABLE | grep -i $disable`" ]; then
tmp="$1"
disable="${tmp#--disable-}"
if [ -z "`echo $COMMAND_LINE_OPTS_ENABLE | grep -i -- $disable`" ]; then
print_help
exit 1
fi
@@ -121,9 +122,11 @@ parse_input()
;;
--with-*)
arg="`echo $1 | sed 's|^--with-\S\S*=||'`"
with=`echo $1 | sed 's|^--with-\(\S\S*\)=.*$|\1|'`
if [ -z "`echo $COMMAND_LINE_OPTS_STRINGS | grep -i $with`" ]; then
tmp="$1"
arg="${tmp#--with-*=}"
tmp="${tmp#--with-}"
with="${tmp%%=*}"
if [ -z "`echo $COMMAND_LINE_OPTS_STRINGS | grep -i -- $with`" ]; then
print_help
exit 1
fi
+360 -157
View File
@@ -2,18 +2,21 @@
#include <libavutil/mathematics.h>
#include <libavutil/avutil.h>
#include <libavutil/avstring.h>
#include <libavutil/opt.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/avconfig.h>
#include <stdint.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
#include "ffemu.h"
#include "fifo_buffer.h"
#include "SDL_thread.h"
struct video_info
{
bool enabled;
AVCodecContext *codec;
AVFrame *conv_frame;
@@ -23,13 +26,16 @@ struct video_info
uint8_t *outbuf;
size_t outbuf_size;
int fmt;
int pix_size;
AVFormatContext *format;
struct SwsContext *sws_ctx;
} video;
struct audio_info
{
bool enabled;
AVCodecContext *codec;
int16_t *buffer;
@@ -55,158 +61,185 @@ struct ffemu
struct muxer_info muxer;
struct ffemu_params params;
SDL_cond *cond;
SDL_mutex *cond_lock;
SDL_mutex *lock;
fifo_buffer_t *audio_fifo;
fifo_buffer_t *video_fifo;
fifo_buffer_t *attr_fifo;
SDL_Thread *thread;
volatile bool alive;
volatile bool can_sleep;
};
// Currently hardcoded atm. :)
static int map_audio_codec(ffemu_audio_codec codec)
static bool init_audio(struct audio_info *audio, struct ffemu_params *param)
{
(void)codec;
return CODEC_ID_VORBIS;
}
// Currently hardcoded atm. :)
static int map_video_codec(ffemu_video_codec codec)
{
(void)codec;
return CODEC_ID_H264;
}
static int init_audio(struct audio_info *audio, struct ffemu_params *param)
{
AVCodec *codec = avcodec_find_encoder(map_audio_codec(param->acodec));
AVCodec *codec = avcodec_find_encoder(CODEC_ID_FLAC);
if (!codec)
return -1;
return false;
audio->codec = avcodec_alloc_context();
avcodec_get_context_defaults(audio->codec);
// Hardcode this atm.
audio->codec->global_quality = 100000;
audio->codec->flags |= CODEC_FLAG_QSCALE;
audio->codec->sample_rate = param->samplerate;
audio->codec->time_base = (AVRational) { 1, param->samplerate };
audio->codec->channels = param->channels;
audio->codec->sample_fmt = AV_SAMPLE_FMT_S16;
if (avcodec_open(audio->codec, codec) != 0)
return -1;
return false;
audio->buffer = av_malloc(audio->codec->frame_size * param->channels * sizeof(int16_t));
if (!audio->buffer)
return -1;
return false;
audio->outbuf_size = 50000;
audio->outbuf_size = 2000000;
audio->outbuf = av_malloc(audio->outbuf_size);
if (!audio->outbuf)
return -1;
return false;
return 0;
return true;
}
// Hardcode.
static void init_x264_param(AVCodecContext *c)
static bool init_video(struct video_info *video, struct ffemu_params *param)
{
c->coder_type = 1; // coder = 1
c->flags|=CODEC_FLAG_LOOP_FILTER; // flags=+loop
c->me_cmp|= 1; // cmp=+chroma, where CHROMA = 1
c->partitions|=X264_PART_I8X8+X264_PART_I4X4+X264_PART_P8X8+X264_PART_B8X8; // partitions=+parti8x8+parti4x4+partp8x8+partb8x8
c->me_method=ME_HEX; // me_method=hex
c->me_subpel_quality = 7; // subq=7
c->me_range = 16; // me_range=16
c->gop_size = 250; // g=250
c->keyint_min = 25; // keyint_min=25
c->scenechange_threshold = 40; // sc_threshold=40
c->i_quant_factor = 0.71; // i_qfactor=0.71
c->b_frame_strategy = 1; // b_strategy=1
c->qcompress = 0.6; // qcomp=0.6
c->qmin = 10; // qmin=10
c->qmax = 51; // qmax=51
c->max_qdiff = 4; // qdiff=4
c->max_b_frames = 3; // bf=3
c->refs = 3; // refs=3
c->directpred = 1; // directpred=1
c->trellis = 1; // trellis=1
c->flags2|=CODEC_FLAG2_BPYRAMID+CODEC_FLAG2_MIXED_REFS+CODEC_FLAG2_WPRED+CODEC_FLAG2_8X8DCT+CODEC_FLAG2_FASTPSKIP; // flags2=+bpyramid+mixed_refs+wpred+dct8x8+fastpskip
c->weighted_p_pred = 2; // wpredp=2
// libx264-main.ffpreset preset
c->flags2|=CODEC_FLAG2_8X8DCT;
c->flags2^=CODEC_FLAG2_8X8DCT;
}
static int init_video(struct video_info *video, struct ffemu_params *param)
{
AVCodec *codec = avcodec_find_encoder(map_video_codec(param->vcodec));
AVCodec *codec = avcodec_find_encoder(CODEC_ID_FFV1);
if (!codec)
return -1;
return false;
#if AV_HAVE_BIGENDIAN
video->fmt = PIX_FMT_RGB555BE;
#else
video->fmt = PIX_FMT_RGB555LE;
#endif
video->pix_size = sizeof(uint16_t);
if (param->rgb32)
{
video->fmt = PIX_FMT_RGB32;
video->pix_size = sizeof(uint32_t);
}
video->codec = avcodec_alloc_context();
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->crf = 25;
// Might have to change this later to account for RGB codecs.
video->codec->pix_fmt = PIX_FMT_YUV420P;
///// Is this element in all recent ffmpeg versions?
video->codec->thread_count = 4;
/////
video->codec->pix_fmt = PIX_FMT_RGB32;
video->codec->sample_aspect_ratio = av_d2q(param->aspect_ratio * param->out_height / param->out_width, 255);
init_x264_param(video->codec);
if (avcodec_open(video->codec, codec) != 0)
return -1;
return false;
// Allocate a big buffer :p ffmpeg API doesn't seem to give us some clues how big this buffer should be.
video->outbuf_size = 1000000;
video->outbuf_size = 1 << 23;
video->outbuf = av_malloc(video->outbuf_size);
int size = avpicture_get_size(PIX_FMT_YUV420P, param->out_width, param->out_height);
// 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);
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_YUV420P, param->out_width, param->out_height);
avpicture_fill((AVPicture*)video->conv_frame, video->conv_frame_buf, PIX_FMT_RGB32, param->fb_width, param->fb_height);
return 0;
return true;
}
static int init_muxer(ffemu_t *handle)
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)
#else
if (url_fopen(&ctx->pb, ctx->filename, URL_WRONLY) < 0)
#endif
{
av_free(ctx);
return -1;
return false;
}
int stream_cnt = 0;
if (handle->video.enabled)
{
AVStream *stream = av_new_stream(ctx, stream_cnt++);
stream->codec = handle->video.codec;
AVStream *stream = av_new_stream(ctx, stream_cnt++);
stream->codec = handle->video.codec;
if (ctx->oformat->flags & AVFMT_GLOBALHEADER)
handle->video.codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
handle->muxer.vstream = stream;
handle->muxer.vstream->sample_aspect_ratio = handle->video.codec->sample_aspect_ratio;
}
if (ctx->oformat->flags & AVFMT_GLOBALHEADER)
handle->video.codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
handle->muxer.vstream = stream;
handle->muxer.vstream->sample_aspect_ratio = handle->video.codec->sample_aspect_ratio;
if (handle->audio.enabled)
{
AVStream *stream = av_new_stream(ctx, stream_cnt++);
stream->codec = handle->audio.codec;
stream = av_new_stream(ctx, stream_cnt++);
stream->codec = handle->audio.codec;
if (ctx->oformat->flags & AVFMT_GLOBALHEADER)
handle->audio.codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
handle->muxer.astream = stream;
}
if (ctx->oformat->flags & AVFMT_GLOBALHEADER)
handle->audio.codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
handle->muxer.astream = stream;
if (av_write_header(ctx) < 0)
return -1;
return false;
handle->muxer.ctx = ctx;
return 0;
return true;
}
#define MAX_FRAMES 64
static int SDLCALL 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->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 *
handle->video.pix_size * MAX_FRAMES));
handle->alive = true;
handle->can_sleep = true;
assert(handle->thread = SDL_CreateThread(ffemu_thread, handle));
return true;
}
static void deinit_thread(ffemu_t *handle)
{
if (handle->thread)
{
SDL_mutexP(handle->cond_lock);
handle->alive = false;
handle->can_sleep = false;
SDL_mutexV(handle->cond_lock);
SDL_CondSignal(handle->cond);
SDL_WaitThread(handle->thread, NULL);
SDL_DestroyMutex(handle->lock);
SDL_DestroyMutex(handle->cond_lock);
SDL_DestroyCond(handle->cond);
handle->thread = NULL;
}
}
static void deinit_thread_buf(ffemu_t *handle)
{
if (handle->audio_fifo)
{
fifo_free(handle->audio_fifo);
handle->audio_fifo = NULL;
}
if (handle->attr_fifo)
{
fifo_free(handle->attr_fifo);
handle->attr_fifo = NULL;
}
if (handle->video_fifo)
{
fifo_free(handle->video_fifo);
handle->video_fifo = NULL;
}
}
ffemu_t *ffemu_new(const struct ffemu_params *params)
@@ -219,20 +252,17 @@ ffemu_t *ffemu_new(const struct ffemu_params *params)
goto error;
handle->params = *params;
if (handle->params.vcodec != FFEMU_VIDEO_NONE)
handle->video.enabled = true;
if (handle->params.acodec != FFEMU_AUDIO_NONE)
handle->audio.enabled = true;
if (handle->video.enabled)
if (init_video(&handle->video, &handle->params) < 0)
goto error;
if (!init_video(&handle->video, &handle->params))
goto error;
if (handle->audio.enabled)
if (init_audio(&handle->audio, &handle->params) < 0)
goto error;
if (!init_audio(&handle->audio, &handle->params))
goto error;
if (init_muxer(handle) < 0)
if (!init_muxer(handle))
goto error;
if (!init_thread(handle))
goto error;
return handle;
@@ -246,6 +276,9 @@ void ffemu_free(ffemu_t *handle)
{
if (handle)
{
deinit_thread(handle);
deinit_thread_buf(handle);
if (handle->audio.codec)
{
avcodec_close(handle->audio.codec);
@@ -267,43 +300,112 @@ void ffemu_free(ffemu_t *handle)
if (handle->video.conv_frame_buf)
av_free(handle->video.conv_frame_buf);
if (handle->video.sws_ctx)
sws_freeContext(handle->video.sws_ctx);
free(handle);
}
}
// Need to make this thread based, but hey.
int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data)
{
if (!handle->video.enabled)
return -1;
for (;;)
{
SDL_mutexP(handle->lock);
unsigned avail = fifo_write_avail(handle->attr_fifo);
SDL_mutexV(handle->lock);
// This is deprecated, can't find a replacement... :(
// Hardcode pixel format for now (SNES)
struct SwsContext *conv_ctx = sws_getContext(data->width, data->height, PIX_FMT_RGB555LE,
handle->params.out_width, handle->params.out_height, PIX_FMT_YUV420P, handle->params.rescaler == FFEMU_RESCALER_LANCZOS ? SWS_LANCZOS : SWS_POINT,
if (!handle->alive)
return -1;
if (avail >= sizeof(*data))
break;
SDL_mutexP(handle->cond_lock);
if (handle->can_sleep)
{
handle->can_sleep = false;
SDL_CondWait(handle->cond, handle->cond_lock);
handle->can_sleep = true;
}
else
SDL_CondSignal(handle->cond);
SDL_mutexV(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);
return 0;
}
int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
{
for (;;)
{
SDL_mutexP(handle->lock);
unsigned avail = fifo_write_avail(handle->audio_fifo);
SDL_mutexV(handle->lock);
if (!handle->alive)
return -1;
if (avail >= data->frames * handle->params.channels * sizeof(int16_t))
break;
SDL_mutexP(handle->cond_lock);
if (handle->can_sleep)
{
handle->can_sleep = false;
SDL_CondWait(handle->cond, handle->cond_lock);
handle->can_sleep = true;
}
else
SDL_CondSignal(handle->cond);
SDL_mutexV(handle->cond_lock);
}
SDL_mutexP(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);
return 0;
}
static int ffemu_push_video_thread(ffemu_t *handle, const struct ffemu_video_data *data)
{
handle->video.sws_ctx = sws_getCachedContext(handle->video.sws_ctx, data->width, data->height, handle->video.fmt,
handle->params.out_width, handle->params.out_height, PIX_FMT_RGB32, SWS_POINT,
NULL, NULL, NULL);
int linesize = data->pitch;
sws_scale(conv_ctx, (const uint8_t* const*)&data->data, &linesize, 0, handle->params.out_width, handle->video.conv_frame->data, handle->video.conv_frame->linesize);
sws_scale(handle->video.sws_ctx, (const uint8_t* const*)&data->data, &linesize, 0,
handle->video.codec->height, handle->video.conv_frame->data, handle->video.conv_frame->linesize);
handle->video.conv_frame->pts = handle->video.frame_cnt;
handle->video.conv_frame->display_picture_number = handle->video.frame_cnt;
int outsize = avcodec_encode_video(handle->video.codec, handle->video.outbuf, handle->video.outbuf_size, handle->video.conv_frame);
int outsize = avcodec_encode_video(handle->video.codec, handle->video.outbuf,
handle->video.outbuf_size, handle->video.conv_frame);
if (outsize < 0)
return -1;
sws_freeContext(conv_ctx);
AVPacket pkt;
av_init_packet(&pkt);
pkt.stream_index = handle->muxer.vstream->index;
pkt.data = handle->video.outbuf;
pkt.size = outsize;
pkt.pts = av_rescale_q(handle->video.codec->coded_frame->pts, handle->video.codec->time_base, handle->muxer.vstream->time_base);
pkt.pts = av_rescale_q(handle->video.codec->coded_frame->pts, handle->video.codec->time_base,
handle->muxer.vstream->time_base);
if (handle->video.codec->coded_frame->key_frame)
pkt.flags |= AV_PKT_FLAG_KEY;
@@ -319,16 +421,8 @@ int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data)
return 0;
}
int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
static int ffemu_push_audio_thread(ffemu_t *handle, const struct ffemu_audio_data *data)
{
if (!handle->audio.enabled)
return -1;
AVPacket pkt;
av_init_packet(&pkt);
pkt.stream_index = handle->muxer.astream->index;
pkt.data = handle->audio.outbuf;
size_t written_frames = 0;
while (written_frames < data->frames)
{
@@ -344,6 +438,10 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
if (handle->audio.frames_in_buffer == (size_t)handle->audio.codec->frame_size)
{
AVPacket pkt;
av_init_packet(&pkt);
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);
if (out_size < 0)
@@ -351,13 +449,9 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
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.flags |= AV_PKT_FLAG_KEY;
handle->audio.frames_in_buffer = 0;
@@ -375,38 +469,147 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data)
int ffemu_finalize(ffemu_t *handle)
{
// Push out delayed frames. (MPEG codecs)
if (handle->video.enabled)
deinit_thread(handle);
// Push out frames still stuck in queue.
void *video_buf = av_malloc(2 * handle->params.fb_width * handle->params.fb_height * handle->video.pix_size);
if (video_buf)
{
AVPacket pkt;
av_init_packet(&pkt);
pkt.stream_index = handle->muxer.vstream->index;
pkt.data = handle->video.outbuf;
int out_size = 0;
do
struct ffemu_video_data attr_buf;
while (fifo_read_avail(handle->attr_fifo) >= sizeof(attr_buf))
{
out_size = avcodec_encode_video(handle->video.codec, handle->video.outbuf, handle->video.outbuf_size, NULL);
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);
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);
}
av_free(video_buf);
}
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);
}
deinit_thread_buf(handle);
// Push out delayed frames. (MPEG codecs)
AVPacket pkt;
av_init_packet(&pkt);
pkt.stream_index = handle->muxer.vstream->index;
pkt.data = handle->video.outbuf;
int out_size = 0;
do
{
out_size = avcodec_encode_video(handle->video.codec, handle->video.outbuf, handle->video.outbuf_size, NULL);
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);
// Write final data.
av_write_trailer(handle->muxer.ctx);
return 0;
}
static int SDLCALL ffemu_thread(void *data)
{
ffemu_t *ff = data;
// For some reason, FFmpeg has a tendency to crash if we don't overallocate a bit. :s
void *video_buf = av_malloc(2 * ff->params.fb_width * ff->params.fb_height * ff->video.pix_size);
assert(video_buf);
size_t audio_buf_size = 128 * ff->params.channels * sizeof(int16_t);
int16_t *audio_buf = av_malloc(audio_buf_size);
assert(audio_buf);
struct ffemu_video_data attr_buf;
while (ff->alive)
{
bool avail_video = false;
bool avail_audio = false;
SDL_mutexP(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);
if (!avail_video && !avail_audio)
{
SDL_mutexP(ff->cond_lock);
if (ff->can_sleep)
{
ff->can_sleep = false;
SDL_CondWait(ff->cond, ff->cond_lock);
ff->can_sleep = true;
}
else
SDL_CondSignal(ff->cond);
SDL_mutexV(ff->cond_lock);
}
if (avail_video)
{
SDL_mutexP(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);
attr_buf.data = video_buf;
ffemu_push_video_thread(ff, &attr_buf);
}
if (avail_audio)
{
SDL_mutexP(ff->lock);
fifo_read(ff->audio_fifo, audio_buf, audio_buf_size);
SDL_mutexV(ff->lock);
SDL_CondSignal(ff->cond);
struct ffemu_audio_data aud = {
.frames = 128,
.data = audio_buf
};
ffemu_push_audio_thread(ff, &aud);
}
}
av_free(video_buf);
av_free(audio_buf);
return 0;
}
+7 -43
View File
@@ -8,36 +8,6 @@
extern "C" {
#endif
// Available video codecs
typedef enum ffemu_video_codec
{
FFEMU_VIDEO_NONE,
FFEMU_VIDEO_H264,
FFEMU_VIDEO_MPEG4,
} ffemu_video_codec;
// Available audio codecs
typedef enum ffemu_audio_codec
{
FFEMU_AUDIO_NONE,
FFEMU_AUDIO_VORBIS,
FFEMU_AUDIO_MP3,
FFEMU_AUDIO_AAC,
} ffemu_audio_codec;
// Available pixel formats
typedef enum ffemu_pixel_format
{
FFEMU_FMT_XBGR1555,
FFEMU_FMT_RGB888,
} ffemu_pixel_format;
typedef enum ffemu_rescaler
{
FFEMU_RESCALER_LANCZOS,
FFEMU_RESCALER_POINT
} ffemu_rescaler;
struct ffemu_rational
{
unsigned num;
@@ -47,20 +17,15 @@ struct ffemu_rational
// Parameters passed to ffemu_new()
struct ffemu_params
{
// Video codec to use. If not recording video, select FFEMU_VIDEO_NONE.
ffemu_video_codec vcodec;
// Desired output resolution.
unsigned out_width;
unsigned out_height;
// Total size of framebuffer used in input.
unsigned fb_width;
unsigned fb_height;
float aspect_ratio;
// Rescaler for video.
ffemu_rescaler rescaler;
// Pixel format for video input.
ffemu_pixel_format format;
// FPS of video input.
struct ffemu_rational fps;
@@ -71,15 +36,15 @@ struct ffemu_params
// Define some video codec dependent option. (E.g. h264 profiles)
uint64_t video_opt;
// Audio codec. If not recording audio, select FFEMU_AUDIO_NONE.
ffemu_audio_codec acodec;
// 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;
@@ -119,7 +84,6 @@ int ffemu_push_audio(ffemu_t *handle, const struct ffemu_audio_data *data);
int ffemu_finalize(ffemu_t *handle);
#ifdef __cplusplus
}
#endif
+111
View File
@@ -0,0 +1,111 @@
/* 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 "screenshot.h"
#include "strl.h"
#include <stdio.h>
#include <time.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "general.h"
// Simple 24bpp .BMP writer.
static void write_header(FILE *file, unsigned width, unsigned height)
{
unsigned line_size = (width * 3 + 3) & ~3;
unsigned size = line_size * height + 54;
unsigned size_array = line_size * height;
// Generic BMP stuff.
const uint8_t header[] = {
'B', 'M',
(size >> 0) & 0xff, (size >> 8) & 0xff, (size >> 16) & 0xff, (size >> 24) & 0xff,
0, 0, 0, 0,
54, 0, 0, 0,
40, 0, 0, 0,
(width >> 0) & 0xff, (width >> 8) & 0xff, (width >> 16) & 0xff, (width >> 24) & 0xff,
(height >> 0) & 0xff, (height >> 8) & 0xff, (height >> 16) & 0xff, (height >> 24) & 0xff,
1, 0,
24, 0,
0, 0, 0, 0,
(size_array >> 0) & 0xff, (size_array >> 8) & 0xff, (size_array >> 16) & 0xff, (size_array >> 24) & 0xff,
19, 11, 0, 0,
19, 11, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0
};
fwrite(header, 1, sizeof(header), file);
}
static void dump_content(FILE *file, const uint16_t *frame, unsigned width, unsigned height, unsigned pitch)
{
pitch >>= 1;
unsigned line_size = (width * 3 + 3) & ~3;
uint8_t line[line_size];
memset(line, 0, sizeof(line));
// BMP likes reverse ordering for some reason :v
for (int j = height - 1; j >= 0; j--)
{
uint8_t *dst = line;
const uint16_t *src = frame + j * pitch;
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;
}
fwrite(line, 1, sizeof(line), file);
}
}
bool screenshot_dump(const char *folder, const uint16_t *frame,
unsigned width, unsigned height, unsigned pitch)
{
time_t cur_time;
time(&cur_time);
char timefmt[128];
strftime(timefmt, sizeof(timefmt), "SSNES-%m%d-%H%M%S.bmp", localtime(&cur_time));
char filename[256];
strlcpy(filename, folder, sizeof(filename));
strlcat(filename, "/", sizeof(filename));
strlcat(filename, timefmt, sizeof(filename));
FILE *file = fopen(filename, "wb");
if (!file)
{
SSNES_ERR("Failed to open file \"%s\" for screenshot.\n", filename);
return false;
}
write_header(file, width, height);
dump_content(file, frame, width, height, pitch);
fclose(file);
return true;
}
+27
View File
@@ -0,0 +1,27 @@
/* 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_SCREENSHOT_H
#define __SSNES_SCREENSHOT_H
#include <stdint.h>
#include <stdbool.h>
bool screenshot_dump(const char *folder, const uint16_t *frame,
unsigned width, unsigned height, unsigned pitch);
#endif
+127 -28
View File
@@ -18,7 +18,7 @@
#include "general.h"
#include "conf/config_file.h"
#include <assert.h>
#include <string.h>
#include "strl.h"
#include "config.def.h"
#ifdef HAVE_CONFIG_H
@@ -47,6 +47,12 @@ static void set_defaults(void)
case VIDEO_XVIDEO:
def_video = "xvideo";
break;
case VIDEO_SDL:
def_video = "sdl";
break;
case VIDEO_EXT:
def_video = "ext";
break;
default:
break;
}
@@ -65,18 +71,27 @@ static void set_defaults(void)
case AUDIO_ROAR:
def_audio = "roar";
break;
case AUDIO_COREAUDIO:
def_audio = "coreaudio";
break;
case AUDIO_AL:
def_audio = "openal";
break;
case AUDIO_SDL:
def_audio = "sdl";
break;
case AUDIO_DSOUND:
def_audio = "dsound";
break;
case AUDIO_XAUDIO:
def_audio = "xaudio";
break;
case AUDIO_PULSE:
def_audio = "pulse";
break;
case AUDIO_EXT:
def_audio = "ext";
break;
default:
break;
}
@@ -94,20 +109,23 @@ static void set_defaults(void)
}
if (def_video)
strncpy(g_settings.video.driver, def_video, sizeof(g_settings.video.driver) - 1);
strlcpy(g_settings.video.driver, def_video, sizeof(g_settings.video.driver));
if (def_audio)
strncpy(g_settings.audio.driver, def_audio, sizeof(g_settings.audio.driver) - 1);
strlcpy(g_settings.audio.driver, def_audio, sizeof(g_settings.audio.driver));
if (def_input)
strncpy(g_settings.input.driver, def_input, sizeof(g_settings.input.driver) - 1);
strlcpy(g_settings.input.driver, def_input, sizeof(g_settings.input.driver));
g_settings.video.xscale = xscale;
g_settings.video.yscale = yscale;
g_settings.video.fullscreen = fullscreen;
g_settings.video.fullscreen = g_extern.force_fullscreen ? true : fullscreen;
g_settings.video.fullscreen_x = fullscreen_x;
g_settings.video.fullscreen_y = fullscreen_y;
g_settings.video.force_16bit = force_16bit;
g_settings.video.disable_composition = disable_composition;
g_settings.video.vsync = vsync;
g_settings.video.smooth = video_smooth;
g_settings.video.force_aspect = force_aspect;
g_settings.video.crop_overscan = crop_overscan;
g_settings.video.aspect_ratio = SNES_ASPECT_RATIO;
g_settings.video.shader_type = SSNES_SHADER_AUTO;
@@ -124,12 +142,15 @@ static void set_defaults(void)
g_settings.video.second_pass_smooth = second_pass_smooth;
#endif
g_settings.video.hires_record = hires_record;
g_settings.video.post_filter_record = post_filter_record;
g_settings.audio.enable = audio_enable;
g_settings.audio.out_rate = out_rate;
g_settings.audio.in_rate = in_rate;
g_settings.audio.rate_step = audio_rate_step;
if (audio_device)
strncpy(g_settings.audio.device, audio_device, sizeof(g_settings.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;
@@ -144,15 +165,10 @@ static void set_defaults(void)
g_settings.autosave_interval = autosave_interval;
assert(sizeof(g_settings.input.binds[0]) >= sizeof(snes_keybinds_1));
assert(sizeof(g_settings.input.binds[1]) >= sizeof(snes_keybinds_2));
assert(sizeof(g_settings.input.binds[2]) >= sizeof(snes_keybinds_3));
assert(sizeof(g_settings.input.binds[3]) >= sizeof(snes_keybinds_4));
assert(sizeof(g_settings.input.binds[4]) >= sizeof(snes_keybinds_5));
assert(sizeof(g_settings.input.binds[1]) >= sizeof(snes_keybinds_rest));
memcpy(g_settings.input.binds[0], snes_keybinds_1, sizeof(snes_keybinds_1));
memcpy(g_settings.input.binds[1], snes_keybinds_2, sizeof(snes_keybinds_2));
memcpy(g_settings.input.binds[2], snes_keybinds_3, sizeof(snes_keybinds_3));
memcpy(g_settings.input.binds[3], snes_keybinds_4, sizeof(snes_keybinds_4));
memcpy(g_settings.input.binds[4], snes_keybinds_5, sizeof(snes_keybinds_5));
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.netplay_client_swap_input = netplay_client_swap_input;
@@ -175,6 +191,7 @@ void parse_config(void)
#endif
}
#ifdef HAVE_CONFIGFILE
static config_file_t *open_default_config_file(void)
{
config_file_t *conf = NULL;
@@ -187,8 +204,8 @@ static config_file_t *open_default_config_file(void)
if (appdata)
{
char conf_path[strlen(appdata) + strlen("/ssnes.cfg ")];
strcpy(conf_path, appdata);
strcat(conf_path, "/ssnes.cfg");
strlcpy(conf_path, appdata, sizeof(conf_path));
strlcat(conf_path, "/ssnes.cfg", sizeof(conf_path));
conf = config_file_new(conf_path);
}
}
@@ -197,8 +214,8 @@ static config_file_t *open_default_config_file(void)
if (home)
{
char conf_path[strlen(home) + strlen("/.ssnes.cfg ")];
strcpy(conf_path, home);
strcat(conf_path, "/.ssnes.cfg");
strlcpy(conf_path, home, sizeof(conf_path));
strlcat(conf_path, "/.ssnes.cfg", sizeof(conf_path));
conf = config_file_new(conf_path);
}
if (!conf)
@@ -206,21 +223,21 @@ static config_file_t *open_default_config_file(void)
#else
const char *xdg = getenv("XDG_CONFIG_HOME");
if (!xdg)
SSNES_WARN("XDG_CONFIG_HOME is not defined. Will look for config in $HOME/.ssnesrc ...\n");
SSNES_WARN("XDG_CONFIG_HOME is not defined. Will look for config in $HOME/.ssnes.cfg ...\n");
const char *home = getenv("HOME");
if (xdg)
{
char conf_path[strlen(xdg) + strlen("/ssnes/ssnes.cfg ")];
strcpy(conf_path, xdg);
strcat(conf_path, "/ssnes/ssnes.cfg");
strlcpy(conf_path, xdg, sizeof(conf_path));
strlcat(conf_path, "/ssnes/ssnes.cfg", sizeof(conf_path));
conf = config_file_new(conf_path);
}
else if (home)
{
char conf_path[strlen(home) + strlen("/.ssnes.cfg ")];
strcpy(conf_path, home);
strcat(conf_path, "/.ssnes.cfg");
strlcpy(conf_path, home, sizeof(conf_path));
strlcat(conf_path, "/.ssnes.cfg", sizeof(conf_path));
conf = config_file_new(conf_path);
}
// Try this as a last chance...
@@ -230,6 +247,7 @@ static config_file_t *open_default_config_file(void)
return conf;
}
#endif
// Macros to ease config getting.
#define CONFIG_GET_BOOL(var, key) if (config_get_bool(conf, key, &tmp_bool)) \
@@ -244,7 +262,7 @@ static config_file_t *open_default_config_file(void)
#define CONFIG_GET_STRING(var, key) do { \
if (config_get_string(conf, key, &tmp_str)) \
{ \
strncpy(g_settings.var, tmp_str, sizeof(g_settings.var) - 1); \
strlcpy(g_settings.var, tmp_str, sizeof(g_settings.var)); \
free(tmp_str); \
} \
} while(0)
@@ -281,10 +299,18 @@ static void parse_config_file(void)
CONFIG_GET_DOUBLE(video.yscale, "video_yscale");
CONFIG_GET_INT(video.fullscreen_x, "video_fullscreen_x");
CONFIG_GET_INT(video.fullscreen_y, "video_fullscreen_y");
CONFIG_GET_BOOL(video.fullscreen, "video_fullscreen");
if (!g_extern.force_fullscreen)
{
CONFIG_GET_BOOL(video.fullscreen, "video_fullscreen");
}
CONFIG_GET_BOOL(video.force_16bit, "video_force_16bit");
CONFIG_GET_BOOL(video.disable_composition, "video_disable_composition");
CONFIG_GET_BOOL(video.vsync, "video_vsync");
CONFIG_GET_BOOL(video.smooth, "video_smooth");
CONFIG_GET_BOOL(video.force_aspect, "video_force_aspect");
CONFIG_GET_BOOL(video.crop_overscan, "video_crop_overscan");
CONFIG_GET_DOUBLE(video.aspect_ratio, "video_aspect_ratio");
CONFIG_GET_STRING(video.cg_shader_path, "video_cg_shader");
@@ -302,8 +328,13 @@ static void parse_config_file(void)
CONFIG_GET_DOUBLE(video.msg_pos_y, "video_message_pos_y");
#endif
#ifdef HAVE_FILTER
CONFIG_GET_BOOL(video.hires_record, "video_hires_record");
CONFIG_GET_BOOL(video.post_filter_record, "video_post_filter_record");
#ifdef HAVE_DYLIB
CONFIG_GET_STRING(video.filter_path, "video_filter");
CONFIG_GET_STRING(video.external_driver, "video_external_driver");
CONFIG_GET_STRING(audio.external_driver, "audio_external_driver");
#endif
#if defined(HAVE_CG) || defined(HAVE_XML)
@@ -356,8 +387,10 @@ static void parse_config_file(void)
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");
CONFIG_GET_STRING(screenshot_directory, "screenshot_directory");
CONFIG_GET_BOOL(rewind_enable, "rewind_enable");
@@ -369,6 +402,8 @@ static void parse_config_file(void)
CONFIG_GET_BOOL(pause_nonactive, "pause_nonactive");
CONFIG_GET_INT(autosave_interval, "autosave_interval");
CONFIG_GET_STRING(cheat_database, "cheat_database_path");
read_keybinds(conf);
config_file_free(conf);
@@ -400,6 +435,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
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)
@@ -414,6 +450,11 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
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)
@@ -429,6 +470,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
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)
@@ -443,6 +485,11 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
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)
@@ -458,6 +505,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
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)
@@ -472,6 +520,11 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
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)
@@ -487,6 +540,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
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)
@@ -501,6 +555,11 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
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)
@@ -516,6 +575,7 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
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)
@@ -530,6 +590,11 @@ static const struct bind_map bind_maps[MAX_PLAYERS][MAX_BINDS - 1] = {
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)
},
};
@@ -547,17 +612,20 @@ static const struct key_map sdlk_map[] = {
{ "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 },
{ "kp_plus", SDLK_KP_PLUS },
{ "kp_minus", SDLK_KP_MINUS },
{ "add", SDLK_KP_PLUS },
{ "subtract", SDLK_KP_MINUS },
{ "f1", SDLK_F1 },
{ "f2", SDLK_F2 },
{ "f3", SDLK_F3 },
@@ -570,6 +638,37 @@ static const struct key_map sdlk_map[] = {
{ "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 },
{ "nul", SDLK_UNKNOWN },
};
+180
View File
@@ -0,0 +1,180 @@
/* 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/>.
*/
// SHA256 implementation from bSNES. Written by valditx.
//
#include "sha256.h"
#include <string.h>
#include <stdio.h>
#define PTR(t, a) ((t*)(a))
#define SWAP32(x) ((uint32_t)( \
(((uint32_t)(x) & 0x000000ff) << 24) | \
(((uint32_t)(x) & 0x0000ff00) << 8) | \
(((uint32_t)(x) & 0x00ff0000) >> 8) | \
(((uint32_t)(x) & 0xff000000) >> 24) \
))
#define ST32(a, d) *PTR(uint32_t, a) = (d)
#define ST32BE(a, d) ST32(a, SWAP32(d))
#define LD32(a) *PTR(uint32_t, a)
#define LD32BE(a) SWAP32(LD32(a))
#define LSL32(x, n) ((uint32_t)(x) << (n))
#define LSR32(x, n) ((uint32_t)(x) >> (n))
#define ROR32(x, n) (LSR32(x, n) | LSL32(x, 32 - (n)))
// First 32 bits of the fractional parts of the square roots of the first 8 primes 2..19
static const uint32_t T_H[8] = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
};
// First 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311
static const uint32_t T_K[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
};
struct sha256_ctx
{
uint8_t in[64];
unsigned inlen;
uint32_t w[64];
uint32_t h[8];
uint64_t len;
};
static void sha256_init(struct sha256_ctx *p)
{
memset(p, 0, sizeof(struct sha256_ctx));
memcpy(p->h, T_H, sizeof(T_H));
}
static void sha256_block(struct sha256_ctx *p)
{
unsigned i;
uint32_t s0, s1;
uint32_t a, b, c, d, e, f, g, h;
uint32_t t1, t2, maj, ch;
for (i = 0; i < 16; i++)
p->w[i] = LD32BE(p->in + i * 4);
for (i = 16; i < 64; i++)
{
s0 = ROR32(p->w[i - 15], 7) ^ ROR32(p->w[i - 15], 18) ^ LSR32(p->w[i - 15], 3);
s1 = ROR32(p->w[i - 2], 17) ^ ROR32(p->w[i - 2], 19) ^ LSR32(p->w[i - 2], 10);
p->w[i] = p->w[i - 16] + s0 + p->w[i - 7] + s1;
}
a = p->h[0]; b = p->h[1]; c = p->h[2]; d = p->h[3];
e = p->h[4]; f = p->h[5]; g = p->h[6]; h = p->h[7];
for (i = 0; i < 64; i++)
{
s0 = ROR32(a, 2) ^ ROR32(a, 13) ^ ROR32(a, 22);
maj = (a & b) ^ (a & c) ^ (b & c);
t2 = s0 + maj;
s1 = ROR32(e, 6) ^ ROR32(e, 11) ^ ROR32(e, 25);
ch = (e & f) ^ (~e & g);
t1 = h + s1 + ch + T_K[i] + p->w[i];
h = g; g = f; f = e; e = d + t1;
d = c; c = b; b = a; a = t1 + t2;
}
p->h[0] += a; p->h[1] += b; p->h[2] += c; p->h[3] += d;
p->h[4] += e; p->h[5] += f; p->h[6] += g; p->h[7] += h;
// Next block
p->inlen = 0;
}
static void sha256_chunk(struct sha256_ctx *p, const uint8_t *s, unsigned len)
{
unsigned l;
p->len += len;
while (len)
{
l = 64 - p->inlen;
l = (len < l) ? len : l;
memcpy(p->in + p->inlen, s, l);
s += l;
p->inlen += l;
len -= l;
if (p->inlen == 64)
sha256_block(p);
}
}
static void sha256_final(struct sha256_ctx *p)
{
uint64_t len;
p->in[p->inlen++] = 0x80;
if (p->inlen > 56)
{
memset(p->in + p->inlen, 0, 64 - p->inlen);
sha256_block(p);
}
memset(p->in + p->inlen, 0, 56 - p->inlen);
len = p->len << 3;
ST32BE(p->in + 56, len >> 32);
ST32BE(p->in + 60, len);
sha256_block(p);
}
static void sha256_subhash(struct sha256_ctx *p, uint32_t *t)
{
for (unsigned i = 0; i < 8; i++)
ST32BE(t++, p->h[i]);
}
void sha256_hash(char *out, const uint8_t *in, size_t size)
{
struct sha256_ctx sha;
union
{
uint32_t u32[8];
uint8_t u8[32];
} shahash;
sha256_init(&sha);
sha256_chunk(&sha, in, size);
sha256_final(&sha);
sha256_subhash(&sha, shahash.u32);
for (unsigned i = 0; i < 32; i++)
snprintf(out + 2 * i, 3, "%02x", (unsigned)shahash.u8[i]);
}
+28
View File
@@ -0,0 +1,28 @@
/* 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_SHA256_H
#define __SSNES_SHA256_H
#include <stdint.h>
#include <stddef.h>
// Hashes sha256 and outputs a human readable string for comparing with the cheat XML values.
void sha256_hash(char *out, const uint8_t *in, size_t size);
#endif
+6 -2
View File
@@ -31,7 +31,10 @@ extract_zip()
die "Cannot find .smc nor .sfc file in archive"
fi
unzip -p "$1" "$FILENAME" > "$TMPROM"
# The people who made the [!] naming convention obviously have never used regex ... <_<
FILENAME_FIXED="`echo $FILENAME | sed -e 's|\[|\\\\[|g' -e 's|!|\\\\!|g' -e 's|\]|\\\\]|g'`"
unzip -p "$1" "$FILENAME_FIXED" > "$TMPROM"
}
extract_7z()
@@ -79,13 +82,14 @@ esac
SAVENAME="`basename \"$COMP_FILE\" $EXTENSION`"
DIRNAME="`dirname \"$COMP_FILE\"`"
SAVENAME="${SAVENAME}.srm"
echo "Savename: ${DIRNAME}/${SAVENAME}"
SSNES_PATH="`which ssnes`"
if [ -z "$SSNES_PATH" ]; then
die "Cannot find SSNES in path."
fi
echo "Temp ROM: $TMPROM"
echo "Savename: ${DIRNAME}/${SAVENAME}"
"$SSNES_PATH" "$TMPROM" -s "${DIRNAME}/${SAVENAME}" "$@"
rm "$TMPROM"
rm -r "$TMPDIR"
+396 -117
View File
@@ -31,6 +31,9 @@
#include "rewind.h"
#include "movie.h"
#include "netplay.h"
#include "strl.h"
#include "screenshot.h"
#include "cheats.h"
#include <assert.h>
#ifdef HAVE_SRC
#include <samplerate.h>
@@ -56,13 +59,26 @@ struct global g_extern = {
};
// To avoid continous switching if we hold the button down, we require that the button must go from pressed, unpressed back to pressed to be able to toggle between then.
static void set_fast_forward_button(bool new_button_state)
static void set_fast_forward_button(bool new_button_state, bool new_hold_button_state)
{
bool update_sync = false;
static bool old_button_state = false;
static bool old_hold_button_state = false;
static bool syncing_state = false;
if (new_button_state && !old_button_state)
{
syncing_state = !syncing_state;
update_sync = true;
}
else if (old_hold_button_state != new_hold_button_state)
{
syncing_state = new_hold_button_state;
update_sync = true;
}
if (update_sync)
{
if (g_extern.video_active)
driver.video->set_nonblock_state(driver.video_data, syncing_state);
if (g_extern.audio_active)
@@ -73,24 +89,69 @@ static void set_fast_forward_button(bool new_button_state)
else
g_extern.audio_data.chunk_size = g_extern.audio_data.block_chunk_size;
}
old_button_state = new_button_state;
old_hold_button_state = new_hold_button_state;
}
static void take_screenshot(const uint16_t *frame, unsigned width, unsigned height)
{
if (!*(g_settings.screenshot_directory))
return;
bool ret = screenshot_dump(g_settings.screenshot_directory, frame,
width, height, (height == 448 || height == 478) ? 1024 : 2048);
const char *msg = NULL;
if (ret)
{
SSNES_LOG("Taking screenshot!\n");
msg = "Taking screenshot!";
}
else
{
SSNES_WARN("Failed to take screenshot ...\n");
msg = "Failed to take screenshot!";
}
msg_queue_clear(g_extern.msg_queue);
msg_queue_push(g_extern.msg_queue, msg, 1, 180);
}
// libsnes: 0.065
// Format received is 16-bit 0RRRRRGGGGGBBBBB
static void video_frame(const uint16_t *data, unsigned width, unsigned height)
{
if ( !g_extern.video_active )
if (!g_extern.video_active)
return;
if (g_settings.video.crop_overscan)
{
if (height == 239)
{
data += 7 * 1024; // Skip 7 top scanlines.
height = 224;
}
else if (height == 478)
{
data += 15 * 512; // Skip 15 top scanlines.
height = 448;
}
}
if (g_extern.do_screenshot)
take_screenshot(data, width, height);
// Slightly messy code,
// but we really need to do processing before blocking on VSync for best possible scheduling.
#ifdef HAVE_FFMPEG
if (g_extern.recording)
if (g_extern.recording && (!g_extern.filter.active || !g_settings.video.post_filter_record))
{
struct ffemu_video_data ffemu_data = {
.data = data,
.pitch = height == 448 || height == 478 ? 1024 : 2048,
.width = width,
.height = height
.height = height,
};
ffemu_push_video(g_extern.rec, &ffemu_data);
}
@@ -98,7 +159,7 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height)
const char *msg = msg_queue_pull(g_extern.msg_queue);
#ifdef HAVE_FILTER
#ifdef HAVE_DYLIB
if (g_extern.filter.active)
{
unsigned owidth = width;
@@ -106,6 +167,20 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height)
g_extern.filter.psize(&owidth, &oheight);
g_extern.filter.prender(g_extern.filter.colormap, g_extern.filter.buffer,
g_extern.filter.pitch, data, (height == 448 || height == 478) ? 1024 : 2048, width, height);
#ifdef HAVE_FFMPEG
if (g_extern.recording && g_settings.video.post_filter_record)
{
struct ffemu_video_data ffemu_data = {
.data = g_extern.filter.buffer,
.pitch = g_extern.filter.pitch,
.width = owidth,
.height = oheight,
};
ffemu_push_video(g_extern.rec, &ffemu_data);
}
#endif
if (!driver.video->frame(driver.video_data, g_extern.filter.buffer, owidth, oheight, g_extern.filter.pitch, msg))
g_extern.video_active = false;
}
@@ -119,75 +194,105 @@ static void video_frame(const uint16_t *data, unsigned width, unsigned height)
static void audio_sample(uint16_t left, uint16_t right)
{
if ( !g_extern.audio_active )
if (!g_extern.audio_active)
return;
const float *output_data = NULL;
unsigned output_frames = 0;
#ifdef HAVE_FFMPEG
g_extern.audio_data.conv_outsamples[g_extern.audio_data.data_ptr] = left;
#endif
g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(int16_t)left/0x8000;
#ifdef HAVE_FFMPEG
g_extern.audio_data.conv_outsamples[g_extern.audio_data.data_ptr] = right;
#endif
g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(int16_t)right/0x8000;
if (g_extern.audio_data.data_ptr < g_extern.audio_data.chunk_size)
return;
#ifdef HAVE_FFMPEG
if (g_extern.recording)
{
static int16_t static_data[2];
static_data[0] = left;
static_data[1] = right;
struct ffemu_audio_data ffemu_data = {
.data = static_data,
.frames = 1
.data = g_extern.audio_data.conv_outsamples,
.frames = g_extern.audio_data.data_ptr / 2
};
ffemu_push_audio(g_extern.rec, &ffemu_data);
}
#endif
g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(int16_t)left/0x8000;
g_extern.audio_data.data[g_extern.audio_data.data_ptr++] = (float)(int16_t)right/0x8000;
ssnes_dsp_input_t dsp_input = {
.samples = g_extern.audio_data.data,
.frames = g_extern.audio_data.data_ptr / 2
};
if (g_extern.audio_data.data_ptr >= g_extern.audio_data.chunk_size)
{
ssnes_dsp_output_t dsp_output = {
.should_resample = SSNES_TRUE
};
if (g_extern.frame_is_reverse) // Disable fucked up audio when rewinding...
memset(g_extern.audio_data.data, 0, g_extern.audio_data.chunk_size * sizeof(float));
if (g_extern.audio_data.dsp_plugin)
g_extern.audio_data.dsp_plugin->process(g_extern.audio_data.dsp_handle, &dsp_output, &dsp_input);
if (g_extern.frame_is_reverse) // Disable fucked up audio when rewinding...
memset(g_extern.audio_data.data, 0, g_extern.audio_data.chunk_size * sizeof(float));
#ifdef HAVE_SRC
SRC_DATA src_data = {
SRC_DATA src_data = {
#else
struct hermite_data src_data = {
struct hermite_data src_data = {
#endif
.data_in = g_extern.audio_data.data,
.data_out = g_extern.audio_data.outsamples,
.input_frames = g_extern.audio_data.chunk_size / 2,
.output_frames = g_extern.audio_data.chunk_size * 8,
.end_of_input = 0,
.src_ratio = (double)g_settings.audio.out_rate / (double)g_settings.audio.in_rate,
};
.data_in = dsp_output.samples ? dsp_output.samples : g_extern.audio_data.data,
.data_out = g_extern.audio_data.outsamples,
.input_frames = dsp_output.samples ? dsp_output.frames : (g_extern.audio_data.data_ptr / 2),
.output_frames = g_extern.audio_data.chunk_size * 8,
.end_of_input = 0,
.src_ratio = (double)g_settings.audio.out_rate / (double)g_settings.audio.in_rate,
};
if (dsp_output.should_resample)
{
#ifdef HAVE_SRC
src_process(g_extern.audio_data.source, &src_data);
#else
hermite_process(g_extern.audio_data.source, &src_data);
#endif
if (g_extern.audio_data.use_float)
{
if (driver.audio->write(driver.audio_data, g_extern.audio_data.outsamples, src_data.output_frames_gen * sizeof(float) * 2) < 0)
{
fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n");
g_extern.audio_active = false;
}
}
else
{
for (unsigned i = 0; i < src_data.output_frames_gen * 2; i++)
{
int32_t val = g_extern.audio_data.outsamples[i] * 0x8000;
g_extern.audio_data.conv_outsamples[i] = (val > 0x7FFF) ? 0x7FFF : (val < -0x8000 ? -0x8000 : (int16_t)val);
}
if (driver.audio->write(driver.audio_data, g_extern.audio_data.conv_outsamples, src_data.output_frames_gen * sizeof(int16_t) * 2) < 0)
{
fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n");
g_extern.audio_active = false;
}
}
g_extern.audio_data.data_ptr = 0;
output_data = g_extern.audio_data.outsamples;
output_frames = src_data.output_frames_gen;
}
else
{
output_data = dsp_output.samples;
output_frames = dsp_output.frames;
}
if (g_extern.audio_data.use_float)
{
if (driver.audio->write(driver.audio_data, output_data, output_frames * sizeof(float) * 2) < 0)
{
fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n");
g_extern.audio_active = false;
}
}
else
{
for (unsigned i = 0; i < output_frames * 2; i++)
{
int32_t val = output_data[i] * 0x8000;
g_extern.audio_data.conv_outsamples[i] = (val > 0x7FFF) ? 0x7FFF : (val < -0x8000 ? -0x8000 : (int16_t)val);
}
if (driver.audio->write(driver.audio_data, g_extern.audio_data.conv_outsamples, output_frames * sizeof(int16_t) * 2) < 0)
{
fprintf(stderr, "SSNES [ERROR]: Audio backend failed to write. Will continue without sound.\n");
g_extern.audio_active = false;
}
}
g_extern.audio_data.data_ptr = 0;
}
static void input_poll(void)
@@ -209,9 +314,13 @@ static int16_t input_state(bool port, unsigned device, unsigned index, unsigned
}
}
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, port, device, index, id);
if (g_extern.bsv_movie && !g_extern.bsv_movie_playback)
@@ -220,28 +329,34 @@ static int16_t input_state(bool port, unsigned device, unsigned index, unsigned
return res;
}
static void fill_pathname(char *out_path, char *in_path, const char *replace)
static void fill_pathname(char *out_path, const char *in_path, const char *replace, size_t size)
{
char tmp_path[strlen(in_path) + 1];
strcpy(tmp_path, in_path);
strlcpy(tmp_path, in_path, sizeof(tmp_path));
char *tok = NULL;
tok = strrchr(tmp_path, '.');
if (tok != NULL)
*tok = '\0';
strcpy(out_path, tmp_path);
strcat(out_path, replace);
assert(strlcpy(out_path, tmp_path, size) < size);
assert(strlcat(out_path, replace, size) < size);
}
static void fill_pathname_noext(char *out_path, const char *in_path, const char *replace, size_t size)
{
assert(strlcpy(out_path, in_path, size) < size);
assert(strlcat(out_path, replace, size) < size);
}
#ifdef _WIN32
#define SSNES_DEFAULT_CONF_PATH_STR "\n\tDefaults to ssnes.cfg in same directory as ssnes.exe"
#define SSNES_DEFAULT_CONF_PATH_STR "\n\tDefaults to ssnes.cfg in same directory as ssnes.exe."
#elif defined(__APPLE__)
#define SSNES_DEFAULT_CONF_PATH_STR " Defaults to $HOME/.ssnes.cfg"
#define SSNES_DEFAULT_CONF_PATH_STR " Defaults to $HOME/.ssnes.cfg."
#else
#define SSNES_DEFAULT_CONF_PATH_STR " Defaults to $XDG_CONFIG_HOME/ssnes/ssnes.cfg"
#define SSNES_DEFAULT_CONF_PATH_STR " Defaults to $XDG_CONFIG_HOME/ssnes/ssnes.cfg,\n\t\tor $HOME/.ssnes.cfg, if $XDG_CONFIG_HOME is not defined."
#endif
#ifdef _WIN32
#define PACKAGE_VERSION "0.4-beta"
#define PACKAGE_VERSION "0.7"
#endif
#include "config.features.h"
@@ -252,6 +367,7 @@ static void print_features(void)
puts("");
puts("Features:");
_PSUPP(sdl, "SDL", "SDL drivers");
_PSUPP(xvideo, "XVideo", "XVideo output");
_PSUPP(alsa, "ALSA", "audio driver");
_PSUPP(oss, "OSS", "audio driver");
_PSUPP(jack, "Jack", "audio driver");
@@ -260,9 +376,10 @@ static void print_features(void)
_PSUPP(pulse, "PulseAudio", "audio driver");
_PSUPP(xaudio, "XAudio2", "audio driver");
_PSUPP(al, "OpenAL", "audio driver");
_PSUPP(filter, "Filter", "CPU based video filters");
_PSUPP(dylib, "External", "External filter and driver support");
_PSUPP(cg, "Cg", "Cg pixel shaders");
_PSUPP(xml, "XML", "bSNES XML pixel shaders");
_PSUPP(sdl_image, "SDL_image", "SDL_image image loading");
_PSUPP(fbo, "FBO", "OpenGL render-to-texture (multi-pass shaders)");
_PSUPP(dynamic, "Dynamic", "Dynamic run-time loading of libsnes library");
_PSUPP(ffmpeg, "FFmpeg", "On-the-fly recording of gameplay with libavcodec");
@@ -270,6 +387,7 @@ static void print_features(void)
_PSUPP(configfile, "Config file", "Configuration file support");
_PSUPP(freetype, "FreeType", "TTF font rendering with FreeType");
_PSUPP(netplay, "Netplay", "Peer-to-peer netplay");
_PSUPP(python, "Python", "Script support in shaders");
}
#undef _PSUPP
@@ -279,8 +397,9 @@ static void print_help(void)
puts("ssnes: Simple Super Nintendo Emulator (libsnes) -- v" PACKAGE_VERSION " --");
puts("===================================================================");
puts("Usage: ssnes [rom file] [options...]");
puts("\t-h/--help: Show this help message");
puts("\t-s/--save: Path for save file (*.srm). Required when rom is input from stdin");
puts("\t-h/--help: Show this help message.");
puts("\t-s/--save: Path for save file (*.srm). Required when rom is input from stdin.");
puts("\t-f/--fullscreen: Start SSNES in fullscreen regardless of config settings.");
puts("\t-S/--savestate: Path to use for save states. If not selected, *.state will be assumed.");
#ifdef HAVE_CONFIGFILE
puts("\t-c/--config: Path for config file." SSNES_DEFAULT_CONF_PATH_STR);
@@ -291,6 +410,7 @@ static void print_help(void)
puts("\t--sufamiA: Path to A slot of Sufami Turbo. Load Sufami base cart as regular rom.");
puts("\t--sufamiB: Path to B slot of Sufami Turbo.");
puts("\t-m/--mouse: Connect a virtual mouse into designated port of the SNES (1 or 2).");
puts("\t-N/--nodevice: Disconnects the controller device connected to the emulated SNES (1 or 2).");
puts("\t\tThis argument can be specified several times to connect more mice.");
puts("\t-p/--scope: Connect a virtual SuperScope into port 2 of the SNES.");
puts("\t-j/--justifier: Connect a virtual Konami Justifier into port 2 of the SNES.");
@@ -305,10 +425,12 @@ static void print_help(void)
#endif
#ifdef HAVE_FFMPEG
puts("\t-r/--record: Path to record video file. Settings for video/audio codecs are found in config file.");
puts("\t-r/--record: Path to record video file.\n\t\tSettings for video/audio codecs are found in config file.");
#endif
puts("\t-v/--verbose: Verbose logging");
puts("\t-U/--ups: Specifies path for UPS patch that will be applied to ROM.\n");
puts("\t-v/--verbose: Verbose logging.");
puts("\t-U/--ups: Specifies path for UPS patch that will be applied to ROM.");
puts("\t-X/--xml: Specifies path to XML memory map.");
puts("\t-D/--detach: Detach SSNES from the running console. Not relevant for all platforms.\n");
print_features();
}
@@ -316,11 +438,11 @@ static void print_help(void)
static void set_basename(const char *path)
{
char tmp[strlen(path) + 1];
strcpy(tmp, path);
strlcpy(tmp, path, sizeof(tmp));
char *dst = strrchr(tmp, '.');
if (dst)
*dst = '\0';
strncpy(g_extern.basename, tmp, sizeof(g_extern.basename) - 1);
strlcpy(g_extern.basename, tmp, sizeof(g_extern.basename));
}
static void parse_input(int argc, char *argv[])
@@ -336,6 +458,7 @@ static void parse_input(int argc, char *argv[])
struct option opts[] = {
{ "help", 0, NULL, 'h' },
{ "save", 1, NULL, 's' },
{ "fullscreen", 0, NULL, 'f' },
#ifdef HAVE_FFMPEG
{ "record", 1, NULL, 'r' },
#endif
@@ -345,6 +468,7 @@ static void parse_input(int argc, char *argv[])
{ "config", 0, NULL, 'c' },
#endif
{ "mouse", 1, NULL, 'm' },
{ "nodevice", 1, NULL, 'N' },
{ "scope", 0, NULL, 'p' },
{ "savestate", 1, NULL, 'S' },
{ "bsx", 1, NULL, 'b' },
@@ -360,6 +484,8 @@ static void parse_input(int argc, char *argv[])
{ "frames", 1, NULL, 'F' },
{ "port", 1, &val, 'p' },
{ "ups", 1, NULL, 'U' },
{ "xml", 1, NULL, 'X' },
{ "detach", 0, NULL, 'D' },
{ NULL, 0, NULL, 0 }
};
@@ -377,7 +503,7 @@ static void parse_input(int argc, char *argv[])
#define CONFIG_FILE_ARG
#endif
char optstring[] = "hs:vS:m:p4jJg:b:B:Y:Z:P:HC:F:U:" FFMPEG_RECORD_ARG CONFIG_FILE_ARG;
char optstring[] = "hs:fvS:m:p4jJg:b:B:Y:Z:P:HC:F:U:DN:X:" FFMPEG_RECORD_ARG CONFIG_FILE_ARG;
for(;;)
{
val = 0;
@@ -406,37 +532,41 @@ static void parse_input(int argc, char *argv[])
break;
case 's':
strncpy(g_extern.savefile_name_srm, optarg, sizeof(g_extern.savefile_name_srm) - 1);
strlcpy(g_extern.savefile_name_srm, optarg, sizeof(g_extern.savefile_name_srm));
g_extern.has_set_save_path = true;
break;
case 'f':
g_extern.force_fullscreen = true;
break;
case 'g':
strncpy(g_extern.gb_rom_path, optarg, sizeof(g_extern.gb_rom_path) - 1);
strlcpy(g_extern.gb_rom_path, optarg, sizeof(g_extern.gb_rom_path));
g_extern.game_type = SSNES_CART_SGB;
break;
case 'b':
strncpy(g_extern.bsx_rom_path, optarg, sizeof(g_extern.bsx_rom_path) - 1);
strlcpy(g_extern.bsx_rom_path, optarg, sizeof(g_extern.bsx_rom_path));
g_extern.game_type = SSNES_CART_BSX;
break;
case 'B':
strncpy(g_extern.bsx_rom_path, optarg, sizeof(g_extern.bsx_rom_path) - 1);
strlcpy(g_extern.bsx_rom_path, optarg, sizeof(g_extern.bsx_rom_path));
g_extern.game_type = SSNES_CART_BSX_SLOTTED;
break;
case 'Y':
strncpy(g_extern.sufami_rom_path[0], optarg, sizeof(g_extern.sufami_rom_path[0]) - 1);
strlcpy(g_extern.sufami_rom_path[0], optarg, sizeof(g_extern.sufami_rom_path[0]));
g_extern.game_type = SSNES_CART_SUFAMI;
break;
case 'Z':
strncpy(g_extern.sufami_rom_path[1], optarg, sizeof(g_extern.sufami_rom_path[1]) - 1);
strlcpy(g_extern.sufami_rom_path[1], optarg, sizeof(g_extern.sufami_rom_path[1]));
g_extern.game_type = SSNES_CART_SUFAMI;
break;
case 'S':
strncpy(g_extern.savestate_name, optarg, sizeof(g_extern.savestate_name) - 1);
strlcpy(g_extern.savestate_name, optarg, sizeof(g_extern.savestate_name));
g_extern.has_set_state_path = true;
break;
@@ -455,25 +585,36 @@ static void parse_input(int argc, char *argv[])
g_extern.has_mouse[port - 1] = true;
break;
case 'N':
port = strtol(optarg, NULL, 0);
if (port < 1 || port > 2)
{
SSNES_ERR("Disconnected device from port 1 or 2.\n");
print_help();
exit(1);
}
g_extern.disconnect_device[port - 1] = true;
break;
case 'p':
g_extern.has_scope[1] = true;
break;
#ifdef HAVE_CONFIGFILE
case 'c':
strncpy(g_extern.config_path, optarg, sizeof(g_extern.config_path) - 1);
strlcpy(g_extern.config_path, optarg, sizeof(g_extern.config_path));
break;
#endif
#ifdef HAVE_FFMPEG
case 'r':
strncpy(g_extern.record_path, optarg, sizeof(g_extern.record_path) - 1);
strlcpy(g_extern.record_path, optarg, sizeof(g_extern.record_path));
g_extern.recording = true;
break;
#endif
case 'P':
strncpy(g_extern.bsv_movie_path, optarg, sizeof(g_extern.bsv_movie_path) - 1);
strlcpy(g_extern.bsv_movie_path, optarg, sizeof(g_extern.bsv_movie_path));
g_extern.bsv_movie_playback = true;
break;
@@ -483,7 +624,7 @@ static void parse_input(int argc, char *argv[])
case 'C':
g_extern.netplay_enable = true;
strncpy(g_extern.netplay_server, optarg, sizeof(g_extern.netplay_server) - 1);
strlcpy(g_extern.netplay_server, optarg, sizeof(g_extern.netplay_server));
break;
case 'F':
@@ -493,7 +634,17 @@ static void parse_input(int argc, char *argv[])
break;
case 'U':
strncpy(g_extern.ups_name, optarg, sizeof(g_extern.ups_name) - 1);
strlcpy(g_extern.ups_name, optarg, sizeof(g_extern.ups_name));
break;
case 'X':
strlcpy(g_extern.xml_name, optarg, sizeof(g_extern.xml_name));
break;
case 'D':
#ifdef _WIN32
FreeConsole();
#endif
break;
case 0:
@@ -530,9 +681,9 @@ static void parse_input(int argc, char *argv[])
}
// strl* would be nice :D
if (!g_extern.has_set_save_path)
fill_pathname(g_extern.savefile_name_srm, g_extern.basename, ".srm");
fill_pathname_noext(g_extern.savefile_name_srm, g_extern.basename, ".srm", sizeof(g_extern.savefile_name_srm));
if (!g_extern.has_set_state_path)
fill_pathname(g_extern.savestate_name, g_extern.basename, ".state");
fill_pathname_noext(g_extern.savestate_name, g_extern.basename, ".state", sizeof(g_extern.savestate_name));
}
else if (strlen(g_extern.savefile_name_srm) == 0)
{
@@ -563,21 +714,26 @@ static void init_controllers(void)
}
else if (g_extern.has_multitap)
{
SSNES_LOG("Connecting multitap to port 2.\n");
SSNES_LOG("Connecting Multitap to port 2.\n");
psnes_set_controller_port_device(SNES_PORT_2, SNES_DEVICE_MULTITAP);
}
else
{
for (int i = 0; i < 2; i++)
for (unsigned i = 0; i < 2; i++)
{
if (g_extern.has_mouse[i])
if (g_extern.disconnect_device[i])
{
SSNES_LOG("Connecting mouse to port %d\n", i + 1);
SSNES_LOG("Disconnecting device from port %u.\n", i + 1);
psnes_set_controller_port_device(i, SNES_DEVICE_NONE);
}
else if (g_extern.has_mouse[i])
{
SSNES_LOG("Connecting mouse to port %u.\n", i + 1);
psnes_set_controller_port_device(i, SNES_DEVICE_MOUSE);
}
else if (g_extern.has_scope[i])
{
SSNES_LOG("Connecting scope to port %d\n", i + 1);
SSNES_LOG("Connecting scope to port %u.\n", i + 1);
psnes_set_controller_port_device(i, SNES_DEVICE_SUPER_SCOPE);
}
}
@@ -619,22 +775,26 @@ static inline void save_files(void)
switch (g_extern.game_type)
{
case SSNES_CART_NORMAL:
SSNES_LOG("Saving regular SRAM.\n");
save_ram_file(g_extern.savefile_name_srm, SNES_MEMORY_CARTRIDGE_RAM);
save_ram_file(g_extern.savefile_name_rtc, SNES_MEMORY_CARTRIDGE_RTC);
break;
case SSNES_CART_SGB:
SSNES_LOG("Saving Gameboy SRAM.\n");
save_ram_file(g_extern.savefile_name_srm, SNES_MEMORY_GAME_BOY_RAM);
save_ram_file(g_extern.savefile_name_rtc, SNES_MEMORY_GAME_BOY_RTC);
break;
case SSNES_CART_BSX:
case SSNES_CART_BSX_SLOTTED:
SSNES_LOG("Saving BSX (P)RAM.\n");
save_ram_file(g_extern.savefile_name_srm, SNES_MEMORY_BSX_RAM);
save_ram_file(g_extern.savefile_name_psrm, SNES_MEMORY_BSX_PRAM);
break;
case SSNES_CART_SUFAMI:
SSNES_LOG("Saving Sufami turbo A/B RAM.\n");
save_ram_file(g_extern.savefile_name_asrm, SNES_MEMORY_SUFAMI_TURBO_A_RAM);
save_ram_file(g_extern.savefile_name_bsrm, SNES_MEMORY_SUFAMI_TURBO_B_RAM);
break;
@@ -648,30 +808,53 @@ static inline void save_files(void)
#ifdef HAVE_FFMPEG
static void init_recording(void)
{
// Hardcode these options at the moment. Should be specificed in the config file later on.
if (g_extern.recording)
{
// Not perfectly SNES accurate, but this can be adjusted later during processing
// and playback losslessly, and we please the containers more by
// using "sane" values.
struct ffemu_rational ntsc_fps = {60000, 1000};
struct ffemu_rational pal_fps = {50000, 1000};
struct ffemu_params params = {
.vcodec = FFEMU_VIDEO_H264,
.acodec = FFEMU_AUDIO_VORBIS,
.rescaler = FFEMU_RESCALER_POINT,
.out_width = 512,
.out_height = 448,
.out_width = 256,
.out_height = 224,
.fb_width = 1024,
.fb_height = 512,
.channels = 2,
.samplerate = 32000,
.filename = g_extern.record_path,
.fps = psnes_get_region() == SNES_REGION_NTSC ? ntsc_fps : pal_fps,
.aspect_ratio = 4.0/3
.aspect_ratio = 4.0 / 3,
.rgb32 = false,
};
SSNES_LOG("Recording with FFmpeg to %s.\n", g_extern.record_path);
if (g_settings.video.hires_record)
{
params.out_width = 512;
params.out_height = 448;
}
if (g_settings.video.post_filter_record && g_extern.filter.active)
{
g_extern.filter.psize(&params.out_width, &params.out_height);
params.rgb32 = true;
unsigned max_width = 512;
unsigned max_height = 512;
g_extern.filter.psize(&max_width, &max_height);
params.fb_width = g_extern.filter.pitch >> 2;
params.fb_height = next_pow2(max_height);
}
SSNES_LOG("Recording with FFmpeg to %s @ %ux%u. (FB size: %ux%u 32-bit: %s)\n", g_extern.record_path, params.out_width, params.out_height, params.fb_width, params.fb_height, params.rgb32 ? "yes" : "no");
g_extern.rec = ffemu_new(&params);
if (!g_extern.rec)
{
SSNES_ERR("Failed to start FFmpeg recording.\n");
g_extern.recording = false;
}
else
g_settings.video.crop_overscan = true;
}
}
@@ -697,6 +880,20 @@ static void deinit_msg_queue(void)
msg_queue_free(g_extern.msg_queue);
}
#ifdef HAVE_XML
static void init_cheats(void)
{
if (*g_settings.cheat_database)
g_extern.cheat = cheat_manager_new(g_settings.cheat_database);
}
static void deinit_cheats(void)
{
if (g_extern.cheat)
cheat_manager_free(g_extern.cheat);
}
#endif
static void init_rewind(void)
{
if (g_settings.rewind_enable)
@@ -731,6 +928,7 @@ static void init_movie(void)
}
msg_queue_push(g_extern.msg_queue, "Starting movie playback!", 2, 180);
g_settings.rewind_granularity = 1;
SSNES_LOG("Starting movie playback!\n");
}
}
@@ -751,7 +949,6 @@ static void init_netplay(void)
struct snes_callbacks cbs = {
.frame_cb = video_frame,
.sample_cb = audio_sample,
.poll_cb = input_poll,
.state_cb = input_state
};
@@ -851,37 +1048,40 @@ static void fill_pathnames(void)
case SSNES_CART_BSX_SLOTTED:
// BSX PSRM
if (!g_extern.has_set_save_path)
fill_pathname(g_extern.savefile_name_srm, g_extern.bsx_rom_path, ".srm");
fill_pathname(g_extern.savefile_name_srm, g_extern.bsx_rom_path, ".srm", sizeof(g_extern.savefile_name_srm));
if (!g_extern.has_set_state_path)
fill_pathname(g_extern.savestate_name, g_extern.bsx_rom_path, ".state");
fill_pathname(g_extern.savefile_name_psrm, g_extern.savefile_name_srm, ".psrm");
fill_pathname(g_extern.savestate_name, g_extern.bsx_rom_path, ".state", sizeof(g_extern.savestate_name));
fill_pathname(g_extern.savefile_name_psrm, g_extern.savefile_name_srm, ".psrm", sizeof(g_extern.savefile_name_psrm));
break;
case SSNES_CART_SUFAMI:
// SUFAMI ARAM
fill_pathname(g_extern.savefile_name_asrm, g_extern.savefile_name_srm, ".asrm");
fill_pathname(g_extern.savefile_name_asrm, g_extern.savefile_name_srm, ".asrm", sizeof(g_extern.savefile_name_asrm));
// SUFAMI BRAM
fill_pathname(g_extern.savefile_name_bsrm, g_extern.savefile_name_srm, ".bsrm");
fill_pathname(g_extern.savefile_name_bsrm, g_extern.savefile_name_srm, ".bsrm", sizeof(g_extern.savefile_name_bsrm));
break;
case SSNES_CART_SGB:
if (!g_extern.has_set_save_path)
fill_pathname(g_extern.savefile_name_srm, g_extern.gb_rom_path, ".srm");
fill_pathname(g_extern.savefile_name_srm, g_extern.gb_rom_path, ".srm", sizeof(g_extern.savefile_name_srm));
if (!g_extern.has_set_state_path)
fill_pathname(g_extern.savestate_name, g_extern.gb_rom_path, ".state");
fill_pathname(g_extern.savefile_name_rtc, g_extern.savefile_name_srm, ".rtc");
fill_pathname(g_extern.savestate_name, g_extern.gb_rom_path, ".state", sizeof(g_extern.savestate_name));
fill_pathname(g_extern.savefile_name_rtc, g_extern.savefile_name_srm, ".rtc", sizeof(g_extern.savefile_name_rtc));
break;
default:
// Infer .rtc save path from save ram path.
fill_pathname(g_extern.savefile_name_rtc, g_extern.savefile_name_srm, ".rtc");
fill_pathname(g_extern.savefile_name_rtc, g_extern.savefile_name_srm, ".rtc", sizeof(g_extern.savefile_name_rtc));
}
if (!g_extern.bsv_movie_playback)
fill_pathname(g_extern.bsv_movie_path, g_extern.savefile_name_srm, "");
fill_pathname(g_extern.bsv_movie_path, g_extern.savefile_name_srm, "", sizeof(g_extern.bsv_movie_path));
if (!(*g_extern.ups_name) && *g_extern.basename)
fill_pathname(g_extern.ups_name, g_extern.basename, ".ups");
fill_pathname_noext(g_extern.ups_name, g_extern.basename, ".ups", sizeof(g_extern.ups_name));
if (!(*g_extern.xml_name) && *g_extern.basename)
fill_pathname_noext(g_extern.xml_name, g_extern.basename, ".xml", sizeof(g_extern.xml_name));
}
// Save or load state here.
@@ -942,13 +1142,17 @@ static void check_savestates(void)
static void check_fullscreen(void)
{
static bool was_pressed = false;
bool pressed;
// If we go fullscreen we drop all drivers and reinit to be safe.
if (driver.input->key_pressed(driver.input_data, SSNES_FULLSCREEN_TOGGLE_KEY))
if ((pressed = driver.input->key_pressed(driver.input_data, SSNES_FULLSCREEN_TOGGLE_KEY)) && !was_pressed)
{
g_settings.video.fullscreen = !g_settings.video.fullscreen;
uninit_drivers();
init_drivers();
}
was_pressed = pressed;
}
static void check_stateslots(void)
@@ -1032,8 +1236,7 @@ static void check_rewind(void)
if (g_extern.bsv_movie)
{
for (unsigned i = 0; i < (g_settings.rewind_granularity ? g_settings.rewind_granularity : 1); i++)
bsv_movie_frame_rewind(g_extern.bsv_movie);
bsv_movie_frame_rewind(g_extern.bsv_movie);
}
}
else
@@ -1067,6 +1270,7 @@ static void check_movie_record(void)
}
else
{
g_settings.rewind_granularity = 1;
char path[512];
if (g_extern.state_slot > 0)
snprintf(path, sizeof(path), "%s%d.bsv", g_extern.bsv_movie_path, g_extern.state_slot);
@@ -1162,7 +1366,7 @@ static void check_reset(void)
static void check_shader_dir(void)
{
static bool old_pressed_next = false;
static bool old_pressed_prev = true;
static bool old_pressed_prev = false;
if (!g_extern.shader_dir.elems || !driver.video->xml_shader)
return;
@@ -1188,7 +1392,7 @@ static void check_shader_dir(void)
{
const char *shader = g_extern.shader_dir.elems[g_extern.shader_dir.ptr];
strncpy(g_settings.video.bsnes_shader_path, shader, sizeof(g_settings.video.bsnes_shader_path) - 1);
strlcpy(g_settings.video.bsnes_shader_path, shader, sizeof(g_settings.video.bsnes_shader_path));
g_settings.video.shader_type = SSNES_SHADER_BSNES;
msg_queue_clear(g_extern.msg_queue);
@@ -1205,6 +1409,54 @@ static void check_shader_dir(void)
old_pressed_prev = pressed_prev;
}
static void check_cheats(void)
{
if (!g_extern.cheat)
return;
static bool old_pressed_prev = false;
static bool old_pressed_next = false;
static bool old_pressed_toggle = false;
bool pressed_next = driver.input->key_pressed(driver.input_data, SSNES_CHEAT_INDEX_PLUS);
bool pressed_prev = driver.input->key_pressed(driver.input_data, SSNES_CHEAT_INDEX_MINUS);
bool pressed_toggle = driver.input->key_pressed(driver.input_data, SSNES_CHEAT_TOGGLE);
if (pressed_next && !old_pressed_next)
cheat_manager_index_next(g_extern.cheat);
else if (pressed_prev && !old_pressed_prev)
cheat_manager_index_prev(g_extern.cheat);
else if (pressed_toggle && !old_pressed_toggle)
cheat_manager_toggle(g_extern.cheat);
old_pressed_prev = pressed_prev;
old_pressed_next = pressed_next;
old_pressed_toggle = pressed_toggle;
}
#endif
static void check_screenshot(void)
{
static bool old_pressed = false;
bool pressed = driver.input->key_pressed(driver.input_data, SSNES_SCREENSHOT);
g_extern.do_screenshot = pressed && !old_pressed;
old_pressed = pressed;
}
#ifdef HAVE_DYLIB
static void check_dsp_config(void)
{
if (!g_extern.audio_data.dsp_plugin || !g_extern.audio_data.dsp_plugin->config)
return;
static bool old_pressed = false;
bool pressed = driver.input->key_pressed(driver.input_data, SSNES_DSP_CONFIG);
if (pressed && !old_pressed)
g_extern.audio_data.dsp_plugin->config(g_extern.audio_data.dsp_handle);
old_pressed = pressed;
}
#endif
static void do_state_checks(void)
@@ -1212,11 +1464,16 @@ static void do_state_checks(void)
if (!g_extern.netplay)
{
check_reset();
#ifdef HAVE_XML
check_cheats();
#endif
check_pause();
if (g_extern.is_paused)
return;
set_fast_forward_button(driver.input->key_pressed(driver.input_data, SSNES_FAST_FORWARD_KEY));
set_fast_forward_button(
driver.input->key_pressed(driver.input_data, SSNES_FAST_FORWARD_KEY),
driver.input->key_pressed(driver.input_data, SSNES_FAST_FORWARD_HOLD_KEY));
if (!g_extern.bsv_movie)
{
@@ -1231,10 +1488,21 @@ static void do_state_checks(void)
#ifdef HAVE_XML
check_shader_dir();
#endif
#ifdef HAVE_DYLIB
check_dsp_config();
#endif
}
check_fullscreen();
check_input_rate();
#ifdef HAVE_DYLIB
// DSP plugin doesn't use variable input rate.
if (!g_extern.audio_data.dsp_plugin)
#endif
check_input_rate();
check_screenshot();
}
static void fill_title_buf(void)
@@ -1264,6 +1532,10 @@ int main(int argc, char *argv[])
if (!init_rom_file(g_extern.game_type))
goto error;
#ifdef HAVE_XML
init_cheats();
#endif
init_msg_queue();
init_movie();
@@ -1281,14 +1553,13 @@ int main(int argc, char *argv[])
#ifdef HAVE_NETPLAY
psnes_set_video_refresh(g_extern.netplay ? video_frame_net : video_frame);
psnes_set_audio_sample(g_extern.netplay ? audio_sample_net : audio_sample);
psnes_set_input_poll(g_extern.netplay ? input_poll_net : input_poll);
psnes_set_input_state(g_extern.netplay ? input_state_net : input_state);
#else
psnes_set_video_refresh(video_frame);
psnes_set_audio_sample(audio_sample);
psnes_set_input_poll(input_poll);
psnes_set_input_state(input_state);
#endif
psnes_set_input_poll(input_poll);
init_controllers();
@@ -1302,6 +1573,10 @@ int main(int argc, char *argv[])
// Main loop
for(;;)
{
// DSP plugin GUI events.
if (g_extern.audio_data.dsp_handle && g_extern.audio_data.dsp_plugin->events)
g_extern.audio_data.dsp_plugin->events(g_extern.audio_data.dsp_handle);
// Time to drop?
if (driver.input->key_pressed(driver.input_data, SSNES_QUIT_KEY) ||
!driver.video->alive(driver.video_data) || g_extern.bsv_movie_end)
@@ -1365,6 +1640,10 @@ int main(int argc, char *argv[])
if (!g_extern.netplay)
deinit_rewind();
#ifdef HAVE_XML
deinit_cheats();
#endif
deinit_movie();
deinit_msg_queue();
+49 -2
View File
@@ -5,7 +5,13 @@
#### Video
# Windowed xscale and yscale (Real x res: 296 * xscale, real y scale: 224 * xscale)
# 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: 224 * xscale * aspect_ratio, real y scale: 224 * xscale)
# video_xscale = 3.0
# video_yscale = 3.0
@@ -16,6 +22,12 @@
# 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
@@ -28,6 +40,9 @@
# 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"
@@ -87,12 +102,18 @@
# 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 and xaudio
# 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
@@ -207,6 +228,9 @@
# 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
@@ -242,6 +266,9 @@
# Reset the emulated SNES.
# input_reset = h
# Configures DSP plugin
# input_dsp_config = c
# Pause gameplay when window focus is lost.
# pause_nonactive = true
@@ -252,3 +279,23 @@
# 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 =
# Cheats.
# input_cheat_index_plus = y
# input_cheat_index_minus = t
# input_cheat_toggle = u
# 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
+52
View File
@@ -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/>.
*/
#include "strl.h"
#ifndef HAVE_STRL
// Implementation of strlcpy()/strlcat() based on OpenBSD.
size_t strlcpy(char *dest, const char *source, size_t size)
{
size_t src_size = 0;
size_t n = size;
if (n)
while (--n && (*dest++ = *source++)) src_size++;
if (!n)
{
if (size) *dest = '\0';
while (*source++) src_size++;
}
return src_size;
}
size_t strlcat(char *dest, const char *source, size_t size)
{
size_t len = strlen(dest);
dest += len;
if (len > size)
size = 0;
else
size -= len;
return len + strlcpy(dest, source, size);
}
#endif
+38
View File
@@ -0,0 +1,38 @@
/* 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_STRL_H
#define __SSNES_STRL_H
#include <string.h>
#include <stddef.h>
#ifdef HAVE_CONFIG
#include "config.h"
#endif
#ifndef HAVE_STRL
// Avoid possible naming collitions during link since we prefer to use the actual name.
#define strlcpy(dst, src, size) __strlcpy_ssnes(dst, src, size)
#define strlcat(dst, src, size) __strlcat_ssnes(dst, src, size)
size_t strlcpy(char *dest, const char *source, size_t size);
size_t strlcat(char *dest, const char *source, size_t size);
#endif
#endif
+6
View File
@@ -86,6 +86,7 @@ static struct bind binds[] = {
MISC_BIND("Save state slot increase", state_slot_increase)
MISC_BIND("Save state slot decrease", state_slot_decrease)
MISC_BIND("Toggle fast forward", toggle_fast_forward)
MISC_BIND("Hold fast forward", hold_fast_forward)
MISC_BIND("Audio input rate step up", rate_step_up)
MISC_BIND("Audio input rate step down", rate_step_down)
MISC_BIND("Rewind", rewind)
@@ -94,6 +95,11 @@ static struct bind binds[] = {
MISC_BIND("Reset", reset)
MISC_BIND("Next shader", shader_next)
MISC_BIND("Previous shader", shader_prev)
MISC_BIND("Toggle cheat on/off", cheat_toggle)
MISC_BIND("Cheat index plus", cheat_index_plus)
MISC_BIND("Cheat index minus", cheat_index_minus)
MISC_BIND("Screenshot", screenshot)
MISC_BIND("DSP config", dsp_config)
};
static void get_binds(config_file_t *conf, int player, int joypad)