Compare commits

...
438 Commits
Author SHA1 Message Date
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 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
Themaister 844a523706 Should fix error with scaling. 2011-03-30 14:57:45 +02:00
Themaister d05379b340 Should fix FBO issue. 2011-03-30 11:12:27 +02:00
Themaister 006948b973 Warmup for 0.4-beta. Some FBO stuff is still borked. 2011-03-30 11:05:56 +02:00
Themaister e5196026a1 Add more precise error messages. 2011-03-30 00:10:16 +02:00
Themaister 66d18e4ad7 Adhere more closely to XML shader specs. 2011-03-29 23:45:10 +02:00
Themaister b539df1ccd Added settings to ssnes-joyconfig. 2011-03-29 23:40:12 +02:00
Themaister c0117bfbb6 Remove debugging fprintf. 2011-03-29 23:38:11 +02:00
Themaister 074234f182 Oh damn you Windows and your spoony file handling... 2011-03-29 23:21:56 +02:00
Themaister bab03ba8c5 No need for these. 2011-03-29 19:27:53 +02:00
Themaister 4878a283ed Properly deinit Cg. 2011-03-29 19:18:06 +02:00
Themaister d3eed4f8c4 Properly ifdef. 2011-03-29 19:15:02 +02:00
Themaister 647db4d5de Hrm. 2011-03-29 19:13:14 +02:00
Themaister 3455f83fa7 Hopefully fixes awkward issue with scale not being applied correctly. 2011-03-29 19:09:10 +02:00
Themaister f730ba4043 Oh yeah :) Can toggle shader in run-time! 2011-03-29 18:59:06 +02:00
Themaister caf87b1198 Font handling and stuff. 2011-03-29 18:28:31 +02:00
Themaister 1706763a4c Start supporting shaders hiding in a directory. 2011-03-29 18:04:41 +02:00
Themaister c1b8cdb35b Fix issue with size attribute first. 2011-03-27 21:34:33 +02:00
Themaister 936b7f5395 Seems to work now. 2011-03-27 21:21:46 +02:00
Themaister a01e3347f7 Seems to work :) 2011-03-27 20:54:14 +02:00
Themaister 142411c08c Fix some garbage. 2011-03-27 20:33:14 +02:00
Themaister 29808c4421 Start on outscale/size support. 2011-03-27 20:29:47 +02:00
Themaister 48dcd174a7 Add resetting support. (About time, ey?! :D) 2011-03-24 20:41:28 +01:00
Themaister 6c85e6e702 Configurable FBO support. 2011-03-23 23:48:13 +01:00
Themaister e484e22e2f Fix up UPS :) Should work fine now at least for .sfc base roms. 2011-03-23 23:31:33 +01:00
Themaister 8ef54de812 Start adding UPS support. 2011-03-23 12:18:24 +01:00
Themaister c1d5a6e140 Fixes scaling bug for shaders. 2011-03-23 11:05:18 +01:00
Themaister ea153f9df2 Install ssnes-zip 2011-03-20 22:48:13 +01:00
Themaister 1ba9854070 Typo 2011-03-20 21:46:34 +01:00
Themaister 012e955a05 RAR. Untested. 2011-03-20 19:46:04 +01:00
Themaister 37c0b49a99 7z 2011-03-20 19:16:22 +01:00
Themaister de30ce9ef5 Start on a zip loader wrapper. 2011-03-20 13:40:01 +01:00
Themaister 027a21278e Whoops 2011-03-20 02:13:44 +01:00
Themaister f77fe62ab0 Should fix viewport bug when rendering fonts. 2011-03-20 02:10:02 +01:00
Themaister d42c35e1aa Update makefile for win32. 2011-03-19 20:42:49 +01:00
Themaister d5761b6385 Can disable netplay. 2011-03-19 20:41:07 +01:00
Themaister bb3ab3b49c Lots of cleanups for XVideo.
Render video @ 2x scale to combat chroma downsampling. Fix 32-bit
rendering.
2011-03-19 18:53:37 +01:00
Themaister 0441725045 XSync. 2011-03-19 17:19:51 +01:00
Themaister ac4e6e89a4 Bigger buffer for gfx_window_title. 2011-03-19 16:54:29 +01:00
Themaister 12d21cf7a6 Make title handling more sane. 2011-03-26 18:34:58 +01:00
Themaister 3fda3effda Fix crash in XVideo font rendering. 2011-03-26 18:04:18 +01:00
Themaister 7e07b821aa Forgot Makefile.win32 2011-03-17 22:29:43 +01:00
Themaister e502a8b566 Updates for 0.3.2 2011-03-17 22:28:44 +01:00
Themaister ec51ceb010 Add usage of snes_library_id(). 2011-03-17 01:25:44 +01:00
Themaister 3fa037daee Fix minor issues. 2011-03-16 21:48:44 +01:00
Themaister 46c6955cfe Fix odd viewport problem with shaders. 2011-03-16 12:47:31 +01:00
Themaister 252425c35c Some cleanups. 2011-03-16 12:28:02 +01:00
Themaister d309d472e1 Code cleanup. Accumulate scale values. 2011-03-15 18:14:01 +01:00
Themaister 898c4e8c6c Works quite decently now. :) 2011-03-14 23:59:31 +01:00
Themaister 56ac7b4649 Something's working :D 2011-03-14 23:48:19 +01:00
Themaister 9dd05b7101 New params, but is broken for now. 2011-03-14 23:20:51 +01:00
Themaister e2daab9ce9 Fetch some scale values (this is not in the standard (yet!)) 2011-03-14 22:51:03 +01:00
Themaister ef19ea7a85 Add fbo structures, ready to implement. :v 2011-03-14 22:09:35 +01:00
Themaister 01cf24f15f Add filter attributes. 2011-03-14 21:28:30 +01:00
Themaister f870080497 Render fonts. Very dirty. :( 2011-03-13 20:52:06 +01:00
Themaister c5942ec5a2 Fix Jack pkgconf version. 2011-03-13 18:09:27 +01:00
Themaister 50d78fb804 Endian fixes 2011-03-13 15:49:00 +01:00
Themaister 8053aca3c3 Render 32-bit 2011-03-13 15:28:57 +01:00
Themaister 0f3171edea Title 2011-03-13 15:11:53 +01:00
Themaister 6b478b9dda Fullscreen :D 2011-03-13 14:46:27 +01:00
Themaister caaa9cea40 Hide cursor 2011-03-13 14:26:22 +01:00
Themaister ea7f8f5552 Add proper close handling. 2011-03-13 14:12:43 +01:00
Themaister 69caca6017 Fixes for resize. 2011-03-13 13:11:33 +01:00
Themaister 703cfadf63 Fix up X input. 2011-03-13 12:02:06 +01:00
Themaister adfd461c3e Start on XVideo 2011-03-13 04:51:09 +01:00
Themaister bb79dbaa98 Getting ready for an eventual XVideo driver. 2011-03-13 00:44:49 +01:00
Themaister 394de09907 Should hopefully fix flickering :v 2011-03-12 19:28:56 +01:00
Themaister 560cafccaf Futile attempt to try and fix font flickering. 2011-03-12 19:09:25 +01:00
Themaister 0b09032164 Hrm 2011-03-12 18:42:59 +01:00
Themaister 1ee7162784 It's not copypasta anymore :D 2011-03-12 17:01:57 +01:00
Themaister 4949a131f8 Multi-passing in XML should work. 2011-03-12 16:33:01 +01:00
Themaister 8b241c3deb Preparation for new XML shader specs. 2011-03-12 15:30:57 +01:00
Themaister 78c457b996 Update Roar driver. 2011-03-11 19:23:32 +01:00
Themaister 0be1b0b88b More build fixes. 2011-03-07 20:22:55 +01:00
Themaister 3ef0276b2a Will probably not need, but hey. 2011-03-07 20:17:27 +01:00
Themaister 42d19f1a35 Fix build for Win32. 2011-03-07 20:05:03 +01:00
Themaister 1cf9381cb0 Fix colormaps and stuff. 2011-03-07 19:56:40 +01:00
Themaister f94dc125e5 An initial take on bsnes CPU filters. 2011-03-07 19:12:14 +01:00
Themaister 96b978d55a Start on modular CPU filters. Abandon the old CPU filter stuff. 2011-03-07 17:22:03 +01:00
Themaister ec367d333d Add error message when FBO fails. 2011-03-07 00:03:10 +01:00
Themaister 9535097894 Fix build for Win32. 2011-03-06 22:18:25 +01:00
Themaister b541c27226 FBO seems to work! :D 2011-03-06 19:56:35 +01:00
Themaister 41b2af66d5 Add settings. 2011-03-06 18:19:31 +01:00
Themaister 84b2b9c7a5 Fix up Jack deprecation. 2011-03-06 16:58:33 +01:00
Themaister 52c8d96dca FBOs. Hacky atm, need more flexibility. 2011-03-06 16:52:49 +01:00
Themaister 8c6519a53b Should fix an awkward bug with Quickbuild. 2011-03-03 22:06:58 +01:00
Themaister 336adb9f85 Preparations for 0.3.1 2011-03-01 21:45:04 +01:00
Themaister 7053006e77 Add more verbose messages, should fix netplay hangup on Win32. 2011-03-01 14:46:58 +01:00
Themaister bbf4804d12 Obey CFLAGS and LDFLAGS when testing libs (nice when profiling.) 2011-02-28 22:30:06 +01:00
Themaister 7c16424ba3 Shader enabling, and automatic resolution detection. 2011-02-28 16:59:31 +01:00
Themaister de9dc686ac Add hicontrast. 2011-02-27 20:42:02 +01:00
Themaister 069e9b574f Some waterpaint shader :) 2011-02-27 01:16:33 +01:00
Themaister d5319a879a Had missed out pause binding completely. 2011-02-27 00:24:50 +01:00
Themaister f64880aee1 Try glFlush() to remedy occational font flicker... 2011-02-26 23:56:45 +01:00
Themaister fed1857918 Might fix odd font flickering. 2011-02-26 19:32:59 +01:00
Themaister 9aeb40155e Add a comment or two. 2011-02-26 18:49:41 +01:00
Themaister e49aeb1071 Remove redundant define. 2011-02-26 18:48:17 +01:00
Themaister 13f8ec6f3b Added "nul" binds. 2011-02-26 18:47:32 +01:00
Themaister f75c6df270 Should force granularity to 1 when using movies. 2011-02-26 15:09:04 +01:00
Themaister 60b0afce95 Is this it? :D 2011-02-26 01:13:52 +01:00
Themaister f24653768f It does work, somewhat! :D 2011-02-26 00:59:17 +01:00
Themaister f135972a6b Arghh... This is so goddamn annoying :( 2011-02-26 00:31:13 +01:00
Themaister 42c68e21a2 Movie rewind still very broken :( 2011-02-25 12:37:05 +01:00
Themaister e8865060ca A start on movie rewind. 2011-02-25 11:47:27 +01:00
Themaister b07827a856 Should fix GLSL issue on OSX. 2011-02-22 23:53:01 +01:00
Themaister 0735398e64 Updates for 0.3. 2011-02-22 22:24:58 +01:00
Themaister ec2d10e069 Add default path to OSX and check in APPDATA for Win32. 2011-02-22 11:28:28 +01:00
Themaister 4cdb6151e5 Add netplay_client_swap_input. 2011-02-20 12:12:53 +01:00
Themaister e482b1bd18 Fix potential problem. Some cleanup. 2011-02-18 23:51:51 +01:00
Themaister 9be3c13a07 Hrm. This stuff is very error prone. :V 2011-02-18 15:50:57 +01:00
Themaister 8a4bcacf88 hrm... 2011-02-18 14:49:15 +01:00
Themaister 6b9b37b528 Should fix an extreme edge case. 2011-02-18 13:39:31 +01:00
Themaister 8e87c1ff19 Could this be it? :v 2011-02-18 03:42:43 +01:00
Themaister aa648c1a49 Hm... Still borked. Seems hard to fix. 2011-02-18 03:14:32 +01:00
Themaister 056a590671 Hm. More progress, but still not quite right. 2011-02-18 02:16:32 +01:00
Themaister 97f2503794 Some initial work on UDP networking. Still borked. 2011-02-18 02:01:47 +01:00
Themaister 143079efbe Should fix abrupt application termination on Unix when netplay ends. 2011-02-16 18:33:45 +01:00
Themaister 7bcc173c5f Should fix some issues with Win32 netplay. 2011-02-15 17:29:03 +01:00
Themaister 0485f783b3 Updates for Win32. 2011-02-15 16:42:55 +01:00
Themaister f3c07325bd Fix Windows build. 2011-02-15 15:57:59 +01:00
Themaister e8eabb0102 Update docs. 2011-02-15 15:42:29 +01:00
Themaister b73f5e77ff Cleanups, add --frames. 2011-02-15 15:32:26 +01:00
Themaister d07712b308 Woot. looks like it works o.O 2011-02-15 15:10:03 +01:00
Themaister 89ba64d27f Borked, borked... 2011-02-14 22:37:57 +01:00
Themaister 1a6f112d8a More changes to netplay. Still quite broken, but it's not far away! 2011-02-14 20:11:46 +01:00
Themaister 70e8681487 Started to work on a look-ahead algorithm.
Still very broken, but frame-by-frame sync works at least :)
2011-02-14 16:10:53 +01:00
Themaister 17ae0f72df Do not try to poll when we've disconnected. 2011-02-13 21:19:20 +01:00
Themaister 466485e62e Lots more error messages. 2011-02-13 20:24:54 +01:00
Themaister f6ee0845f2 Add some message. 2011-02-13 19:50:45 +01:00
Themaister df1eee1a74 Redundant... 2011-02-13 19:00:27 +01:00
Themaister 46b46ba0b1 Update docs a bit. 2011-02-13 18:47:48 +01:00
Themaister a763848644 Fix some issues with message queues. 2011-02-13 18:19:37 +01:00
Themaister 9ba2d6c9fa Send SRAM to client. 2011-02-13 17:45:14 +01:00
Themaister fd1367e257 Update build for Win32 2011-02-13 16:49:18 +01:00
Themaister 85dbc05ded Some start on netplay. :) 2011-02-13 16:40:24 +01:00
Themaister 1eea7786bc Should fix possible error where game can hang for prolonged time on
exit.
2011-02-11 19:54:42 +01:00
Themaister d039bedad4 Fix up some path handling with srm/states. 2011-02-11 14:27:19 +01:00
Themaister ad6719426c Fix up autosaving a bit. 2011-02-11 13:44:31 +01:00
Themaister 719e4942ce Add autosave possibility. 2011-02-10 21:16:59 +01:00
Themaister ad9a08aea6 Updates for 0.2.2 2011-02-09 23:34:40 +01:00
Themaister 604fe709d5 Re-enable RSound for Win32 2011-02-09 23:04:11 +01:00
Themaister 81e8368099 Update RSound to use callback locks. 2011-02-08 17:50:38 +01:00
Themaister 2137ca8e3f Add a warning message if XDG_CONFIG_HOME is not found. 2011-02-07 15:27:07 +01:00
Themaister 672824539c Argsel. 2011-02-07 11:17:25 +01:00
Themaister 1915be5aea Add libsnes.hpp to the build so dynamic build doesn't require libsnes
per se.
2011-02-07 11:15:32 +01:00
Themaister bdea56370d Typo 2011-02-07 11:14:46 +01:00
Themaister e5e0c33ed6 Fix SDL include path. 2011-02-07 00:22:44 +01:00
Themaister d0c10e7523 Use new callback interface for RSound. Fix when restarting audio driver
fails.
2011-02-06 23:55:17 +01:00
Themaister 91d2cea259 Make the hermite resampling core more robust. 2011-02-06 18:38:04 +01:00
Themaister d9e2072733 Update build for win32. 2011-02-06 13:37:41 +01:00
Themaister 820b124165 Add hermite resampler core :) SRC is disabled by default. 2011-02-06 13:29:48 +01:00
Themaister caf3eb7b05 Update gitignore 2011-02-06 12:57:52 +01:00
Themaister 1b5e63f024 Add hermite resampler. 2011-02-06 12:57:12 +01:00
Themaister 93202f191c Add pausing when window loses focus :p 2011-02-05 21:45:44 +01:00
Themaister 3033fb6791 Pausing support. 2011-02-05 20:46:58 +01:00
Themaister 87589609bf Some clarification. 2011-02-05 14:14:56 +01:00
Themaister 757514242d Remove redundant flush. 2011-02-05 11:32:23 +01:00
Themaister 359d228632 Fix rewind issue. 2011-02-05 11:31:35 +01:00
Themaister 24dda40863 Remove unneeded lib. 2011-02-04 23:53:33 +01:00
Themaister 6c0eebd4bf Remove more stuff and add a comment. 2011-02-04 23:47:36 +01:00
Themaister 31db2a30bb Remove unneeded glue logic. -lSDLmain fixed it :) 2011-02-04 23:45:54 +01:00
Themaister 5e241d79c8 Wut! :D 2011-02-04 23:45:51 +01:00
Themaister 96389ef83b Some testing fixes for OSX. 2011-02-04 22:47:37 +01:00
Themaister af010712f3 Use a more proper blending technique for fonts. 2011-02-04 16:24:09 +01:00
Themaister c2896db558 Fix some sed issues that seem to appear on Mac. 2011-02-04 15:31:05 +01:00
Themaister 57eebebcdd Fix OpenAL build for OSX. 2011-02-04 14:43:14 +01:00
Themaister 9ad9cbd7de Build fixes for OSX. (PPC) 2011-02-04 13:49:53 +01:00
Themaister 1b5adde885 Hrm. 2011-02-02 20:15:00 +01:00
Themaister ca35dd79d2 Add misc bind for movie_record_toggle. 2011-02-02 19:34:44 +01:00
Themaister 491f2e3e76 Update FPS for ffmpeg slightly. 2011-02-02 17:19:13 +01:00
Themaister b6591e21cd Update makefile for win32 as well. 2011-02-02 13:42:08 +01:00
Themaister 67ed89729d Use state slots for movies as well. 2011-02-02 13:37:01 +01:00
Themaister 6033e75dc4 Added some docs 2011-02-02 12:51:17 +01:00
Themaister 5f8993cb83 Movie playback seems to work! :D 2011-02-02 12:45:56 +01:00
Themaister 2446300dd9 Some more logic hooked up. 2011-02-02 12:10:27 +01:00
Themaister b97c45df78 Initial code for movie playback. 2011-02-02 11:47:05 +01:00
Themaister 9034e82298 Add rewind granularity. (Faster, but less accurate rewind.) 2011-02-01 17:30:18 +01:00
Themaister ae395f05e1 Debork config handling. :D 2011-02-01 17:13:26 +01:00
Themaister 0c1d359091 Make clang happy :) 2011-01-31 21:23:07 +01:00
Themaister dd8a9c67c9 Do not set rewind_enable automatically to true. 2011-01-31 19:53:44 +01:00
Themaister de331bcd9e Add rewind bind to ssnes-joyconfig. 2011-01-31 18:49:50 +01:00
Themaister a81d6bc247 Fix up stuff. Alignment is done in client code. 2011-01-31 18:27:21 +01:00
Themaister 47d0b12d40 Configurable rewind stuff. 2011-01-31 18:06:57 +01:00
Themaister 849e9aa734 Do not play audio when rewinding, sound is borked even when shuffling. 2011-01-31 17:24:31 +01:00
Themaister 351e098da1 Basic rewind works! 2011-01-31 16:48:42 +01:00
Themaister c5f825eaa5 Build fixes. 2011-01-31 16:10:40 +01:00
Themaister d0a4102efa Add initial state manager code. 2011-01-31 16:06:46 +01:00
Themaister 3aa4181380 Add possibility to configure misc binds as well. 2011-01-30 20:36:45 +01:00
Themaister 43dc30c3a5 Updates for 0.2.1 2011-01-30 13:44:30 +01:00
Themaister 98e6613df0 Add in feature check printing for XAudio and Pulse. 2011-01-30 00:30:54 +01:00
Themaister 3ce07891e4 Update Makefile to add in rsound.dll and stuff. 2011-01-30 00:04:14 +01:00
Themaister 2ff47ab7c4 Compile in RSound for Win32 as well. 2011-01-29 23:53:38 +01:00
Themaister 8ba639c7d5 Fix log message. 2011-01-29 23:23:54 +01:00
Themaister e0403e4dae Indentation error. 2011-01-29 19:53:23 +01:00
Themaister 7776525c96 Add a simple README for Win32. 2011-01-29 19:43:05 +01:00
Themaister 25fe9b7f58 Add possibility to alter sound input rate on-the-fly. 2011-01-29 18:42:21 +01:00
Themaister fff7aba8ec Use threaded mainloop for Pulse. Seems to fix most issues. 2011-01-29 02:00:57 +01:00
Themaister e83f3ed444 Initial PulseAudio code. 2011-01-29 01:15:09 +01:00
Themaister 334f11d5bc Add logging when XAudio fails. 2011-01-28 20:57:13 +01:00
Themaister 5bcde44684 Remove some undeeded warning/error messages. 2011-01-27 23:05:20 +01:00
Themaister 2459793f9c Set O_BINARY in Win32 on stdin reads. 2011-01-27 22:43:12 +01:00
Themaister cadd7b9728 Make SDL take priority over XAudio. 2011-01-27 14:43:46 +01:00
Themaister 644f247026 Should fix GL on other cards than nVidia... 2011-01-27 14:17:01 +01:00
Themaister 8c1ef3051d Add XAudio support. Need a separate xaudio-c.dll which actually implements the interface. 2011-01-27 01:57:25 +01:00
Themaister d296e15f26 Merge branch 'master' of git://github.com/Themaister/SSNES 2011-01-27 00:58:01 +01:00
Themaister 5a5cd46f9b Merge branch 'master' of git://github.com/Themaister/SSNES 2011-01-24 22:43:52 +01:00
Themaister 20d9345a4e Do not link in snes.dll when doing dynamic build for win32. 2011-01-19 16:11:45 +01:00
115 changed files with 14238 additions and 3847 deletions

No files matched your search

+4
View File
@@ -1,3 +1,7 @@
*.o
*.so
*.dll
ssnes
/config.h
/config.mk
/tools/ssnes-joyconfig
+100 -28
View File
@@ -2,51 +2,102 @@ include config.mk
TARGET = ssnes tools/ssnes-joyconfig
OBJ = ssnes.o file.o driver.o settings.o dynamic.o message.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 =
LIBS = -lm
DEFINES = -DHAVE_CONFIG_H
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)
else
OBJ += audio/hermite.o
endif
ifeq ($(HAVE_CONFIGFILE), 1)
OBJ += conf/config_file.o
endif
ifeq ($(HAVE_NETPLAY), 1)
OBJ += netplay.o
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
ifeq ($(OSX),1)
LIBS += -framework OpenAL
else
LIBS += -lopenal
endif
endif
ifeq ($(HAVE_JACK),1)
OBJ += audio/jack.o
LIBS += -ljack
LIBS += $(JACK_LIBS)
DEFINES += $(JACK_CFLAGS)
endif
ifeq ($(HAVE_PULSE), 1)
OBJ += audio/pulse.o
LIBS += $(PULSE_LIBS)
DEFINES += $(PULSE_CFLAGS)
endif
ifeq ($(HAVE_SDL), 1)
OBJ += gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o
LIBS += $(SDL_LIBS) -lGL
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
else
LIBS += -lGL
endif
endif
ifeq ($(HAVE_XVIDEO), 1)
OBJ += gfx/xvideo.o input/x11_input.o
LIBS += $(XVIDEO_LIBS)
DEFINES += $(XVIDEO_CFLAGS)
endif
ifeq ($(HAVE_CG), 1)
@@ -55,17 +106,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)
OBJ += hqflt/hq.o
OBJ += hqflt/grayscale.o
OBJ += hqflt/bleed.o
OBJ += hqflt/ntsc.o
OBJ += hqflt/snes_ntsc/snes_ntsc.o
ifeq ($(HAVE_DYLIB), 1)
OBJ += gfx/ext.o audio/ext.o
LIBS += $(DYLIB_LIB)
endif
ifeq ($(HAVE_FREETYPE), 1)
@@ -74,23 +122,43 @@ 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 -std=gnu99 -I.
CFLAGS += -Wall -O3 -g -I. -pedantic
ifneq ($(findstring icc,$(CC)),)
CFLAGS += -std=c99
else
CFLAGS += -std=gnu99
endif
all: $(TARGET) config.mk
@@ -111,26 +179,30 @@ 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,ssnes-joyconfig}.1 $(DESTDIR)$(PREFIX)/share/man/man1
install -m644 docs/ssnes.1 $(DESTDIR)$(PREFIX)/share/man/man1
install -m644 docs/ssnes-joyconfig.1 $(DESTDIR)$(PREFIX)/share/man/man1
install -m755 ssnes-zip $(DESTDIR)$(PREFIX)/bin
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/{ssnes,ssnes-joyconfig}
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)
+77 -6
View File
@@ -1,18 +1,28 @@
TARGET = ssnes.exe
JTARGET = ssnes-joyconfig.exe
OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o message.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_SRC = 1
HAVE_DINPUT = 1
HAVE_SRC = 0
HAVE_SDL = 1
HAVE_SDL_IMAGE = 1
HAVE_XML = 1
HAVE_FREETYPE = 1
HAVE_XAUDIO = 1
HAVE_RSOUND = 1
HAVE_DYLIB = 1
HAVE_NETPLAY = 1
HAVE_FBO = 1
HAVE_CG = 1
HAVE_PYTHON = 1
HAVE_FFMPEG = 0
libsnes ?= -lsnes
LIBS =
LIBS = -lm
DEFINES = -I. -DHAVE_CONFIGFILE
LDFLAGS = -L. -static-libgcc -s
@@ -23,20 +33,54 @@ 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/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_RSOUND), 1)
OBJ += audio/rsound.o
DEFINES += -DHAVE_RSOUND
LIBS += -lrsound
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_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
@@ -49,6 +93,32 @@ 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 -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
@@ -75,6 +145,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
@@ -84,7 +155,7 @@ clean:
rm -f tools/*.o
dist: all
zip -r ssnes-win32-0.2.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll libsamplerate-0.dll freetype6.dll $(JTARGET)
zip -r ssnes-win32-0.6.2.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll freetype6.dll xaudio-c.dll rsound.dll pthreadGC2.dll cg.dll cgGL.dll libjpeg-8.dll libpng15-15.dll python32.dll SDL_image.dll $(JTARGET)
libs:
wget https://github.com/downloads/Themaister/SSNES/SSNES-win32-libs.zip --no-check-certificate
+151
View File
@@ -0,0 +1,151 @@
TARGET = ssnes.exe
JTARGET = ssnes-joyconfig.exe
OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o strl.o screenshot.o
JOBJ = conf/config_file.o tools/main-stub.o tools/ssnes-joyconfig.o
CC = gcc
CXX = g++
HAVE_DINPUT = 1
HAVE_SRC = 0
HAVE_SDL = 1
HAVE_XML = 1
HAVE_FREETYPE = 1
HAVE_XAUDIO = 1
HAVE_RSOUND = 0
HAVE_DYLIB = 1
HAVE_NETPLAY = 1
HAVE_FBO = 1
HAVE_CG = 0
HAVE_PYTHON = 0
libsnes ?= -lsnes
LIBS = -lm
DEFINES = -I. -DHAVE_CONFIGFILE
LDFLAGS = -L. -static-libgcc -s
SRC_LIBS = -lsamplerate-0
SDL_LIBS = -lmingw32 -lSDL -lSDLmain -lwinmm -mwindows -lm -luser32 -lgdi32 -lwinmm -ldxguid
SDL_CFLAGS = -ISDL
ifeq ($(HAVE_SRC), 1)
LIBS += $(SRC_LIBS)
DEFINES += $(SRC_CFLAGS) -DHAVE_SRC
else
OBJ += audio/hermite.o
endif
ifeq ($(HAVE_SDL), 1)
OBJ += gfx/sdl.o gfx/gl.o input/sdl.o audio/sdl.o fifo_buffer.o
LIBS += $(SDL_LIBS) -lopengl32
DEFINES += $(SDL_CFLAGS) -DHAVE_SDL
endif
ifeq ($(HAVE_CG), 1)
OBJ += gfx/shader_cg.o
LIBS += -lcg -lcgGL
DEFINES += -DHAVE_CG
endif
ifeq ($(HAVE_XAUDIO), 1)
OBJ += audio/xaudio.o
DEFINES += -DHAVE_XAUDIO
endif
ifeq ($(HAVE_RSOUND), 1)
OBJ += audio/rsound.o
DEFINES += -DHAVE_RSOUND
LIBS += -lrsound
endif
ifeq ($(HAVE_XML), 1)
OBJ += gfx/shader_glsl.o gfx/image.o gfx/snes_state.o sha256.o cheats.o
DEFINES += $(XML_CFLAGS) -DHAVE_XML -DLIBXML_STATIC
LIBS += -lxml2 -lz -lws2_32
endif
ifeq ($(HAVE_DYLIB), 1)
DEFINES += -DHAVE_DYLIB
endif
ifeq ($(HAVE_NETPLAY), 1)
DEFINES += -DHAVE_NETPLAY
OBJ += netplay.o
LIBS += -lws2_32
endif
ifeq ($(HAVE_FREETYPE), 1)
OBJ += gfx/fonts.o
DEFINES += -DHAVE_FREETYPE -Ifreetype2
LIBS += -lz -lfreetype6
endif
ifeq ($(DYNAMIC), 1)
DEFINES += -DHAVE_DYNAMIC
else
LIBS += $(libsnes)
endif
ifeq ($(HAVE_FBO), 1)
DEFINES += -DHAVE_FBO
endif
ifeq ($(HAVE_DYLIB), 1)
OBJ += gfx/ext.o audio/ext.o
endif
ifeq ($(HAVE_PYTHON), 1)
LIBS += -lpython32
DEFINES += -DHAVE_PYTHON -Ipython
OBJ += gfx/py_state/py_state.o
endif
ifeq ($(HAVE_DINPUT), 1)
LIBS += -ldinput8 -ldxguid -lole32
DEFINES += -DHAVE_DINPUT
OBJ += input/dinput.o
endif
ifneq ($(V),1)
Q := @
endif
CFLAGS = -Wall -O3 -std=gnu99 -I.
all: $(TARGET) $(JTARGET)
$(TARGET): $(OBJ)
$(Q)$(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS)
@$(if $(Q), $(shell echo echo LD $@),)
%.o: %.c
$(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
@$(if $(Q), $(shell echo echo CC $<),)
$(JTARGET): $(JOBJ)
$(Q)$(CC) -o $@ $(JOBJ) $(SDL_LIBS) $(LDFLAGS)
@$(if $(Q), $(shell echo echo LD $@),)
clean:
rm -f *.o
rm -f audio/*.o
rm -f conf/*.o
rm -f gfx/*.o
rm -f record/*.o
rm -f hqflt/*.o
rm -f input/*.o
rm -f hqflt/snes_ntsc/*.o
rm -f $(TARGET)
rm -f ssnes-joyconfig.exe
rm -f tools/*.o
dist: all
zip -r ssnes-win64-0.6.2.zip $(TARGET) ssnes.cfg snes.dll xaudio-c.dll README.win32.txt $(JTARGET)
libs:
wget https://github.com/downloads/Themaister/SSNES/ssnes-win64-libs.zip --no-check-certificate
unzip ssnes-win64-libs.zip
.PHONY: all install uninstall clean dist libs
+4 -2
View File
@@ -18,15 +18,15 @@ SSNES requires these libraries to build:
- [libsnes](http://byuu.org/bsnes/)
- SDL
- libsamplerate
SSNES can utilize these libraries if enabled:
- nvidia-cg-toolkit
- libxml2 (bSNES XML shaders)
- libfreetype2 (TTF font rendering on screen)
- libsamplerate
SSNES needs one of these audio driver libraries:
SSNES needs at least one of these audio driver libraries:
- ALSA
- OSS
@@ -35,6 +35,8 @@ SSNES needs one of these audio driver libraries:
- OpenAL
- JACK
- SDL
- XAudio2 (Win32)
- PulseAudio
# Building libsnes
+39
View File
@@ -0,0 +1,39 @@
===================================================
SSNES - Simple Super Nintendo Emulator System
===================================================
SSNES is a simplistic emulator frontend designed for use without any GUI.
It is used from command line, but is designed to play well with eventual GUI frontends,
as the configuration file format is very simple.
This allows for great portability and focus on what matters in a frontend, such as video and audio performance.
Its core libraries for video and input are OpenGL and SDL, with many different audio library supported.
On first use you should get accustomed to how SSNES works.
- SSNES emphasizes simplicity. It has no graphical user interface, and is primarily used through the command line.
It is recommended for experienced users.
It is possible though to create a GUI which can use SSNES.
- SSNES isn't dependant on any emulator core, such as bSNES, SNES9x or ZSNES.
SSNES only talks to the libsnes interface,
which was created initially by bSNES but it has an implementation available for SNES9x as well.
- SSNES is configured through a config file. In the Win32 distribution, there should be a ssnes.cfg included.
This config file serves as a base for your custom configuration file.
It is recommended that you go through the config file and alter any configurations you might use.
This is a good way to learn about the features SSNES have.
It is possible to pick which configuration file you want with the '-c' flag.
E.g.: ssnes.exe -c myconfig.cfg rom.sfc.
- When unsure which command line flags to use, use the command: ssnes.exe --help.
- When something is not working as expected, try using verbose logging first to see what is happening:
E.g.: ssnes.exe -v romfile.sfc
If something is not working, and you want to file a bug report,
please include console output with the -v flag turned on.
- SSNES does not support zipped roms directly. It only supports roms with extentions .sfc and .smc.
However, SSNES can read roms from stdin,
which makes it possible to create wrapper programs that can support every zip format under the sun.
This can also be performed by a GUI frontend.
+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 )
+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
+124
View File
@@ -0,0 +1,124 @@
/////
// 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 2
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;
} 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
+110
View File
@@ -0,0 +1,110 @@
/* 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/>.
*/
// Hermite resampler based on bsnes' audio library.
#include "hermite.h"
#include <stdlib.h>
#define MAX_CHANS 8
struct hermite_resampler
{
float chan_data[MAX_CHANS][4];
double r_frac;
unsigned chans;
};
static inline float hermite_kernel(float mu1, float a, float b, float c, float d)
{
float mu2, mu3, m0, m1, a0, a1, a2, a3;
mu2 = mu1 * mu1;
mu3 = mu2 * mu1;
m0 = (c - a) * 0.5;
m1 = (d - b) * 0.5;
a0 = +2 * mu3 - 3 * mu2 + 1;
a1 = mu3 - 2 * mu2 + mu1;
a2 = mu3 - mu2;
a3 = -2 * mu3 + 3 * mu2;
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
}
hermite_resampler_t *hermite_new(unsigned channels)
{
if (channels > MAX_CHANS)
return NULL;
hermite_resampler_t *re = calloc(1, sizeof(*re));
if (!re)
return NULL;
re->chans = channels;
return re;
}
void hermite_process(hermite_resampler_t *re, struct hermite_data *data)
{
double r_step = 1.0 / data->src_ratio;
size_t processed_out = 0;
size_t processed_in = 0;
size_t in_frames = data->input_frames;
size_t out_frames = data->output_frames;
const float *in_data = data->data_in;
float *out_data = data->data_out;
while (processed_in < in_frames && processed_out < out_frames)
{
while (re->r_frac <= 1.0 && processed_out < out_frames)
{
re->r_frac += r_step;
for (unsigned i = 0; i < re->chans; i++)
{
float res = hermite_kernel(re->r_frac,
re->chan_data[i][0], re->chan_data[i][1], re->chan_data[i][2], re->chan_data[i][3]);
*out_data++ = res;
}
processed_out++;
}
if (re->r_frac >= 1.0)
{
re->r_frac -= 1.0;
for (unsigned i = 0; i < re->chans; i++)
{
re->chan_data[i][0] = re->chan_data[i][1];
re->chan_data[i][1] = re->chan_data[i][2];
re->chan_data[i][2] = re->chan_data[i][3];
re->chan_data[i][3] = *in_data++;
}
processed_in++;
}
}
data->input_frames_used = processed_in;
data->output_frames_gen = processed_out;
}
void hermite_free(hermite_resampler_t *re)
{
free(re);
}
+50
View File
@@ -0,0 +1,50 @@
/* 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/>.
*/
// Hermite resampler based on bsnes' audio library.
// Attempts to be similiar to the libsamplerate process interface.
#ifndef __SSNES_HERMITE_H
#define __SSNES_HERMITE_H
#include <stddef.h>
#include <stdbool.h>
typedef struct hermite_resampler hermite_resampler_t;
hermite_resampler_t *hermite_new(unsigned channels);
struct hermite_data
{
const float *data_in;
float *data_out;
size_t input_frames;
size_t output_frames;
size_t input_frames_used;
size_t output_frames_gen;
bool end_of_input; // Just used to clone the SRC API.
double src_ratio;
};
void hermite_process(hermite_resampler_t *re, struct hermite_data *data);
void hermite_free(hermite_resampler_t *re);
#endif
+10 -2
View File
@@ -103,7 +103,15 @@ static size_t find_buffersize(jack_t *jd, int latency)
{
int frames = latency * g_settings.audio.out_rate / 1000;
int jack_latency = jack_port_get_total_latency(jd->client, jd->ports[0]);
jack_latency_range_t range;
int jack_latency = 0;
for (int i = 0; i < 2; i++)
{
jack_port_get_latency_range(jd->ports[i], JackPlaybackLatency, &range);
if (range.max > jack_latency)
jack_latency = range.max;
}
SSNES_LOG("JACK: Jack latency is %d frames.\n", jack_latency);
int buffer_frames = frames - jack_latency;
@@ -116,7 +124,7 @@ static size_t find_buffersize(jack_t *jd, int latency)
return buffer_frames * sizeof(jack_default_audio_sample_t);
}
static void* __jack_init(const char* device, 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 )
+8 -1
View File
@@ -17,8 +17,15 @@
#include "driver.h"
#ifdef __APPLE__
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
#endif
#include <time.h>
#include <string.h>
@@ -49,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));
+27 -17
View File
@@ -15,28 +15,43 @@
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "driver.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 +60,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);
@@ -55,21 +70,21 @@ static void* __oss_init(const char* device, int rate, int latency)
int channels = 2;
int format = AFMT_S16_LE;
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 +98,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 +154,4 @@ const audio_driver_t audio_oss = {
.free = __oss_free,
.ident = "oss"
};
+252
View File
@@ -0,0 +1,252 @@
/* 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 "general.h"
#include <pulse/pulseaudio.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
typedef struct
{
pa_threaded_mainloop *mainloop;
pa_context *context;
pa_stream *stream;
bool nonblock;
} pa_t;
static void __pulse_free(void *data)
{
pa_t *pa = data;
if (pa)
{
if (pa->mainloop)
pa_threaded_mainloop_stop(pa->mainloop);
if (pa->stream)
{
pa_stream_disconnect(pa->stream);
pa_stream_unref(pa->stream);
}
if (pa->context)
{
pa_context_disconnect(pa->context);
pa_context_unref(pa->context);
}
if (pa->mainloop)
pa_threaded_mainloop_free(pa->mainloop);
free(pa);
}
}
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;
switch (pa_context_get_state(c))
{
case PA_CONTEXT_READY:
case PA_CONTEXT_TERMINATED:
case PA_CONTEXT_FAILED:
pa_threaded_mainloop_signal(pa->mainloop, 0);
break;
default:
break;
}
}
static void stream_state_cb(pa_stream *s, void *data)
{
pa_t *pa = data;
switch (pa_stream_get_state(s)) {
case PA_STREAM_READY:
case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED:
pa_threaded_mainloop_signal(pa->mainloop, 0);
break;
default:
break;
}
}
static void stream_request_cb(pa_stream *s, size_t length, void *data)
{
(void)length;
(void)s;
pa_t *pa = data;
pa_threaded_mainloop_signal(pa->mainloop, 0);
}
static void stream_latency_update_cb(pa_stream *s, void *data)
{
(void)s;
pa_t *pa = data;
pa_threaded_mainloop_signal(pa->mainloop, 0);
}
static void* __pulse_init(const char* device, unsigned rate, unsigned latency)
{
pa_t *pa = calloc(1, sizeof(*pa));
if (!pa)
goto error;
pa->mainloop = pa_threaded_mainloop_new();
if (!pa->mainloop)
goto error;
pa->context = pa_context_new(pa_threaded_mainloop_get_api(pa->mainloop), "SSNES");
if (!pa->context)
goto error;
pa_context_set_state_callback(pa->context, context_state_cb, pa);
if (pa_context_connect(pa->context, device, PA_CONTEXT_NOFLAGS, NULL) < 0)
goto error;
pa_threaded_mainloop_lock(pa->mainloop);
if (pa_threaded_mainloop_start(pa->mainloop) < 0)
goto error;
pa_threaded_mainloop_wait(pa->mainloop);
if (pa_context_get_state(pa->context) != PA_CONTEXT_READY)
goto unlock_error;
pa_sample_spec spec = {
.format = is_little_endian() ? PA_SAMPLE_FLOAT32LE : PA_SAMPLE_FLOAT32BE,
.channels = 2,
.rate = rate
};
pa->stream = pa_stream_new(pa->context, "audio", &spec, NULL);
if (!pa->stream)
goto unlock_error;
pa_stream_set_state_callback(pa->stream, stream_state_cb, pa);
pa_stream_set_write_callback(pa->stream, stream_request_cb, pa);
pa_stream_set_latency_update_callback(pa->stream, stream_latency_update_cb, pa);
pa_buffer_attr buffer_attr = {
.maxlength = -1,
.tlength = pa_usec_to_bytes(latency * PA_USEC_PER_MSEC, &spec),
.prebuf = -1,
.minreq = -1,
.fragsize = -1
};
if (pa_stream_connect_playback(pa->stream, NULL, &buffer_attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0)
goto error;
pa_threaded_mainloop_wait(pa->mainloop);
if (pa_stream_get_state(pa->stream) != PA_STREAM_READY)
goto unlock_error;
pa_threaded_mainloop_unlock(pa->mainloop);
return pa;
unlock_error:
pa_threaded_mainloop_unlock(pa->mainloop);
error:
__pulse_free(pa);
return NULL;
}
static ssize_t __pulse_write(void* data, const void* buf, size_t size)
{
pa_t *pa = data;
pa_threaded_mainloop_lock(pa->mainloop);
unsigned length = pa_stream_writable_size(pa->stream);
pa_threaded_mainloop_unlock(pa->mainloop);
while (length < size)
{
pa_threaded_mainloop_wait(pa->mainloop);
pa_threaded_mainloop_lock(pa->mainloop);
length = pa_stream_writable_size(pa->stream);
pa_threaded_mainloop_unlock(pa->mainloop);
if (pa->nonblock)
break;
}
size_t write_size = length < size ? length : size;
pa_threaded_mainloop_lock(pa->mainloop);
pa_stream_write(pa->stream, buf, write_size, NULL, 0LL, PA_SEEK_RELATIVE);
pa_threaded_mainloop_unlock(pa->mainloop);
return write_size;
}
static bool __pulse_stop(void *data)
{
(void)data;
return true;
}
static bool __pulse_start(void *data)
{
(void)data;
return true;
}
static void __pulse_set_nonblock_state(void *data, bool state)
{
pa_t *pa = data;
pa->nonblock = state;
}
static bool __pulse_use_float(void *data)
{
(void)data;
return true;
}
const audio_driver_t audio_pulse = {
.init = __pulse_init,
.write = __pulse_write,
.stop = __pulse_stop,
.start = __pulse_start,
.set_nonblock_state = __pulse_set_nonblock_state,
.use_float = __pulse_use_float,
.free = __pulse_free,
.ident = "pulse"
};
+11 -8
View File
@@ -22,6 +22,7 @@
#include <errno.h>
#include <stdio.h>
#include <stdbool.h>
#include "general.h"
typedef struct
{
@@ -29,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));
@@ -37,13 +38,14 @@ static void* __roar_init(const char* device, int rate, int latency)
return NULL;
roar_vs_t *vss;
if ( (vss = roar_vs_new_simple(NULL, NULL, rate, 2, ROAR_CODEC_PCM_S_LE, 16, ROAR_DIR_PLAY, &err)) == NULL )
if ((vss = roar_vs_new_simple(device, "SSNES", rate, 2, ROAR_CODEC_PCM_S, 16, ROAR_DIR_PLAY, &err)) == NULL)
{
fprintf(stderr, "roar_vs: \"%s\"\n", roar_vs_strerr(err));
SSNES_ERR("RoarAudio: \"%s\"\n", roar_vs_strerr(err));
free(roar);
return NULL;
}
roar_vs_role(vss, ROAR_ROLE_GAME, NULL);
roar->vss = vss;
return roar;
@@ -54,19 +56,20 @@ static ssize_t __roar_write(void* data, const void* buf, size_t size)
roar_t *roar = data;
ssize_t rc;
if ( size == 0 )
if (size == 0)
return 0;
int err;
size_t written = 0;
while (written < size)
{
if ((rc = roar_vs_write(roar->vss, (const char*)buf + written, size - written, &err)) < size)
size_t write_amt = size - written;
if ((rc = roar_vs_write(roar->vss, (const char*)buf + written, write_amt, &err)) < write_amt)
{
if (rc < 0)
if (roar->nonblocking)
return rc;
else if (rc < 0)
return -1;
else if (roar->nonblocking)
return 0;
}
written += rc;
}
+78 -31
View File
@@ -19,16 +19,42 @@
#include "driver.h"
#include <stdlib.h>
#include <rsound.h>
#include "fifo_buffer.h"
#include <stdbool.h>
#include "SDL.h"
typedef struct rsd
{
rsound_t *rd;
int latency;
int rate;
int nonblock;
bool nonblock;
volatile bool has_error;
fifo_buffer_t *buffer;
SDL_mutex *cond_lock;
SDL_cond *cond;
} rsd_t;
static void* __rsd_init(const char* device, int rate, int latency)
static ssize_t audio_cb(void *data, size_t bytes, void *userdata)
{
rsd_t *rsd = userdata;
size_t avail = fifo_read_avail(rsd->buffer);
size_t write_size = bytes > avail ? avail : bytes;
fifo_read(rsd->buffer, data, write_size);
SDL_CondSignal(rsd->cond);
return write_size;
}
static void err_cb(void *userdata)
{
rsd_t *rsd = userdata;
rsd->has_error = true;
SDL_CondSignal(rsd->cond);
}
static void* __rsd_init(const char* device, unsigned rate, unsigned latency)
{
rsd_t *rsd = calloc(1, sizeof(rsd_t));
if ( rsd == NULL )
@@ -42,17 +68,25 @@ static void* __rsd_init(const char* device, int rate, int latency)
return NULL;
}
rsd->cond_lock = SDL_CreateMutex();
rsd->cond = SDL_CreateCond();
rsd->buffer = fifo_new(1024 * 4);
int channels = 2;
int format = RSD_S16_NE;
rsd_set_param(rd, RSD_CHANNELS, &channels);
rsd_set_param(rd, RSD_SAMPLERATE, &rate);
rsd_set_param(rd, RSD_LATENCY, &latency);
if ( device != NULL )
rsd_set_param(rd, RSD_HOST, (void*)device);
rsd_set_param(rd, RSD_FORMAT, &format);
rsd_set_callback(rd, audio_cb, err_cb, 256, rsd);
if ( rsd_start(rd) < 0 )
{
free(rsd);
@@ -60,14 +94,7 @@ static void* __rsd_init(const char* device, int rate, int latency)
return NULL;
}
int min_latency = (rsd_delay_ms(rd) > latency) ? (rsd_delay_ms(rd) * 3 / 2) : latency;
rsd_set_param(rd, RSD_LATENCY, &min_latency);
rsd->rd = rd;
rsd->latency = min_latency;
rsd->rate = rate;
return rsd;
}
@@ -75,26 +102,46 @@ static ssize_t __rsd_write(void* data, const void* buf, size_t size)
{
rsd_t *rsd = data;
if ( (rsd_delay_ms(rsd->rd) > rsd->latency || rsd_get_avail(rsd->rd) < size) && rsd->nonblock )
return 0;
if ( size == 0 )
return 0;
rsd_delay_wait(rsd->rd);
if ( rsd_write(rsd->rd, buf, size) == 0 )
if (rsd->has_error)
return -1;
if ( rsd_delay_ms(rsd->rd) < rsd->latency/2 )
if (rsd->nonblock)
{
int ms = rsd->latency/2;
size_t size = (ms * rsd->rate * 4) / 1000;
void *temp = calloc(1, size);
rsd_write(rsd->rd, temp, size);
free(temp);
rsd_callback_lock(rsd->rd);
size_t avail = fifo_write_avail(rsd->buffer);
size_t write_amt = avail > size ? size : avail;
fifo_write(rsd->buffer, buf, write_amt);
rsd_callback_unlock(rsd->rd);
return write_amt;
}
else
{
size_t written = 0;
while (written < size && !rsd->has_error)
{
rsd_callback_lock(rsd->rd);
size_t avail = fifo_write_avail(rsd->buffer);
return size;
if (avail == 0)
{
rsd_callback_unlock(rsd->rd);
if (!rsd->has_error)
{
SDL_mutexP(rsd->cond_lock);
SDL_CondWait(rsd->cond, rsd->cond_lock);
SDL_mutexV(rsd->cond_lock);
}
}
else
{
size_t write_amt = size - written > avail ? avail : size - written;
fifo_write(rsd->buffer, (const char*)buf + written, write_amt);
rsd_callback_unlock(rsd->rd);
written += write_amt;
}
}
return written;
}
}
static bool __rsd_stop(void *data)
@@ -126,6 +173,11 @@ static void __rsd_free(void *data)
rsd_stop(rsd->rd);
rsd_free(rsd->rd);
fifo_free(rsd->buffer);
SDL_DestroyMutex(rsd->cond_lock);
SDL_DestroyCond(rsd->cond);
free(rsd);
}
@@ -138,9 +190,4 @@ const audio_driver_t audio_rsound = {
.free = __rsd_free,
.ident = "rsound"
};
+12 -25
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,35 +35,22 @@ 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.
memset(stream + write_size, 0, len - write_size);
}
// Interesting hack from http://www-graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
static inline uint32_t next_pow2(uint32_t v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
static inline int find_num_frames(int rate, int latency)
{
int frames = (rate * latency) / 1000;
@@ -71,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)
@@ -111,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);
}
@@ -130,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;
}
@@ -142,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)
{
@@ -154,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;
}
@@ -193,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);
}
+160
View File
@@ -0,0 +1,160 @@
/*
Simple C interface for XAudio2
Author: Hans-Kristian Arntzen
License: Public Domain
*/
// This will currently only compile with MSVC2k10.
// Should be compilable with some MinGW trickery as well.
#include "xaudio-c.h"
struct xaudio2 : public IXAudio2VoiceCallback
{
xaudio2(unsigned samplerate, unsigned channels, unsigned bits, unsigned size) :
buf(nullptr), pXAudio2(nullptr), pMasterVoice(nullptr), pSourceVoice(nullptr),
buffers(0), bufptr(0)
{
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
HRESULT hr;
if (FAILED(hr = XAudio2Create(&pXAudio2, 0)))
throw -1;
if (FAILED(hr = pXAudio2->CreateMasteringVoice(
&pMasterVoice, channels, samplerate, 0, 0, nullptr)))
throw -1;
WAVEFORMATEX wfx;
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = channels;
wfx.nSamplesPerSec = samplerate;
wfx.nBlockAlign = channels * bits / 8;
wfx.wBitsPerSample = bits;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
wfx.cbSize = 0;
if (FAILED(hr = pXAudio2->CreateSourceVoice(
&pSourceVoice, &wfx, XAUDIO2_VOICE_NOSRC, XAUDIO2_DEFAULT_FREQ_RATIO, this, nullptr, nullptr)))
throw -1;
pSourceVoice->Start();
hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (!hEvent)
throw -1;
max_buffers = 8;
bufsize = size / max_buffers;
buf = new uint8_t[bufsize * max_buffers];
write_buffer = 0;
}
~xaudio2()
{
if (pSourceVoice)
{
pSourceVoice->Stop();
pSourceVoice->DestroyVoice();
}
if (pMasterVoice)
pMasterVoice->DestroyVoice();
if (pXAudio2)
pXAudio2->Release();
if (hEvent)
CloseHandle(hEvent);
if (buf)
delete[] buf;
}
size_t write_avail() const
{
return bufsize * (max_buffers - buffers - 1);
}
size_t write(const void *buffer, size_t bytes)
{
size_t written = 0;
while (written < bytes)
{
size_t need = min(bytes - written, bufsize - bufptr);
memcpy((uint8_t*)buf + write_buffer * bufsize + bufptr, (const uint8_t*)buffer + written, need);
written += need;
bufptr += need;
if (bufptr == bufsize)
{
while (buffers == max_buffers - 1)
WaitForSingleObject(hEvent, INFINITE);
XAUDIO2_BUFFER xa2buffer = {0};
xa2buffer.AudioBytes = bufsize;
xa2buffer.pAudioData = buf + write_buffer * bufsize;
xa2buffer.pContext = nullptr;
InterlockedIncrement(&buffers);
pSourceVoice->SubmitSourceBuffer(&xa2buffer);
bufptr = 0;
write_buffer = (write_buffer + 1) % max_buffers;
}
}
return bytes;
}
STDMETHOD_(void, OnBufferStart) (void *) {}
STDMETHOD_(void, OnBufferEnd) (void *)
{
InterlockedDecrement(&buffers);
SetEvent(hEvent);
}
STDMETHOD_(void, OnLoopEnd) (void *) {}
STDMETHOD_(void, OnStreamEnd) () {}
STDMETHOD_(void, OnVoiceError) (void *, HRESULT) {}
STDMETHOD_(void, OnVoiceProcessingPassEnd) ()
{
SetEvent(hEvent);
}
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) {}
uint8_t *buf;
IXAudio2 *pXAudio2;
IXAudio2MasteringVoice *pMasterVoice;
IXAudio2SourceVoice *pSourceVoice;
HANDLE hEvent;
volatile long buffers;
unsigned max_buffers;
unsigned bufsize;
unsigned bufptr;
unsigned write_buffer;
};
XAUDIOC_API xaudio2_t *xaudio2_new(unsigned samplerate, unsigned channels, unsigned bits, size_t size)
{
xaudio2 *handle = nullptr;
try {
handle = new xaudio2(samplerate, channels, bits, size);
} catch(...) { delete handle; return nullptr; }
return handle;
}
XAUDIOC_API size_t xaudio2_write_avail(xaudio2_t *handle)
{
try {
return handle->write_avail();
} catch(...) { return 0; }
}
XAUDIOC_API size_t xaudio2_write(xaudio2_t *handle, const void *buf, size_t bytes)
{
try {
return handle->write(buf, bytes);
} catch(...) { return 0; }
}
XAUDIOC_API void xaudio2_free(xaudio2_t *handle)
{
delete handle;
}
+38
View File
@@ -0,0 +1,38 @@
/*
Simple C interface for XAudio2
Author: Hans-Kristian Arntzen
License: Public Domain
*/
#ifndef XAUDIO_C_H
#define XAUDIO_C_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifdef XAUDIOC_EXPORTS
#define XAUDIOC_API __declspec(dllexport)
#else
#define XAUDIOC_API __declspec(dllimport)
#endif
typedef struct xaudio2 xaudio2_t;
XAUDIOC_API xaudio2_t* xaudio2_new(unsigned samplerate, unsigned channels, unsigned bits, size_t bufsize);
XAUDIOC_API size_t xaudio2_write_avail(xaudio2_t *handle);
XAUDIOC_API size_t xaudio2_write(xaudio2_t *handle, const void *data, size_t bytes);
XAUDIOC_API void xaudio2_free(xaudio2_t *handle);
typedef xaudio2_t* (*xaudio2_new_t)(unsigned, unsigned, unsigned, size_t);
typedef size_t (*xaudio2_write_avail_t)(xaudio2_t*);
typedef size_t (*xaudio2_write_t)(xaudio2_t*, const void*, size_t);
typedef void (*xaudio2_free_t)(xaudio2_t*);
#ifdef __cplusplus
}
#endif
#endif
+153
View File
@@ -0,0 +1,153 @@
/* 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 "xaudio-c.h"
#include "general.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef struct
{
xaudio2_t *xa;
bool nonblock;
} xa_t;
static xaudio2_new_t pxanew = NULL;
static xaudio2_write_t pxawrite = NULL;
static xaudio2_write_avail_t pxawrite_avail = NULL;
static xaudio2_free_t pxafree = NULL;
static HMODULE lib = NULL;
#define LIB_NAME "xaudio-c.dll"
#define SYM(X) ((void*)GetProcAddress(lib, "xaudio2_" #X))
static void deinit_lib(void)
{
FreeModule(lib);
lib = NULL;
}
static bool init_lib(void)
{
if (lib)
return true;
lib = LoadLibrary(LIB_NAME);
if (!lib)
return false;
pxanew = SYM(new);
pxawrite = SYM(write);
pxawrite_avail = SYM(write_avail);
pxafree = SYM(free);
if (!pxanew || !pxawrite || !pxawrite_avail || !pxafree)
{
deinit_lib();
return false;
}
return true;
}
static void* __xa_init(const char* device, unsigned rate, unsigned latency)
{
if (!init_lib())
return NULL;
if (latency < 8)
latency = 8; // Do not allow shenanigans.
xa_t *xa = calloc(1, sizeof(xa_t));
if (xa == NULL)
return NULL;
size_t bufsize = latency * rate / 1000;
bufsize = next_pow2(bufsize);
SSNES_LOG("XAudio2: Requesting %d ms latency, using %d ms latency.\n", latency, (int)bufsize * 1000 / rate);
xa->xa = pxanew(rate, 2, 16, bufsize << 2);
if (!xa->xa)
{
SSNES_ERR("Failed to init XAudio2.\n");
free(xa);
return NULL;
}
return xa;
}
static ssize_t __xa_write(void* data, const void* buf, size_t size)
{
xa_t *xa = data;
if (xa->nonblock)
{
size_t avail = pxawrite_avail(xa->xa);
if (avail < size)
size = avail;
}
return pxawrite(xa->xa, buf, size);
}
static bool __xa_stop(void *data)
{
(void)data;
return true;
}
static void __xa_set_nonblock_state(void *data, bool state)
{
xa_t *xa = data;
xa->nonblock = state;
}
static bool __xa_start(void *data)
{
(void)data;
return true;
}
static void __xa_free(void *data)
{
xa_t *xa = data;
if (xa && xa->xa)
{
if (pxafree)
pxafree(xa->xa);
free(xa);
}
deinit_lib();
}
const audio_driver_t audio_xa = {
.init = __xa_init,
.write = __xa_write,
.stop = __xa_stop,
.start = __xa_start,
.set_nonblock_state = __xa_set_nonblock_state,
.free = __xa_free,
.ident = "xaudio"
};
+144
View File
@@ -0,0 +1,144 @@
/* 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 "autosave.h"
#include "SDL.h"
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include "general.h"
struct autosave
{
volatile bool quit;
SDL_mutex *lock;
SDL_mutex *cond_lock;
SDL_cond *cond;
SDL_Thread *thread;
void *buffer;
const void *snes_buffer;
const char *path;
size_t bufsize;
unsigned interval;
};
static int autosave_thread(void *data)
{
autosave_t *save = data;
while (!save->quit)
{
autosave_lock(save);
memcpy(save->buffer, save->snes_buffer, save->bufsize);
autosave_unlock(save);
// Should probably deal with this more elegantly.
FILE *file = fopen(save->path, "wb");
if (file)
{
SSNES_LOG("Autosaving SRAM to \"%s\"\n", save->path);
bool failed = false;
failed |= fwrite(save->buffer, 1, save->bufsize, file) != save->bufsize;
failed |= fflush(file) != 0;
failed |= fclose(file) != 0;
if (failed)
SSNES_WARN("Failed to autosave SRAM! Disk might be full.\n");
}
SDL_mutexP(save->cond_lock);
if (!save->quit)
SDL_CondWaitTimeout(save->cond, save->cond_lock, save->interval * 1000);
SDL_mutexV(save->cond_lock);
}
return 0;
}
autosave_t *autosave_new(const char *path, const void *data, size_t size, unsigned interval)
{
autosave_t *handle = calloc(1, sizeof(*handle));
if (!handle)
return NULL;
handle->bufsize = size;
handle->interval = interval;
handle->path = path;
handle->buffer = malloc(size);
handle->snes_buffer = data;
if (!handle->buffer)
{
free(handle);
return NULL;
}
handle->lock = SDL_CreateMutex();
handle->cond_lock = SDL_CreateMutex();
handle->cond = SDL_CreateCond();
handle->thread = SDL_CreateThread(autosave_thread, handle);
return handle;
}
void autosave_lock(autosave_t *handle)
{
SDL_mutexP(handle->lock);
}
void autosave_unlock(autosave_t *handle)
{
SDL_mutexV(handle->lock);
}
void autosave_free(autosave_t *handle)
{
SDL_mutexP(handle->cond_lock);
handle->quit = true;
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);
free(handle->buffer);
free(handle);
}
void lock_autosave(void)
{
for (unsigned i = 0; i < sizeof(g_extern.autosave)/sizeof(g_extern.autosave[0]); i++)
{
if (g_extern.autosave[i])
autosave_lock(g_extern.autosave[i]);
}
}
void unlock_autosave(void)
{
for (unsigned i = 0; i < sizeof(g_extern.autosave)/sizeof(g_extern.autosave[0]); i++)
{
if (g_extern.autosave[i])
autosave_unlock(g_extern.autosave[i]);
}
}
+33
View File
@@ -0,0 +1,33 @@
/* 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_AUTOSAVE_H
#define __SSNES_AUTOSAVE_H
#include <stddef.h>
typedef struct autosave autosave_t;
autosave_t *autosave_new(const char *path, const void *data, size_t size, unsigned interval);
void autosave_lock(autosave_t *handle);
void autosave_unlock(autosave_t *handle);
void autosave_free(autosave_t *handle);
void lock_autosave(void);
void unlock_autosave(void);
#endif
+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.
+126 -104
View File
@@ -31,20 +31,21 @@
#endif
#ifdef HAVE_SDL
#include <SDL/SDL.h>
#include "SDL.h"
#else
#error HAVE_SDL is not defined!
#error "HAVE_SDL is not defined!"
#endif
#ifdef HAVE_SRC
#include <samplerate.h>
#else
#error HAVE_SRC is not defined!
#endif
///////////////// 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
@@ -53,29 +54,57 @@
#define AUDIO_AL 5
#define AUDIO_JACK 6
#define AUDIO_SDL 8
#define AUDIO_XAUDIO 9
#define AUDIO_PULSE 10
#define AUDIO_EXT 15
////////////////////////
#define INPUT_SDL 7
#define INPUT_X 12
////////////////////////
#if defined(HAVE_SDL)
#define VIDEO_DEFAULT_DRIVER VIDEO_GL
#if HAVE_ALSA
#define AUDIO_DEFAULT_DRIVER AUDIO_ALSA
#elif HAVE_OSS
#define AUDIO_DEFAULT_DRIVER AUDIO_OSS
#elif HAVE_JACK
#define AUDIO_DEFAULT_DRIVER AUDIO_JACK
#elif HAVE_RSOUND
#define AUDIO_DEFAULT_DRIVER AUDIO_RSOUND
#elif HAVE_ROAR
#define AUDIO_DEFAULT_DRIVER AUDIO_ROAR
#elif HAVE_AL
#define AUDIO_DEFAULT_DRIVER AUDIO_AL
#elif HAVE_SDL
#define AUDIO_DEFAULT_DRIVER AUDIO_SDL
#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)
#define AUDIO_DEFAULT_DRIVER AUDIO_ALSA
#elif defined(HAVE_PULSE)
#define AUDIO_DEFAULT_DRIVER AUDIO_PULSE
#elif defined(HAVE_OSS)
#define AUDIO_DEFAULT_DRIVER AUDIO_OSS
#elif defined(HAVE_JACK)
#define AUDIO_DEFAULT_DRIVER AUDIO_JACK
#elif defined(HAVE_AL)
#define AUDIO_DEFAULT_DRIVER AUDIO_AL
#elif defined(HAVE_SDL)
#define AUDIO_DEFAULT_DRIVER AUDIO_SDL
#elif defined(HAVE_XAUDIO)
#define AUDIO_DEFAULT_DRIVER AUDIO_XAUDIO
#elif defined(HAVE_RSOUND)
#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!"
#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
////////////////
@@ -88,8 +117,11 @@ static const float yscale = 3.0; // Real y res = 224 * yscale
// Fullscreen
static const bool fullscreen = false; // To start in Fullscreen or not
static const unsigned fullscreen_x = 1280;
static const unsigned fullscreen_y = 720;
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;
// Video VSYNC (recommended)
static const bool vsync = true;
@@ -100,6 +132,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;
@@ -107,6 +142,12 @@ static const unsigned font_size = 48;
static const float message_pos_offset_x = 0.05;
static const float message_pos_offset_y = 0.05;
// Render-to-texture before rendering to screen (multi-pass shaders)
static const bool render_to_texture = false;
static const float fbo_scale_x = 2.0;
static const float fbo_scale_y = 2.0;
static const bool second_pass_smooth = true;
#define SNES_ASPECT_RATIO (4.0/3)
////////////////
@@ -121,7 +162,10 @@ static const unsigned out_rate = 48000;
// Input samplerate from libSNES.
// Lower this (slightly) if you are experiencing frequent audio dropouts while vsync is enabled.
static const unsigned in_rate = 31980;
static const float in_rate = 31980.0;
// When changing input rate on-the-fly
static const float audio_rate_step = 0.25;
// Audio device (e.g. hw:0,0 or /dev/audio). If NULL, will use defaults.
static const char* audio_device = NULL;
@@ -133,9 +177,28 @@ static const int out_latency = 64;
static const bool audio_sync = true;
// Defines the quality (and cpu reqirements) of samplerate conversion.
#ifdef HAVE_SRC
#define SAMPLERATE_QUALITY SRC_LINEAR
#endif
// Enables use of rewind. This will incur some memory footprint depending on the save state buffer.
// This rewind only works when using bSNES core atm.
static const bool rewind_enable = false;
// The buffer size for the rewind buffer. This needs to be about 15-20MB per minute. Very game dependant.
static const unsigned rewind_buffer_size = 20 << 20; // 20MiB
// How many frames to rewind at a time.
static const unsigned rewind_granularity = 1;
// Pause gameplay when gameplay loses focus.
static const bool pause_nonactive = true;
// Saves non-volatile SRAM at a regular interval. It is measured in seconds. A value of 0 disables autosave.
static const unsigned autosave_interval = 0;
// When being client over netplay, use keybinds for player 1 rather than player 2.
static const bool netplay_client_swap_input = true;
////////////////////
@@ -152,97 +215,56 @@ static const bool audio_sync = true;
// 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 },
{ SSNES_QUIT_KEY, SDLK_ESCAPE, NO_BTN, AXIS_NONE },
{ SSNES_STATE_SLOT_MINUS, SDLK_F6, NO_BTN, AXIS_NONE },
{ SSNES_STATE_SLOT_PLUS, SDLK_F7, NO_BTN, AXIS_NONE },
{ SSNES_AUDIO_INPUT_RATE_PLUS, SDLK_KP_PLUS, NO_BTN, AXIS_NONE },
{ SSNES_AUDIO_INPUT_RATE_MINUS, SDLK_KP_MINUS, NO_BTN, AXIS_NONE },
{ SSNES_REWIND, SDLK_r, NO_BTN, AXIS_NONE },
{ SSNES_MOVIE_RECORD_TOGGLE, SDLK_o, NO_BTN, AXIS_NONE },
{ SSNES_PAUSE_TOGGLE, SDLK_p, NO_BTN, AXIS_NONE },
{ SSNES_RESET, SDLK_h, NO_BTN, AXIS_NONE },
{ SSNES_SHADER_NEXT, SDLK_m, NO_BTN, AXIS_NONE },
{ SSNES_SHADER_PREV, SDLK_n, NO_BTN, AXIS_NONE },
{ SSNES_CHEAT_INDEX_PLUS, SDLK_y, NO_BTN, AXIS_NONE },
{ SSNES_CHEAT_INDEX_MINUS, SDLK_t, NO_BTN, AXIS_NONE },
{ SSNES_CHEAT_TOGGLE, SDLK_u, NO_BTN, AXIS_NONE },
{ SSNES_SCREENSHOT, SDLK_PRINT, NO_BTN, AXIS_NONE },
{ SSNES_DSP_CONFIG, SDLK_c, NO_BTN, AXIS_NONE },
{ -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 }
};
+46 -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;
@@ -50,10 +56,22 @@ static const bool _jack_supp = true;
static const bool _jack_supp = false;
#endif
#ifdef HAVE_FILTER
static const bool _filter_supp = true;
#ifdef HAVE_PULSE
static const bool _pulse_supp = true;
#else
static const bool _filter_supp = false;
static const bool _pulse_supp = false;
#endif
#ifdef HAVE_XAUDIO
static const bool _xaudio_supp = true;
#else
static const bool _xaudio_supp = false;
#endif
#ifdef HAVE_DYLIB
static const bool _dylib_supp = true;
#else
static const bool _dylib_supp = false;
#endif
#ifdef HAVE_CG
@@ -68,6 +86,18 @@ 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
static const bool _fbo_supp = false;
#endif
#ifdef HAVE_DYNAMIC
static const bool _dynamic_supp = true;
#else
@@ -98,4 +128,16 @@ static const bool _freetype_supp = true;
static const bool _freetype_supp = false;
#endif
#ifdef HAVE_NETPLAY
static const bool _netplay_supp = true;
#else
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
+4
View File
@@ -34,6 +34,10 @@ Use config file found in PATH as a base. Old configuration values can be overwri
\fB--output PATH, -o PATH\fR
Writes the final config to a file, rather than \fBstdout\fR. If -i and -o point to the same file, the file found in PATH will simply be updated with the new configs.
.TP
\fB--misc, -m\fR
Also configure some binds that aren't directly related to SNES emulation, such as save states, fullscreen toggling, etc.
.TP
\fB--help, -h\fR
Prints help message.
+40 -1
View File
@@ -47,7 +47,7 @@ Should this not be defined, \fBssnes\fR will look in platform specific paths to
/etc/ssnes.cfg (when installed), or ssnes.cfg in the source tarball serves as a skeleton configuration file.
.IP
Unix-like systems will look in $XDG_CONFIG_HOME/ssnes/ssnes.cfg first. Then it will try $HOME/.ssnesrc. Last, it will try /etc/ssnes.cfg. If no configuration is found, default settings will be assumed. A configuration file does not need to define every possible option, only those that should be overridden.
Unix-like systems will look in $XDG_CONFIG_HOME/ssnes/ssnes.cfg first. Then it will try $HOME/.ssnes.cfg. Last, it will try /etc/ssnes.cfg. If no configuration is found, default settings will be assumed. A configuration file does not need to define every possible option, only those that should be overridden.
.IP
Windows will look in ssnes.cfg in same folder where ssnes.exe resides.
@@ -80,6 +80,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.
@@ -100,9 +103,45 @@ Connects a four-way multitap into port 2 of the SNES. This allows for up to 5 pl
\fB--record PATH, -r PATH\fR
Activates video recording of gameplay into PATH. Video recording options can be configured in config file.
.TP
\fB--bsvplay PATH, -P PATH\fR
Play back a movie recorded in the .bsv format (bSNES). Cart ROM and movie file need to correspond.
It also requires to play back with the same libsnes backend that was used for recording.
.TP
\fB--verbose, -v\fR
Activates verbose logging.
.TP
\fB--host, -H\fR
Be the host of netplay. Waits until a player connects. The host will always assume player 1.
.TP
\fB--connect SERVER, -C SERVER\fR
Connect to a host of netplay. Will always assume player 2.
.TP
\fB--frames FRAMES, -F FRAMES\fR
Sync frames to use when using netplay. More frames allow for more latency, but requires more CPU power.
Set FRAMES to 0 to have perfect sync. 0 frames is only suitable for LAN. Defaults to 0.
.TP
\fB--port PORT\fR
Network port used for netplay. This defaults to 55435. This option affects both TCP and UDP.
.TP
\fB--ups PATCH, -U PATCH\fR
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)
+250 -48
View File
@@ -18,10 +18,11 @@
#include "driver.h"
#include "general.h"
#include "file.h"
#include <stdio.h>
#include <string.h>
#include "hqflt/filters.h"
#include <assert.h>
#include <math.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -31,7 +32,7 @@ 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
@@ -49,11 +50,27 @@ static const audio_driver_t *audio_drivers[] = {
#ifdef HAVE_SDL
&audio_sdl,
#endif
#ifdef HAVE_XAUDIO
&audio_xa,
#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
};
@@ -61,6 +78,9 @@ static const input_driver_t *input_drivers[] = {
#ifdef HAVE_SDL
&input_sdl,
#endif
#ifdef HAVE_XVIDEO
&input_x,
#endif
};
static void find_audio_driver(void)
@@ -112,7 +132,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);
}
@@ -129,6 +149,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 *(*)(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");
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
@@ -160,10 +245,16 @@ void init_audio(void)
else
g_extern.audio_data.chunk_size = g_extern.audio_data.block_chunk_size;
#ifdef HAVE_SRC
int err;
g_extern.source = src_new(g_settings.audio.src_quality, 2, &err);
if (!g_extern.source)
g_extern.audio_data.source = src_new(g_settings.audio.src_quality, 2, &err);
if (!g_extern.audio_data.source)
g_extern.audio_active = false;
#else
g_extern.audio_data.source = hermite_new(2);
if (!g_extern.audio_data.source)
g_extern.audio_active = false;
#endif
size_t max_bufsamples = g_extern.audio_data.block_chunk_size > g_extern.audio_data.nonblock_chunk_size ?
g_extern.audio_data.block_chunk_size : g_extern.audio_data.nonblock_chunk_size;
@@ -173,6 +264,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)
@@ -186,45 +279,172 @@ void uninit_audio(void)
if ( driver.audio_data && driver.audio )
driver.audio->free(driver.audio_data);
if ( g_extern.source )
src_delete(g_extern.source);
if ( g_extern.audio_data.source )
{
#ifdef HAVE_SRC
src_delete(g_extern.audio_data.source);
#else
hermite_free(g_extern.audio_data.source);
#endif
}
free(g_extern.audio_data.data); g_extern.audio_data.data = NULL;
free(g_extern.audio_data.outsamples); g_extern.audio_data.outsamples = NULL;
free(g_extern.audio_data.conv_outsamples); g_extern.audio_data.conv_outsamples = NULL;
deinit_dsp_plugin();
}
#ifdef HAVE_DYLIB
static void init_filter(void)
{
if (g_extern.filter.active)
return;
if (*g_settings.video.filter_path == '\0')
return;
SSNES_LOG("Loading bSNES filter from \"%s\"\n", g_settings.video.filter_path);
g_extern.filter.lib = dylib_load(g_settings.video.filter_path);
if (!g_extern.filter.lib)
{
SSNES_ERR("Failed to load filter \"%s\"\n", g_settings.video.filter_path);
return;
}
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");
dylib_close(g_extern.filter.lib);
g_extern.filter.lib = NULL;
return;
}
g_extern.filter.active = true;
unsigned width = 512;
unsigned height = 512;
g_extern.filter.psize(&width, &height);
unsigned pow2_x = next_pow2(ceil(width));
unsigned pow2_y = next_pow2(ceil(height));
unsigned maxsize = pow2_x > pow2_y ? pow2_x : pow2_y;
g_extern.filter.scale = maxsize / 256;
g_extern.filter.buffer = malloc(256 * 256 * g_extern.filter.scale * g_extern.filter.scale * sizeof(uint32_t));
g_extern.filter.pitch = 256 * g_extern.filter.scale * sizeof(uint32_t);
assert(g_extern.filter.buffer);
g_extern.filter.colormap = malloc(32768 * sizeof(uint32_t));
assert(g_extern.filter.colormap);
// Set up conversion map from 16-bit XRGB1555 to 32-bit ARGB.
for (int i = 0; i < 32768; i++)
{
unsigned r = (i >> 10) & 31;
unsigned g = (i >> 5) & 31;
unsigned b = (i >> 0) & 31;
r = (r << 3) | (r >> 2);
g = (g << 3) | (g >> 2);
b = (b << 3) | (b >> 2);
g_extern.filter.colormap[i] = (r << 16) | (g << 8) | (b << 0);
}
}
#endif
static void deinit_filter(void)
{
if (g_extern.filter.active)
{
g_extern.filter.active = false;
dylib_close(g_extern.filter.lib);
g_extern.filter.lib = NULL;
free(g_extern.filter.buffer);
free(g_extern.filter.colormap);
}
}
#ifdef HAVE_XML
static void init_shader_dir(void)
{
if (!*g_settings.video.shader_dir)
return;
g_extern.shader_dir.elems = dir_list_new(g_settings.video.shader_dir, ".shader");
g_extern.shader_dir.size = 0;
g_extern.shader_dir.ptr = 0;
if (g_extern.shader_dir.elems)
{
while (g_extern.shader_dir.elems[g_extern.shader_dir.size])
{
SSNES_LOG("Found shader \"%s\"\n", g_extern.shader_dir.elems[g_extern.shader_dir.size]);
g_extern.shader_dir.size++;
}
}
}
static void deinit_shader_dir(void)
{
// It handles NULL, no worries :D
dir_list_free(g_extern.shader_dir.elems);
}
#endif
void init_video_input(void)
{
int scale = 2;
#ifdef HAVE_DYLIB
init_filter();
#endif
#ifdef HAVE_XML
init_shader_dir();
#endif
// We use at least 512x512 textures to accomodate for hi-res games.
unsigned scale = 2;
find_video_driver();
find_input_driver();
// We multiply scales with 2 to allow for hi-res games.
#if HAVE_FILTER
switch (g_settings.video.filter)
if (g_extern.filter.active)
scale = g_extern.filter.scale;
unsigned width;
unsigned height;
if (g_settings.video.fullscreen)
{
case FILTER_HQ2X:
scale = 4;
break;
case FILTER_HQ4X:
case FILTER_NTSC:
scale = 8;
break;
default:
break;
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;
}
}
#endif
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,
.smooth = g_settings.video.smooth,
.input_scale = scale,
.rgb32 = g_extern.filter.active
};
const input_driver_t *tmp = driver.input;
@@ -232,7 +452,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);
}
@@ -264,31 +484,13 @@ void uninit_video_input(void)
if ( driver.input_data != driver.video_data && driver.input )
driver.input->free(driver.input_data);
deinit_filter();
#ifdef HAVE_XML
deinit_shader_dir();
#endif
}
driver_t driver;
#if 0
driver_t driver = {
#if VIDEO_DRIVER == VIDEO_GL
.video = &video_gl,
#else
#error "Define a valid video driver in config.h"
#endif
#if AUDIO_DRIVER == AUDIO_ALSA
.audio = &audio_alsa,
#elif AUDIO_DRIVER == AUDIO_RSOUND
.audio = &audio_rsound,
#elif AUDIO_DRIVER == AUDIO_OSS
.audio = &audio_oss,
#elif AUDIO_DRIVER == AUDIO_ROAR
.audio = &audio_roar,
#elif AUDIO_DRIVER == AUDIO_AL
.audio = &audio_openal,
#else
#error "Define a valid audio driver in config.h"
#endif
};
#endif
+35 -11
View File
@@ -28,14 +28,29 @@
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,
SSNES_QUIT_KEY,
SSNES_STATE_SLOT_PLUS,
SSNES_STATE_SLOT_MINUS,
SSNES_AUDIO_INPUT_RATE_PLUS,
SSNES_AUDIO_INPUT_RATE_MINUS,
SSNES_REWIND,
SSNES_MOVIE_RECORD_TOGGLE,
SSNES_PAUSE_TOGGLE,
SSNES_RESET,
SSNES_SHADER_NEXT,
SSNES_SHADER_PREV,
SSNES_CHEAT_INDEX_PLUS,
SSNES_CHEAT_INDEX_MINUS,
SSNES_CHEAT_TOGGLE,
SSNES_SCREENSHOT,
SSNES_DSP_CONFIG
};
struct snes_keybind
{
int id;
@@ -46,18 +61,19 @@ struct snes_keybind
typedef struct video_info
{
int width;
int height;
unsigned width;
unsigned height;
bool fullscreen;
bool vsync;
bool force_aspect;
bool smooth;
int input_scale; // HQ2X => 2, HQ4X => 4, None => 1
bool rgb32; // Use 32-bit RGBA rather than native XBGR1555.
} video_info_t;
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);
@@ -67,15 +83,14 @@ typedef struct audio_driver
const char *ident;
} audio_driver_t;
#define AXIS_NEG(x) ((uint32_t)(x << 16) | 0xFFFF)
#define AXIS_NEG(x) (((uint32_t)(x) << 16) | 0xFFFFU)
#define AXIS_POS(x) ((uint32_t)(x) | 0xFFFF0000U)
#define AXIS_NONE ((uint32_t)0xFFFFFFFFU)
#define AXIS_NONE (0xFFFFFFFFU)
#define AXIS_NEG_GET(x) ((x >> 16) & 0xFFFF)
#define AXIS_POS_GET(x) (x & 0xFFFF)
#define AXIS_NONE ((uint32_t)0xFFFFFFFFU)
#define AXIS_NEG_GET(x) (((uint32_t)(x) >> 16) & 0xFFFFU)
#define AXIS_POS_GET(x) ((uint32_t)(x) & 0xFFFFU)
#define NO_BTN 0xFFFF // I hope no joypad will ever have this many buttons ... ;)
#define NO_BTN ((uint16_t)0xFFFFU) // I hope no joypad will ever have this many buttons ... ;)
#define HAT_UP_MASK (1 << 15)
#define HAT_DOWN_MASK (1 << 14)
@@ -99,12 +114,14 @@ 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 uint16_t* frame, unsigned width, unsigned height, unsigned pitch, const char *msg); // msg is for showing a message on the screen along with the video frame.
bool (*frame)(void* data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg); // msg is for showing a message on the screen along with the video frame.
void (*set_nonblock_state)(void* data, bool toggle); // Should we care about syncing to vblank? Fast forwarding.
// Is the window still active?
bool (*alive)(void *data);
bool (*focus)(void *data); // Does the window have focus?
bool (*xml_shader)(void *data, const char *path); // Sets XML-shader. Might not be implemented.
void (*free)(void* data);
const char *ident;
} video_driver_t;
@@ -138,8 +155,15 @@ extern const audio_driver_t audio_roar;
extern const audio_driver_t audio_openal;
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 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;
////////////////////////////////////////////////
#endif
+96 -52
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"
@@ -25,31 +26,28 @@
#include <libsnes.hpp>
#ifdef HAVE_DYNAMIC
#ifdef _WIN32
#include <windows.h>
#define SYM(x) do { \
p##x = ((void*)GetProcAddress(lib_handle, #x)); \
if (p##x == NULL) { SSNES_ERR("Failed to load symbol: \"%s\"\n", #x); exit(1); } \
} while(0)
#else
#include <dlfcn.h>
#define SYM(x) do { \
p##x = dlsym(lib_handle, #x); \
if (p##x == NULL) { SSNES_ERR("Failed to load symbol: \"%s\"\n", #x); exit(1); } \
} while(0)
#endif // _WIN32
#endif // HAVE_DYNAMIC
#endif
#ifdef HAVE_DYNAMIC
#ifdef _WIN32
static HMODULE lib_handle;
#else
static void *lib_handle = NULL;
#endif
#define DLSYM(lib, x) dylib_proc(lib, #x)
#define SYM(type, x) do { \
p##x = (type)DLSYM(lib_handle, x); \
if (p##x == NULL) { SSNES_ERR("Failed to load symbol: \"%s\"\n", #x); exit(1); } \
} while(0)
#define OPT_SYM(type, x) do { \
p##x = (type)DLSYM(lib_handle, x); \
} while(0)
static dylib_t lib_handle = NULL;
#endif
void (*psnes_init)(void);
void (*psnes_set_video_refresh)(snes_video_refresh_t);
@@ -57,8 +55,13 @@ void (*psnes_set_audio_sample)(snes_audio_sample_t);
void (*psnes_set_input_poll)(snes_input_poll_t);
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);
@@ -97,40 +100,45 @@ void (*psnes_term)(void);
static void load_dynamic(void)
{
SSNES_LOG("Loading dynamic libsnes from: \"%s\"\n", g_settings.libsnes);
#ifdef _WIN32
lib_handle = LoadLibrary(g_settings.libsnes);
#else
lib_handle = dlopen(g_settings.libsnes, RTLD_LAZY);
#endif
lib_handle = dylib_load(g_settings.libsnes);
if (!lib_handle)
{
SSNES_ERR("Failed to open dynamic library: \"%s\"\n", g_settings.libsnes);
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);
SYM(snes_library_revision_minor);
SYM(snes_library_revision_major);
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
@@ -148,6 +156,9 @@ 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);
SSYM(snes_load_cartridge_normal);
@@ -169,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();
@@ -186,12 +200,42 @@ void uninit_dlsym(void)
{
#ifdef HAVE_DYNAMIC
if (lib_handle)
{
#ifdef _WIN32
FreeLibrary(lib_handle);
#else
dlclose(lib_handle);
#endif
}
dylib_close(lib_handle);
#endif
}
// Platform independent dylib loading.
dylib_t dylib_load(const char *path)
{
#ifdef _WIN32
return LoadLibrary(path);
#else
return dlopen(path, RTLD_LAZY);
#endif
}
function_t dylib_proc(dylib_t lib, const char *proc)
{
#ifdef _WIN32
function_t sym = (function_t)GetProcAddress(lib, proc);
#else
// 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)
SSNES_WARN("Failed to load symbol \"%s\"\n", proc);
return sym;
}
void dylib_close(dylib_t lib)
{
#ifdef _WIN32
FreeLibrary(lib);
#else
dlclose(lib);
#endif
}
+12 -1
View File
@@ -24,6 +24,13 @@
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);
function_t dylib_proc(dylib_t lib, const char *proc);
extern void (*psnes_init)(void);
extern void (*psnes_set_video_refresh)(snes_video_refresh_t);
@@ -31,6 +38,10 @@ 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);
@@ -49,7 +60,6 @@ extern bool (*psnes_load_cartridge_sufami_turbo)(
const char*, const uint8_t*, unsigned,
const char*, const uint8_t*, unsigned);
extern void (*psnes_set_controller_port_device)(bool, unsigned);
extern bool (*psnes_get_region)(void);
@@ -58,6 +68,7 @@ extern unsigned (*psnes_serialize_size)(void);
extern bool (*psnes_serialize)(uint8_t*, unsigned);
extern bool (*psnes_unserialize)(const uint8_t*, unsigned);
extern void (*psnes_reset)(void);
extern void (*psnes_run)(void);
extern void (*psnes_set_cartridge_basename)(const char*);
+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
+324 -95
View File
@@ -22,17 +22,116 @@
#include <libsnes.hpp>
#include <string.h>
#include "dynamic.h"
#include "movie.h"
#include "ups.h"
#include "strl.h"
#ifdef HAVE_XML
#include "sha256.h"
#endif
#ifdef _WIN32
#include <io.h>
#include <fcntl.h>
#include <windows.h>
#else
#include <sys/types.h>
#include <dirent.h>
#endif
// Generic file loader.
ssize_t read_file(const char *path, void **buf)
{
void *rom_buf = NULL;
FILE *file = fopen(path, "rb");
if (!file)
goto error;
fseek(file, 0, SEEK_END);
long len = ftell(file);
ssize_t rc = 0;
rewind(file);
rom_buf = malloc(len + 1);
if (!rom_buf)
{
SSNES_ERR("Couldn't allocate memory!\n");
goto error;
}
if ((rc = fread(rom_buf, 1, len, file)) < len)
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;
error:
if (file)
fclose(file);
free(rom_buf);
*buf = NULL;
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)
{
ssize_t ret;
ssize_t ret = 0;
uint8_t *ret_buf = NULL;
if (file == NULL) // stdin
{
#ifdef _WIN32
setmode(0, O_BINARY);
#endif
SSNES_LOG("Reading ROM from stdin ...\n");
size_t buf_size = 0xFFFFF; // Some initial guesstimate.
size_t buf_ptr = 0;
char *rom_buf = malloc(buf_size);
uint8_t *rom_buf = malloc(buf_size);
if (rom_buf == NULL)
{
SSNES_ERR("Couldn't allocate memory!\n");
@@ -58,91 +157,86 @@ static ssize_t read_rom_file(FILE* file, void** buf)
buf_size *= 2;
}
if ((buf_ptr & 0x7fff) == 512)
{
memmove(rom_buf, rom_buf + 512, buf_ptr - 512);
buf_ptr -= 512;
}
*buf = rom_buf;
ret_buf = rom_buf;
ret = buf_ptr;
}
else
{
fseek(file, 0, SEEK_END);
long length = ftell(file);
ret = ftell(file);
rewind(file);
if ((length & 0x7fff) == 512)
{
length -= 512;
fseek(file, 512, SEEK_SET);
}
void *rom_buf = malloc(length);
if ( rom_buf == NULL )
void *rom_buf = malloc(ret);
if (rom_buf == NULL)
{
SSNES_ERR("Couldn't allocate memory!\n");
return -1;
}
if ( fread(rom_buf, 1, length, file) < length )
if (fread(rom_buf, 1, ret, file) < ret)
{
SSNES_ERR("Didn't read whole file.\n");
free(rom_buf);
return -1;
}
*buf = rom_buf;
ret = length;
ret_buf = rom_buf;
}
// Patch with UPS.
ssize_t ups_patch_size;
void *ups_patch = NULL;
if (*g_extern.ups_name && (ups_patch_size = read_file(g_extern.ups_name, &ups_patch)) >= 0)
{
SSNES_LOG("Found UPS file in \"%s\", attempting to patch ...\n", g_extern.ups_name);
size_t target_size = ret * 4; // Just to be sure ...
uint8_t *patched_rom = malloc(target_size);
if (patched_rom)
{
ups_error_t err = ups_apply_patch(ups_patch, ups_patch_size, ret_buf, ret, patched_rom, &target_size);
if (err == UPS_SUCCESS)
{
free(ret_buf);
ret_buf = patched_rom;
ret = target_size;
SSNES_LOG("ROM patched successfully (UPS)!\n");
}
else
{
free(patched_rom);
SSNES_LOG("ROM failed to patch (UPS).\n");
}
free(ups_patch);
}
}
else if (*g_extern.ups_name)
SSNES_LOG("Could not find UPS patch in: \"%s\".\n", g_extern.ups_name);
// Remove copier header if present (512 first bytes).
if ((ret & 0x7fff) == 512)
{
memmove(ret_buf, ret_buf + 512, ret - 512);
ret -= 512;
}
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;
}
// Generic file loader.
static ssize_t read_file(const char *path, void **buf)
{
void *rom_buf = NULL;
FILE *file = fopen(path, "rb");
if (!file)
{
SSNES_ERR("Couldn't open file: \"%s\"\n", path);
goto error;
}
fseek(file, 0, SEEK_END);
long len = ftell(file);
ssize_t rc = 0;
rewind(file);
rom_buf = malloc(len);
if (!rom_buf)
{
SSNES_ERR("Couldn't allocate memory!\n");
goto error;
}
if ((rc = fread(rom_buf, 1, len, file)) < len)
SSNES_WARN("Didn't read whole file.\n");
*buf = rom_buf;
fclose(file);
return rc;
error:
if (file)
fclose(file);
free(rom_buf);
*buf = NULL;
return -1;
}
// Dump stuff to file.
static bool dump_to_file(const char *path, const void *data, size_t size)
{
FILE *file = fopen(path, "wb");
if (!file)
{
SSNES_ERR("Couldn't dump to file %s\n", path);
return false;
}
else
{
fwrite(data, 1, size, file);
@@ -169,6 +263,8 @@ bool save_state(const char* path)
psnes_serialize(data, size);
bool ret = dump_to_file(path, data, size);
free(data);
if (!ret)
SSNES_ERR("Failed to save state to \"%s\".\n", path);
return ret;
}
@@ -179,7 +275,7 @@ bool load_state(const char* path)
ssize_t size = read_file(path, &buf);
if (size < 0)
{
SSNES_ERR("Failed to load state.\n");
SSNES_ERR("Failed to load state from \"%s\".\n", path);
return false;
}
else
@@ -213,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;
@@ -232,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)
@@ -286,28 +394,23 @@ 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,
NULL, extra_rom_buf, extra_rom_len))
{
SSNES_ERR("Cannot load BSX slotted rom.\n");
goto error;
}
xml_buf, rom_buf, rom_len,
NULL, extra_rom_buf, extra_rom_len))
{
SSNES_ERR("Cannot load BSX slotted rom.\n");
goto error;
}
}
else
@@ -321,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)
@@ -360,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]))
{
@@ -384,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);
@@ -425,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;
}
@@ -474,3 +582,124 @@ bool init_rom_file(enum ssnes_game_type type)
return true;
}
// Yep, this is C alright ;)
char** dir_list_new(const char *dir, const char *ext)
{
size_t path_len = strlen(dir);
size_t cur_ptr = 0;
size_t cur_size = 32;
char **dir_list = NULL;
#ifdef _WIN32
WIN32_FIND_DATAW ffd;
HANDLE hFind = INVALID_HANDLE_VALUE;
size_t final_off = MAX_PATH + path_len + 2;
wchar_t wchar_buf[MAX_PATH + 1];
char utf8_buf[MAX_PATH + 3];
strlcpy(utf8_buf, dir, sizeof(utf8_buf));
strlcat(utf8_buf, "/*", sizeof(utf8_buf));
utf8_buf[MAX_PATH + 2] = '\0';
int ret = MultiByteToWideChar(CP_UTF8, 0, utf8_buf, strlen(utf8_buf), wchar_buf, MAX_PATH);
wchar_buf[ret] = 0;
hFind = FindFirstFileW(wchar_buf, &ffd);
if (hFind == INVALID_HANDLE_VALUE)
goto error;
#else
DIR *directory = NULL;
const struct dirent *entry = NULL;
size_t final_off = sizeof(entry->d_name) + path_len + 2;
directory = opendir(dir);
if (!directory)
goto error;
#endif
dir_list = calloc(cur_size, sizeof(char*));
if (!dir_list)
goto error;
#ifdef _WIN32 // Hard to read? Blame non-POSIX heathens!
do
#else
while ((entry = readdir(directory)))
#endif
{
// Not a perfect search of course, but hopefully good enough in practice.
#ifdef _WIN32
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
continue;
int ret = WideCharToMultiByte(CP_UTF8, 0, ffd.cFileName, wcslen(ffd.cFileName), utf8_buf, MAX_PATH, NULL, NULL);
utf8_buf[ret] = '\0';
if (ext && !strstr(utf8_buf, ext))
continue;
#else
if (ext && !strstr(entry->d_name, ext))
continue;
#endif
dir_list[cur_ptr] = malloc(final_off);
if (!dir_list[cur_ptr])
goto error;
strlcpy(dir_list[cur_ptr], dir, final_off);
strlcat(dir_list[cur_ptr], "/", final_off);
#ifdef _WIN32
strlcat(dir_list[cur_ptr], utf8_buf, final_off);
#else
strlcat(dir_list[cur_ptr], entry->d_name, final_off);
#endif
cur_ptr++;
if (cur_ptr + 1 == cur_size) // Need to reserve for NULL.
{
cur_size *= 2;
dir_list = realloc(dir_list, cur_size * sizeof(char*));
if (!dir_list)
goto error;
// Make sure it's all NULL'd out since we cannot rely on realloc to do this.
memset(dir_list + cur_ptr, 0, (cur_size - cur_ptr) * sizeof(char*));
}
}
#ifdef _WIN32
while (FindNextFileW(hFind, &ffd) != 0);
#endif
#ifdef _WIN32
FindClose(hFind);
#else
closedir(directory);
#endif
return dir_list;
error:
SSNES_ERR("Failed to open directory: \"%s\"\n", dir);
#ifdef _WIN32
if (hFind != INVALID_HANDLE_VALUE)
FindClose(hFind);
#else
if (directory)
closedir(directory);
#endif
dir_list_free(dir_list);
return NULL;
}
void dir_list_free(char **dir_list)
{
if (!dir_list)
return;
char **orig = dir_list;
while (*dir_list)
free(*dir_list++);
free(orig);
}
+6
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);
@@ -34,4 +36,8 @@ void save_ram_file(const char* path, int type);
bool init_rom_file(enum ssnes_game_type type);
// Returns a NULL-terminated list of files in a directory with full paths.
char** dir_list_new(const char *dir, const char *ext);
void dir_list_free(char **dir_list);
#endif
+144 -10
View File
@@ -24,6 +24,13 @@
#include <stdio.h>
#include "record/ffemu.h"
#include "message.h"
#include "rewind.h"
#include "movie.h"
#include "autosave.h"
#include "netplay.h"
#include "dynamic.h"
#include "cheats.h"
#include "audio/ext/ssnes_dsp.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -31,12 +38,24 @@
#ifdef HAVE_SRC
#include <samplerate.h>
#else
#include "audio/hermite.h"
#endif
#define MAX_PLAYERS 5
#define MAX_BINDS 20 // 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
{
SSNES_SHADER_CG,
SSNES_SHADER_BSNES,
SSNES_SHADER_AUTO,
SSNES_SHADER_NONE
};
// All config related settings go here.
struct settings
{
struct
@@ -50,16 +69,28 @@ 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];
unsigned filter;
char filter_path[256];
enum ssnes_shader_type shader_type;
bool render_to_texture;
double fbo_scale_x;
double fbo_scale_y;
char second_pass_shader[256];
bool second_pass_smooth;
char shader_dir[256];
char font_path[256];
unsigned font_size;
float msg_pos_x;
float msg_pos_y;
bool force_16bit;
char external_driver[256];
} video;
struct
@@ -67,11 +98,15 @@ struct settings
char driver[32];
bool enable;
unsigned out_rate;
unsigned in_rate;
float in_rate;
float rate_step;
char device[256];
unsigned latency;
bool sync;
int src_quality;
char dsp_plugin[256];
char external_driver[256];
} audio;
struct
@@ -80,9 +115,20 @@ struct settings
struct snes_keybind binds[MAX_PLAYERS][MAX_BINDS];
float axis_threshold;
unsigned joypad_map[MAX_PLAYERS];
bool netplay_client_swap_input;
} input;
char libsnes[256];
char cheat_database[256];
char screenshot_directory[256];
bool rewind_enable;
unsigned rewind_buffer_size;
unsigned rewind_granularity;
bool pause_nonactive;
unsigned autosave_interval;
};
enum ssnes_game_type
@@ -94,10 +140,11 @@ enum ssnes_game_type
SSNES_CART_SUFAMI,
};
// All run-time- / command line flag-related globals go here.
struct global
{
bool verbose;
SRC_STATE *source;
bool audio_active;
bool video_active;
@@ -106,13 +153,17 @@ struct global
bool has_justifier;
bool has_justifiers;
bool has_multitap;
bool disconnect_device[2];
FILE *rom_file;
enum ssnes_game_type game_type;
uint32_t cart_crc;
char gb_rom_path[256];
char bsx_rom_path[256];
char sufami_rom_path[2][256];
bool has_set_save_path;
bool has_set_state_path;
#ifdef HAVE_CONFIGFILE
char config_path[256];
@@ -125,11 +176,19 @@ struct global
char savefile_name_asrm[512];
char savefile_name_bsrm[512];
char savestate_name[256];
char ups_name[512];
char xml_name[512];
unsigned state_slot;
struct
{
#ifdef HAVE_SRC
SRC_STATE *source;
#else
hermite_resampler_t *source;
#endif
float *data;
size_t data_ptr;
size_t chunk_size;
@@ -140,15 +199,76 @@ 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
{
bool active;
uint32_t *buffer;
uint32_t *colormap;
unsigned pitch;
dylib_t lib;
unsigned scale;
void (*psize)(unsigned *width, unsigned *height);
void (*prender)(uint32_t *colormap, uint32_t *output, unsigned outpitch,
const uint16_t *input, unsigned pitch, unsigned width, unsigned height);
} filter;
msg_queue_t *msg_queue;
// Rewind support.
state_manager_t *state_manager;
void *state_buf;
bool frame_is_reverse;
// Movie record support
bsv_movie_t *bsv_movie;
char bsv_movie_path[256];
bool bsv_movie_end;
bool bsv_movie_playback;
// Pausing support
bool is_paused;
// Autosave support.
autosave_t *autosave[2];
// Netplay.
netplay_t *netplay;
char netplay_server[256];
bool netplay_enable;
bool netplay_is_client;
unsigned netplay_sync_frames;
uint16_t netplay_port;
// FFmpeg record.
#ifdef HAVE_FFMPEG
ffemu_t *rec;
char record_path[256];
bool recording;
#endif
char title_buf[64];
struct
{
char** elems;
size_t size;
size_t ptr;
} shader_dir;
char sha256[64 + 1];
bool do_screenshot;
#ifdef HAVE_XML
cheat_manager_t *cheat;
#endif
};
void parse_config(void);
@@ -156,20 +276,34 @@ 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)
static inline uint32_t next_pow2(uint32_t v)
{
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
#endif
+314
View File
@@ -0,0 +1,314 @@
/* 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;
}
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 = g_settings.video.bsnes_shader_path,
.cg_shader = g_settings.video.cg_shader_path,
.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
+58
View File
@@ -0,0 +1,58 @@
/* 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 "gfx_common.h"
#include "general.h"
#include <sys/time.h>
static float tv_to_fps(const struct timeval *tv, const struct timeval *new_tv, int frames)
{
float time = new_tv->tv_sec - tv->tv_sec + (new_tv->tv_usec - tv->tv_usec)/1000000.0;
return frames/time;
}
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)
{
gettimeofday(&tv, NULL);
snprintf(buf, size, "%s", g_extern.title_buf);
ret = true;
}
if ((frames % 180) == 0 && frames > 0)
{
gettimeofday(&new_tv, NULL);
struct timeval tmp_tv = tv;
tv = new_tv;
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);
ret = true;
}
frames++;
return ret;
}
+26
View File
@@ -0,0 +1,26 @@
/* 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 __GFX_COMMON_H
#define __GFX_COMMON_H
#include <stddef.h>
#include <stdbool.h>
bool gfx_window_title(char *buf, size_t size);
#endif
+922 -197
View File
File diff suppressed because it is too large. Load diff
+67 -1
View File
@@ -19,6 +19,20 @@
#define __GL_COMMON_H
#include "general.h"
#include <assert.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glext.h>
#else
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#endif
static inline bool gl_check_error(void)
{
@@ -29,7 +43,7 @@ static inline bool gl_check_error(void)
SSNES_ERR("GL: Invalid enum.\n");
break;
case GL_INVALID_VALUE:
SSNES_ERR("GL: Invalid value.\n");
SSNES_ERR("GL: Invalid value. (You're not alone.)\n");
break;
case GL_INVALID_OPERATION:
SSNES_ERR("GL: Invalid operation.\n");
@@ -56,4 +70,56 @@ static inline bool gl_check_error(void)
return false;
}
struct gl_fbo_rect
{
unsigned img_width;
unsigned img_height;
unsigned max_img_width;
unsigned max_img_height;
unsigned width;
unsigned height;
};
enum gl_scale_type
{
SSNES_SCALE_ABSOLUTE,
SSNES_SCALE_INPUT,
SSNES_SCALE_VIEWPORT
};
struct gl_fbo_scale
{
enum gl_scale_type type_x;
enum gl_scale_type type_y;
float scale_x;
float scale_y;
unsigned abs_x;
unsigned abs_y;
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
+520
View File
@@ -0,0 +1,520 @@
/* 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)
{
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"
};
+924 -70
View File
File diff suppressed because it is too large. Load diff
+12 -3
View File
@@ -20,6 +20,7 @@
#define __SSNES_CG_H
#include <stdbool.h>
#include "gl_common.h"
bool gl_cg_init(const char *path);
@@ -29,9 +30,17 @@ 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_activate(void);
void gl_cg_deactivate(void);
void gl_cg_use(unsigned index);
unsigned gl_cg_num(void);
bool gl_cg_filter_type(unsigned index, bool *smooth);
void gl_cg_shader_scale(unsigned index, struct gl_fbo_scale *scale);
#endif
+1001 -91
View File
File diff suppressed because it is too large. Load diff
+12 -3
View File
@@ -20,6 +20,7 @@
#define __SSNES_GLSL_H
#include <stdbool.h>
#include "gl_common.h"
bool gl_glsl_init(const char *path);
@@ -29,9 +30,17 @@ 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_activate(void);
void gl_glsl_deactivate(void);
void gl_glsl_use(unsigned index);
unsigned gl_glsl_num(void);
bool gl_glsl_filter_type(unsigned index, bool *smooth);
void gl_glsl_shader_scale(unsigned index, struct gl_fbo_scale *scale);
#endif
+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
+724
View File
@@ -0,0 +1,724 @@
/* 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 "general.h"
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <math.h>
#ifdef HAVE_FREETYPE
#include "fonts.h"
#endif
#include "gfx_common.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/extensions/XShm.h>
#include <X11/extensions/Xv.h>
#include <X11/extensions/Xvlib.h>
// Adapted from bSNES and MPlayer source.
typedef struct xv xv_t;
struct xv
{
Display *display;
GC gc;
Window window;
Colormap colormap;
XShmSegmentInfo shminfo;
Atom quit_atom;
bool focus;
XvPortID port;
int depth;
int visualid;
XvImage *image;
uint32_t fourcc;
unsigned width;
unsigned height;
bool keep_aspect;
uint8_t *ytable;
uint8_t *utable;
uint8_t *vtable;
unsigned luma_i;
unsigned chroma_i;
#ifdef HAVE_FREETYPE
font_renderer_t *font;
#endif
void (*render_func)(xv_t*, const void *frame, unsigned width, unsigned height, unsigned pitch);
};
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)
XvSetPortAttribute(xv->display, xv->port, atom, !state);
else
SSNES_WARN("Failed to set SYNC_TO_VBLANK attribute.\n");
}
static volatile sig_atomic_t g_quit = 0;
static void sighandler(int sig)
{
g_quit = 1;
}
static void init_yuv_tables(xv_t *xv)
{
xv->ytable = malloc(0x8000);
xv->utable = malloc(0x8000);
xv->vtable = malloc(0x8000);
for (unsigned i = 0; i < 0x8000; i++)
{
// Extract RGB555 color data from i
uint8_t r = (i >> 10) & 0x1F, g = (i >> 5) & 0x1F, b = (i) & 0x1F;
r = (r << 3) | (r >> 2); //R5->R8
g = (g << 3) | (g >> 2); //G5->G8
b = (b << 3) | (b >> 2); //B5->B8
int y = (int)(+((double)r * 0.257) + ((double)g * 0.504) + ((double)b * 0.098) + 16.0);
int u = (int)(-((double)r * 0.148) - ((double)g * 0.291) + ((double)b * 0.439) + 128.0);
int v = (int)(+((double)r * 0.439) - ((double)g * 0.368) - ((double)b * 0.071) + 128.0);
xv->ytable[i] = y < 0 ? 0 : y > 255 ? 255 : y;
xv->utable[i] = u < 0 ? 0 : u > 255 ? 255 : u;
xv->vtable[i] = v < 0 ? 0 : v > 255 ? 255 : v;
}
}
// Source: MPlayer
static void hide_mouse(xv_t *xv)
{
Cursor no_ptr;
Pixmap bm_no;
XColor black, dummy;
Colormap colormap;
static char bm_no_data[] = {0, 0, 0, 0, 0, 0, 0, 0};
colormap = DefaultColormap(xv->display, DefaultScreen(xv->display));
if (!XAllocNamedColor(xv->display, colormap, "black", &black, &dummy))
return;
bm_no = XCreateBitmapFromData(xv->display, xv->window, bm_no_data, 8, 8);
no_ptr = XCreatePixmapCursor(xv->display, bm_no, bm_no, &black, &black, 0, 0);
XDefineCursor(xv->display, xv->window, no_ptr);
XFreeCursor(xv->display, no_ptr);
if (bm_no != None)
XFreePixmap(xv->display, bm_no);
XFreeColors(xv->display, colormap, &black.pixel, 1, 0);
}
static Atom XA_NET_WM_STATE;
static Atom XA_NET_WM_STATE_FULLSCREEN;
#define XA_INIT(x) XA##x = XInternAtom(xv->display, #x, False)
#define _NET_WM_STATE_ADD 1
// Source: MPlayer
static void set_fullscreen(xv_t *xv)
{
XA_INIT(_NET_WM_STATE);
XA_INIT(_NET_WM_STATE_FULLSCREEN);
if (!XA_NET_WM_STATE || !XA_NET_WM_STATE_FULLSCREEN)
{
SSNES_WARN("X11: Cannot set fullscreen.\n");
return;
}
XEvent xev;
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.message_type = XA_NET_WM_STATE;
xev.xclient.window = xv->window;
xev.xclient.format = 32;
xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
xev.xclient.data.l[1] = XA_NET_WM_STATE_FULLSCREEN;
xev.xclient.data.l[2] = 0;
xev.xclient.data.l[3] = 0;
xev.xclient.data.l[4] = 0;
XSendEvent(xv->display, DefaultRootWindow(xv->display), False,
SubstructureRedirectMask | SubstructureNotifyMask,
&xev);
}
static void xv_init_font(xv_t *xv, const char *font_path, unsigned font_size)
{
#ifdef HAVE_FREETYPE
if (*font_path)
{
xv->font = font_renderer_new(font_path, font_size);
if (!xv->font)
SSNES_WARN("Failed to init font.\n");
}
#endif
}
// We render @ 2x scale to combat chroma downsampling. Also makes fonts more bearable :)
static void render16_yuy2(xv_t *xv, const void *input_, unsigned width, unsigned height, unsigned pitch)
{
const uint16_t *input = input_;
uint8_t *output = (uint8_t*)xv->image->data;
for (unsigned y = 0; y < height; y++)
{
for (unsigned x = 0; x < width; x++)
{
uint16_t p = *input++;
uint8_t y0 = xv->ytable[p];
uint8_t u = xv->utable[p];
uint8_t v = xv->vtable[p];
unsigned img_width = xv->width << 1;
output[0] = output[img_width] = y0;
output[1] = output[img_width + 1] = u;
output[2] = output[img_width + 2] = y0;
output[3] = output[img_width + 3] = v;
output += 4;
}
input += (pitch >> 1) - width;
output += (xv->width - width) << 2;
}
}
static void render16_uyvy(xv_t *xv, const void *input_, unsigned width, unsigned height, unsigned pitch)
{
const uint16_t *input = input_;
uint8_t *output = (uint8_t*)xv->image->data;
for (unsigned y = 0; y < height; y++)
{
for (unsigned x = 0; x < width; x++)
{
uint16_t p = *input++;
uint8_t y0 = xv->ytable[p];
uint8_t u = xv->utable[p];
uint8_t v = xv->vtable[p];
unsigned img_width = xv->width << 1;
output[0] = output[img_width] = u;
output[1] = output[img_width + 1] = y0;
output[2] = output[img_width + 2] = v;
output[3] = output[img_width + 3] = y0;
output += 4;
}
input += (pitch >> 1) - width;
output += (xv->width - width) << 2;
}
}
static void render32_yuy2(xv_t *xv, const void *input_, unsigned width, unsigned height, unsigned pitch)
{
const uint32_t *input = input_;
uint8_t *output = (uint8_t*)xv->image->data;
for (unsigned y = 0; y < height; y++)
{
for (unsigned x = 0; x < width; x++)
{
uint32_t p = *input++;
p = ((p >> 9) & 0x7c00) | ((p >> 6) & 0x03e0) | ((p >> 3) & 0x1f); // ARGB -> RGB15
uint8_t y0 = xv->ytable[p];
uint8_t u = xv->utable[p];
uint8_t v = xv->vtable[p];
unsigned img_width = xv->width << 1;
output[0] = output[img_width] = y0;
output[1] = output[img_width + 1] = u;
output[2] = output[img_width + 2] = y0;
output[3] = output[img_width + 3] = v;
output += 4;
}
input += (pitch >> 2) - width;
output += (xv->width - width) << 2;
}
}
static void render32_uyvy(xv_t *xv, const void *input_, unsigned width, unsigned height, unsigned pitch)
{
const uint32_t *input = input_;
uint16_t *output = (uint16_t*)xv->image->data;
for (unsigned y = 0; y < height; y++)
{
for (unsigned x = 0; x < width; x++)
{
uint32_t p = *input++;
p = ((p >> 9) & 0x7c00) | ((p >> 6) & 0x03e0) | ((p >> 3) & 0x1f); // ARGB -> RGB15
uint8_t y0 = xv->ytable[p];
uint8_t u = xv->utable[p];
uint8_t v = xv->vtable[p];
unsigned img_width = xv->width << 1;
output[0] = output[img_width] = u;
output[1] = output[img_width + 1] = y0;
output[2] = output[img_width + 2] = v;
output[3] = output[img_width + 3] = y0;
output += 4;
}
input += (pitch >> 2) - width;
output += (xv->width - width) << 2;
}
}
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)
return NULL;
xv->display = XOpenDisplay(NULL);
if (!XShmQueryExtension(xv->display))
{
SSNES_ERR("XVideo: XShm extension not found.\n");
goto error;
}
xv->keep_aspect = video->force_aspect;
// Find an appropriate Xv port.
xv->port = 0;
XvAdaptorInfo *adaptor_info;
unsigned adaptor_count = 0;
XvQueryAdaptors(xv->display, DefaultRootWindow(xv->display), &adaptor_count, &adaptor_info);
for (unsigned i = 0; i < adaptor_count; i++)
{
// Find adaptor that supports both input (memory->drawable) and image (drawable->screen) masks.
if (adaptor_info[i].num_formats < 1) continue;
if (!(adaptor_info[i].type & XvInputMask)) continue;
if (!(adaptor_info[i].type & XvImageMask)) continue;
xv->port = adaptor_info[i].base_id;
xv->depth = adaptor_info[i].formats->depth;
xv->visualid = adaptor_info[i].formats->visual_id;
break;
}
XvFreeAdaptorInfo(adaptor_info);
if (xv->port == 0)
{
SSNES_ERR("XVideo: Failed to find valid XvPort.\n");
goto error;
}
XVisualInfo visualtemplate;
visualtemplate.visualid = xv->visualid;
visualtemplate.screen = DefaultScreen(xv->display);
visualtemplate.depth = xv->depth;
visualtemplate.visual = 0;
int visualmatches = 0;
XVisualInfo *visualinfo = XGetVisualInfo(xv->display, VisualIDMask | VisualScreenMask | VisualDepthMask, &visualtemplate, &visualmatches);
if (visualmatches < 1 || !visualinfo->visual)
{
if (visualinfo) XFree(visualinfo);
SSNES_ERR("XVideo: Unable to find Xv-compatible visual.\n");
goto error;
}
xv->colormap = XCreateColormap(xv->display, DefaultRootWindow(xv->display), visualinfo->visual, AllocNone);
XSetWindowAttributes attributes;
attributes.colormap = xv->colormap;
attributes.border_pixel = 0;
attributes.event_mask = StructureNotifyMask | DestroyNotify | ClientMessage;
unsigned width = video->fullscreen ? ((video->width == 0) ? 256 : video->width) : video->width;
unsigned height = video->fullscreen ? ((video->height == 0) ? 224 : video->height) : video->height;
xv->window = XCreateWindow(xv->display, DefaultRootWindow(xv->display),
0, 0, width, height,
0, xv->depth, InputOutput, visualinfo->visual,
CWColormap | CWBorderPixel | CWEventMask, &attributes);
XFree(visualinfo);
XSetWindowBackground(xv->display, xv->window, 0);
XMapWindow(xv->display, xv->window);
char buf[128];
if (gfx_window_title(buf, sizeof(buf)))
XStoreName(xv->display, xv->window, buf);
if (video->fullscreen)
set_fullscreen(xv);
hide_mouse(xv);
xv->gc = XCreateGC(xv->display, xv->window, 0, 0);
// Set colorkey to auto paint, so that Xv video output is always visible
Atom atom = XInternAtom(xv->display, "XV_AUTOPAINT_COLORKEY", true);
if(atom != None) XvSetPortAttribute(xv->display, xv->port, atom, 1);
int format_count;
XvImageFormatValues *format = XvListImageFormats(xv->display, xv->port, &format_count);
bool has_format = false;
for (int i = 0; i < format_count; i++)
{
if (format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked)
{
if (format[i].component_order[0] == 'Y' && format[i].component_order[1] == 'U'
&& format[i].component_order[2] == 'Y' && format[i].component_order[3] == 'V')
{
has_format = true;
xv->fourcc = format[i].id;
xv->render_func = video->rgb32 ? render32_yuy2 : render16_yuy2;
xv->luma_i = 0; xv->chroma_i = 1;
break;
}
}
}
if (!has_format) for (int i = 0; i < format_count; i++)
{
if (format[i].type == XvYUV && format[i].bits_per_pixel == 16 && format[i].format == XvPacked)
{
if (format[i].component_order[0] == 'U' && format[i].component_order[1] == 'Y'
&& format[i].component_order[2] == 'V' && format[i].component_order[3] == 'Y')
{
has_format = true;
xv->fourcc = format[i].id;
xv->render_func = video->rgb32 ? render32_uyvy : render16_uyvy;
xv->chroma_i = 0; xv->luma_i = 1;
break;
}
}
}
free(format);
if (!has_format)
{
SSNES_ERR("XVideo: unable to find a supported image format.\n");
goto error;
}
xv->width = 512;
xv->height = 512;
xv->image = XvShmCreateImage(xv->display, xv->port, xv->fourcc, NULL, xv->width, xv->height, &xv->shminfo);
if (!xv->image)
{
SSNES_ERR("XVideo: XShmCreateImage failed.\n");
goto error;
}
xv->width = xv->image->width;
xv->height = xv->image->height;
xv->shminfo.shmid = shmget(IPC_PRIVATE, xv->image->data_size, IPC_CREAT | 0777);
xv->shminfo.shmaddr = xv->image->data = shmat(xv->shminfo.shmid, NULL, 0);
xv->shminfo.readOnly = false;
if (!XShmAttach(xv->display, &xv->shminfo))
{
SSNES_ERR("XVideo: XShmAttach failed.\n");
goto error;
}
XSync(xv->display, False);
memset(xv->image->data, 128, xv->image->data_size);
xv->quit_atom = XInternAtom(xv->display, "WM_DELETE_WINDOW", False);
if (xv->quit_atom)
XSetWMProtocols(xv->display, xv->window, &xv->quit_atom, 1);
struct sigaction sa;
sa.sa_handler = sighandler;
sa.sa_flags = SA_RESTART;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
xv_set_nonblock_state(xv, !video->vsync);
xv->focus = true;
xv_init_font(xv, g_settings.video.font_path, g_settings.video.font_size);
void *xinput = input_x.init();
if (xinput)
{
*input = &input_x;
*input_data = xinput;
}
else
*input = NULL;
init_yuv_tables(xv);
return xv;
error:
free(xv);
return NULL;
}
static bool check_resize(xv_t *xv, unsigned width, unsigned height)
{
// We render @ 2x scale to combat chroma downsampling.
if (xv->width != (width << 1) || xv->height != (height << 1))
{
xv->width = width << 1;
xv->height = height << 1;
XShmDetach(xv->display, &xv->shminfo);
shmdt(xv->shminfo.shmaddr);
shmctl(xv->shminfo.shmid, IPC_RMID, NULL);
XFree(xv->image);
memset(&xv->shminfo, 0, sizeof(xv->shminfo));
xv->image = XvShmCreateImage(xv->display, xv->port, xv->fourcc, NULL, xv->width, xv->height, &xv->shminfo);
if (xv->image == None)
{
SSNES_ERR("Failed to create image.\n");
return false;
}
xv->width = xv->image->width;
xv->height = xv->image->height;
xv->shminfo.shmid = shmget(IPC_PRIVATE, xv->image->data_size, IPC_CREAT | 0777);
if (xv->shminfo.shmid < 0)
{
SSNES_ERR("Failed to init SHM.\n");
return false;
}
xv->shminfo.shmaddr = xv->image->data = shmat(xv->shminfo.shmid, NULL, 0);
xv->shminfo.readOnly = false;
if (!XShmAttach(xv->display, &xv->shminfo))
{
SSNES_ERR("Failed to reattch XvShm image.\n");
return false;
}
XSync(xv->display, False);
memset(xv->image->data, 128, xv->image->data_size);
}
return true;
}
static void calc_out_rect(bool keep_aspect, unsigned *x, unsigned *y, unsigned *width, unsigned *height, unsigned vp_width, unsigned vp_height)
{
if (!keep_aspect)
{
*x = 0; *y = 0; *width = vp_width; *height = vp_height;
}
else
{
float desired_aspect = g_settings.video.aspect_ratio;
float device_aspect = (float)vp_width / vp_height;
// If the aspect ratios of screen and desired aspect ratio are sufficiently equal (floating point stuff),
// assume they are actually equal.
if (fabs(device_aspect - desired_aspect) < 0.0001)
{
*x = 0; *y = 0; *width = vp_width; *height = vp_height;
}
else if (device_aspect > desired_aspect)
{
float delta = (desired_aspect / device_aspect - 1.0) / 2.0 + 0.5;
*x = vp_width * (0.5 - delta); *y = 0; *width = 2.0 * vp_width * delta; *height = vp_height;
}
else
{
float delta = (device_aspect / desired_aspect - 1.0) / 2.0 + 0.5;
*x = 0; *y = vp_height * (0.5 - delta); *width = vp_width; *height = 2.0 * vp_height * delta;
}
}
}
// TODO: Is there some way to render directly like GL? :(
static void xv_render_msg(xv_t *xv, const char *msg, unsigned width, unsigned height)
{
#ifdef HAVE_FREETYPE
if (!xv->font)
return;
struct font_output_list out;
font_renderer_msg(xv->font, msg, &out);
struct font_output *head = out.head;
int _base_x = g_settings.video.msg_pos_x * width;
int _base_y = height - g_settings.video.msg_pos_y * height;
unsigned luma_i = xv->luma_i;
unsigned chroma_i = xv->chroma_i;
while (head)
{
int base_x = _base_x + head->off_x;
int base_y = _base_y - head->off_y;
if (base_y >= 0)
{
for (int y = 0; y < head->height && (base_y + y) < height; y++)
{
if (base_x < 0)
continue;
const uint8_t *a = head->output + head->pitch * y;
uint8_t *out = (uint8_t*)xv->image->data + (base_y - head->height + y) * (width << 1) + (base_x << 1);
for (int x = 0; x < (head->width << 1) && (base_x + x) < width; x += 2)
{
// Blend luma
uint8_t blend = a[x >> 1];
unsigned blended = blend + (((256 - blend) * (unsigned)out[x + luma_i]) >> 8);
blended = blended > 255 ? 255 : blended;
out[x + luma_i] = blended;
// Blend chroma
blended = (128 * blend + ((256 - blend) * (unsigned)out[x + chroma_i])) >> 8;
blended = blended > 255 ? 255 : blended;
out[x + chroma_i] = blended;
}
}
}
head = head->next;
}
font_renderer_free_output(&out);
#else
(void)xv;
(void)msg;
(void)width;
(void)height;
#endif
}
static bool xv_frame(void *data, const void* frame, unsigned width, unsigned height, unsigned pitch, const char *msg)
{
xv_t *xv = data;
if (!check_resize(xv, width, height))
return false;
XWindowAttributes target;
XGetWindowAttributes(xv->display, xv->window, &target);
xv->render_func(xv, frame, width, height, pitch);
if (msg)
xv_render_msg(xv, msg, xv->width, xv->height);
unsigned x, y, owidth, oheight;
calc_out_rect(xv->keep_aspect, &x, &y, &owidth, &oheight, target.width, target.height);
XvShmPutImage(xv->display, xv->port, xv->window, xv->gc, xv->image,
0, 0, width << 1, height << 1,
x, y, owidth, oheight,
true);
XSync(xv->display, False);
char buf[128];
if (gfx_window_title(buf, sizeof(buf)))
XStoreName(xv->display, xv->window, buf);
return true;
}
static bool xv_alive(void *data)
{
xv_t *xv = data;
XEvent event;
while (XPending(xv->display))
{
XNextEvent(xv->display, &event);
switch (event.type)
{
case ClientMessage:
if (event.xclient.data.l[0] == xv->quit_atom)
return false;
break;
case DestroyNotify:
return false;
case MapNotify: // Find something that works better.
xv->focus = true;
break;
case UnmapNotify:
xv->focus = false;
break;
default:
break;
}
}
return !g_quit;
}
static bool xv_focus(void *data)
{
xv_t *xv = data;
return xv->focus;
}
static void xv_free(void *data)
{
xv_t *xv = data;
XShmDetach(xv->display, &xv->shminfo);
shmdt(xv->shminfo.shmaddr);
shmctl(xv->shminfo.shmid, IPC_RMID, NULL);
XFree(xv->image);
if(xv->window)
XUnmapWindow(xv->display, xv->window);
if(xv->colormap)
XFreeColormap(xv->display, xv->colormap);
XCloseDisplay(xv->display);
free(xv->ytable);
free(xv->utable);
free(xv->vtable);
#ifdef HAVE_FREETYPE
if (xv->font)
font_renderer_free(xv->font);
#endif
free(xv);
}
const video_driver_t video_xvideo = {
.init = xv_init,
.frame = xv_frame,
.alive = xv_alive,
.set_nonblock_state = xv_set_nonblock_state,
.focus = xv_focus,
.free = xv_free,
.ident = "xvideo"
};
-45
View File
@@ -1,45 +0,0 @@
#include "bleed.h"
#include <stdlib.h>
#include <string.h>
void bleed_filter(uint16_t *data, int width, int height)
{
uint16_t tmp[4];
int ptr = 0;
uint16_t r[4];
uint16_t g[4];
uint16_t b[4];
uint16_t bleed_r;
uint16_t bleed_g;
uint16_t bleed_b;
float rand_map[4];
for (int h = 0; h < height; h++ )
{
memcpy(tmp, data, sizeof(tmp));
ptr = 3;
for (int i = 0; i < 4; i++)
{
rand_map[i] = (rand() % 20)/1000.0; // 0.02 * 4 = 0.08
}
for (int i = 3; i < width; i++, ptr++)
{
tmp[ptr & 0x3] = data[i];
for (int y = 0; y < 4; y++)
{
r[y] = (tmp[(ptr + y) & 0x3] >> 10) & 0x1F;
g[y] = (tmp[(ptr + y) & 0x3] >> 5) & 0x1F;
b[y] = (tmp[(ptr + y) & 0x3] >> 0) & 0x1F;
}
bleed_r = r[0] * (0.05 + rand_map[0] ) + r[1] * (0.10 + rand_map[1] ) + r[2] * (0.20 + rand_map[2]) + r[3] * (0.57 + rand_map[3] ); // 0.92
bleed_g = g[0] * (0.03 + rand_map[2]/3) + g[1] * (0.10 + rand_map[0]/3) + g[2] * (0.20 + rand_map[3]/3) + g[3] * (0.63 + rand_map[1]/3); // 0.96
bleed_b = b[0] * (0.05 + rand_map[3] ) + b[1] * (0.10 + rand_map[2] ) + b[2] * (0.20 + rand_map[1]) + b[3] * (0.57 + rand_map[0] ); // 0.92
data[i] = (bleed_r << 10) | (bleed_g << 5) | (bleed_b);
}
data += width;
}
}
-3
View File
@@ -1,3 +0,0 @@
#include <stdint.h>
void bleed_filter(uint16_t *data, int width, int height);
-94
View File
@@ -1,94 +0,0 @@
/*
Copyright (C) 2007 guest(r) - [email protected]
This program 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 Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
The 2xSaL shader processes a gfx. surface and redraws it 2x finer.
A linear post-resize can fit the image to any resolution.
Note: set scaler to normal2x.
*/
/* Default Vertex shader */
void main_vertex
(
float4 position : POSITION,
float4 color : COLOR,
float2 texCoord : TEXCOORD0,
uniform float4x4 modelViewProj,
out float4 oPosition : POSITION,
out float4 oColor : COLOR,
out float2 otexCoord : TEXCOORD
)
{
oPosition = mul(modelViewProj, position);
oColor = color;
otexCoord = texCoord;
}
struct output
{
float4 color : COLOR;
};
struct input
{
float2 video_size;
float2 texture_size;
float2 output_size;
};
struct deltas
{
float2 UL, UR, DL, DR;
};
output main_fragment (float2 tex : TEXCOORD0, uniform input IN, uniform sampler2D s_p : TEXUNIT0)
{
float2 texsize = IN.texture_size;
float dx = pow(texsize.x, -1.0) * 0.25;
float dy = pow(texsize.y, -1.0) * 0.25;
float3 dt = float3(1.0, 1.0, 1.0);
deltas VAR = {
tex + float2(-dx, -dy),
tex + float2(dx, -dy),
tex + float2(-dx, dy),
tex + float2(dx, dy)
};
float3 c00 = tex2D(s_p, VAR.UL).xyz;
float3 c20 = tex2D(s_p, VAR.UR).xyz;
float3 c02 = tex2D(s_p, VAR.DL).xyz;
float3 c22 = tex2D(s_p, VAR.DR).xyz;
float m1=dot(abs(c00-c22),dt)+0.001;
float m2=dot(abs(c02-c20),dt)+0.001;
output OUT;
OUT.color = float4((m1*(c02+c20)+m2*(c22+c00))/(2.0*(m1+m2)),1.0);
return OUT;
}
-95
View File
@@ -1,95 +0,0 @@
/* Default Vertex shader */
void main_vertex
(
float4 position : POSITION,
float4 color : COLOR,
float2 texCoord : TEXCOORD0,
uniform float4x4 modelViewProj,
out float4 oPosition : POSITION,
out float4 oColor : COLOR,
out float2 otexCoord : TEXCOORD
)
{
oPosition = mul(modelViewProj, position);
oColor = color;
otexCoord = texCoord;
}
float Luminance = 0.09f;
static const float fMiddleGray = 0.18f;
static const float fWhiteCutoff = 0.8f;
#define NUM 13
float2 PixelOffsets[NUM] =
{
{ -0.006, -0.006 },
{ -0.005, -0.005 },
{ -0.004, -0.004 },
{ -0.003, -0.003 },
{ -0.002, -0.002 },
{ -0.001, -0.001 },
{ 0.000, 0.000 },
{ 0.001, 0.001 },
{ 0.002, 0.002 },
{ 0.003, 0.003 },
{ 0.004, 0.004 },
{ 0.005, 0.005 },
{ 0.006, 0.006 },
};
static const float BlurWeights[NUM] =
{
0.002216,
0.008764,
0.026995,
0.064759,
0.120985,
0.176033,
0.199471,
0.176033,
0.120985,
0.064759,
0.026995,
0.008764,
0.002216,
};
struct output
{
float4 color : COLOR;
};
output main_fragment( float2 Tex : TEXCOORD0, uniform sampler2D s0 : TEXUNIT0 )
{
float2 BloomSettings;
BloomSettings.x = 1;
BloomSettings.y = 0.3;
float3 pixel;
float3 Color = 0;
for(int i = 0; i < NUM; i++)
{
pixel = tex2D(s0, Tex + PixelOffsets[i] * 5.0f)+BloomSettings.y;
pixel *= fMiddleGray / (Luminance + 0.001f);
pixel *= (1.0f + (pixel / (fWhiteCutoff * fWhiteCutoff)));
pixel -= 5.0f;
pixel = max(pixel,0.0f);
pixel /= (10.0f + pixel);
Color += pixel * BlurWeights[i];
}
Color *= BloomSettings.x;
output OUT;
OUT.color = float4(Color,1.0) + tex2D(s0,Tex);
return OUT;
}
-55
View File
@@ -1,55 +0,0 @@
/* Default Vertex shader */
void main_vertex
(
float4 position : POSITION,
float4 color : COLOR,
float2 texCoord : TEXCOORD0,
uniform float4x4 modelViewProj,
out float4 oPosition : POSITION,
out float4 oColor : COLOR,
out float2 otexCoord : TEXCOORD
)
{
oPosition = mul(modelViewProj, position);
oColor = color;
otexCoord = texCoord;
}
const float2 samples[12]
= {
-0.326212, -0.405805,
-0.840144, -0.073580,
-0.695914, 0.457137,
-0.203345, 0.620716,
0.962340, -0.194983,
0.473434, -0.480026,
0.519456, 0.767022,
0.185461, -0.893124,
0.507431, 0.064425,
0.896420, 0.412458,
-0.321940, -0.932615,
-0.791559, -0.597705
};
struct output
{
float4 col : COLOR;
};
output main_fragment(in float2 Tex : TEXCOORD0, uniform sampler2D s0 : TEXUNIT0 )
{
float4 color = tex2D( s0, Tex.xy);
float BlurFactor = 0.0025; //set between 0.001 and 0.05
for (int i = 0; i < 12; i++)
{
color += tex2D(s0, Tex + BlurFactor * samples[i]);
}
output OUT;
OUT.col = color / 13;
return OUT;
}
-100
View File
@@ -1,100 +0,0 @@
/* Default Vertex shader */
void main_vertex
(
float4 position : POSITION,
float4 color : COLOR,
float2 texCoord : TEXCOORD0,
uniform float4x4 modelViewProj,
out float4 oPosition : POSITION,
out float4 oColor : COLOR,
out float2 otexCoord : TEXCOORD
)
{
oPosition = mul(modelViewProj, position);
oColor = color;
otexCoord = texCoord;
}
#define TEX2D(c) tex2D(decal,(c))
#define PI 3.141592653589
#define phase 0.0
#define gamma 2.5
#define distortion 0.04
struct output
{
float4 color : COLOR;
};
struct input
{
float2 video_size;
float2 texture_size;
float2 output_size;
};
float2 barrelDistortion(float2 coord)
{
float2 cc = coord - 0.5;
float dist = dot(cc, cc);
return coord + cc * (dist + distortion * dist * dist) * distortion;
}
output main_fragment(float2 texCoord : TEXCOORD0, uniform sampler2D decal : TEXUNIT0, uniform input IN)
{
output OUT;
float2 rubyInputSize = IN.video_size;
float2 rubyOutputSize = IN.output_size;
float2 rubyTextureSize = IN.texture_size;
float2 xy = barrelDistortion(texCoord.xy);
float2 one = 1.0/rubyTextureSize;
xy = xy + float2(0.0 , -0.5 * (phase + (1-phase) * rubyInputSize.y/rubyOutputSize.y) * one.y);
float4 texels[8];
texels[0] = TEX2D(xy + float2(-one.x,0.0));
texels[1] = TEX2D(xy);
texels[2] = TEX2D(xy + float2(one.x, 0.0));
texels[3] = TEX2D(xy + float2(2 * one.x, 0.0));
texels[4] = TEX2D(xy + float2(-one.x,one.y));
texels[5] = TEX2D(xy + float2(0.0, one.y));
texels[6] = TEX2D(xy + one);
texels[7] = TEX2D(xy + float2(2 * one.x, one.y));
float2 uv_ratio = frac(xy*rubyTextureSize);
float4 col, col2;
float4 coeffs = float4(1.0 + uv_ratio.x, uv_ratio.x, 1.0 - uv_ratio.x, 2.0 - uv_ratio.x);
coeffs = (sin(PI * coeffs) * sin(PI * coeffs / 2.0)) / (coeffs * coeffs);
coeffs = coeffs / (coeffs.x+coeffs.y+coeffs.z+coeffs.w);
col = clamp(coeffs.x * texels[0] + coeffs.y * texels[1] + coeffs.z * texels[2] + coeffs.w * texels[3], 0.0, 1.0);
col2 = clamp(coeffs.x * texels[4] + coeffs.y * texels[5] + coeffs.z * texels[6] + coeffs.w * texels[7], 0.0, 1.0);
col = pow(col, gamma);
col2 = pow(col2, gamma);
float4 wid = 2 + 2 * pow(col, 4.0);
float4 weights = uv_ratio.y/0.3;
weights = 0.51*exp(-pow(weights*sqrt(2/wid),wid))/0.3/(0.6+0.2*wid);
wid = 2 + 4 * pow(col2,4.0);
float4 weights2 = (1.0-uv_ratio.y)/0.3;
weights2 = 0.51*exp(-pow(weights2*sqrt(2/wid),wid))/0.3/(0.6+0.2*wid);
float4 mcol = 1.0;
if ( fmod(xy.x*rubyOutputSize.x,2.0) < 1.0)
mcol.g = 0.7;
else
mcol.rb = 0.7;
//OUT.color = pow(mcol*(col * weights + col2 * weights2), 1.0/2.2);
OUT.color = 1.0;
return OUT;
}
-94
View File
@@ -1,94 +0,0 @@
/* Default Vertex shader */
void main_vertex
(
float4 position : POSITION,
float4 color : COLOR,
float2 texCoord : TEXCOORD0,
uniform float4x4 modelViewProj,
out float4 oPosition : POSITION,
out float4 oColor : COLOR,
out float2 otexCoord : TEXCOORD
)
{
oPosition = mul(modelViewProj, position);
oColor = color;
otexCoord = texCoord;
}
struct output
{
float4 color : COLOR;
};
struct input
{
float2 video_size;
float2 texture_size;
float2 output_size;
};
const float mx = 0.325; // start smoothing wt.
const float k = -0.250; // wt. decrease factor
const float max_w = 0.25; // max filter weigth
const float min_w =-0.05; // min filter weigth
const float lum_add = 0.25; // effects smoothing
output main_fragment(float2 texCoord : TEXCOORD0, uniform sampler2D decal : TEXUNIT0, uniform input IN)
{
float x = 0.5 * (1.0 / IN.texture_size.x);
float y = 0.5 * (1.0 / IN.texture_size.y);
float2 dg1 = float2( x, y);
float2 dg2 = float2(-x, y);
float2 dx = float2(x, 0.0);
float2 dy = float2(0.0, y);
float4 TexCoord[5];
TexCoord[0] = float4(texCoord, 0.0, 0.0);
TexCoord[1].xy = TexCoord[0].xy - dg1;
TexCoord[1].zw = TexCoord[0].xy - dy;
TexCoord[2].xy = TexCoord[0].xy - dg2;
TexCoord[2].zw = TexCoord[0].xy + dx;
TexCoord[3].xy = TexCoord[0].xy + dg1;
TexCoord[3].zw = TexCoord[0].xy + dy;
TexCoord[4].xy = TexCoord[0].xy + dg2;
TexCoord[4].zw = TexCoord[0].xy - dx;
float3 c00 = tex2D(decal, TexCoord[1].xy).xyz;
float3 c10 = tex2D(decal, TexCoord[1].zw).xyz;
float3 c20 = tex2D(decal, TexCoord[2].xy).xyz;
float3 c01 = tex2D(decal, TexCoord[4].zw).xyz;
float3 c11 = tex2D(decal, TexCoord[0].xy).xyz;
float3 c21 = tex2D(decal, TexCoord[2].zw).xyz;
float3 c02 = tex2D(decal, TexCoord[4].xy).xyz;
float3 c12 = tex2D(decal, TexCoord[3].zw).xyz;
float3 c22 = tex2D(decal, TexCoord[3].xy).xyz;
float3 dt = float3(1.0, 1.0, 1.0);
float md1 = dot(abs(c00 - c22), dt);
float md2 = dot(abs(c02 - c20), dt);
float w1 = dot(abs(c22 - c11), dt) * md2;
float w2 = dot(abs(c02 - c11), dt) * md1;
float w3 = dot(abs(c00 - c11), dt) * md2;
float w4 = dot(abs(c20 - c11), dt) * md1;
float t1 = w1 + w3;
float t2 = w2 + w4;
float ww = max(t1, t2) + 0.0001;
c11 = (w1 * c00 + w2 * c20 + w3 * c22 + w4 * c02 + ww * c11) / (t1 + t2 + ww);
float lc1 = k / (0.12 * dot(c10 + c12 + c11, dt) + lum_add);
float lc2 = k / (0.12 * dot(c01 + c21 + c11, dt) + lum_add);
w1 = clamp(lc1 * dot(abs(c11 - c10), dt) + mx, min_w, max_w);
w2 = clamp(lc2 * dot(abs(c11 - c21), dt) + mx, min_w, max_w);
w3 = clamp(lc1 * dot(abs(c11 - c12), dt) + mx, min_w, max_w);
w4 = clamp(lc2 * dot(abs(c11 - c01), dt) + mx, min_w, max_w);
output OUT;
OUT.color = float4(w1 * c10 + w2 * c21 + w3 * c12 + w4 * c01 + (1.0 - w1 - w2 - w3 - w4) * c11, 1.0);
return OUT;
}
-87
View File
@@ -1,87 +0,0 @@
/*
Author: Themaister
License: Public domain
*/
/* Default Vertex shader */
void main_vertex
(
float4 position : POSITION,
float4 color : COLOR,
float2 texCoord : TEXCOORD0,
uniform float4x4 modelViewProj,
out float4 oPosition : POSITION,
out float4 oColor : COLOR,
out float2 otexCoord : TEXCOORD
)
{
oPosition = mul(modelViewProj, position);
oColor = color;
otexCoord = texCoord;
}
struct output
{
float4 color : COLOR;
};
struct input
{
float2 video_size;
float2 texture_size;
float2 output_size;
};
struct deltas
{
float2 UL, UR, DL, DR;
};
float3 quad_inter(float3 x0, float3 x1, float3 x2, float x)
{
float3 poly[3];
poly[2] = 0.5*x0 - x1 + 0.5*x2;
poly[1] = -1.5*x0 + 2.0*x1 - 0.5*x2;
poly[0] = x0;
return poly[2] * x * x + poly[1] * x + poly[0];
}
output main_fragment (float2 tex : TEXCOORD0, uniform input IN, uniform sampler2D s0 : TEXUNIT0)
{
float2 texsize = IN.texture_size;
float sharpness = 2.0;
float dx = float(pow(sharpness * texsize.x, -1.0));
float dy = float(pow(sharpness * texsize.y, -1.0));
float3 c00 = tex2D(s0, tex + float2(-dx, -dy)).xyz;
float3 c01 = tex2D(s0, tex + float2(-dx, 0)).xyz;
float3 c02 = tex2D(s0, tex + float2(-dx, dy)).xyz;
float3 c10 = tex2D(s0, tex + float2(0, -dy)).xyz;
float3 c11 = tex2D(s0, tex + float2(0, 0)).xyz;
float3 c12 = tex2D(s0, tex + float2(0, dy)).xyz;
float3 c20 = tex2D(s0, tex + float2(dx, -dy)).xyz;
float3 c21 = tex2D(s0, tex + float2(dx, 0)).xyz;
float3 c22 = tex2D(s0, tex + float2(dx, dy)).xyz;
float frac_amt_x = frac(tex.x * texsize.x);
float frac_amt_y = frac(tex.y * texsize.y);
float3 loval = quad_inter(c00, c10, c20, frac_amt_x + 0.5);
float3 midval = quad_inter(c01, c11, c21, frac_amt_x + 0.5);
float3 hival = quad_inter(c02, c12, c22, frac_amt_x + 0.5);
float3 res = quad_inter(loval, midval, hival, frac_amt_y + 0.5);
output OUT;
// Bilinear!
// float3 first = lerp(c00, c20, frac(tex.x * texsize.x + 0.5));
// float3 second = lerp(c02, c22, frac(tex.x * texsize.x + 0.5));
// float3 res = lerp(first, second, frac(tex.y * texsize.y + 0.5));
// OUT.color = float4(res, 1.0);
OUT.color = float4(res, 1.0);
return OUT;
}
-46
View File
@@ -1,46 +0,0 @@
/* Default Vertex shader */
void main_vertex
(
float4 position : POSITION,
float4 color : COLOR,
float2 texCoord : TEXCOORD0,
uniform float4x4 modelViewProj,
out float4 oPosition : POSITION,
out float4 oColor : COLOR,
out float2 otexCoord : TEXCOORD
)
{
oPosition = mul(modelViewProj, position);
oColor = color;
otexCoord = texCoord;
}
struct output
{
float4 color : COLOR;
};
struct input
{
float2 video_size;
float2 texture_size;
float2 output_size;
};
output main_fragment(float2 texCoord : TEXCOORD0, uniform sampler2D decal : TEXUNIT0, uniform input IN)
{
output OUT;
float winHeight = IN.output_size.y;
float4 tex_color = tex2D(decal, texCoord);
OUT.color = (1.0 - 0.60 * floor(fmod(texCoord.y * winHeight, 2.0))) * tex_color;
return OUT;
}
-38
View File
@@ -1,38 +0,0 @@
/* Default Vertex shader */
void main_vertex
(
float4 position : POSITION,
float4 color : COLOR,
float2 texCoord : TEXCOORD0,
uniform float4x4 modelViewProj,
out float4 oPosition : POSITION,
out float4 oColor : COLOR,
out float2 otexCoord : TEXCOORD
)
{
oPosition = mul(modelViewProj, position);
oColor = color;
otexCoord = texCoord;
}
struct output
{
float4 color : COLOR;
};
output main_fragment( in float2 Tex : TEXCOORD0, uniform sampler2D s0 : TEXUNIT0 )
{
float4 Color = tex2D( s0, Tex.xy );
float Sharpenfactor = 30; //Make this between 10 and 50;
Color -= tex2D( s0, Tex.xy+0.0001)*Sharpenfactor;
Color += tex2D( s0, Tex.xy-0.0001)*Sharpenfactor;
Color.a = 1.0;
output OUT;
OUT.color = Color;
return OUT;
}
-21
View File
@@ -1,21 +0,0 @@
/* Very simple grayscale filter.
* Author: Hans-Kristian Arntzen
* License: Public domain
*/
#include "grayscale.h"
// Input format 0RRRRRGGGGGBBBBB. Colors themselves could be switched around.
void grayscale_filter(uint16_t *data, int width, int height)
{
for (int i = 0; i < width * height; i++ )
{
int r, g, b, color;
r = (data[i] >> 10) & 0x1F;
g = (data[i] >> 5) & 0x1F;
b = (data[i] >> 0) & 0x1F;
color = (int)(r * 0.3 + g * 0.59 + b * 0.11);
data[i] = (color << 10) | (color << 5) | color;
}
}
-7
View File
@@ -1,7 +0,0 @@
/* Very simple grayscale filter.
* Author: Hans-Kristian Arntzen
* License: Public domain
*/
#include <stdint.h>
void grayscale_filter(uint16_t *data, int width, int height);
-555
View File
@@ -1,555 +0,0 @@
/* This source code is Copyright (C) 2006-2009 by WolfWings. */
/* It is, however, released under the ISC license. */
/* en.wikipedia.org/wiki/ISC_Licence */
/* --------------------------------------------------------- */
/* It is a formula-level rederiviation of the HQ2x technique */
/* and is, thus, not a derivative work of the original code, */
/* only the original equations behind the code. */
#include <stdlib.h>
#include <string.h>
#include "pastlib.h"
static const uint8_t blends_2x[14*4] = {
/* 5 2 4 1 */
8, 4, 4, 0,
8, 4, 0, 4,
8, 0, 4, 4,
16, 0, 0, 0,
12, 4, 0, 0,
12, 0, 4, 0,
12, 0, 0, 4,
4, 6, 6, 0, /* Interp9 */
12, 2, 2, 0, /* Interp7 */
14, 1, 1, 0, /* Interp10*/
10, 4, 2, 0, /* Interp6 */
10, 2, 4, 0, /* Interp6 */
4, 6, 6, 0, /* Added for secondary blending used in HQ4x */
8, 4, 4, 0, /* Added for secondary blending used in HQ4x */
};
static const uint8_t blends_4x[14*16] = {
/* 5 2 4 1 5 2 4 1 5 2 4 1 5 2 4 1 */
8, 4, 4, 0, 10, 4, 2, 0, 10, 2, 4, 0, 12, 2, 2, 0, /* Needs to be split. B */
10, 0, 0, 6, 10, 4, 0, 2, 12, 0, 0, 4, 14, 0, 0, 2,
10, 0, 0, 6, 10, 0, 4, 2, 12, 0, 0, 4, 14, 0, 0, 2,
16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, /* Solid colors */
10, 6, 0, 0, 10, 6, 0, 0, 14, 2, 0, 0, 14, 2, 0, 0,
10, 0, 6, 0, 14, 0, 2, 0, 10, 0, 6, 0, 14, 0, 2, 0,
10, 0, 0, 6, 12, 0, 0, 4, 12, 0, 0, 4, 14, 0, 0, 2,
0, 8, 8, 0, 4, 8, 4, 0, 0, 6,10, 0, 12, 2, 2, 0, /* Needs to be split. A */
8, 4, 4, 0, 12, 4, 0, 0, 12, 0, 4, 0, 16, 0, 0, 0,
8, 4, 4, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0,
12, 4, 0, 0, 4,12, 0, 0, 10, 0, 4, 0, 14, 0, 2, 0,
12, 0, 4, 0, 10, 4, 0, 0, 4, 0,12, 0, 14, 2, 0, 0,
0, 8, 8, 0, 0,10, 6, 0, 4, 4, 8, 0, 12, 2, 2, 0, /* Needs to be split. A */
0, 8, 8, 0, 8, 8, 0, 0, 8, 0, 8, 0, 16, 0, 0, 0, /* Needs to be split. B */
};
static const uint8_t tree_hq[0x800] = {
/* 6 6 6 6 6 6 6 6
* 2 2 2 2 2 2 2 2
* 4 4 4 4 4 4 4 4
* 8 8 8 8 8 8 8 8
*/
0, 0, 2, 2, 1, 1,13,13, 0, 0, 2, 2, 1, 1,13, 8, /* */
0, 0, 2, 2, 1, 1,12, 6, 0, 0, 2, 2, 1, 1, 8, 8, /* 3 */
0, 0, 5,10, 4, 4,13,13, 0, 0, 5, 5,11, 4,13,13, /* 1 */
0, 0, 5,10, 4, 4,12,13, 0, 0, 5, 5,11, 4,13,13, /* 31 */
0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 6, 8, /* 7 */
0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6, /* 3 7 */
0, 0, 5,10, 4, 4, 7,13, 0, 0, 5, 5,11, 4,13,13, /* 17 */
0, 0, 5,10, 4, 4, 9, 9, 0, 0, 5,10,11,11, 9, 9, /* 317 */
0, 0, 2, 2, 1, 1,13, 8, 0, 0, 2, 2, 1, 1, 8, 8, /* 9 */
0, 0, 2, 2, 1, 1,12, 8, 0, 0, 2, 2, 1, 1, 8, 6, /* 3 9 */
0, 0, 5, 5, 4, 4,13,13, 0, 0, 5, 5, 4, 4,13,13, /* 1 9 */
0, 0, 5, 5, 4, 4,12,13, 0, 0, 5, 5, 4, 4,12,13, /* 31 9 */
0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 8, 6, /* 79 */
0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6, /* 3 79 */
0, 0, 5, 5, 4, 4, 7, 7, 0, 0, 5, 5, 4, 4,13,13, /* 179 */
0, 0, 5, 5, 4, 4, 9, 9, 0, 0, 5, 5, 4, 4, 9, 9, /* 3179 */
/* DIFF26 */
0, 0, 2, 2, 1, 1,13,13, 0, 0, 2, 2, 1, 1,13, 8,
0, 0, 2, 2, 1, 1,12, 6, 0, 0, 2, 2, 1, 1, 8, 8,
0, 0, 5, 5, 4, 4,13,13, 0, 0, 5, 5,11, 4,13,13,
0, 0, 5, 5, 4, 4,12,13, 0, 0, 5, 5,11, 4,13,13,
0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 6, 8,
0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5, 5, 4, 4, 7,13, 0, 0, 5, 5,11, 4,13,13,
0, 0, 5, 5, 4, 4, 9, 9, 0, 0, 5, 5,11,11, 9, 9,
0, 0, 2, 2, 1, 1,13, 8, 0, 0, 2, 2, 1, 1, 8, 8,
0, 0, 2, 2, 1, 1,12, 8, 0, 0, 2, 2, 1, 1, 8, 6,
0, 0, 5, 5, 4, 4,13,13, 0, 0, 5, 5, 4, 4,13,13,
0, 0, 5, 5, 4, 4,12,13, 0, 0, 5, 5, 4, 4,12,13,
0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 8, 6,
0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5, 5, 4, 4, 7, 7, 0, 0, 5, 5, 4, 4,13,13,
0, 0, 5, 5, 4, 4, 9, 9, 0, 0, 5, 5, 4, 4, 9, 9,
/* DIFF24 */
0, 0, 2, 2, 1, 1, 6, 3, 0, 0, 2, 2, 1, 1, 3, 6,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5, 5,11, 4, 3, 3,
0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5, 5,11, 4, 3, 3,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5, 5,11, 4, 3, 3,
0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5,10,11,11, 3, 3,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
/* DIFF26 DIFF24 */
0, 0, 2, 2, 1, 1, 6, 3, 0, 0, 2, 2, 1, 1, 3, 6,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5,11, 4, 3, 3,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5,11, 4, 3, 3,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5,11, 4, 3, 3,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5,11,11, 3, 3,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
/* DIFF84 */
0, 0, 2, 2, 1, 1,13,13, 0, 0, 2, 2, 1, 1,13, 8,
0, 0, 2, 2, 1, 1,12, 6, 0, 0, 2, 2, 1, 1, 8, 8,
0, 0, 5,10, 4, 4,13,13, 0, 0, 5, 5, 4, 4,13,13,
0, 0, 5,10, 4, 4,12,13, 0, 0, 5, 5, 4, 4,13,13,
0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 6, 8,
0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5,10, 4, 4, 7,13, 0, 0, 5, 5, 4, 4,13,13,
0, 0, 5,10, 4, 4, 9, 9, 0, 0, 5,10, 4, 4, 9, 9,
0, 0, 2, 2, 1, 1,13, 8, 0, 0, 2, 2, 1, 1, 8, 8,
0, 0, 2, 2, 1, 1,12, 8, 0, 0, 2, 2, 1, 1, 8, 6,
0, 0, 5, 5, 4, 4,13,13, 0, 0, 5, 5, 4, 4,13,13,
0, 0, 5, 5, 4, 4,12,13, 0, 0, 5, 5, 4, 4,12,13,
0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 8, 6,
0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5, 5, 4, 4, 7, 7, 0, 0, 5, 5, 4, 4,13,13,
0, 0, 5, 5, 4, 4, 9, 9, 0, 0, 5, 5, 4, 4, 9, 9,
/* DIFF26 DIFF84 */
0, 0, 2, 2, 1, 1,13,13, 0, 0, 2, 2, 1, 1,13, 8,
0, 0, 2, 2, 1, 1,12, 6, 0, 0, 2, 2, 1, 1, 8, 8,
0, 0, 5, 5, 4, 4,13,13, 0, 0, 5, 5, 4, 4,13,13,
0, 0, 5, 5, 4, 4,12,13, 0, 0, 5, 5, 4, 4,13,13,
0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 6, 8,
0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5, 5, 4, 4, 7,13, 0, 0, 5, 5, 4, 4,13,13,
0, 0, 5, 5, 4, 4, 9, 9, 0, 0, 5, 5, 4, 4, 9, 9,
0, 0, 2, 2, 1, 1,13, 8, 0, 0, 2, 2, 1, 1, 8, 8,
0, 0, 2, 2, 1, 1,12, 8, 0, 0, 2, 2, 1, 1, 8, 6,
0, 0, 5, 5, 4, 4,13,13, 0, 0, 5, 5, 4, 4,13,13,
0, 0, 5, 5, 4, 4,12,13, 0, 0, 5, 5, 4, 4,12,13,
0, 0, 2, 2, 1, 1, 7, 8, 0, 0, 2, 2, 1, 1, 8, 6,
0, 0, 2, 2, 1, 1, 8, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5, 5, 4, 4, 7, 7, 0, 0, 5, 5, 4, 4,13,13,
0, 0, 5, 5, 4, 4, 9, 9, 0, 0, 5, 5, 4, 4, 9, 9,
/* DIFF24 DIFF84 */
0, 0, 2, 2, 1, 1, 6, 3, 0, 0, 2, 2, 1, 1, 3, 6,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 5,10, 4, 4, 3, 3, 0, 0, 5,10, 4, 4, 3, 3,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
/* DIFF26 DIFF24 DIFF84 */
0, 0, 2, 2, 1, 1, 6, 3, 0, 0, 2, 2, 1, 1, 3, 6,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 2, 2, 1, 1, 6, 6, 0, 0, 2, 2, 1, 1, 6, 6,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3,
0, 0, 5, 5, 4, 4, 3, 3, 0, 0, 5, 5, 4, 4, 3, 3};
#define RB(y,u,v) (((y)&0x3FF)<<22)|(((u)&0x3FF)<<11)|((v)&0x3FF)
#define GO(y,v) ((((y)+64)&0x3FF)<<22)|(16<<11)|(((v)+16)&0x3FF)
static const uint32_t DiffTable[128] = {
RB(-85, 160, 80), RB(-83, 155, 77), RB(-80, 150, 75), RB(-77, 145, 72),
RB(-75, 140, 70), RB(-72, 135, 67), RB(-69, 130, 65), RB(-67, 125, 62),
RB(-64, 120, 60), RB(-61, 115, 57), RB(-59, 110, 55), RB(-56, 105, 52),
RB(-53, 100, 50), RB(-51, 95, 47), RB(-48, 90, 45), RB(-45, 85, 42),
RB(-43, 80, 40), RB(-40, 75, 37), RB(-37, 70, 35), RB(-35, 65, 32),
RB(-32, 60, 30), RB(-29, 55, 27), RB(-27, 50, 25), RB(-24, 45, 22),
RB(-21, 40, 20), RB(-19, 35, 17), RB(-16, 30, 15), RB(-13, 25, 12),
RB(-11, 20, 10), RB( -8, 15, 7), RB( -5, 10, 5), RB( -3, 5, 2),
RB( 0, 0, 0), RB( 2, -5, -2), RB( 5, -10, -5), RB( 7, -15, -7),
RB( 10, -20, -10), RB( 13, -25, -12), RB( 15, -30, -15), RB( 18, -35, -17),
RB( 21, -40, -20), RB( 23, -45, -22), RB( 26, -50, -25), RB( 29, -55, -27),
RB( 31, -60, -30), RB( 34, -65, -32), RB( 37, -70, -35), RB( 39, -75, -37),
RB( 42, -80, -40), RB( 45, -85, -42), RB( 47, -90, -45), RB( 50, -95, -47),
RB( 53,-100, -50), RB( 55,-105, -52), RB( 58,-110, -55), RB( 61,-115, -57),
RB( 63,-120, -60), RB( 66,-125, -62), RB( 69,-130, -65), RB( 71,-135, -67),
RB( 74,-140, -70), RB( 77,-145, -72), RB( 79,-150, -75), RB( 82,-155, -77),
GO(-84, -160), GO(-81, -155), GO(-78, -150), GO(-76, -145),
GO(-73, -140), GO(-70, -135), GO(-68, -130), GO(-65, -125),
GO(-63, -120), GO(-60, -115), GO(-57, -110), GO(-55, -105),
GO(-52, -100), GO(-49, -95), GO(-47, -90), GO(-44, -85),
GO(-42, -80), GO(-39, -75), GO(-36, -70), GO(-34, -65),
GO(-31, -60), GO(-28, -55), GO(-26, -50), GO(-23, -45),
GO(-21, -40), GO(-18, -35), GO(-15, -30), GO(-13, -25),
GO(-10, -20), GO( -7, -15), GO( -5, -10), GO( -2, -5),
GO( 0, 0), GO( 2, 4), GO( 5, 9), GO( 7, 14),
GO( 10, 19), GO( 13, 24), GO( 15, 29), GO( 18, 34),
GO( 21, 39), GO( 23, 44), GO( 26, 49), GO( 28, 54),
GO( 31, 59), GO( 34, 64), GO( 36, 69), GO( 39, 74),
GO( 42, 79), GO( 44, 84), GO( 47, 89), GO( 49, 94),
GO( 52, 99), GO( 55, 104), GO( 57, 109), GO( 60, 114),
GO( 63, 119), GO( 65, 124), GO( 68, 129), GO( 70, 134),
GO( 73, 139), GO( 76, 144), GO( 78, 149), GO( 81, 154)};
inline static int ExpandedDiff (uint32_t c0, uint32_t c1) {
uint32_t r, g;
g = c0;
g += 0x4008020;
g -= c1;
r = DiffTable[(int)((unsigned char)g) + 0];
r ^= (0x3FF << 11);
g >>= 10;
r += DiffTable[(int)((unsigned char)g) + 0];
g >>= 11;
r += DiffTable[(int)((unsigned char)g) + 64];
r &= 0xE01F03E0;
return r;
}
#define DIFF56 0x001
#define DIFF52 0x002
#define DIFF54 0x004
#define DIFF58 0x008
#define DIFF53 0x010
#define DIFF51 0x020
#define DIFF57 0x040
#define DIFF59 0x080
#define DIFF26 0x100
#define DIFF24 0x200
#define DIFF84 0x400
#define DIFF86 0x800
/* lastLineDiffs is previous line's 52, 53, 26, all >> 1 */
static uint8_t lastLineDiffs[__PAST_LIBRARY_WIDTH];
#define RotatePattern(x) ((((x) & 0x777) << 1) | (((x) & 0x888) >> 3))
void ProcessHQ2x(const pixel * restrict in, pixel * restrict out) {
signed int y, x;
unsigned int pattern, newpattern;
uint32_t pixels[9];
int prevline, nextline;
memset(lastLineDiffs, 0, sizeof(lastLineDiffs));
prevline = 1;
nextline = 1 + __PAST_LIBRARY_WIDTH;
y = __PAST_LIBRARY_HEIGHT - 1;
do {
pixels[1-1] =
pixels[2-1] = RGBUnpack(in[prevline-1]); /* Pixel 2 */
pixels[3-1] = RGBUnpack(in[prevline ]); /* Pixel 3 */
pixels[4-1] =
pixels[5-1] = RGBUnpack(in[ 0]); /* Pixel 5 */
pixels[6-1] = RGBUnpack(in[ 1]); /* Pixel 6 */
pixels[7-1] =
pixels[8-1] = RGBUnpack(in[nextline-1]); /* Pixel 8 */
pixels[9-1] = RGBUnpack(in[nextline ]); /* Pixel 9 */
pattern = 0;
x = __PAST_LIBRARY_WIDTH - 1;
do {
newpattern = 0;
if (pattern & DIFF26) newpattern |= DIFF51;
if (pattern & DIFF56) newpattern |= DIFF54;
if (pattern & DIFF86) newpattern |= DIFF57;
if (pattern & DIFF53) newpattern |= DIFF24;
if (pattern & DIFF59) newpattern |= DIFF84;
if (lastLineDiffs[x] & (DIFF52 >> 1)) newpattern |= DIFF58;
if (lastLineDiffs[x] & (DIFF26 >> 1)) newpattern |= DIFF59;
if (lastLineDiffs[x] & (DIFF53 >> 1)) newpattern |= DIFF86;
pattern = newpattern;
if (ExpandedDiff(pixels[5-1], pixels[2-1])) pattern |= DIFF52;
if (ExpandedDiff(pixels[5-1], pixels[3-1])) pattern |= DIFF53;
if (ExpandedDiff(pixels[5-1], pixels[6-1])) pattern |= DIFF56;
if (ExpandedDiff(pixels[2-1], pixels[6-1])) pattern |= DIFF26;
lastLineDiffs[x] = pattern >> 1;
newpattern = tree_hq[pattern & 0x7FF];
pattern = RotatePattern(pattern);
out[ 0] =
RGBPack(((pixels[5-1] * blends_2x[(newpattern * 4) + 0]) +
(pixels[2-1] * blends_2x[(newpattern * 4) + 1]) +
(pixels[4-1] * blends_2x[(newpattern * 4) + 2]) +
(pixels[1-1] * blends_2x[(newpattern * 4) + 3])) / 16);
newpattern = tree_hq[pattern & 0x7FF];
pattern = RotatePattern(pattern);
out[ 1] =
RGBPack(((pixels[5-1] * blends_2x[(newpattern * 4) + 0]) +
(pixels[6-1] * blends_2x[(newpattern * 4) + 1]) +
(pixels[2-1] * blends_2x[(newpattern * 4) + 2]) +
(pixels[3-1] * blends_2x[(newpattern * 4) + 3])) / 16);
newpattern = tree_hq[pattern & 0x7FF];
pattern = RotatePattern(pattern);
out[(__PAST_LIBRARY_WIDTH*2)+1] =
RGBPack(((pixels[5-1] * blends_2x[(newpattern * 4) + 0]) +
(pixels[8-1] * blends_2x[(newpattern * 4) + 1]) +
(pixels[6-1] * blends_2x[(newpattern * 4) + 2]) +
(pixels[9-1] * blends_2x[(newpattern * 4) + 3])) / 16);
newpattern = tree_hq[pattern & 0x7FF];
pattern = RotatePattern(pattern);
out[(__PAST_LIBRARY_WIDTH*2) ] =
RGBPack(((pixels[5-1] * blends_2x[(newpattern * 4) + 0]) +
(pixels[4-1] * blends_2x[(newpattern * 4) + 1]) +
(pixels[8-1] * blends_2x[(newpattern * 4) + 2]) +
(pixels[7-1] * blends_2x[(newpattern * 4) + 3])) / 16);
out += 2;
in++;
x--;
if (x == 0) {
pixels[1-1] = pixels[2-1];
pixels[2-1] = pixels[3-1];
pixels[4-1] = pixels[5-1];
pixels[5-1] = pixels[6-1];
pixels[7-1] = pixels[8-1];
pixels[8-1] = pixels[9-1];
continue;
}
pixels[1-1] = pixels[2-1];
pixels[2-1] = pixels[3-1];
pixels[3-1] = RGBUnpack(in[prevline]); /* Pixel 3 */
pixels[4-1] = pixels[5-1];
pixels[5-1] = pixels[6-1];
pixels[6-1] = RGBUnpack(in[1 ]); /* Pixel 6 */
pixels[7-1] = pixels[8-1];
pixels[8-1] = pixels[9-1];
pixels[9-1] = RGBUnpack(in[nextline]); /* Pixel 9 */
} while (x >= 0);
prevline = 1 - __PAST_LIBRARY_WIDTH;
out += (__PAST_LIBRARY_WIDTH * 2);
y--;
if (y > 0) {
continue;
}
nextline = 0;
} while (y >= 0);
}
void ProcessHQ4x(const pixel * restrict in, pixel * restrict out) {
signed int y, x;
unsigned int pattern, newpattern;
uint32_t pixels[9];
int prevline, nextline;
memset(lastLineDiffs, 0, sizeof(lastLineDiffs));
prevline = 1;
nextline = 1 + __PAST_LIBRARY_WIDTH;
y = __PAST_LIBRARY_HEIGHT - 1;
do {
pixels[1-1] =
pixels[2-1] = RGBUnpack(in[prevline-1]); /* Pixel 2 */
pixels[3-1] = RGBUnpack(in[prevline ]); /* Pixel 3 */
pixels[4-1] =
pixels[5-1] = RGBUnpack(in[ 0]); /* Pixel 5 */
pixels[6-1] = RGBUnpack(in[ 1]); /* Pixel 6 */
pixels[7-1] =
pixels[8-1] = RGBUnpack(in[nextline-1]); /* Pixel 8 */
pixels[9-1] = RGBUnpack(in[nextline ]); /* Pixel 9 */
pattern = 0;
x = __PAST_LIBRARY_WIDTH - 1;
do {
newpattern = 0;
if (pattern & DIFF26) newpattern |= DIFF51;
if (pattern & DIFF56) newpattern |= DIFF54;
if (pattern & DIFF86) newpattern |= DIFF57;
if (pattern & DIFF53) newpattern |= DIFF24;
if (pattern & DIFF59) newpattern |= DIFF84;
if (lastLineDiffs[x] & (DIFF52 >> 1)) newpattern |= DIFF58;
if (lastLineDiffs[x] & (DIFF26 >> 1)) newpattern |= DIFF59;
if (lastLineDiffs[x] & (DIFF53 >> 1)) newpattern |= DIFF86;
pattern = newpattern;
if (ExpandedDiff(pixels[5-1], pixels[2-1])) pattern |= DIFF52;
if (ExpandedDiff(pixels[5-1], pixels[3-1])) pattern |= DIFF53;
if (ExpandedDiff(pixels[5-1], pixels[6-1])) pattern |= DIFF56;
if (ExpandedDiff(pixels[2-1], pixels[6-1])) pattern |= DIFF26;
lastLineDiffs[x] = pattern >> 1;
newpattern = tree_hq[pattern & 0x7FF];
pattern = RotatePattern(pattern);
out[ 0] =
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x0]) +
(pixels[2-1] * blends_4x[(newpattern * 16) + 1 + 0x0]) +
(pixels[4-1] * blends_4x[(newpattern * 16) + 2 + 0x0]) +
(pixels[1-1] * blends_4x[(newpattern * 16) + 3 + 0x0])) / 16);
out[ 1] =
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x4]) +
(pixels[2-1] * blends_4x[(newpattern * 16) + 1 + 0x4]) +
(pixels[4-1] * blends_4x[(newpattern * 16) + 2 + 0x4]) +
(pixels[1-1] * blends_4x[(newpattern * 16) + 3 + 0x4])) / 16);
out[(__PAST_LIBRARY_WIDTH*0x4) ] =
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x8]) +
(pixels[2-1] * blends_4x[(newpattern * 16) + 1 + 0x8]) +
(pixels[4-1] * blends_4x[(newpattern * 16) + 2 + 0x8]) +
(pixels[1-1] * blends_4x[(newpattern * 16) + 3 + 0x8])) / 16);
out[(__PAST_LIBRARY_WIDTH*0x4)+1] =
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0xC]) +
(pixels[2-1] * blends_4x[(newpattern * 16) + 1 + 0xC]) +
(pixels[4-1] * blends_4x[(newpattern * 16) + 2 + 0xC]) +
(pixels[1-1] * blends_4x[(newpattern * 16) + 3 + 0xC])) / 16);
newpattern = tree_hq[pattern & 0x7FF];
pattern = RotatePattern(pattern);
out[ 3] =
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x0]) +
(pixels[6-1] * blends_4x[(newpattern * 16) + 1 + 0x0]) +
(pixels[2-1] * blends_4x[(newpattern * 16) + 2 + 0x0]) +
(pixels[3-1] * blends_4x[(newpattern * 16) + 3 + 0x0])) / 16);
out[(__PAST_LIBRARY_WIDTH*0x4)+3] =
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x4]) +
(pixels[6-1] * blends_4x[(newpattern * 16) + 1 + 0x4]) +
(pixels[2-1] * blends_4x[(newpattern * 16) + 2 + 0x4]) +
(pixels[3-1] * blends_4x[(newpattern * 16) + 3 + 0x4])) / 16);
out[ 2] =
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x8]) +
(pixels[6-1] * blends_4x[(newpattern * 16) + 1 + 0x8]) +
(pixels[2-1] * blends_4x[(newpattern * 16) + 2 + 0x8]) +
(pixels[3-1] * blends_4x[(newpattern * 16) + 3 + 0x8])) / 16);
out[(__PAST_LIBRARY_WIDTH*0x4)+2] =
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0xC]) +
(pixels[6-1] * blends_4x[(newpattern * 16) + 1 + 0xC]) +
(pixels[2-1] * blends_4x[(newpattern * 16) + 2 + 0xC]) +
(pixels[3-1] * blends_4x[(newpattern * 16) + 3 + 0xC])) / 16);
newpattern = tree_hq[pattern & 0x7FF];
pattern = RotatePattern(pattern);
out[(__PAST_LIBRARY_WIDTH*0xC)+3] =
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x0]) +
(pixels[8-1] * blends_4x[(newpattern * 16) + 1 + 0x0]) +
(pixels[6-1] * blends_4x[(newpattern * 16) + 2 + 0x0]) +
(pixels[9-1] * blends_4x[(newpattern * 16) + 3 + 0x0])) / 16);
out[(__PAST_LIBRARY_WIDTH*0xC)+2] =
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x4]) +
(pixels[8-1] * blends_4x[(newpattern * 16) + 1 + 0x4]) +
(pixels[6-1] * blends_4x[(newpattern * 16) + 2 + 0x4]) +
(pixels[9-1] * blends_4x[(newpattern * 16) + 3 + 0x4])) / 16);
out[(__PAST_LIBRARY_WIDTH*0x8)+3] =
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x8]) +
(pixels[8-1] * blends_4x[(newpattern * 16) + 1 + 0x8]) +
(pixels[6-1] * blends_4x[(newpattern * 16) + 2 + 0x8]) +
(pixels[9-1] * blends_4x[(newpattern * 16) + 3 + 0x8])) / 16);
out[(__PAST_LIBRARY_WIDTH*0x8)+2] =
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0xC]) +
(pixels[8-1] * blends_4x[(newpattern * 16) + 1 + 0xC]) +
(pixels[6-1] * blends_4x[(newpattern * 16) + 2 + 0xC]) +
(pixels[9-1] * blends_4x[(newpattern * 16) + 3 + 0xC])) / 16);
newpattern = tree_hq[pattern & 0x7FF];
pattern = RotatePattern(pattern);
out[(__PAST_LIBRARY_WIDTH*0xC) ] =
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x0]) +
(pixels[4-1] * blends_4x[(newpattern * 16) + 1 + 0x0]) +
(pixels[8-1] * blends_4x[(newpattern * 16) + 2 + 0x0]) +
(pixels[7-1] * blends_4x[(newpattern * 16) + 3 + 0x0])) / 16);
out[(__PAST_LIBRARY_WIDTH*0x8) ] =
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x4]) +
(pixels[4-1] * blends_4x[(newpattern * 16) + 1 + 0x4]) +
(pixels[8-1] * blends_4x[(newpattern * 16) + 2 + 0x4]) +
(pixels[7-1] * blends_4x[(newpattern * 16) + 3 + 0x4])) / 16);
out[(__PAST_LIBRARY_WIDTH*0xC)+1] =
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0x8]) +
(pixels[4-1] * blends_4x[(newpattern * 16) + 1 + 0x8]) +
(pixels[8-1] * blends_4x[(newpattern * 16) + 2 + 0x8]) +
(pixels[7-1] * blends_4x[(newpattern * 16) + 3 + 0x8])) / 16);
out[(__PAST_LIBRARY_WIDTH*0x8)+1] =
RGBPack(((pixels[5-1] * blends_4x[(newpattern * 16) + 0 + 0xC]) +
(pixels[4-1] * blends_4x[(newpattern * 16) + 1 + 0xC]) +
(pixels[8-1] * blends_4x[(newpattern * 16) + 2 + 0xC]) +
(pixels[7-1] * blends_4x[(newpattern * 16) + 3 + 0xC])) / 16);
out += 4;
in++;
x--;
if (x == 0) {
pixels[1-1] = pixels[2-1];
pixels[2-1] = pixels[3-1];
pixels[4-1] = pixels[5-1];
pixels[5-1] = pixels[6-1];
pixels[7-1] = pixels[8-1];
pixels[8-1] = pixels[9-1];
continue;
}
pixels[1-1] = pixels[2-1];
pixels[2-1] = pixels[3-1];
pixels[3-1] = RGBUnpack(in[prevline]); /* Pixel 3 */
pixels[4-1] = pixels[5-1];
pixels[5-1] = pixels[6-1];
pixels[6-1] = RGBUnpack(in[1 ]); /* Pixel 6 */
pixels[7-1] = pixels[8-1];
pixels[8-1] = pixels[9-1];
pixels[9-1] = RGBUnpack(in[nextline]); /* Pixel 9 */
} while (x >= 0);
prevline = 1 - __PAST_LIBRARY_WIDTH;
out += (__PAST_LIBRARY_WIDTH * 0xC);
y--;
if (y > 0) {
continue;
}
nextline = 0;
} while (y >= 0);
}
-19
View File
@@ -1,19 +0,0 @@
#include "ntsc.h"
#include <stdlib.h>
#include <stdbool.h>
void ntsc_filter(uint16_t * restrict out, const uint16_t * restrict in, int width, int height)
{
static int phase = 0;
static snes_ntsc_t ntsc;
static bool inited = false;
if (inited == false)
{
snes_ntsc_init(&ntsc, &snes_ntsc_composite);
inited = true;
}
snes_ntsc_blit(&ntsc, in, width, phase, width, height, out, SNES_NTSC_OUT_WIDTH(width) * sizeof(uint16_t));
phase ^= 1;
}
-9
View File
@@ -1,9 +0,0 @@
#ifndef _NTSC_FILTER_H
#define _NTSC_FILTER_H
#include <stdint.h>
#include "snes_ntsc/snes_ntsc.h"
void ntsc_filter(uint16_t * restrict out, const uint16_t * restrict in, int width, int height);
#endif
-54
View File
@@ -1,54 +0,0 @@
/* This source code is Copyright (C) 2006-2009 by WolfWings. */
/* It is, however, released under the ISC license. */
/* en.wikipedia.org/wiki/ISC_Licence */
/* --------------------------------------------------------- */
/* It is a formula-level rederiviation of the HQ2x technique */
/* and is, thus, not a derivative work of the original code, */
/* only the original equations behind the code. */
#ifndef __PAST_LIBRARY_H
#define __PAST_LIBRARY_H
#include <stdint.h>
#ifndef __PAST_LIBRARY_WIDTH
#define __PAST_LIBRARY_WIDTH 256
#endif
#ifndef __PAST_LIBRARY_HEIGHT
#define __PAST_LIBRARY_HEIGHT 224
#endif
typedef uint16_t pixel;
/* These two functions are what need to be modified to interface other
* pixel formats with the library. They need to return values inside a
* format like this: (Red/Blue can be reversed.)
*
* 0000-00GG-GGG0-0000-0RRR-RR00-000B-BBBB
*
* This is VERY fast to create from 15-bit (NOT 16-bit) RGB/BGR, as
* below.
*
* If you can't return data in this format, you'll have to rebuild the
* ExpandedDiff function and DiffTable array to compensate, and possibly
* change the blending functions in the Process* functions to handle any
* guard-bits and masking.
*/
static inline uint32_t RGBUnpack(pixel i) {
uint32_t o = i;
o = (o * 0x10001);
o = o & 0x03E07C1F;
return o;
}
static inline pixel RGBPack(uint32_t x) {
x &= 0x03E07C1F;
x |= (x >> 16);
return x;
}
void ProcessHQ2x(const pixel * restrict inbuffer, pixel * restrict outbuffer);
void ProcessHQ4x(const pixel * restrict inbuffer, pixel * restrict outbuffer);
#endif /* __PAST_LIBRARY_H */
-251
View File
@@ -1,251 +0,0 @@
/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */
#include "snes_ntsc.h"
/* Copyright (C) 2006-2007 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module 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 Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
snes_ntsc_setup_t const snes_ntsc_monochrome = { 0,-1, 0, 0,.2, 0,.2,-.2,-.2,-1, 1, 0, 0 };
snes_ntsc_setup_t const snes_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 };
snes_ntsc_setup_t const snes_ntsc_svideo = { 0, 0, 0, 0,.2, 0,.2, -1, -1, 0, 1, 0, 0 };
snes_ntsc_setup_t const snes_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 1, 0, 0 };
#define alignment_count 3
#define burst_count 3
#define rescale_in 8
#define rescale_out 7
#define artifacts_mid 1.0f
#define fringing_mid 1.0f
#define std_decoder_hue 0
#define rgb_bits 7 /* half normal range to allow for doubled hires pixels */
#define gamma_size 32
#include "snes_ntsc_impl.h"
/* 3 input pixels -> 8 composite samples */
pixel_info_t const snes_ntsc_pixels [alignment_count] = {
{ PIXEL_OFFSET( -4, -9 ), { 1, 1, .6667f, 0 } },
{ PIXEL_OFFSET( -2, -7 ), { .3333f, 1, 1, .3333f } },
{ PIXEL_OFFSET( 0, -5 ), { 0, .6667f, 1, 1 } },
};
static void merge_kernel_fields( snes_ntsc_rgb_t* io )
{
int n;
for ( n = burst_size; n; --n )
{
snes_ntsc_rgb_t p0 = io [burst_size * 0] + rgb_bias;
snes_ntsc_rgb_t p1 = io [burst_size * 1] + rgb_bias;
snes_ntsc_rgb_t p2 = io [burst_size * 2] + rgb_bias;
/* merge colors without losing precision */
io [burst_size * 0] =
((p0 + p1 - ((p0 ^ p1) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
io [burst_size * 1] =
((p1 + p2 - ((p1 ^ p2) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
io [burst_size * 2] =
((p2 + p0 - ((p2 ^ p0) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
++io;
}
}
static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out )
{
int n;
for ( n = burst_count; n; --n )
{
unsigned i;
for ( i = 0; i < rgb_kernel_size / 2; i++ )
{
snes_ntsc_rgb_t error = color -
out [i ] - out [(i+12)%14+14] - out [(i+10)%14+28] -
out [i + 7] - out [i + 5 +14] - out [i + 3 +28];
DISTRIBUTE_ERROR( i+3+28, i+5+14, i+7 );
}
out += alignment_count * rgb_kernel_size;
}
}
void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup )
{
int merge_fields;
int entry;
init_t impl;
if ( !setup )
setup = &snes_ntsc_composite;
init( &impl, setup );
merge_fields = setup->merge_fields;
if ( setup->artifacts <= -1 && setup->fringing <= -1 )
merge_fields = 1;
for ( entry = 0; entry < snes_ntsc_palette_size; entry++ )
{
/* Reduce number of significant bits of source color. Clearing the
low bits of R and B were least notictable. Modifying green was too
noticeable. */
int ir = entry >> 8 & 0x1E;
int ig = entry >> 4 & 0x1F;
int ib = entry << 1 & 0x1E;
#if SNES_NTSC_BSNES_COLORTBL
if ( setup->bsnes_colortbl )
{
int bgr15 = (ib << 10) | (ig << 5) | ir;
unsigned long rgb16 = setup->bsnes_colortbl [bgr15];
ir = rgb16 >> 11 & 0x1E;
ig = rgb16 >> 6 & 0x1F;
ib = rgb16 & 0x1E;
}
#endif
{
float rr = impl.to_float [ir];
float gg = impl.to_float [ig];
float bb = impl.to_float [ib];
float y, i, q = RGB_TO_YIQ( rr, gg, bb, y, i );
int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g );
snes_ntsc_rgb_t rgb = PACK_RGB( r, g, b );
snes_ntsc_rgb_t* out = ntsc->table [entry];
gen_kernel( &impl, y, i, q, out );
if ( merge_fields )
merge_kernel_fields( out );
correct_errors( rgb, out );
}
}
}
#ifndef SNES_NTSC_NO_BLITTERS
void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width,
int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch )
{
int chunk_count = (in_width - 1) / snes_ntsc_in_chunk;
for ( ; in_height; --in_height )
{
SNES_NTSC_IN_T const* line_in = input;
SNES_NTSC_BEGIN_ROW( ntsc, burst_phase,
snes_ntsc_black, snes_ntsc_black, SNES_NTSC_ADJ_IN( *line_in ) );
snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out;
int n;
++line_in;
for ( n = chunk_count; n; --n )
{
/* order of input and output pixels must not be altered */
SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) );
SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) );
SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) );
SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
line_in += 3;
line_out += 7;
}
/* finish final pixels */
SNES_NTSC_COLOR_IN( 0, snes_ntsc_black );
SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 1, snes_ntsc_black );
SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 2, snes_ntsc_black );
SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
input += in_row_width;
rgb_out = (char*) rgb_out + out_pitch;
}
}
void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width,
int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch )
{
int chunk_count = (in_width - 2) / (snes_ntsc_in_chunk * 2);
for ( ; in_height; --in_height )
{
SNES_NTSC_IN_T const* line_in = input;
SNES_NTSC_HIRES_ROW( ntsc, burst_phase,
snes_ntsc_black, snes_ntsc_black, snes_ntsc_black,
SNES_NTSC_ADJ_IN( line_in [0] ),
SNES_NTSC_ADJ_IN( line_in [1] ) );
snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out;
int n;
line_in += 2;
for ( n = chunk_count; n; --n )
{
/* twice as many input pixels per chunk */
SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) );
SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) );
SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) );
SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 3, SNES_NTSC_ADJ_IN( line_in [3] ) );
SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 4, SNES_NTSC_ADJ_IN( line_in [4] ) );
SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 5, SNES_NTSC_ADJ_IN( line_in [5] ) );
SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
line_in += 6;
line_out += 7;
}
SNES_NTSC_COLOR_IN( 0, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 1, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 2, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 3, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 4, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_COLOR_IN( 5, snes_ntsc_black );
SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
input += in_row_width;
rgb_out = (char*) rgb_out + out_pitch;
}
}
#endif
-236
View File
@@ -1,236 +0,0 @@
/* SNES NTSC video filter */
/* snes_ntsc 0.2.2 */
#ifndef SNES_NTSC_H
#define SNES_NTSC_H
#include "snes_ntsc_config.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown
in parenthesis and should remain fairly stable in future versions. */
typedef struct snes_ntsc_setup_t
{
/* Basic parameters */
double hue; /* -1 = -180 degrees +1 = +180 degrees */
double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */
double contrast; /* -1 = dark (0.5) +1 = light (1.5) */
double brightness; /* -1 = dark (0.5) +1 = light (1.5) */
double sharpness; /* edge contrast enhancement/blurring */
/* Advanced parameters */
double gamma; /* -1 = dark (1.5) +1 = light (0.5) */
double resolution; /* image resolution */
double artifacts; /* artifacts caused by color changes */
double fringing; /* color artifacts caused by brightness changes */
double bleed; /* color bleed (color resolution reduction) */
int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */
float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */
unsigned long const* bsnes_colortbl; /* undocumented; set to 0 */
} snes_ntsc_setup_t;
/* Video format presets */
extern snes_ntsc_setup_t const snes_ntsc_composite; /* color bleeding + artifacts */
extern snes_ntsc_setup_t const snes_ntsc_svideo; /* color bleeding only */
extern snes_ntsc_setup_t const snes_ntsc_rgb; /* crisp image */
extern snes_ntsc_setup_t const snes_ntsc_monochrome;/* desaturated + artifacts */
/* Initializes and adjusts parameters. Can be called multiple times on the same
snes_ntsc_t object. Can pass NULL for either parameter. */
typedef struct snes_ntsc_t snes_ntsc_t;
void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup );
/* Filters one or more rows of pixels. Input pixel format is set by SNES_NTSC_IN_FORMAT
and output RGB depth is set by SNES_NTSC_OUT_DEPTH. Both default to 16-bit RGB.
In_row_width is the number of pixels to get to the next input row. Out_pitch
is the number of *bytes* to get to the next output row. */
void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input,
long in_row_width, int burst_phase, int in_width, int in_height,
void* rgb_out, long out_pitch );
void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input,
long in_row_width, int burst_phase, int in_width, int in_height,
void* rgb_out, long out_pitch );
/* Number of output pixels written by low-res blitter for given input width. Width
might be rounded down slightly; use SNES_NTSC_IN_WIDTH() on result to find rounded
value. Guaranteed not to round 256 down at all. */
#define SNES_NTSC_OUT_WIDTH( in_width ) \
((((in_width) - 1) / snes_ntsc_in_chunk + 1) * snes_ntsc_out_chunk)
/* Number of low-res input pixels that will fit within given output width. Might be
rounded down slightly; use SNES_NTSC_OUT_WIDTH() on result to find rounded
value. */
#define SNES_NTSC_IN_WIDTH( out_width ) \
(((out_width) / snes_ntsc_out_chunk - 1) * snes_ntsc_in_chunk + 1)
/* Interface for user-defined custom blitters */
enum { snes_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */
enum { snes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */
enum { snes_ntsc_black = 0 }; /* palette index for black */
enum { snes_ntsc_burst_count = 3 }; /* burst phase cycles through 0, 1, and 2 */
/* Begins outputting row and starts three pixels. First pixel will be cut off a bit.
Use snes_ntsc_black for unused pixels. Declares variables, so must be before first
statement in a block (unless you're using C++). */
#define SNES_NTSC_BEGIN_ROW( ntsc, burst, pixel0, pixel1, pixel2 ) \
char const* ktable = \
(char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\
SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, SNES_NTSC_IN_FORMAT, ktable )
/* Begins input pixel */
#define SNES_NTSC_COLOR_IN( index, color ) \
SNES_NTSC_COLOR_IN_( index, color, SNES_NTSC_IN_FORMAT, ktable )
/* Generates output pixel. Bits can be 24, 16, 15, 14, 32 (treated as 24), or 0:
24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB)
16: RRRRRGGG GGGBBBBB (5-6-5 RGB)
15: RRRRRGG GGGBBBBB (5-5-5 RGB)
14: BBBBBGG GGGRRRRR (5-5-5 BGR, native SNES format)
0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */
#define SNES_NTSC_RGB_OUT( index, rgb_out, bits ) \
SNES_NTSC_RGB_OUT_14_( index, rgb_out, bits, 1 )
/* Hires equivalents */
#define SNES_NTSC_HIRES_ROW( ntsc, burst, pixel1, pixel2, pixel3, pixel4, pixel5 ) \
char const* ktable = \
(char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\
unsigned const snes_ntsc_pixel1_ = (pixel1);\
snes_ntsc_rgb_t const* kernel1 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel1_ );\
unsigned const snes_ntsc_pixel2_ = (pixel2);\
snes_ntsc_rgb_t const* kernel2 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel2_ );\
unsigned const snes_ntsc_pixel3_ = (pixel3);\
snes_ntsc_rgb_t const* kernel3 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel3_ );\
unsigned const snes_ntsc_pixel4_ = (pixel4);\
snes_ntsc_rgb_t const* kernel4 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel4_ );\
unsigned const snes_ntsc_pixel5_ = (pixel5);\
snes_ntsc_rgb_t const* kernel5 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel5_ );\
snes_ntsc_rgb_t const* kernel0 = kernel1;\
snes_ntsc_rgb_t const* kernelx0;\
snes_ntsc_rgb_t const* kernelx1 = kernel1;\
snes_ntsc_rgb_t const* kernelx2 = kernel1;\
snes_ntsc_rgb_t const* kernelx3 = kernel1;\
snes_ntsc_rgb_t const* kernelx4 = kernel1;\
snes_ntsc_rgb_t const* kernelx5 = kernel1
#define SNES_NTSC_HIRES_OUT( x, rgb_out, bits ) {\
snes_ntsc_rgb_t raw_ =\
kernel0 [ x ] + kernel2 [(x+5)%7+14] + kernel4 [(x+3)%7+28] +\
kernelx0 [(x+7)%7+7] + kernelx2 [(x+5)%7+21] + kernelx4 [(x+3)%7+35] +\
kernel1 [(x+6)%7 ] + kernel3 [(x+4)%7+14] + kernel5 [(x+2)%7+28] +\
kernelx1 [(x+6)%7+7] + kernelx3 [(x+4)%7+21] + kernelx5 [(x+2)%7+35];\
SNES_NTSC_CLAMP_( raw_, 0 );\
SNES_NTSC_RGB_OUT_( rgb_out, (bits), 0 );\
}
/* private */
enum { snes_ntsc_entry_size = 128 };
enum { snes_ntsc_palette_size = 0x2000 };
typedef unsigned long snes_ntsc_rgb_t;
struct snes_ntsc_t {
snes_ntsc_rgb_t table [snes_ntsc_palette_size] [snes_ntsc_entry_size];
};
enum { snes_ntsc_burst_size = snes_ntsc_entry_size / snes_ntsc_burst_count };
#define SNES_NTSC_RGB16( ktable, n ) \
(snes_ntsc_rgb_t const*) (ktable + ((n & 0x001E) | (n >> 1 & 0x03E0) | (n >> 2 & 0x3C00)) * \
(snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t)))
#define SNES_NTSC_BGR15( ktable, n ) \
(snes_ntsc_rgb_t const*) (ktable + ((n << 9 & 0x3C00) | (n & 0x03E0) | (n >> 10 & 0x001E)) * \
(snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t)))
/* common 3->7 ntsc macros */
#define SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \
unsigned const snes_ntsc_pixel0_ = (pixel0);\
snes_ntsc_rgb_t const* kernel0 = ENTRY( table, snes_ntsc_pixel0_ );\
unsigned const snes_ntsc_pixel1_ = (pixel1);\
snes_ntsc_rgb_t const* kernel1 = ENTRY( table, snes_ntsc_pixel1_ );\
unsigned const snes_ntsc_pixel2_ = (pixel2);\
snes_ntsc_rgb_t const* kernel2 = ENTRY( table, snes_ntsc_pixel2_ );\
snes_ntsc_rgb_t const* kernelx0;\
snes_ntsc_rgb_t const* kernelx1 = kernel0;\
snes_ntsc_rgb_t const* kernelx2 = kernel0
#define SNES_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\
snes_ntsc_rgb_t raw_ =\
kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\
kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\
SNES_NTSC_CLAMP_( raw_, shift );\
SNES_NTSC_RGB_OUT_( rgb_out, bits, shift );\
}
/* common ntsc macros */
#define snes_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1))
#define snes_ntsc_clamp_mask (snes_ntsc_rgb_builder * 3 / 2)
#define snes_ntsc_clamp_add (snes_ntsc_rgb_builder * 0x101)
#define SNES_NTSC_CLAMP_( io, shift ) {\
snes_ntsc_rgb_t sub = (io) >> (9-(shift)) & snes_ntsc_clamp_mask;\
snes_ntsc_rgb_t clamp = snes_ntsc_clamp_add - sub;\
io |= clamp;\
clamp -= sub;\
io &= clamp;\
}
#define SNES_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\
unsigned color_;\
kernelx##index = kernel##index;\
kernel##index = (color_ = (color), ENTRY( table, color_ ));\
}
/* x is always zero except in snes_ntsc library */
/* original routine */
#if 0
#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\
if ( bits == 16 )\
rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\
if ( bits == 24 || bits == 32 )\
rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\
if ( bits == 15 )\
rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\
if ( bits == 14 )\
rgb_out = (raw_>>(24-x)& 0x001F)|(raw_>>(9-x)&0x03E0)|(raw_<<(6+x)&0x7C00);\
if ( bits == 0 )\
rgb_out = raw_ << x;\
}
#endif
#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\
rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\
}
// Custom SSNES routine for 15 bit.
/* custom bsnes routine -- hooks into bsnes colortable */
#if 0
#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\
if ( bits == 16 ) {\
rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\
rgb_out = ((rgb_out&0xf800)>>11)|((rgb_out&0x07c0)>>1)|((rgb_out&0x001f)<<10);\
rgb_out = colortable[rgb_out];\
} else if ( bits == 24 || bits == 32 ) {\
rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\
rgb_out = ((rgb_out&0xf80000)>>19)|((rgb_out&0x00f800)>>6)|((rgb_out&0x0000f8)<<7);\
rgb_out = colortable[rgb_out];\
} else if ( bits == 15 ) {\
rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\
rgb_out = ((rgb_out&0x7c00)>>10)|((rgb_out&0x03e0))|((rgb_out&0x001f)<<10);\
rgb_out = colortable[rgb_out];\
} else {\
rgb_out = raw_ << x;\
}\
}
#endif
#ifdef __cplusplus
}
#endif
#endif
-27
View File
@@ -1,27 +0,0 @@
/* Configure library by modifying this file */
#ifndef SNES_NTSC_CONFIG_H
#define SNES_NTSC_CONFIG_H
/* Format of source pixels */
/* #define SNES_NTSC_IN_FORMAT SNES_NTSC_RGB16 */
#define SNES_NTSC_IN_FORMAT SNES_NTSC_BGR15
/* The following affect the built-in blitter only; a custom blitter can
handle things however it wants. */
/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */
#define SNES_NTSC_OUT_DEPTH 15
/* Type of input pixel values */
#define SNES_NTSC_IN_T unsigned short
/* Each raw pixel input value is passed through this. You might want to mask
the pixel index if you use the high bits as flags, etc. */
#define SNES_NTSC_ADJ_IN( in ) (((in >> 10) & 0x1F) | (in & 0x3E0) | ((in << 10) & 0x7C00))
/* For each pixel, this is the basic operation:
output_color = SNES_NTSC_ADJ_IN( SNES_NTSC_IN_T ) */
#endif
-439
View File
@@ -1,439 +0,0 @@
/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */
/* Common implementation of NTSC filters */
#include <assert.h>
#include <math.h>
/* Copyright (C) 2006 Shay Green. This module is free software; you
can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. This
module 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 Lesser General Public License for more
details. You should have received a copy of the GNU Lesser General Public
License along with this module; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#define DISABLE_CORRECTION 0
#undef PI
#define PI 3.14159265358979323846f
#ifndef LUMA_CUTOFF
#define LUMA_CUTOFF 0.20
#endif
#ifndef gamma_size
#define gamma_size 1
#endif
#ifndef rgb_bits
#define rgb_bits 8
#endif
#ifndef artifacts_max
#define artifacts_max (artifacts_mid * 1.5f)
#endif
#ifndef fringing_max
#define fringing_max (fringing_mid * 2)
#endif
#ifndef STD_HUE_CONDITION
#define STD_HUE_CONDITION( setup ) 1
#endif
#define ext_decoder_hue (std_decoder_hue + 15)
#define rgb_unit (1 << rgb_bits)
#define rgb_offset (rgb_unit * 2 + 0.5f)
enum { burst_size = snes_ntsc_entry_size / burst_count };
enum { kernel_half = 16 };
enum { kernel_size = kernel_half * 2 + 1 };
typedef struct init_t
{
float to_rgb [burst_count * 6];
float to_float [gamma_size];
float contrast;
float brightness;
float artifacts;
float fringing;
float kernel [rescale_out * kernel_size * 2];
} init_t;
#define ROTATE_IQ( i, q, sin_b, cos_b ) {\
float t;\
t = i * cos_b - q * sin_b;\
q = i * sin_b + q * cos_b;\
i = t;\
}
static void init_filters( init_t* impl, snes_ntsc_setup_t const* setup )
{
#if rescale_out > 1
float kernels [kernel_size * 2];
#else
float* const kernels = impl->kernel;
#endif
/* generate luma (y) filter using sinc kernel */
{
/* sinc with rolloff (dsf) */
float const rolloff = 1 + (float) setup->sharpness * (float) 0.032;
float const maxh = 32;
float const pow_a_n = (float) pow( rolloff, maxh );
float sum;
int i;
/* quadratic mapping to reduce negative (blurring) range */
float to_angle = (float) setup->resolution + 1;
to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1);
kernels [kernel_size * 3 / 2] = maxh; /* default center value */
for ( i = 0; i < kernel_half * 2 + 1; i++ )
{
int x = i - kernel_half;
float angle = x * to_angle;
/* instability occurs at center point with rolloff very close to 1.0 */
if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 )
{
float rolloff_cos_a = rolloff * (float) cos( angle );
float num = 1 - rolloff_cos_a -
pow_a_n * (float) cos( maxh * angle ) +
pow_a_n * rolloff * (float) cos( (maxh - 1) * angle );
float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff;
float dsf = num / den;
kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5;
}
}
/* apply blackman window and find sum */
sum = 0;
for ( i = 0; i < kernel_half * 2 + 1; i++ )
{
float x = PI * 2 / (kernel_half * 2) * i;
float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 );
sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman);
}
/* normalize kernel */
sum = 1.0f / sum;
for ( i = 0; i < kernel_half * 2 + 1; i++ )
{
int x = kernel_size * 3 / 2 - kernel_half + i;
kernels [x] *= sum;
assert( kernels [x] == kernels [x] ); /* catch numerical instability */
}
}
/* generate chroma (iq) filter using gaussian kernel */
{
float const cutoff_factor = -0.03125f;
float cutoff = (float) setup->bleed;
int i;
if ( cutoff < 0 )
{
/* keep extreme value accessible only near upper end of scale (1.0) */
cutoff *= cutoff;
cutoff *= cutoff;
cutoff *= cutoff;
cutoff *= -30.0f / 0.65f;
}
cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff;
for ( i = -kernel_half; i <= kernel_half; i++ )
kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff );
/* normalize even and odd phases separately */
for ( i = 0; i < 2; i++ )
{
float sum = 0;
int x;
for ( x = i; x < kernel_size; x += 2 )
sum += kernels [x];
sum = 1.0f / sum;
for ( x = i; x < kernel_size; x += 2 )
{
kernels [x] *= sum;
assert( kernels [x] == kernels [x] ); /* catch numerical instability */
}
}
}
/*
printf( "luma:\n" );
for ( i = kernel_size; i < kernel_size * 2; i++ )
printf( "%f\n", kernels [i] );
printf( "chroma:\n" );
for ( i = 0; i < kernel_size; i++ )
printf( "%f\n", kernels [i] );
*/
/* generate linear rescale kernels */
#if rescale_out > 1
{
float weight = 1.0f;
float* out = impl->kernel;
int n = rescale_out;
do
{
float remain = 0;
int i;
weight -= 1.0f / rescale_in;
for ( i = 0; i < kernel_size * 2; i++ )
{
float cur = kernels [i];
float m = cur * weight;
*out++ = m + remain;
remain = cur - m;
}
}
while ( --n );
}
#endif
}
static float const default_decoder [6] =
{ 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f };
static void init( init_t* impl, snes_ntsc_setup_t const* setup )
{
impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset;
impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit;
#ifdef default_palette_contrast
if ( !setup->palette )
impl->contrast *= default_palette_contrast;
#endif
impl->artifacts = (float) setup->artifacts;
if ( impl->artifacts > 0 )
impl->artifacts *= artifacts_max - artifacts_mid;
impl->artifacts = impl->artifacts * artifacts_mid + artifacts_mid;
impl->fringing = (float) setup->fringing;
if ( impl->fringing > 0 )
impl->fringing *= fringing_max - fringing_mid;
impl->fringing = impl->fringing * fringing_mid + fringing_mid;
init_filters( impl, setup );
/* generate gamma table */
if ( gamma_size > 1 )
{
float const to_float = 1.0f / (gamma_size - (gamma_size > 1));
float const gamma = 1.1333f - (float) setup->gamma * 0.5f;
/* match common PC's 2.2 gamma to TV's 2.65 gamma */
int i;
for ( i = 0; i < gamma_size; i++ )
impl->to_float [i] =
(float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness;
}
/* setup decoder matricies */
{
float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue;
float sat = (float) setup->saturation + 1;
float const* decoder = setup->decoder_matrix;
if ( !decoder )
{
decoder = default_decoder;
if ( STD_HUE_CONDITION( setup ) )
hue += PI / 180 * (std_decoder_hue - ext_decoder_hue);
}
{
float s = (float) sin( hue ) * sat;
float c = (float) cos( hue ) * sat;
float* out = impl->to_rgb;
int n;
n = burst_count;
do
{
float const* in = decoder;
int n = 3;
do
{
float i = *in++;
float q = *in++;
*out++ = i * c - q * s;
*out++ = i * s + q * c;
}
while ( --n );
if ( burst_count <= 1 )
break;
ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */
}
while ( --n );
}
}
}
/* kernel generation */
#define RGB_TO_YIQ( r, g, b, y, i ) (\
(y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\
(i = (r) * 0.596f - (g) * 0.275f - (b) * 0.321f),\
((r) * 0.212f - (g) * 0.523f + (b) * 0.311f)\
)
#define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\
r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\
g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\
(type) (y + to_rgb [4] * i + to_rgb [5] * q)\
)
#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1)
enum { rgb_kernel_size = burst_size / alignment_count };
enum { rgb_bias = rgb_unit * 2 * snes_ntsc_rgb_builder };
typedef struct pixel_info_t
{
int offset;
float negate;
float kernel [4];
} pixel_info_t;
#if rescale_in > 1
#define PIXEL_OFFSET_( ntsc, scaled ) \
(kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \
(kernel_size * 2 * scaled))
#define PIXEL_OFFSET( ntsc, scaled ) \
PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\
(((scaled) + rescale_out * 10) % rescale_out) ),\
(1.0f - (((ntsc) + 100) & 2))
#else
#define PIXEL_OFFSET( ntsc, scaled ) \
(kernel_size / 2 + (ntsc) - (scaled)),\
(1.0f - (((ntsc) + 100) & 2))
#endif
extern pixel_info_t const snes_ntsc_pixels [alignment_count];
/* Generate pixel at all burst phases and column alignments */
static void gen_kernel( init_t* impl, float y, float i, float q, snes_ntsc_rgb_t* out )
{
/* generate for each scanline burst phase */
float const* to_rgb = impl->to_rgb;
int burst_remain = burst_count;
y -= rgb_offset;
do
{
/* Encode yiq into *two* composite signals (to allow control over artifacting).
Convolve these with kernels which: filter respective components, apply
sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack
into integer. Based on algorithm by NewRisingSun. */
pixel_info_t const* pixel = snes_ntsc_pixels;
int alignment_remain = alignment_count;
do
{
/* negate is -1 when composite starts at odd multiple of 2 */
float const yy = y * impl->fringing * pixel->negate;
float const ic0 = (i + yy) * pixel->kernel [0];
float const qc1 = (q + yy) * pixel->kernel [1];
float const ic2 = (i - yy) * pixel->kernel [2];
float const qc3 = (q - yy) * pixel->kernel [3];
float const factor = impl->artifacts * pixel->negate;
float const ii = i * factor;
float const yc0 = (y + ii) * pixel->kernel [0];
float const yc2 = (y - ii) * pixel->kernel [2];
float const qq = q * factor;
float const yc1 = (y + qq) * pixel->kernel [1];
float const yc3 = (y - qq) * pixel->kernel [3];
float const* k = &impl->kernel [pixel->offset];
int n;
++pixel;
for ( n = rgb_kernel_size; n; --n )
{
float i = k[0]*ic0 + k[2]*ic2;
float q = k[1]*qc1 + k[3]*qc3;
float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 +
k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset;
if ( rescale_out <= 1 )
k--;
else if ( k < &impl->kernel [kernel_size * 2 * (rescale_out - 1)] )
k += kernel_size * 2 - 1;
else
k -= kernel_size * 2 * (rescale_out - 1) + 2;
{
int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g );
*out++ = PACK_RGB( r, g, b ) - rgb_bias;
}
}
}
while ( alignment_count > 1 && --alignment_remain );
if ( burst_count <= 1 )
break;
to_rgb += 6;
ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */
}
while ( --burst_remain );
}
static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out );
#if DISABLE_CORRECTION
#define CORRECT_ERROR( a ) { out [i] += rgb_bias; }
#define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; }
#else
#define CORRECT_ERROR( a ) { out [a] += error; }
#define DISTRIBUTE_ERROR( a, b, c ) {\
snes_ntsc_rgb_t fourth = (error + 2 * snes_ntsc_rgb_builder) >> 2;\
fourth &= (rgb_bias >> 1) - snes_ntsc_rgb_builder;\
fourth -= rgb_bias >> 2;\
out [a] += fourth;\
out [b] += fourth;\
out [c] += fourth;\
out [i] += error - (fourth * 3);\
}
#endif
#define RGB_PALETTE_OUT( rgb, out_ )\
{\
unsigned char* out = (out_);\
snes_ntsc_rgb_t clamped = (rgb);\
SNES_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\
out [0] = (unsigned char) (clamped >> 21);\
out [1] = (unsigned char) (clamped >> 11);\
out [2] = (unsigned char) (clamped >> 1);\
}
/* blitter related */
#ifndef restrict
#if defined (__GNUC__)
#define restrict __restrict__
#elif defined (_MSC_VER) && _MSC_VER > 1300
#define restrict __restrict
#else
/* no support for restricted pointers */
#define restrict
#endif
#endif
#include <limits.h>
#if SNES_NTSC_OUT_DEPTH <= 16
#if USHRT_MAX == 0xFFFF
typedef unsigned short snes_ntsc_out_t;
#else
#error "Need 16-bit int type"
#endif
#else
#if UINT_MAX == 0xFFFFFFFF
typedef unsigned int snes_ntsc_out_t;
#elif ULONG_MAX == 0xFFFFFFFF
typedef unsigned long snes_ntsc_out_t;
#else
#error "Need 32-bit int type"
#endif
#endif
+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]);
}
}
}
+62 -64
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;
@@ -59,13 +69,10 @@ static void* sdl_input_init(void)
sdl->num_buttons[i] = SDL_JoystickNumButtons(sdl->joysticks[i]);
sdl->num_hats[i] = SDL_JoystickNumHats(sdl->joysticks[i]);
}
else
{
SSNES_WARN("Desired SDL joystick #%u on port %u, but SDL can only detect %u joysticks ...\n",
g_settings.input.joypad_map[i], i + 1, sdl->num_joysticks);
}
}
#endif
sdl->use_keyboard = true;
return sdl;
}
@@ -81,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))
{
@@ -123,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_key_pressed(key->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;
}
@@ -162,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]);
@@ -171,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:
@@ -251,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:
@@ -276,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);
}
}
@@ -301,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;
default:
break;
}
}
#ifdef HAVE_DINPUT
sdl_dinput_poll(sdl->di);
#else
SDL_JoystickUpdate();
#endif
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
+11 -5
View File
@@ -20,19 +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;
+237
View File
@@ -0,0 +1,237 @@
/* 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 "SDL.h"
#include <stdbool.h>
#include "general.h"
#include <stdint.h>
#include <stdlib.h>
#include "ssnes_sdl_input.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
typedef struct x11_input
{
sdl_input_t *sdl;
Display *display;
char state[32];
} x11_input_t;
struct key_bind
{
int x;
int sdl;
};
static int keysym_lut[SDLK_LAST];
static const struct key_bind lut_binds[] = {
{ XK_Left, SDLK_LEFT },
{ XK_Right, SDLK_RIGHT },
{ XK_Up, SDLK_UP },
{ XK_Down, SDLK_DOWN },
{ XK_Return, SDLK_RETURN },
{ XK_Tab, SDLK_TAB },
{ XK_Insert, SDLK_INSERT },
{ XK_Delete, SDLK_DELETE },
{ XK_Shift_R, SDLK_RSHIFT },
{ XK_Shift_L, SDLK_LSHIFT },
{ XK_Control_L, SDLK_LCTRL },
{ XK_Alt_L, SDLK_LALT },
{ XK_space, SDLK_SPACE },
{ XK_Escape, SDLK_ESCAPE },
{ XK_KP_Add, SDLK_KP_PLUS },
{ XK_KP_Subtract, SDLK_KP_MINUS },
{ XK_BackSpace, SDLK_BACKSPACE },
{ XK_KP_Enter, SDLK_KP_ENTER },
{ XK_KP_Add, SDLK_KP_PLUS },
{ XK_KP_Subtract, SDLK_KP_MINUS },
{ XK_KP_Multiply, SDLK_KP_MULTIPLY },
{ XK_KP_Divide, SDLK_KP_DIVIDE },
{ XK_grave, SDLK_BACKQUOTE },
{ XK_KP_0, SDLK_KP0 },
{ XK_KP_1, SDLK_KP1 },
{ XK_KP_2, SDLK_KP2 },
{ XK_KP_3, SDLK_KP3 },
{ XK_KP_4, SDLK_KP4 },
{ XK_KP_5, SDLK_KP5 },
{ XK_KP_6, SDLK_KP6 },
{ XK_KP_7, SDLK_KP7 },
{ XK_KP_8, SDLK_KP8 },
{ XK_KP_9, SDLK_KP9 },
{ XK_0, SDLK_0 },
{ XK_1, SDLK_1 },
{ XK_2, SDLK_2 },
{ XK_3, SDLK_3 },
{ XK_4, SDLK_4 },
{ XK_5, SDLK_5 },
{ XK_6, SDLK_6 },
{ XK_7, SDLK_7 },
{ XK_8, SDLK_8 },
{ XK_9, SDLK_9 },
{ XK_F1, SDLK_F1 },
{ XK_F2, SDLK_F2 },
{ XK_F3, SDLK_F3 },
{ XK_F4, SDLK_F4 },
{ XK_F5, SDLK_F5 },
{ XK_F6, SDLK_F6 },
{ XK_F7, SDLK_F7 },
{ XK_F8, SDLK_F8 },
{ XK_F9, SDLK_F9 },
{ XK_F10, SDLK_F10 },
{ XK_F11, SDLK_F11 },
{ XK_F12, SDLK_F12 },
{ XK_a, SDLK_a },
{ XK_b, SDLK_b },
{ XK_c, SDLK_c },
{ XK_d, SDLK_d },
{ XK_e, SDLK_e },
{ XK_f, SDLK_f },
{ XK_g, SDLK_g },
{ XK_h, SDLK_h },
{ XK_i, SDLK_i },
{ XK_j, SDLK_j },
{ XK_k, SDLK_k },
{ XK_l, SDLK_l },
{ XK_m, SDLK_m },
{ XK_n, SDLK_n },
{ XK_o, SDLK_o },
{ XK_p, SDLK_p },
{ XK_q, SDLK_q },
{ XK_r, SDLK_r },
{ XK_s, SDLK_s },
{ XK_t, SDLK_t },
{ XK_u, SDLK_u },
{ XK_v, SDLK_v },
{ XK_w, SDLK_w },
{ XK_x, SDLK_x },
{ XK_y, SDLK_y },
{ XK_z, SDLK_z },
};
static void init_lut(void)
{
memset(keysym_lut, 0, sizeof(keysym_lut));
for (unsigned i = 0; i < sizeof(lut_binds) / sizeof(lut_binds[0]); i++)
keysym_lut[lut_binds[i].sdl] = lut_binds[i].x;
}
static void* x_input_init(void)
{
x11_input_t *x11 = calloc(1, sizeof(*x11));
if (!x11)
return NULL;
x11->display = XOpenDisplay(NULL);
if (!x11->display)
{
free(x11);
return NULL;
}
x11->sdl = input_sdl.init();
if (!x11->sdl)
{
free(x11);
return NULL;
}
init_lut();
x11->sdl->use_keyboard = false;
return x11;
}
static bool x_key_pressed(x11_input_t *x11, int key)
{
key = keysym_lut[key];
int keycode = XKeysymToKeycode(x11->display, key);
bool ret = x11->state[keycode >> 3] & (1 << (keycode & 7));
return ret;
}
static bool x_is_pressed(x11_input_t *x11, const struct snes_keybind *binds, unsigned id)
{
for (int i = 0; binds[i].id != -1; i++)
{
if (binds[i].id == id)
return x_key_pressed(x11, binds[i].key);
}
return false;
}
static bool x_bind_button_pressed(void *data, int key)
{
x11_input_t *x11 = data;
bool pressed = x_is_pressed(x11, g_settings.input.binds[0], key);
if (!pressed)
return input_sdl.key_pressed(x11->sdl, key);
return pressed;
}
static int16_t x_input_state(void *data, const struct snes_keybind **binds, bool port, unsigned device, unsigned index, unsigned id)
{
x11_input_t *x11 = data;
bool pressed = false;
switch (device)
{
case SNES_DEVICE_JOYPAD:
pressed = x_is_pressed(x11, binds[(port == SNES_PORT_1) ? 0 : 1], id);
if (!pressed)
pressed = input_sdl.input_state(x11->sdl, binds, port, device, index, id);
return pressed;
case SNES_DEVICE_MULTITAP:
pressed = x_is_pressed(x11, binds[(port == SNES_PORT_2) ? 1 + index : 0], id);
if (!pressed)
pressed = input_sdl.input_state(x11->sdl, binds, port, device, index, id);
return pressed;
default:
return 0;
}
}
static void x_input_free(void *data)
{
x11_input_t *x11 = data;
input_sdl.free(x11->sdl);
XCloseDisplay(x11->display);
free(data);
}
static void x_input_poll(void *data)
{
x11_input_t *x11 = data;
XQueryKeymap(x11->display, x11->state);
input_sdl.poll(x11->sdl);
}
const input_driver_t input_x = {
.init = x_input_init,
.poll = x_input_poll,
.input_state = x_input_state,
.key_pressed = x_bind_button_pressed,
.free = x_input_free,
.ident = "x"
};
Executable
+133
View File
@@ -0,0 +1,133 @@
#ifndef LIBSNES_HPP
#define LIBSNES_HPP
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define SNES_PORT_1 0
#define SNES_PORT_2 1
#define SNES_DEVICE_NONE 0
#define SNES_DEVICE_JOYPAD 1
#define SNES_DEVICE_MULTITAP 2
#define SNES_DEVICE_MOUSE 3
#define SNES_DEVICE_SUPER_SCOPE 4
#define SNES_DEVICE_JUSTIFIER 5
#define SNES_DEVICE_JUSTIFIERS 6
#define SNES_DEVICE_ID_JOYPAD_B 0
#define SNES_DEVICE_ID_JOYPAD_Y 1
#define SNES_DEVICE_ID_JOYPAD_SELECT 2
#define SNES_DEVICE_ID_JOYPAD_START 3
#define SNES_DEVICE_ID_JOYPAD_UP 4
#define SNES_DEVICE_ID_JOYPAD_DOWN 5
#define SNES_DEVICE_ID_JOYPAD_LEFT 6
#define SNES_DEVICE_ID_JOYPAD_RIGHT 7
#define SNES_DEVICE_ID_JOYPAD_A 8
#define SNES_DEVICE_ID_JOYPAD_X 9
#define SNES_DEVICE_ID_JOYPAD_L 10
#define SNES_DEVICE_ID_JOYPAD_R 11
#define SNES_DEVICE_ID_MOUSE_X 0
#define SNES_DEVICE_ID_MOUSE_Y 1
#define SNES_DEVICE_ID_MOUSE_LEFT 2
#define SNES_DEVICE_ID_MOUSE_RIGHT 3
#define SNES_DEVICE_ID_SUPER_SCOPE_X 0
#define SNES_DEVICE_ID_SUPER_SCOPE_Y 1
#define SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER 2
#define SNES_DEVICE_ID_SUPER_SCOPE_CURSOR 3
#define SNES_DEVICE_ID_SUPER_SCOPE_TURBO 4
#define SNES_DEVICE_ID_SUPER_SCOPE_PAUSE 5
#define SNES_DEVICE_ID_JUSTIFIER_X 0
#define SNES_DEVICE_ID_JUSTIFIER_Y 1
#define SNES_DEVICE_ID_JUSTIFIER_TRIGGER 2
#define SNES_DEVICE_ID_JUSTIFIER_START 3
#define SNES_REGION_NTSC 0
#define SNES_REGION_PAL 1
#define SNES_MEMORY_CARTRIDGE_RAM 0
#define SNES_MEMORY_CARTRIDGE_RTC 1
#define SNES_MEMORY_BSX_RAM 2
#define SNES_MEMORY_BSX_PRAM 3
#define SNES_MEMORY_SUFAMI_TURBO_A_RAM 4
#define SNES_MEMORY_SUFAMI_TURBO_B_RAM 5
#define SNES_MEMORY_GAME_BOY_RAM 6
#define SNES_MEMORY_GAME_BOY_RTC 7
#define SNES_MEMORY_WRAM 100
#define SNES_MEMORY_APURAM 101
#define SNES_MEMORY_VRAM 102
#define SNES_MEMORY_OAM 103
#define SNES_MEMORY_CGRAM 104
typedef void (*snes_video_refresh_t)(const uint16_t *data, unsigned width, unsigned height);
typedef void (*snes_audio_sample_t)(uint16_t left, uint16_t right);
typedef void (*snes_input_poll_t)(void);
typedef int16_t (*snes_input_state_t)(bool port, unsigned device, unsigned index, unsigned id);
unsigned snes_library_revision_major(void);
unsigned snes_library_revision_minor(void);
void snes_set_video_refresh(snes_video_refresh_t);
void snes_set_audio_sample(snes_audio_sample_t);
void snes_set_input_poll(snes_input_poll_t);
void snes_set_input_state(snes_input_state_t);
void snes_set_controller_port_device(bool port, unsigned device);
void snes_set_cartridge_basename(const char *basename);
void snes_init(void);
void snes_term(void);
void snes_power(void);
void snes_reset(void);
void snes_run(void);
unsigned snes_serialize_size(void);
bool snes_serialize(uint8_t *data, unsigned size);
bool snes_unserialize(const uint8_t *data, unsigned size);
void snes_cheat_reset(void);
void snes_cheat_set(unsigned index, bool enabled, const char *code);
bool snes_load_cartridge_normal(
const char *rom_xml, const uint8_t *rom_data, unsigned rom_size
);
bool snes_load_cartridge_bsx_slotted(
const char *rom_xml, const uint8_t *rom_data, unsigned rom_size,
const char *bsx_xml, const uint8_t *bsx_data, unsigned bsx_size
);
bool snes_load_cartridge_bsx(
const char *rom_xml, const uint8_t *rom_data, unsigned rom_size,
const char *bsx_xml, const uint8_t *bsx_data, unsigned bsx_size
);
bool snes_load_cartridge_sufami_turbo(
const char *rom_xml, const uint8_t *rom_data, unsigned rom_size,
const char *sta_xml, const uint8_t *sta_data, unsigned sta_size,
const char *stb_xml, const uint8_t *stb_data, unsigned stb_size
);
bool snes_load_cartridge_super_game_boy(
const char *rom_xml, const uint8_t *rom_data, unsigned rom_size,
const char *dmg_xml, const uint8_t *dmg_data, unsigned dmg_size
);
void snes_unload_cartridge(void);
bool snes_get_region(void);
uint8_t* snes_get_memory_data(unsigned id);
unsigned snes_get_memory_size(unsigned id);
#ifdef __cplusplus
}
#endif
#endif
+314
View File
@@ -0,0 +1,314 @@
/* 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 "movie.h"
#include <stdio.h>
#include <stdlib.h>
#include "general.h"
#include "dynamic.h"
#include <assert.h>
// CRC32 implementation taken from BSNES source :)
static const uint32_t crc32_table[256] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
uint32_t crc32_adjust(uint32_t crc32, uint8_t input)
{
return ((crc32 >> 8) & 0x00ffffff) ^ crc32_table[(crc32 ^ input) & 0xff];
}
uint32_t crc32_calculate(const uint8_t *data, unsigned length)
{
uint32_t crc32 = ~0;
for(unsigned i = 0; i < length; i++)
crc32 = crc32_adjust(crc32, data[i]);
return ~crc32;
}
struct bsv_movie
{
FILE *file;
uint8_t *state;
long *frame_pos; // A ring buffer keeping track of many key presses were requested per frame.
size_t frame_mask;
uint16_t current_frame_count;
size_t frame_ptr;
bool playback;
unsigned min_file_pos;
bool first_rewind;
bool did_rewind;
};
#define BSV_MAGIC 0x42535631
#define MAGIC_INDEX 0
#define SERIALIZER_INDEX 1
#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)
{
if (is_little_endian()) // Little-endian
return val;
else
return (val >> 24) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | (val << 24);
}
static inline uint16_t swap_if_big16(uint16_t val)
{
if (is_little_endian())
return val;
else
return (val >> 8) | (val << 8);
}
static bool init_playback(bsv_movie_t *handle, const char *path)
{
handle->playback = true;
handle->file = fopen(path, "rb");
if (!handle->file)
{
SSNES_ERR("Couldn't open BSV file \"%s\" for playback.\n", path);
return false;
}
uint32_t header[4] = {0};
if (fread(header, sizeof(uint32_t), 4, handle->file) != 4)
{
SSNES_ERR("Couldn't read movie header!\n");
return false;
}
if (swap_if_big32(header[MAGIC_INDEX]) != BSV_MAGIC)
{
SSNES_ERR("Movie file is not a valid BSV1 file!\n");
return false;
}
if (swap_if_big32(header[CRC_INDEX]) != g_extern.cart_crc)
{
SSNES_ERR("Cart CRC32s differ! Cannot play back!\n");
return false;
}
uint32_t state_size = swap_if_big32(header[STATE_SIZE_INDEX]);
handle->state = malloc(state_size);
if (!handle->state)
return false;
handle->min_file_pos = sizeof(header) + state_size;
if (fread(handle->state, 1, state_size, handle->file) != state_size)
{
SSNES_ERR("Couldn't read state from movie.\n");
return false;
}
if (psnes_serialize_size() != state_size)
{
SSNES_ERR("Movie format seems to have a different serializer version. Cannot continue.\n");
exit(1);
}
// Unserialize to start playback.
psnes_unserialize(handle->state, state_size);
return true;
}
static bool init_record(bsv_movie_t *handle, const char *path)
{
handle->file = fopen(path, "wb");
if (!handle->file)
{
SSNES_ERR("Couldn't open BSV \"%s\" for recording.\n", path);
return false;
}
uint32_t header[4] = {0};
header[MAGIC_INDEX] = swap_if_big32(BSV_MAGIC);
//header[SERIALIZER_INDEX] = 0; // TODO: Fix this.
header[CRC_INDEX] = swap_if_big32(g_extern.cart_crc);
uint32_t state_size = psnes_serialize_size();
header[STATE_SIZE_INDEX] = swap_if_big32(state_size);
fwrite(header, 4, sizeof(uint32_t), handle->file);
handle->state = malloc(state_size);
if (!handle->state)
return false;
handle->min_file_pos = sizeof(header) + state_size;
psnes_serialize(handle->state, state_size);
fwrite(handle->state, 1, state_size, handle->file);
return true;
}
void bsv_movie_free(bsv_movie_t *handle)
{
if (handle)
{
if (handle->file)
fclose(handle->file);
free(handle->state);
free(handle->frame_pos);
free(handle);
}
}
bool bsv_movie_get_input(bsv_movie_t *handle, int16_t *input)
{
if (fread(input, sizeof(int16_t), 1, handle->file) != 1)
return false;
*input = swap_if_big16(*input);
return true;
}
void bsv_movie_set_input(bsv_movie_t *handle, int16_t input)
{
fwrite(&input, sizeof(int16_t), 1, handle->file);
}
bsv_movie_t *bsv_movie_init(const char *path, enum ssnes_movie_type type)
{
bsv_movie_t *handle = calloc(1, sizeof(*handle));
if (!handle)
return NULL;
if (type == SSNES_MOVIE_PLAYBACK)
{
if (!init_playback(handle, path))
goto error;
}
else if (!init_record(handle, path))
goto error;
if (!(handle->frame_pos = calloc((1 << 20), sizeof(long)))) // Just pick something really large :D
goto error;
handle->frame_mask = (1 << 20) - 1;
return handle;
error:
bsv_movie_free(handle);
return NULL;
}
void bsv_movie_set_frame_start(bsv_movie_t *handle)
{
//fprintf(stderr, "Starting frame: %u, Pos: %ld\n", (unsigned)handle->frame_ptr, ftell(handle->file));
handle->frame_pos[handle->frame_ptr] = ftell(handle->file);
}
void bsv_movie_set_frame_end(bsv_movie_t *handle)
{
//fprintf(stderr, "Frame++\n");
handle->frame_ptr = (handle->frame_ptr + 1) & handle->frame_mask;
handle->first_rewind = !handle->did_rewind;
handle->did_rewind = false;
}
void bsv_movie_frame_rewind(bsv_movie_t *handle)
{
//fprintf(stderr, "Frame--\n");
handle->did_rewind = true;
if (handle->frame_ptr <= 1)
{
handle->frame_ptr = 0;
fseek(handle->file, handle->min_file_pos, SEEK_SET);
}
else
{
handle->frame_ptr = (handle->frame_ptr - (handle->first_rewind ? 1 : 2)) & handle->frame_mask;
fseek(handle->file, handle->frame_pos[handle->frame_ptr], SEEK_SET);
}
if (ftell(handle->file) <= (long)handle->min_file_pos)
{
if (!handle->playback)
{
fseek(handle->file, 4 * sizeof(uint32_t), SEEK_SET);
psnes_serialize(handle->state, psnes_serialize_size());
fwrite(handle->state, 1, psnes_serialize_size(), handle->file);
}
else
fseek(handle->file, handle->min_file_pos, SEEK_SET);
}
}
+51
View File
@@ -0,0 +1,51 @@
/* 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_MOVIE_H
#define __SSNES_MOVIE_H
#include <stdint.h>
#include <stdbool.h>
typedef struct bsv_movie bsv_movie_t;
enum ssnes_movie_type
{
SSNES_MOVIE_PLAYBACK,
SSNES_MOVIE_RECORD,
};
bsv_movie_t *bsv_movie_init(const char *path, enum ssnes_movie_type type);
// Playback
bool bsv_movie_get_input(bsv_movie_t *handle, int16_t *input);
// Recording
void bsv_movie_set_input(bsv_movie_t *handle, int16_t input);
// Used for rewinding while playback/record.
void bsv_movie_set_frame_start(bsv_movie_t *handle); // Debugging purposes.
void bsv_movie_set_frame_end(bsv_movie_t *handle);
void bsv_movie_frame_rewind(bsv_movie_t *handle);
void bsv_movie_free(bsv_movie_t *handle);
uint32_t crc32_calculate(const uint8_t *data, unsigned length);
uint32_t crc32_adjust(uint32_t crc32, uint8_t input);
#endif
+701
View File
@@ -0,0 +1,701 @@
/* 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 "netplay.h"
#include "general.h"
#include "autosave.h"
#include "dynamic.h"
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <assert.h>
#ifdef _WIN32
#define _WIN32_WINNT 0x0501
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
// Woohoo, Winsock has headers from the STONE AGE! :D
#define close(x) closesocket(x)
#define CONST_CAST (const char*)
#define NONCONST_CAST (char*)
#else
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h>
#define CONST_CAST
#define NONCONST_CAST
#endif
#define PREV_PTR(x) ((x) == 0 ? handle->buffer_size - 1 : (x) - 1)
#define NEXT_PTR(x) ((x + 1) % handle->buffer_size)
struct delta_frame
{
uint8_t *state;
uint16_t real_input_state;
uint16_t simulated_input_state;
bool is_simulated;
uint16_t self_state;
bool used_real;
};
#define UDP_FRAME_PACKETS 8
struct netplay
{
struct snes_callbacks cbs;
int fd; // TCP connection for state sending, etc. Could perhaps be used for messaging later on. :)
int udp_fd; // UDP connection for game state updates.
unsigned port; // Which port is governed by netplay?
bool has_connection;
struct delta_frame *buffer;
size_t buffer_size;
size_t self_ptr; // Ptr where we are now.
size_t other_ptr; // Points to the last reliable state that self ever had.
size_t read_ptr; // Ptr to where we are reading. Generally, other_ptr <= read_ptr <= self_ptr.
size_t tmp_ptr; // A temporary pointer used on replay.
size_t state_size;
size_t is_replay; // Are we replaying old frames?
bool can_poll; // We don't want to poll several times on a frame.
uint32_t packet_buffer[UDP_FRAME_PACKETS * 2]; // To compat UDP packet loss we also send old data along with the packets.
uint32_t frame_count;
uint32_t read_frame_count;
struct addrinfo *addr;
struct sockaddr_storage their_addr;
bool has_client_addr;
};
void input_poll_net(void)
{
if (!netplay_should_skip(g_extern.netplay) && netplay_can_poll(g_extern.netplay))
netplay_poll(g_extern.netplay);
}
void video_frame_net(const uint16_t *data, unsigned width, unsigned height)
{
if (!netplay_should_skip(g_extern.netplay))
netplay_callbacks(g_extern.netplay)->frame_cb(data, width, height);
}
void audio_sample_net(uint16_t left, uint16_t right)
{
if (!netplay_should_skip(g_extern.netplay))
netplay_callbacks(g_extern.netplay)->sample_cb(left, right);
}
int16_t input_state_net(bool port, unsigned device, unsigned index, unsigned id)
{
if (netplay_is_alive(g_extern.netplay))
return netplay_input_state(g_extern.netplay, port, device, index, id);
else
return netplay_callbacks(g_extern.netplay)->state_cb(port, device, index, id);
}
static bool init_tcp_socket(netplay_t *handle, const char *server, uint16_t port)
{
struct addrinfo hints, *res = NULL;
memset(&hints, 0, sizeof(hints));
#ifdef _WIN32 // Lolol, no AF_UNSPEC, wtf.
hints.ai_family = AF_INET;
#else
hints.ai_family = AF_UNSPEC;
#endif
hints.ai_socktype = SOCK_STREAM;
if (!server)
hints.ai_flags = AI_PASSIVE;
char port_buf[16];
snprintf(port_buf, sizeof(port_buf), "%hu", (unsigned short)port);
if (getaddrinfo(server, port_buf, &hints, &res) < 0)
return false;
if (!res)
return false;
handle->fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (handle->fd < 0)
{
SSNES_ERR("Failed to init socket...\n");
if (res)
freeaddrinfo(res);
return false;
}
if (server)
{
if (connect(handle->fd, res->ai_addr, res->ai_addrlen) < 0)
{
SSNES_ERR("Failed to connect to server.\n");
close(handle->fd);
freeaddrinfo(res);
return false;
}
}
else
{
int yes = 1;
setsockopt(handle->fd, SOL_SOCKET, SO_REUSEADDR, CONST_CAST &yes, sizeof(int));
if (bind(handle->fd, res->ai_addr, res->ai_addrlen) < 0 || listen(handle->fd, 1) < 0)
{
SSNES_ERR("Failed to bind socket.\n");
close(handle->fd);
freeaddrinfo(res);
return false;
}
int new_fd = accept(handle->fd, NULL, NULL);
if (new_fd < 0)
{
SSNES_ERR("Failed to accept socket.\n");
close(handle->fd);
freeaddrinfo(res);
return false;
}
close(handle->fd);
handle->fd = new_fd;
}
freeaddrinfo(res);
return true;
}
static bool init_udp_socket(netplay_t *handle, const char *server, uint16_t port)
{
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
#ifdef _WIN32 // Lolol, no AF_UNSPEC, wtf.
hints.ai_family = AF_INET;
#else
hints.ai_family = AF_UNSPEC;
#endif
hints.ai_socktype = SOCK_DGRAM;
if (!server)
hints.ai_flags = AI_PASSIVE;
char port_buf[16];
snprintf(port_buf, sizeof(port_buf), "%hu", (unsigned short)port);
if (getaddrinfo(server, port_buf, &hints, &handle->addr) < 0)
return false;
if (!handle->addr)
return false;
handle->udp_fd = socket(handle->addr->ai_family, handle->addr->ai_socktype, handle->addr->ai_protocol);
if (handle->udp_fd < 0)
{
SSNES_ERR("Failed to init socket...\n");
return false;
}
if (!server)
{
// Note sure if we have to do this for UDP, but hey :)
int yes = 1;
setsockopt(handle->udp_fd, SOL_SOCKET, SO_REUSEADDR, CONST_CAST &yes, sizeof(int));
if (bind(handle->udp_fd, handle->addr->ai_addr, handle->addr->ai_addrlen) < 0)
{
SSNES_ERR("Failed to bind socket.\n");
close(handle->udp_fd);
}
freeaddrinfo(handle->addr);
handle->addr = NULL;
}
return true;
}
static bool init_socket(netplay_t *handle, const char *server, uint16_t port)
{
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
WSACleanup();
return false;
}
#else
signal(SIGPIPE, SIG_IGN); // Do not like SIGPIPE killing our app :(
#endif
if (!init_tcp_socket(handle, server, port))
return false;
if (!init_udp_socket(handle, server, port))
return false;
return true;
}
bool netplay_can_poll(netplay_t *handle)
{
return handle->can_poll;
}
static bool send_info(netplay_t *handle)
{
uint32_t header[3] = { htonl(g_extern.cart_crc), htonl(psnes_serialize_size()), htonl(psnes_get_memory_size(SNES_MEMORY_CARTRIDGE_RAM)) };
if (send(handle->fd, CONST_CAST header, sizeof(header), 0) != sizeof(header))
return false;
// Get SRAM data from Player 1 :)
uint8_t *sram = psnes_get_memory_data(SNES_MEMORY_CARTRIDGE_RAM);
unsigned sram_size = psnes_get_memory_size(SNES_MEMORY_CARTRIDGE_RAM);
while (sram_size > 0)
{
ssize_t ret = recv(handle->fd, NONCONST_CAST sram, sram_size, 0);
if (ret <= 0)
{
SSNES_ERR("Failed to receive SRAM data from host.\n");
return false;
}
sram += ret;
sram_size -= ret;
}
return true;
}
static bool get_info(netplay_t *handle)
{
uint32_t header[3];
if (recv(handle->fd, NONCONST_CAST header, sizeof(header), 0) != sizeof(header))
{
SSNES_ERR("Failed to receive header from client.\n");
return false;
}
if (g_extern.cart_crc != ntohl(header[0]))
{
SSNES_ERR("Cart CRC32s differ! Cannot use different games!\n");
return false;
}
if (psnes_serialize_size() != ntohl(header[1]))
{
SSNES_ERR("Serialization sizes differ, make sure you're using exact same libsnes implementations!\n");
return false;
}
if (psnes_get_memory_size(SNES_MEMORY_CARTRIDGE_RAM) != ntohl(header[2]))
{
SSNES_ERR("Cartridge SRAM sizes do not correspond!\n");
return false;
}
// Send SRAM data to our Player 2 :)
const uint8_t *sram = psnes_get_memory_data(SNES_MEMORY_CARTRIDGE_RAM);
unsigned sram_size = psnes_get_memory_size(SNES_MEMORY_CARTRIDGE_RAM);
while (sram_size > 0)
{
ssize_t ret = send(handle->fd, CONST_CAST sram, sram_size, 0);
if (ret <= 0)
{
SSNES_ERR("Failed to send SRAM data to client.\n");
return false;
}
sram += ret;
sram_size -= ret;
}
return true;
}
static void init_buffers(netplay_t *handle)
{
handle->buffer = calloc(handle->buffer_size, sizeof(*handle->buffer));
handle->state_size = psnes_serialize_size();
for (unsigned i = 0; i < handle->buffer_size; i++)
{
handle->buffer[i].state = malloc(handle->state_size);
handle->buffer[i].is_simulated = true;
}
}
netplay_t *netplay_new(const char *server, uint16_t port, unsigned frames, const struct snes_callbacks *cb)
{
netplay_t *handle = calloc(1, sizeof(*handle));
if (!handle)
return NULL;
handle->cbs = *cb;
handle->port = server ? 0 : 1;
if (!init_socket(handle, server, port))
{
free(handle);
return NULL;
}
if (server)
{
if (!send_info(handle))
{
close(handle->fd);
free(handle);
return NULL;
}
}
else
{
if (!get_info(handle))
{
close(handle->fd);
free(handle);
return NULL;
}
}
handle->buffer_size = frames + 1;
init_buffers(handle);
handle->has_connection = true;
return handle;
}
bool netplay_is_alive(netplay_t *handle)
{
return handle->has_connection;
}
static bool send_chunk(netplay_t *handle)
{
const struct sockaddr *addr = NULL;
if (handle->addr)
addr = handle->addr->ai_addr;
else if (handle->has_client_addr)
addr = (const struct sockaddr*)&handle->their_addr;
if (addr)
{
if (sendto(handle->udp_fd, CONST_CAST handle->packet_buffer, sizeof(handle->packet_buffer), 0, addr, sizeof(struct sockaddr)) != sizeof(handle->packet_buffer))
{
SSNES_WARN("Netplay connection hung up. Will continue without netplay.\n");
handle->has_connection = false;
return false;
}
}
return true;
}
#define MAX_RETRIES 64
#define RETRY_MS 500
static int poll_input(netplay_t *handle, bool block)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(handle->udp_fd, &fds);
FD_SET(handle->fd, &fds);
int max_fd = (handle->fd > handle->udp_fd ? handle->fd : handle->udp_fd) + 1;
struct timeval tv = {
.tv_sec = 0,
.tv_usec = block ? (RETRY_MS * 1000) : 0
};
int i = 0;
do
{
if (select(max_fd, &fds, NULL, NULL, &tv) < 0)
return -1;
// Somewhat hacky, but we aren't using the TCP connection for anything useful atm.
// Will probably add some proper messaging system here later.
if (FD_ISSET(handle->fd, &fds))
return -1;
if (FD_ISSET(handle->udp_fd, &fds))
return 1;
if (block && !send_chunk(handle))
{
SSNES_WARN("Netplay connection hung up. Will continue without netplay.\n");
handle->has_connection = false;
return -1;
}
if (block)
SSNES_LOG("Network lag of %d ms, resending packet... Attempt %d of %d ...\n", RETRY_MS, i, MAX_RETRIES);
// Seems to be necessary on Win32.
FD_ZERO(&fds);
FD_SET(handle->udp_fd, &fds);
FD_SET(handle->fd, &fds);
} while ((i++ < MAX_RETRIES) && block);
if (block)
return -1;
return 0;
}
// Grab our own input state and send this over the network.
static bool get_self_input_state(netplay_t *handle)
{
struct delta_frame *ptr = &handle->buffer[handle->self_ptr];
uint32_t state = 0;
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 (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;
}
}
memmove(handle->packet_buffer, handle->packet_buffer + 2, sizeof (handle->packet_buffer) - 2 * sizeof(uint32_t));
handle->packet_buffer[(UDP_FRAME_PACKETS - 1) * 2] = htonl(handle->frame_count);
handle->packet_buffer[(UDP_FRAME_PACKETS - 1) * 2 + 1] = htonl(state);
if (!send_chunk(handle))
{
SSNES_WARN("Netplay connection hung up. Will continue without netplay.\n");
handle->has_connection = false;
return false;
}
ptr->self_state = state;
handle->self_ptr = NEXT_PTR(handle->self_ptr);
return true;
}
// TODO: Somewhat better prediction. :P
static void simulate_input(netplay_t *handle)
{
size_t ptr = PREV_PTR(handle->self_ptr);
size_t prev = PREV_PTR(handle->read_ptr);
handle->buffer[ptr].simulated_input_state = handle->buffer[prev].real_input_state;
handle->buffer[ptr].is_simulated = true;
handle->buffer[ptr].used_real = false;
}
static void parse_packet(netplay_t *handle, uint32_t *buffer, unsigned size)
{
for (unsigned i = 0; i < size * 2; i++)
buffer[i] = ntohl(buffer[i]);
for (unsigned i = 0; i < size && handle->read_frame_count <= handle->frame_count; i++)
{
uint32_t frame = buffer[2 * i];
uint32_t state = buffer[2 * i + 1];
if (frame == handle->read_frame_count)
{
handle->buffer[handle->read_ptr].is_simulated = false;
handle->buffer[handle->read_ptr].real_input_state = state;
handle->read_ptr = NEXT_PTR(handle->read_ptr);
handle->read_frame_count++;
}
}
}
static bool receive_data(netplay_t *handle, uint32_t *buffer, size_t size)
{
socklen_t addrlen = sizeof(handle->their_addr);
if (recvfrom(handle->udp_fd, NONCONST_CAST buffer, size, 0, (struct sockaddr*)&handle->their_addr, &addrlen) != size)
return false;
handle->has_client_addr = true;
return true;
}
// Poll network to see if we have anything new. If our network buffer is full, we simply have to block for new input data.
bool netplay_poll(netplay_t *handle)
{
if (!handle->has_connection)
return false;
handle->can_poll = false;
if (!get_self_input_state(handle))
return false;
// We skip reading the first frame so the host has a change to grab our host info so we don't block forever :')
if (handle->frame_count == 0)
{
handle->buffer[0].used_real = true;
handle->buffer[0].is_simulated = false;
handle->buffer[0].real_input_state = 0;
handle->read_ptr = NEXT_PTR(handle->read_ptr);
handle->read_frame_count++;
return true;
}
//fprintf(stderr, "Other ptr: %lu, Read ptr: %lu, Self ptr: %lu\n", handle->other_ptr, handle->read_ptr, handle->self_ptr);
if (handle->buffer_size > 1)
{
assert(handle->other_ptr != handle->self_ptr);
assert(handle->read_ptr != handle->self_ptr);
}
assert(handle->other_ptr == handle->read_ptr);
// We might have reached the end of the buffer, where we simply have to block.
int res = poll_input(handle, handle->other_ptr == NEXT_PTR(handle->self_ptr));
if (res == -1)
{
handle->has_connection = false;
SSNES_WARN("Netplay connection timed out. Will continue without netplay.\n");
return false;
}
if (res == 1)
{
uint32_t first_read = handle->read_frame_count;
do
{
uint32_t buffer[UDP_FRAME_PACKETS * 2];
if (!receive_data(handle, buffer, sizeof(buffer)))
{
SSNES_WARN("Netplay connection hung up. Will continue without netplay.\n");
handle->has_connection = false;
return false;
}
parse_packet(handle, buffer, UDP_FRAME_PACKETS);
} while ((handle->read_frame_count <= handle->frame_count) &&
poll_input(handle, (handle->other_ptr == NEXT_PTR(handle->self_ptr)) &&
(first_read == handle->read_frame_count)) == 1);
}
else
{
// Cannot allow this. Should not happen though.
if (NEXT_PTR(handle->self_ptr) == handle->other_ptr)
{
SSNES_WARN("Netplay connection hung up. Will continue without netplay.\n");
return false;
}
}
if (handle->read_ptr != handle->self_ptr)
simulate_input(handle);
else
handle->buffer[PREV_PTR(handle->self_ptr)].used_real = true;
return true;
}
int16_t netplay_input_state(netplay_t *handle, bool port, unsigned device, unsigned index, unsigned id)
{
uint16_t input_state = 0;
size_t ptr = 0;
if (handle->is_replay)
ptr = handle->tmp_ptr;
else
ptr = PREV_PTR(handle->self_ptr);
if ((port ? 1 : 0) == handle->port)
{
if (handle->buffer[ptr].is_simulated)
input_state = handle->buffer[ptr].simulated_input_state;
else
input_state = handle->buffer[ptr].real_input_state;
}
else
input_state = handle->buffer[ptr].self_state;
return ((1 << id) & input_state) ? 1 : 0;
}
void netplay_free(netplay_t *handle)
{
close(handle->fd);
close(handle->udp_fd);
for (unsigned i = 0; i < handle->buffer_size; i++)
free(handle->buffer[i].state);
free(handle->buffer);
if (handle->addr)
freeaddrinfo(handle->addr);
free(handle);
}
const struct snes_callbacks* netplay_callbacks(netplay_t *handle)
{
return &handle->cbs;
}
bool netplay_should_skip(netplay_t *handle)
{
return handle->is_replay && handle->has_connection;
}
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.
void netplay_post_frame(netplay_t *handle)
{
handle->frame_count++;
// Nothing to do...
if (handle->other_ptr == handle->read_ptr)
return;
// Skip ahead if we predicted correctly. Skip until our simulation failed.
while (handle->other_ptr != handle->read_ptr)
{
struct delta_frame *ptr = &handle->buffer[handle->other_ptr];
if ((ptr->simulated_input_state != ptr->real_input_state) && !ptr->used_real)
break;
handle->other_ptr = NEXT_PTR(handle->other_ptr);
}
if (handle->other_ptr != handle->read_ptr)
{
// Replay frames
handle->is_replay = true;
handle->tmp_ptr = handle->other_ptr;
psnes_unserialize(handle->buffer[handle->other_ptr].state, handle->state_size);
while (handle->tmp_ptr != handle->self_ptr)
{
psnes_serialize(handle->buffer[handle->tmp_ptr].state, handle->state_size);
lock_autosave();
psnes_run();
unlock_autosave();
handle->tmp_ptr = NEXT_PTR(handle->tmp_ptr);
}
handle->other_ptr = handle->read_ptr;
handle->is_replay = false;
}
}
+60
View File
@@ -0,0 +1,60 @@
/* 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_NETPLAY_H
#define __SSNES_NETPLAY_H
#include <stdint.h>
#include <stdbool.h>
#include <libsnes.hpp>
void input_poll_net(void);
int16_t input_state_net(bool port, unsigned device, unsigned index, unsigned id);
void video_frame_net(const uint16_t *data, unsigned width, unsigned height);
void audio_sample_net(uint16_t left, uint16_t right);
typedef struct netplay netplay_t;
struct snes_callbacks
{
snes_video_refresh_t frame_cb;
snes_audio_sample_t sample_cb;
snes_input_state_t state_cb;
};
// Creates a new netplay handle. A NULL host means we're hosting (player 1). :)
netplay_t *netplay_new(const char *server, uint16_t port, unsigned frames, const struct snes_callbacks *cb);
void netplay_free(netplay_t *handle);
// Call this before running snes_run()
void netplay_pre_frame(netplay_t *handle);
// Call this after running snes_run()
void netplay_post_frame(netplay_t *handle);
// Checks if input port/index is controlled by netplay or not.
bool netplay_is_alive(netplay_t *handle);
bool netplay_poll(netplay_t *handle);
int16_t netplay_input_state(netplay_t *handle, bool port, unsigned device, unsigned index, unsigned id);
// If we're fast-forward replaying to resync, check if we should actually show frame.
bool netplay_should_skip(netplay_t *handle);
bool netplay_can_poll(netplay_t *handle);
const struct snes_callbacks* netplay_callbacks(netplay_t *handle);
#endif
File renamed without changes.
+47 -12
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,43 +16,72 @@ 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 ALSA -lasound snd_pcm_open
check_lib DYLIB $DYLIB dlopen
check_lib NETPLAY -lc socket
check_pkgconf ALSA alsa
check_header OSS sys/soundcard.h
check_lib AL -lopenal alcOpenDevice
check_lib RSOUND -lrsound rsd_init
check_lib ROAR -lroar roar_vs_new
check_lib JACK -ljack jack_client_open
check_header OSS_BSD soundcard.h
check_lib OSS_LIB -lossaudio
if [ "$OS" = "Darwin" ]; then
check_lib AL "-framework OpenAL" alcOpenDevice
else
check_lib AL -lopenal alcOpenDevice
fi
if [ "$OS" = "Darwin" ]; then
check_lib FBO "-framework OpenGL" glFramebufferTexture2D
else
check_lib FBO -lGL glFramebufferTexture2D
fi
check_pkgconf RSOUND rsound 1.1
check_pkgconf ROAR libroar
check_pkgconf JACK jack 0.120.1
check_pkgconf PULSE libpulse
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_critical SRC "Cannot find libsamplerate."
check_lib DYNAMIC -ldl dlopen
check_lib DYNAMIC $DYLIB dlopen
check_pkgconf FREETYPE freetype2
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 SDL FILTER CG XML DYNAMIC FFMPEG AVCODEC AVFORMAT AVCORE AVUTIL SWSCALE SRC CONFIGFILE FREETYPE"
VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK PULSE SDL DYLIB CG XML SDL_IMAGE DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE SRC CONFIGFILE FREETYPE XVIDEO NETPLAY FBO STRL PYTHON"
create_config_make config.mk $VARS
create_config_header config.h $VARS
+9 -2
View File
@@ -1,7 +1,7 @@
. qb/qb.params.sh
PACKAGE_NAME=ssnes
PACKAGE_VERSION=0.2
PACKAGE_VERSION=0.6.2
# Adds a command line opt to ./configure --help
# $1: Variable (HAVE_ALSA, HAVE_OSS, etc)
@@ -10,14 +10,21 @@ PACKAGE_VERSION=0.2
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" no
add_command_line_enable DYLIB "Enable dynamic loading support" auto
add_command_line_enable NETPLAY "Enable netplay support" auto
add_command_line_enable SRC "Enable libsamplerate support" no
add_command_line_enable CONFIGFILE "Disable support for config file" yes
add_command_line_enable CG "Enable Cg shader support" auto
add_command_line_enable XML "Enable bSNES-style XML shader support" auto
add_command_line_enable FBO "Enable render-to-texture (FBO) support" auto
add_command_line_enable ALSA "Enable ALSA support" auto
add_command_line_enable OSS "Enable OSS support" auto
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 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
+15 -14
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 -o`"
if [ ! -z "`echo $unamestr | grep -i Linux`" ]; then
unamestr="`uname -a`"
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
@@ -50,16 +51,16 @@ if [ "$USE_LANG_CXX" = "yes" ]; then
CXX=`which g++ c++ 2> /dev/null | grep ^/ | head -n 1`
fi
if [ -z $CXX ]; then
echo "Could not find C compiler in path. Exiting ..."
echo "Could not find C++ compiler in path. Exiting ..."
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
+63 -25
View File
@@ -41,20 +41,24 @@ 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
extralibs="$4"
$CC -o $TEMP_EXE $TEMP_C $INCLUDE_DIRS $LIBRARY_DIRS $extralibs $2 2>/dev/null >/dev/null && answer=yes && eval HAVE_$1=yes
$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,35 +71,70 @@ 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
extralibs="$4"
$CXX -o $TEMP_EXE $TEMP_CXX $INCLUDE_DIRS $LIBRARY_DIRS $extralibs $2 2>/dev/null >/dev/null && answer=yes && eval HAVE_$1=yes
$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
+13 -10
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"
@@ -95,7 +94,7 @@ parse_input()
case "$1" in
--prefix=*)
prefix="`echo $1 | sed -e 's|^--prefix=\(\S\S*\)$|\1|' -e 's|\(\S\S*\)/$|\1|'`"
prefix="`echo $1 | sed -e 's|^--prefix=||' -e 's|^\(.*\)/$|\1|'`"
if [ "$prefix" != "$1" ]; then
PREFIX="$prefix"
@@ -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
+339 -155
View File
@@ -2,6 +2,7 @@
#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 <stdint.h>
@@ -10,10 +11,11 @@
#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;
@@ -25,11 +27,11 @@ struct video_info
AVFormatContext *format;
struct SwsContext *sws_ctx;
} video;
struct audio_info
{
bool enabled;
AVCodecContext *codec;
int16_t *buffer;
@@ -39,6 +41,8 @@ struct audio_info
void *outbuf;
size_t outbuf_size;
int pix_fmt;
} audio;
struct muxer_info
@@ -55,158 +59,172 @@ 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 = 200000;
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;
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 = 2000000;
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 * sizeof(int16_t) * 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 +237,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 +261,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,25 +285,93 @@ 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, PIX_FMT_RGB555LE,
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->params.out_width, 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;
@@ -295,8 +381,6 @@ int ffemu_push_video(ffemu_t *handle, const struct ffemu_video_data *data)
if (outsize < 0)
return -1;
sws_freeContext(conv_ctx);
AVPacket pkt;
av_init_packet(&pkt);
pkt.stream_index = handle->muxer.vstream->index;
@@ -319,16 +403,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 +420,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 +431,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 +451,146 @@ 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.
uint16_t *video_buf = av_malloc(handle->params.fb_width * handle->params.fb_height * sizeof(uint16_t));
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;
uint16_t *video_buf = av_malloc(ff->params.fb_width * ff->params.fb_height * sizeof(uint16_t));
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;
}
+5 -17
View File
@@ -11,30 +11,23 @@ extern "C" {
// Available video codecs
typedef enum ffemu_video_codec
{
FFEMU_VIDEO_NONE,
FFEMU_VIDEO_H264,
FFEMU_VIDEO_MPEG4,
FFEMU_VIDEO_FFV1,
} 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;
@@ -53,14 +46,12 @@ struct ffemu_params
// 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,9 +62,6 @@ 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;
Loaded 100 of 115 files, more files were not shown because too many files have changed in this diff. Show more